There are questions remain, We'll search for the answers together. But one thing we known for sure,the future is not set!

【原创文章】从代码深探ectouch商品销售为0的原因及详解索源追码思路

ectouch 百蔬君 9992℃ 已收录 2评论

ectouch是模板堂出品的基于ecshop的手机触屏免费开源移动商城,是ecshop,ectouch和ecmobile三位好基友中的一位。作为ecshop的好基友,和最近炒得火热深陷抄袭丑闻,出品粗制乱造忽悠用户ecshop APP的某8ecshop相比,深得ecshop的真传,其ectouche程序明显比较上档次。在这里得承认,几家ecshop大佬的前台美工和UI设计真的都相当漂亮,真假难分,伯仲难分,实在佩服啊,作为一般的用户实在很难区分,因为从网站架构和美工看都是技术牛,那些产品的文案与设计实在都精美极了。

ectouch目前公开下载的只有utf-8版,这个原因促使我把整个ecshop从gbk转码成utf,详情请看文章:ecshop由GBK完全转码为UTF运行无错完美解决方法分享。在这里需要说明的是就算ecshop是gbk,我安装utf版的ectouch一样正常使用,没有出现乱码等问题。因为他们没有交互,独立文件夹来的,没有引用ecshop的文件,连数据库都是独立建表。下图是我的ectouch首页,

 

Snap1

 

从界面来看是非常漂亮的,但是从代码优化来看,个人觉得引用太多css和js文件,代码优化应当是不够的。

Snap2

Snap3

输了一下,仅jquery就引用了4个,在网页中还插入了很多javescript,整合这些代码方面应当是没有仔细整理的,就是代码堆砌和功能实现就完了。代码的优化对于网页的加载和亲和性应当都是很重要的。由于ectouch是新时代的产物,那么在兼容模式下或者低版本情况下,下面的导航是会乱码的。不过没有仔细看原因,应当是某项属性不被老浏览器支持。

Snap4

 

下面说我今天想说的重点。

 

每一款产品都有一个销量是多少的展示,我想每位站长都想要展示这个品种总共卖了多少件,这样可以吸引顾客,引起他们的购物欲,在坚持不刷单,数据不造假的情况下,我觉得这个是非常重要的。一个偶然的机会我发现首页所有的产品销量都是零,这和pc端没有同步啊,后来我发现分类页的销量有时候有,有时候没有,但是产品页中的销量是安装ectouch后的购买记录,而首页和分类页中显示的销量就算是安装ectouch后交易成功的销量显示也是不正确的。这对于一个不刷单,但是同时又想展示真实销量的我来说难以接受,于是我决定寻找这个原因。

在这里分享一下我找问题的方法,就是从前台的元素开始。找到前台显示“销售0件”的地方,右键,选择“查看元素”

Snap5

看到了“销量0件”前后的代码了吧,我们所需要的就是<span class=”pull-right”>或者class=”pull-right”这一个特征值,然后全站查找,熟悉ecshop架构或者主题构造的就会知道,我们的重点关注目标就是相关的dwt文件和lbi文件。

Snap6 Snap7

 

熟悉ecshop架构的很快就会意识到,default\goods.dwt就是商品详情页中调用求销量函数的地方,而default\library\goods_list.lbi就是首页和分类页中显示产品销量的地方,从这两个地方查找原因就可以得到我们想要的结果。

goods.dwt中相关代码为

<span class="pull-left">{$lang.amount}:<b id="ECS_GOODS_AMOUNT" class="ect-colory"></b></span><span class="pull-right">{$lang.sort_sales}:{$sales_count} {$lang.piece}</span>

$lang开头的是语言文字,$sales_count才是判断销量的函数。全站搜索“sales_count”,之后在结果在查找和goods相关的文件,经过一阵检查和扫描,在\include\apps\default\controller\UserController.class.php中发现了踪迹,代码为

 $this->assign(‘now_time’, gmtime());
$this->assign(‘sales_count’, model(‘GoodsBase’)->get_sales_count($this->goods_id));

从这个代码可以看到sales_count是由get_sales_count这个函数来查询销售情况的,这正是我们需要的源头,一定要找到sql语句,才能弄明白是怎么回事。

全站搜索“function get_sales_count”,发现存在于\include\base\model\GoodsBaseModel.class.php中,代码如下

function get_sales_count($goods_id) {
return get_goods_count($goods_id);
}

继续搜索“ function get_goods_count”,终于在include\apps\default\common\function.php中找到了相关的SQL代码。

