摘要:綜合以上問題得出以下結論業務處理失敗消息要以的方式向上傳遞給調用者業務處理失敗消息以參數的方式傳遞不是很適合,并且不能以的方式返回再次思考,最終從里面想到了一點思路幸好是出身。
場景:創建訂單 實際流程:我需要拍磚 和 看見你們的意見,為團隊少挖坑
業務處理類動作:終端調用(PC端、移動端APP、微信端、Web端)-->控制器 或 接口-->實際的業務處理-->控制器 或 接口-->終端做出相應處理(控制器可能是渲染對應頁面; 接口返回 JSON數據)
檢查用戶是否登陸
驗證商品 ID、購買數量等參數
檢查該商品是否處于上架中
檢查該商品是否可以購買
各種檢查...
創建訂單
記錄 Log
返回訂單創建結果給調用者
創建失敗:...
創建成功:[return true|return Order info]
游戲規則前后端數據格式約定為 JSON格式如下:
{ code: "00000", // 狀態碼 msg: "操作成功!", // 提示信息 data: {} // 數據 }
導火線注:"00000":業務成功狀態碼;非"00000"都為業務失敗。
為了防止服務器端狀態碼泛濫成災,code可以為"",這時 msg 里面則是相應的錯誤信息,只為給用戶提示。
項目開發完畢,測試人員去測試,提如下Bug:
如果用戶未登錄,進個人中心,提示用戶未登錄,然后會去登陸view;而在下單頁,提示用戶未登錄,卻沒有去登陸view。
然后前端童鞋開始去修復該問題,查出如下問題:
個人中心服務器接口返回的數據格式:
{ code: "00008", msg: "用戶未登錄,請登錄", data: [ ] }
下單頁服務器接口返回的數據格式:
{ code: "", msg: "用戶未登錄,請登錄!", data: [ ] }
然后前端童鞋對服務器端童鞋講,這里你應該返回給我code: "00008",我這邊一看 code便知是用戶未登錄,就可以做出相應的操作,這里你只返回提示信息,我這邊不好做更加細膩的操作。
然后,后端童鞋開始嘗試給該地方添加上 code。
開始著手修改代碼:
首先找到接口方法里面發現如下 demo:
php$order = kernel::single("sysapi_ecoupon_order")->create($params, $msg); if (!$order) { return array("code" => "", "data" => array(), "msg" => $msg); } return array("code" => "00000", "data" => $order);
改方法返回array(); 在外部統一入口、出口處再返回 JSON出去。
sysapi_ecoupon_order
phppublic function createNew($params, & $msg) { // 獲取用戶信息 $member_info = app::get("b2c")->model("members")->get_current_member(); if (empty($member_info)) { $msg = app::get("ecoupon")->_("用戶未登錄,請登錄!"); return false; } // 繼續下面的業務處理 }
接口調用的kernel::single("sysapi_ecoupon_order")->create($params, $msg);這里面做實際的業務處理,錯誤信息是通過 $msg 向上傳遞出去,外部沒辦法通過 $msg 獲知對應的 code。然后給前端童鞋講這種情況沒辦法返回 code給你。
前端就只能通過判斷 msg的方式來修復該問題
然后寫了如下 demo:
javascriptif("用戶未登錄,請登錄!" == data.msg) { // 用戶未登錄,去登錄 // ... }
然后提交,測試,通過,上線,N天后
有人跑過來講:下單頁 與 個人中心的提示有點不同,貌似多了個 "!"。(舉例而已,更多的可能是提示不友好、錯別字等情況)
然后后端同學修改為 $msg = app::get("ecoupon")->_("用戶未登錄,請登錄"); 提交,測試不通過,前端同學再修改為if("用戶未登錄,請登錄" == data.msg),提交,測試通過
// 如此反反復復
終究有一天:產品、測試,前端、后端混戰了一場。N人,卒.....
重新正視問題最終前后端得出結論:要想對用戶實現更加友好的體驗,前后端數據必須有個標識具有唯一性,不變性。而現在用的 msg卻不具備,還是得用 code。并且這里前后端極度耦合msg。
后端童鞋回來繼續修改代碼,開始著手給這里添加上相應的 code。
開始思考該怎么添加 code,現在的問題是 create( ) 方法可能是其他童鞋開發,內部返回的提示信息,我這邊是調用者,不能確定方法內部到底會返回什么提示信息,無解。
忽然,有一天想到,我在調用該方法之前檢查下用戶有沒有登錄就OK了,然后開始寫如下實現:
public function create($params) { $member = app::get("b2c")->model("members")->get_current_member(); // 登錄驗證 if (empty($member)) { return array("code" => "00008"); } $msg = ""; $order = kernel::single("sysapi_ecoupon_order")->create($params, $msg); if (!$order) { return array("code" => "", "data" => array(), "msg" => $msg); } return array("code" => "00000", "data" => $order); }
呵呵,好機智的少年。
然后告訴前端,這里可以返回 code了,前端愉快的刪掉原來那坨判斷 msg的代碼,而在 ajax請求的地方統一判斷 code就能預知用戶未登錄,做出相應的操作。
經測試,上線。一切又回到了美好時光。
隨著時光的流逝,業務的增加,后端童靴發現Order類里面如下 demo:
phppublic function create($params) { $member = app::get("b2c")->model("members")->get_current_member(); // 登錄驗證 if (empty($member)) { return array("code" => "00008"); } // 實際業務處理.... } public function getOrderList($params) { $member = app::get("b2c")->model("members")->get_current_member(); // 登錄驗證 if (empty($member)) { return array("code" => "00008"); } // 實際業務處理.... } public function getOrderDetail($params) { $member = app::get("b2c")->model("members")->get_current_member(); // 登錄驗證 if (empty($member)) { return array("code" => "00008"); } // 實際業務處理.... } // ...
這都是什么玩意............ 然后開始封裝,稍微好了點
又過了一段時間,有人過來說創建訂單還需要優化體驗,
點擊創建訂單提示如下:
超過最大購買量——給出提示,繼續留在創建訂單頁
該商品已賣光或已下架——引導用戶去商品列表頁
這時,前端童鞋告訴后端童鞋,商品下架的時候,你也應該返回一個狀態碼。
后端童鞋開始打算添加 code,發現如下 demo
phpkernel::single("sysapi_ecoupon_order")->create($params, $msg);
這里的提示信息是 $msg 返回的,用戶登錄外部可以提前檢測,這里的商品能否購買要實現添加 code也需要提前檢測,將來要是需要添加類是功能豈不是...... 每需要一個精確的 code返回出去,這里就需要添加檢測,這里代碼將會變得無法直視。
況且這里本該在業務里面檢測,一切不那么友好起來了。
再次思考,代碼寫的不爽了,一定是哪里不對
問題所在開始懷疑 public function create($params, & $msg) { } 這里不應該是通過 & $msg 來作為 調用者與 被調用者之間的 錯誤信息通信約定,一切的問題都出在了這里。錯誤消息向上傳播的約定不合適
如果這里約定的是 code作為錯誤向上傳播一切的問題即將不復存在。在調用業務方法之前的檢測代碼就都可以去掉了,代碼簡約,一切又美好起來。
接下來繼續思考,使用 code作為業務處理失敗消息傳遞問題又來了
現在已有的業務代碼都是如此定義public function create($params, & $msg),怎樣更加友好的替換成 code
如果使用 code,code只能服務器端 與 前端約定的一個具有唯一性的標識(code 比 msg 對國際化的實現更加容易)但是并不能直接展示給用戶,那么就需要定義每個 code 的代表的意義 與 對應的提示信息。那么問題來了,code 應該已怎樣規范來定義所代表的含義
再來看如下常用的兩種方法定義:
public function create($params, & $msg)
public function create($goodsId, $num, & $msg)
第一種方式,參數通過一個 $params數組傳遞過來,方法內部在把錯誤提示放到 $msg中。
好處:$params是個數組,里面參數可以任意添加
缺點:該方法調用者在外部不能知道該方法需要什么參數,必須來看該方法內部實現,做出對應的數組 key的轉換(如: user_id 轉 userId)。方法調用者 與 方法實現 極度耦合。維護成本大、出Bug系數高
第二種方式,按基本類型分別傳遞單個參數
好處:方法調用者根據方法定義就能夠知道方法具體需要的參數,調用方法時不需要作 key轉換,只需要傳遞對應的參數即可
缺點:參數數目過多時,慘不忍睹
這里有如下問題:
這里如何已一種更加容易維護,擴展的方式來處理(Java里面方法參數已對象的方式傳遞可以借鑒)
這里的& $msg 真的合適嗎,如果是第二種方式定義的方法,以后擴展個 $phone 該如何處理?public function create($goodsId, $num, & $msg, $phone="")這樣么?怎么看怎么蛋疼
再來看不通過 & $msg傳遞錯誤信息之后的代碼
phppublic function create($goodsId, $num) { if ( ? ) { // 返回狀態碼 return "0001"; } if ( ? ) { // 返回狀態碼 return "0002"; } // 創建訂單 // ... // 返回訂單信息 return $order; }
看似實現了,但是方法調用者,怎么調用怎么蛋疼,一會返回狀態碼,一會返回訂單信息,完全兩種類型。
綜合以上問題:得出以下結論:
業務處理失敗消息要以 code 的方式向上傳遞給調用者
業務處理失敗消息以參數的方式傳遞不是很適合,并且不能以 return的方式返回
再次思考,最終從 Java里面想到了一點思路(幸好是 Java出身。疑問:為何面試的時候 Java的工作經驗都不算在 PHP工作經驗里呢,并沒有因此而加分)
解決方案:自定義一個異常類,包括 codo屬性 和 msg 屬性
凡是遇到業務不能正常處理的時候就創建一個異常對象,設置對應的 code 或者 msg屬性(為了減少 code泛濫,這里的 code 與 msg 可以2選一,如果前端需要做精準的處理,就設置 code,如果只是為了給用戶提示,就只返回 msg,則可以減少一個 code),然后拋出異常
方法調用者在外部統一捕捉該異常,如 接口的統一入口出口的方法內部處理
因個人工作時間、項目經歷不多、歸根結底經驗不足。現在將該方案寫下來,還望有經驗的大神拍磚,以免給團隊挖坑,以上 $msg 就是 N久以前埋下的坑。
該文章發布在自己站點地址:http://www.webdevs.cn/article/91.html
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/20921.html
摘要:是騰訊云內部自研基于的高可靠強一致可擴展分布式消息隊列,在騰訊內部包括微信手機業務紅包騰訊話費充值廣告訂單等都有廣泛使用。目前已上線騰訊云對外開放,本文對核心技術原理進行分享介紹。 ? 極牛技術實踐分享活動 極牛技術實踐分享系列活動是極牛聯合頂級VC、技術專家,為企業、技術人提供的一種系統的線上技術分享活動。 每期不同的技術主題,和行業專家深度探討,專注解決技術實踐難點,推動技術創新,...
摘要:為了幫助用戶更好地完成消費決策閉環,馬蜂窩上線了大交通業務。現在,用戶在馬蜂窩也可以完成購買機票火車票等操作。第二階段架構轉變及服務化初探從年開始,整個大交通業務開始從架構向服務化演變。 交通方式是用戶旅行前要考慮的核心要素之一。為了幫助用戶更好地完成消費決策閉環,馬蜂窩上線了大交通業務。現在,用戶在馬蜂窩也可以完成購買機票、火車票等操作。 與大多數業務系統相同,我們一樣經歷著從無到有...
閱讀 562·2021-11-25 09:44
閱讀 2648·2021-11-24 09:39
閱讀 2324·2021-11-22 15:29
閱讀 3534·2021-11-15 11:37
閱讀 3402·2021-09-24 10:36
閱讀 2527·2021-09-04 16:41
閱讀 1009·2021-09-03 10:28
閱讀 1872·2019-08-30 15:55