微信分销会员上下级关系出现混乱,从反馈到分析问题如何发生 ,再到若是解决。在此把实际项目遇到的问题分享出来,以供之后和网友参考。php
某日,接到一通领导打来的一通电话。电话主要内容是他曾接到一个用户的反馈电话,说道咱们有一个项目中,微信会员上下级关系存在混乱,用户a关注的用户b,用户b却关注的用户的a。上下级关系很乱,还有不少用户存在一些,用户a关注了用户b,用户b的推荐好友列表中却没有用户a的存在……web
当我拿到这个问题以后,首先的态度非常质疑,代码应该都是没有问题的,可能会存在一些数据没有对上。此处我须要在整个数据库中查找出全部的上下级混乱关系的会员。sql
什么是上下级混乱关系?数据库
用户b的上级是用户a,用户a的上级是用户b。(b>a>b)服务器
用户c的上级是用户b,用户b的上级是用户a,用户a的上级是用户c。(c>b>a>c)
微信
所谓的混乱即是出现了这种无线循环的关系,那么,若是查找出来这种上下级关系呢?框架
怎么解决?函数
根据以上的举例,可以很明显的能看出来一个规律即是这种会员关系中,一圈之中至少存在两个会员的?本身上级的上级……的上级有多是本身。只要找出本身全部的上级来查询是否存在两个相同会员就行了。测试
首先写一个脚原本执行,若是web执行超时,可采用命令行方式执行。this
如下以TP框架语法进行举例:
/** * 排错函数 */ public function member_level(){ $Model = M('weishop_member'); //查询出全部有上级的用户 $exist_sql = "SELECT wm_id,wm_parent_id FROM weishop_member WHERE wm_parent_id !=0"; $exist_parent_users = $Model->query($exist_sql); // debug_show($exist_parent_users); $num = 0; $error_users = array(); foreach($exist_parent_users as $val){ //此处屏蔽的代码能够在调试时放开,测试数据是否正常,避免逻辑错误致使死循环超时 // if($num==2){ // exit; // } $user_status = $this->level_debug($val['wm_id'],$val['wm_parent_id'],true); if($user_status===3){ $error_users[] = $val['wm_id']; } $num++; } echo '<br/>error users:'.implode(',', $error_users).'<br/>'; echo '<br/>Check OK!'; //拿出一个数据来进行 测试排错 // $this->level_debug($exist_parent_users[0]['wm_id'],$exist_parent_users[0]['wm_parent_id'],true); echo 'Exist users number:'.count($exist_parent_users); } /** * 递归排错 * @desc 查询当前用户的上级祖(祖父)中是否存在循环(儿子) * @param $wm_id 会员id * @param $parent_id 上级id * @param $init 是否初始化,即首次执行 * @return 1:用户正常。0:用户还有上级。3:用户关系出错 */ private function level_debug($wm_id,$parent_id,$init=false){ static $user_id = 0;//被检查的用户 static $_son = array();//儿子组 static $num = 0;//循环执行次数 //empty 若是是初始化执行则清空 if($init){ $_son = array(); $num = 0; $user_id = $wm_id; } $num++; if(!isset($_son[$wm_id])){ //若是儿子组中不存在当前用户,则加入当前用户,继续向下查找祖父 $_son[$wm_id] = array( 'wm_id'=>$wm_id, 'wm_parent_id' => $parent_id ); } //调试时测试执行次数,防止逻辑错误,出现死循环超时 if($num==5){ // debug_show($wm_id); // debug_show($_son); } $Model = M('weishop_member'); //查询当前上级 $query_parent_sql = "SELECT wm_id,wm_parent_id FROM weishop_member WHERE wm_id = '$parent_id'"; $parent_user = $Model->query($query_parent_sql); //若是上级存在 而且上级用户还有上级则继续递归 //条件:1.当前用户上级存在。2.上级用户还存在上级 if(is_array($parent_user) && isset($parent_user[0]) && $parent_user[0]['wm_parent_id']!=0){ $buf = $parent_user[0]; //若是当前用户上级的上级存在于儿子组中则进入判断 if(isset($_son[$buf['wm_parent_id']])){ echo '<font color="red">error!</font> user id:'.$user_id.' '.'parent_id :'.$buf['wm_id'].' '.'parent includes ' .implode(',', array_keys($_son)).' execute:'.$num .'<br/> '; return 3; // exit; } //符合条件 继续进入循环探寻祖父 $this->level_debug($buf['wm_id'],$buf['wm_parent_id']); return 0; } //数据量过多,则关闭正经常使用户结果显示 // echo 'user id:'.$user_id.' '.'parent_id :'.$parent_id.' '.'parent includes ' // .implode(',', array_keys($_son)).' execute:'.$num .'<br/> '; return 1; }
以上代码通过屡次修改后的,并非一鼓作气。须要用到的朋友能够仔细分析下逻辑。
通过屡次的修改,以上的代码没有超时执行,跟用户数量仍是有关系,毕竟只有几千个。返回的内容以下:
说明一下,红色error都是关系混乱的用户,在最后咱们能够看到出错的用户抱(bāo)括2和116。所存在上级的用户总共有3158个。
那么问题看来都是由2和116的关系混乱致使的,只须要将他们更正后便可。以后便联系了用户id分别为2和116的用户,将他们的注册时间和推荐时间作一了解,将2的上级清空,恢复为0.
那么再执行刚才的排错来看看结果。
此次检查以后,发现并无关系混乱的用户。一切恢复平静。虽说目前的问题解决了,但并不意味着,之后都会正常,问题的根源尚未找出来。
怎么修复?
带着问题继续在代码中寻找,来到会员绑定的逻辑处。
/** * 绑定微信上下级(未修复) * @param $openid 当前用户openID * @param $parent_id 上级用户id * @return bool */ public function bingding($openid,$parent_id){ //检测当前用户是否有上级而且上级不能为本身,没有则继续 $res = $this->where("wm_openid ='$openid' && wm_id != '$parent_id' && wm_parent_id ='0'")->save(array("wm_parent_id" => $parent_id)); if($res){ return true; } }
粗略一看几乎看不出来啥问题,绑定时首先判断的是当前用户的上级是否存在,若是有天然不能附加上级了。若是没有的状况下,而且上级不能为本身,不然本身跟本身成为上级就乱了套。当时写的代码本觉得是正常。结果出了一个隐性bug。
怎么发生的呢?
上面的逻辑会忽略掉,下级下级的用户偶然一天变成本身的上级(多是由于本身失误的去扫码,扫了本身辛辛苦苦下级用户的码,反而让下级成为了本身的上级),致使关系混乱,在用户方面可能会致使佣金等利益计算错误,在平台层面会影响一笔损失,在此能够给各位一个提醒,凡是跟钱有关的必定要慎重!
至于修复呢,而此处只须要增长一层判断便可解决。
修改后代码奉上:
/** * 绑定微信上下级(已修复) * @param $openid 当前用户openID * @param $parent_id 上级用户id * @return bool */ public function bingding($openid,$parent_id){ //检测是否有下级 $sql = "SELECT count(a.wm_id) as count FROM `weishop_member` as a join `weishop_member` as b on a.wm_parent_id = b.wm_id WHERE b.wm_openid='{$openid}';"; $count_res = $this->query($sql); if(is_array($count_res) || count($count_res)==1){ if(isset($count_res[0]['count']) && $count_res[0]['count']>0){ return false; //若是有下级则不能成为别人的下级 } } //检测当前用户是否有上级而且上级不能为本身,没有则继续 $res = $this->where("wm_openid ='$openid' && wm_id != '$parent_id' && wm_parent_id ='0'")->save(array("wm_parent_id" => $parent_id)); if($res){ return true; } return false; }
若是本身已经有了下级用户就不能成为别人的下级,这样一来,就不会出现这种死循环。固然解决的方案不仅是这一个,符合业务逻辑就好。
修改好代码检查无误后当即将代码更新至服务器,以防止出现更多差错。
基本上这次的bug检查及解决方案就到此结束了,过程当中一些细节性东西不太好描述,最好去实际实践一下,或者是实际用到时慢慢体会。