function get_goods_count($goods_id)
{
/* 统计时间段 */
$period = C('top10_time');
$ext = '';
if ($period == 1) {// 一年
$ext = "AND o.add_time >'" . local_strtotime('-1 years') . "'";
} elseif ($period == 2) {// 半年
$ext = "AND o.add_time > '" . local_strtotime('-6 months') . "'";
} elseif ($period == 3) {// 三个月
$ext = " AND o.add_time > '" . local_strtotime('-3 months') . "'";
} elseif ($period == 4) {// 一个月
$ext = " AND o . add_time > '" . local_strtotime(' - 1 months') . "'";
}
/* 查询该商品销量 */
$sql = 'SELECT IFNULL(SUM(g.goods_number), 0) as count ' .
'FROM '. M()->pre .'order_info AS o, '. M()->pre .'order_goods AS g ' .
"WHERE o . order_id = g . order_id " .
" AND o . order_status = '" . OS_CONFIRMED . "'" .
" AND o . shipping_status " . db_create_in(array(SS_SHIPPED, SS_RECEIVED)) .
" AND o . pay_status " . db_create_in(array(PS_PAYED, PS_PAYING)) .
" AND g . goods_id = '$goods_id'";

$result = M()->getRow($sql);
return $result['count'];
}</blockquote>
修改为
<blockquote>function get_goods_count($goods_id)
{
/* 统计时间段 */
$period = C('top10_time');
$ext = '';
if ($period == 1) {// 一年
$ext = "AND o.add_time >'" . local_strtotime('-1 years') . "'";
} elseif ($period == 2) {// 半年
$ext = "AND o.add_time > '" . local_strtotime('-6 months') . "'";
} elseif ($period == 3) {// 三个月
$ext = " AND o.add_time > '" . local_strtotime('-3 months') . "'";
} elseif ($period == 4) {// 一个月
$ext = " AND o . add_time > '" . local_strtotime(' - 1 months') . "'";
}
/* 查询该商品销量
$sql = 'SELECT IFNULL(SUM(g.goods_number), 0) as count ' .
'FROM '. M()->pre .'order_info AS o, '. M()->pre .'order_goods AS g ' .
"WHERE o . order_id = g . order_id " .
" AND o . order_status = '" . OS_CONFIRMED . "'" .
" AND o . shipping_status " . db_create_in(array(SS_SHIPPED, SS_RECEIVED)) .
" AND o . pay_status " . db_create_in(array(PS_PAYED, PS_PAYING)) .
" AND g . goods_id = '$goods_id'";*/
$sql = 'SELECT IFNULL(SUM(g.goods_number), 0) as count ' .
'FROM '. M()->pre .'order_info AS o, '. M()->pre .'order_goods AS g ' .
"WHERE o . order_id = g . order_id " .
" AND (o.order_status=5 or o.order_status=1)" .
" AND g . goods_id = '$goods_id'";

$result = M()->getRow($sql);
return $result['count'];
}

这时候,我们的产品详情页就会显示所有的销售数据了。

在前面全站搜索“sales_count的时候,发现了与UserController.class.php类似的文件CategoryController.class.php中有相似代码。


$sales_volume = (int) $row['sales_volume'];
if (mt_rand(0, 3) == 3){
$sales_volume = model('GoodsBase')->get_sales_count($row['goods_id']);
$sql = 'REPLACE INTO ' . $this->model->pre . 'touch_goods(`goods_id`, `sales_volume`) VALUES('. $row['goods_id'] .', '.$sales_volume.')';
$this->model->query($sql);
}
if ($row['promote_price'] > 0) {
$promote_price = bargain_price($row['promote_price'], $row['promote_start_date'], $row['promote_end_date']);
} else {
$promote_price = 0;
}
省略若干代码
$arr[$row['goods_id']]['sales_count'] = $sales_volume;
$arr[$row['goods_id']]['sc'] = model('GoodsBase')->get_goods_collect($row['goods_id']);
$arr[$row['goods_id']]['mysc'] = 0;

 

但是这个代码是先通过get_sales_count查询交给$sales_volume,然后在赋值给$arr[$row[‘goods_id’]][‘sales_count’],按理说我们分类页就应当显示销售数据正确啊,但是为何时有时无呢。仔细查看代码才发现,问题就出在

$sales_volume = (int) $row['sales_volume'];
if (mt_rand(0, 3) == 3){
$sales_volume = model('GoodsBase')->get_sales_count($row['goods_id']);
$sql = 'REPLACE INTO ' . $this->model->pre . 'touch_goods(`goods_id`, `sales_volume`) VALUES('. $row['goods_id'] .', '.$sales_volume.')';
$this->model->query($sql);
}

这段代码的意思首先是从ectouch的表中读取该产品的销量,要知道刚刚安装的ectouche这个表中是没有任何数据的,这也是有人说ectouch必须要购买一次才会正确显示销量的原因。购买一次,从ecshop查询销量,之后存入sales_volume字段,那么在这里$sales_volume才会有数据。但是从下面的代码应当可以看出,他不是购买一次才会显示正确的销量吧,他是随机的看心情决定的,有意思吧?”if (mt_rand(0, 3) == 3)“就是从0,3中随机抽取一个数字,如果是3那么就正确查询销量,同时把这个销量的数据插入ectouch专门建立的产品销售数据表的sales_volume字段。如果这个随机数不是3,是0,那么这个$sales_volume就不会赋值了,他读取的就是ectouch中sales_volume字段的值,由于初始值是0,’sales_count’自然也是0了。

