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:★★★★★