1. 程式人生 > >Ecstore訂單促銷策略錯誤讀取其他商品促銷的BUG

Ecstore訂單促銷策略錯誤讀取其他商品促銷的BUG

最近發現一個Ecstore大bug關於訂單促銷這一塊的,能直接影響到最後的結算金額,使結算價格遠遠低於應付金額。在和官方技術人員溝通中,發現最新版的ecstore仍然沒有修復此bug,因此有必要將此bug公佈一下。

活動名稱:第2件商品半價,多款商品可同時享受。
場景重現:
1. 建立1條訂單策略,優先權50,不排他。指定包含購買商品A數量x2的時候,訂單-100元。
2. 建立另1條訂單策略,優先權50,不排他。指定包含購買商品B數量x2的時候,訂單-60元。
3. 建立商品A,單價為200元,商品B單價為120,確保商品單價要大於減免價格。

結果發現買商品A兩件時,結算價格變成了200×2-100-60了,執行了商品A的100元的優惠基礎上同時又執行了商品B的的促銷優惠。同一時間上,生效的第2件半價的訂單促銷策略不止這兩條,檢視其他產品結算價格都正常。起初懷疑是後臺設定問題,但是在仔細比對其他產品的第2件商品半價促銷設定後,並沒有發現有什麼設定上的問題,於是開始懷疑本身的程式碼有bug。

訂單促銷走的是/b2c/lib/cart/postfilter/promotion.php到/b2c/lib/sales/,程式碼封裝得很深,最後定位到產生的問題的檔案/b2c/lib/sales/basic/operator/contain.php這個檔案的validate函式。

原始碼為:

public function validate($operator,$value,$validate) {
        if( !$value  || !$validate ) return false;
        //商品包含某個字
        switch($operator) {
            case '()':
                if(is_array($value)) return in_array($validate,$value);
                $flag = strpos($validate,$value);
                if( $flag===false ) 
			return false;
                else 
			return true;
                break;
            case '!()':
                if(is_array($value)) return !in_array($validate,$value);
                $flag = strpos($validate,$value);
                if( $flag===false ) return false;
                else return true;
                break;
        }
        return false;
    }

這裡函式引數operator是包含的符號'()’,value是策略裡的goods_id(多個的情況下是陣列),validate是購物車裡的goods_id(只可能是單個)。可以看到如果策略裡包含多個商品,則會用in_array判斷購物車商品是否在策略生效的商品清單中,這個沒問題。問題點在於下面這條strpos,如果購物車內的goods_id是123,策略裡生效的goods_id是1234的話,那麼strpos(‘123′,’1234’)是成立的。另外查了一下後臺所有策略的可選項,發現在設定商品名稱匹配的時候也用到了包含,所以,這裡針對性的略做修改,如下:

 case '()':
    if(is_array($value)) return in_array($validate,$value);
				
	//by tiandi 2016.5.9 嘗試修復goods_id=123的商品會載入goods_id=1234訂單促銷的錯誤,不清楚這裡原先為什麼要用strpos,可能並不侷限用於促銷策略模組,
	//所以可能會造成其他問題。如果value字串裡都是數字(goods_id),則不使用strpos,而直接判斷是否和validate相同
	//start 
	if(is_numeric($value)&&($validate != $value)) 
		return false;
	//end
				
        $flag = strpos($validate,$value);
        if( $flag===false ) 
		return false;
        else 
		return true;
        break;

這樣在比對goods_id的時候就不會再出現讀取錯誤的情況了。Ecstore的框架寫得還真是有夠複雜,查明上述問題,幾乎涉及到10個左右的檔案。

PS:已經將該問題提交給官方技術了。

文章評分2次,平均分5.0