这个地方的代码设计实在是让人无法理解,匪夷所思,竟然随机决定是否正确显示产品的销售数据,也必须吐槽一下ectouch程序员的这种不严谨的态度。仔细想想,难道这样做是为了减少数据库的压力?不让每次的点击都操作一次数据库?就是因为为了维护sales_volume这个字段而随意决定是否正确显示销量,我也只能说是醉了,本来一个相当优秀的产品我想因为这一点会严重打击用户对一个因为开源和优秀的架构设计在行业占有鳌头地位产品的信任,这纯粹是玩弄和嬉戏。给ectouch提一点小意见,总体设计那是顶级的,部分小地方存在瑕疵,话不中听,只是希望你们做的更好!!

好了,既然是这样,那么为了正确显示这个统计数据,简单调整一下这个地方。修改为

// 销量统计
// $sales_volume = (int) $row[‘sales_volume’];
$sales_volume = model(‘GoodsBase’)->get_sales_count($row[‘goods_id’]);
if (mt_rand(0, 3) == 3){
$sales_volume = model(‘GoodsBase’)->get_sales_count($row[‘goods_id’]);
$sql = ‘REPLACE INTO ‘ . $this->model->pre . ‘touch_goods(`goods_id`, `sales_volume`) VALUES(‘. $row[‘goods_id’] .’, ‘.$sales_volume.’)’;
$this->model->query($sql);
}

 

就不读取ectouch的sales_volume值了,直接查询ecshop的销量。这样首页,分类页和产品详情页的销量就显示正确了。

开始以为所有问题解决了,后来在不断的测试中发现”促销“页面的产品销售数据也是错误的。

促销的英文是”activity“,找到activity.dwt,发现了“activity_goods_list.dwt”,这个dwt和“/library/goods_list_act.lbi”关联。在这里就发现了

 <dd><span class=”pull-left {if $goods.mysc!= 0}ect-colory{/if}”><i class=”fa {if $goods.mysc!= 0}fa-heart{else}fa-heart-o{/if}”></i> {$act_goods.sc}{$lang.like_num}</span><span class=”pull-right”>{$lang.sort_sales}:{$act_goods.sales_count} {$lang.piece}</span> </dd>

可以看到这里销量的变量是$act_goods.sales_count,老办法,全站搜索”sales_count”,查找和activity相关的页面。

Snap8

可以看到,就和我们开始发现categorycontroller.class.php不远的地方,打开ActivityModel.class.php,

 $arr[$row[‘goods_id’]][‘sales_count’] = $this->get_sales_volume($row[‘goods_id’]);

他这个sales_count的赋值函数变了,现在是get_sales_volume,难怪显示错误!在同一个页面找到了get_sales_volume这个函数,他单独做了查询函数,没有调用function.php中的get_goods_count。

 

/**
* 月销量
* @param unknown $goods_id
* @return number
*/
private function get_sales_volume($goods_id) {
$last_month = local_strtotime(‘-1 months’); // 前一个月
$now_time = gmtime(); // 当前时间
$sql = “select sum(goods_number) as sum from ” . $this->pre . “order_goods AS g ,” . $this->pre . “order_info AS o WHERE o.order_id=g.order_id and g.goods_id = ” . $goods_id . ” and o.pay_status=2 and o.add_time >= ” . $last_month . ” and o.add_time <= ” . $now_time . ” group by g.goods_id”;

$res = $this->row($sql);
return intval($res[‘sum’]);
}

原来促销这里的产品的销售情况只是月销量!!查询的是否个时间段的销售量而不是整个产品的销售情况,难怪对不上号!如果想显示整个产品的销售量,可以修改下sql代码。

/**
* 月销量
* @param unknown $goods_id
* @return number
*/
private function get_sales_volume($goods_id) {
$sql = “select sum(goods_number) as sum from ” . $this->pre . “order_goods AS g ,” . $this->pre . “order_info AS o WHERE o.order_id=g.order_id and g.goods_id = ” . $goods_id . ” and (o.order_status=5 or o.order_status=1) group by g.goods_id”;
$res = $this->row($sql);
return intval($res[‘sum’]);
}

把时间限制去掉,或者你这里直接调用get_goods_count函数吧。

上面介绍了我是怎样追查一个web系统问题及解决问题的思路,百蔬君的想法是授人以鱼不如授人以渔,所以详细介绍了整个追码的过程,这个和加密解密,破解汉化等工作差不多的追码思路,找到相关点,逆源而上。才疏学浅,难免不当之处,欢迎朋友批评指正。

 

 

 

 

 

 

 

转载请注明:百蔬君 » 【原创文章】从代码深探ectouch商品销售为0的原因及详解索源追码思路

喜欢 (11)or分享 (0)
发表我的评论
取消评论

请证明您不是机器人(^v^):

表情
(2)个小伙伴在吐槽
  1. 修改了CategoryController.class.php后,分类页访问出错,错误代码500
    A哦哦呢2016-09-21 16:47 回复