微信分銷會員上下級關係出現混亂,從反饋到分析問題如何發生 ,再到若是解決。在此把實際項目遇到的問題分享出來,以供之後和網友參考。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檢查及解決方案就到此結束了,過程當中一些細節性東西不太好描述,最好去實際實踐一下,或者是實際用到時慢慢體會。