摘要:主要遇到的問題如下涉及商品價格的系統眾多各上層系統調用商品價格接口繁多商品價格相關字段較多為了實現快速上線,我們在原人民幣的商品價格基礎架構上,只能進行少量且合適的改造。可行性調研改造商品中心,商品價格支持盧比。
首發于 樊浩柏科學院
假若,你是某個國內電商平臺的商品中心項目負責人。突然今天,接到了一個這樣的需求:商品在原人民幣價格的基礎架構上,須支持盧比(印度)價格。
需求需求點,可以描述為:
購買的用戶,商品價格需要支持盧比;
營運人員,商品管理系統依然使用人民幣價格;
同樣這個需求,定了以下兩個硬指標:
必須實現需求;
必須快速上線;
問題首先,我們必須承認的是,這確實是個簡單的需求,但這也是個夠坑爹的需求。主要遇到的問題如下:
涉及商品價格的系統眾多;
各上層系統調用商品價格接口繁多;
商品價格相關字段較多;
為了實現快速上線,我們在原人民幣的商品價格基礎架構上,只能進行少量且合適的改造。所以,最后我們的改造方向為:盡量只改造商品價格源頭系統,即商品中心,其他上層系統盡量不改動。
可行性調研改造商品中心,商品價格支持盧比。可行的改造方案有 2 種:
1、數據表價格字段存盧比
將原人名幣價格相關的數據表字段,存盧比值,數據表并新增人名幣字段。
2、接口輸出數據時轉化為盧比
原人名幣相關的數據表字段依然存人民幣值,在接口輸出數據時,將價格相關字段值轉化為盧比。
針對以上方案,我們需要注意 2 個問題:
匯率會每天變化,所以商品價格也會變化;
后續商品價格,可能須支持多幣種;
上述 方案 ①,商品中心只需改造數據表。然后每天根據匯率刷新商品價格,原價格字段就都變成了盧比。方案相對簡單,也容易操作,但缺點是:對任然需要人民幣價格的系統,即商品管理系統須改造。
方案 ②,需要改造商品中心業務邏輯。由于涉及的價格字段較多,改造較復雜,主要優點是:匯率變動對商品價格影響較小,且可拓展支持多幣種價格(可以根據地區標識,獲取相應的商品價格)。
最終,為了系統的可擴展性,我們選擇了方案 ②。
這里主要改造了商品中心,主要解決 透傳地區標識 和 支持多幣種價格 這 2 個問題。
透傳地區標識我們的業務系統主要分為 API 和 Service 項目,API 暴露出 HTTP 接口,API 與 Service 和 Service 與 Service 之前使用 RPC 接口通信。由于商品中心涉及到價格的接口繁多,不可能對每個接口都增加地區標識的參數。所以我們弄了一套調用鏈路透傳地區標識的機制。
機制原理思路就是,先將地區標識放在全局上下文中,API 接口通過 Header 頭X-Location攜帶地區標識;而對于 RPC 接口,我們的 RPC 框架已支持了 Context,不需要改造。
代碼實現由于 RPC 框架已支持了 Context,所以 API 和 RPC 接口透傳全局上下文略有不同。實現如下:
class Location { public static function init() { global $context; if (empty($context["location"])) { return; } // API在這里直接獲取X-Location頭 if (!empty($_SERVER["HTTP_X_LOCATION"])) { $context["location"] = $_SERVER["HTTP_X_LOCATION"]; } // RPC Server會自動獲取Context } }
上述init()方法,需要在項目入口位置初始化。
其中,RPC 接口不需要操作全局上下文。因為 RPC Client 在調用時會自動獲取全局變量$context值并在 RPC 協議數據中追加 Context,同時 RPC Server 在收到請求時會自動獲取 RPC 協議數據中的 Context 值并設置全局變量$context。
RPC Client 傳遞 Context 實現如下:
protected function addGlobalContext($data) { global $context; $context = !is_array($context) ? array() : $context; // data為待請求的RPC協議數據 $data["Context"] = $context; return $data; }
RPC Server 獲取 Context 實現如下:
public function getGlobalContext($packet) { global $context; $context = array(); // packet為接收的RPC協議數據 if(isset($packet["Context"])) { $context = $packet["Context"]; } }
當設置了 Context 后,RPC 通信時協議數據會攜帶location字段,內容如下:
RPC 325 {"data":"{"version":"1.0","user":"xxx","password":"xxx","timestamp":1553225486.5455,"class":"xxx","method":"xxx","params":[1]}","signature":"xxx","Context":{"location":"india"}}
到這里,我們只需要在全局上下文設置地區標識即可。一旦我們設置了地區標識,所有業務系統就會在本次的調用鏈路中透傳這個地區標識。實現如下:
class Location { public static function set($location) { global $context; $context["location"] = $location; // API需要在這里多帶帶設置X-Location頭 header("X-Location: " . $context["location"]); } }
設置了地區標識后,就可以在本次調用鏈路的所有業務系統中直接獲取。實現如下:
class Location { public static function get() { global $context; if (!isset($context["location"])) { return "china"; } return $context["location"]; } }支持多幣種價格 商品中心
有了地區標識后,商品中心服務就可以根據地區標識對價格字段進行轉化了。因為設計到價格的數據表和價格字段較多,這里直接從數據層(Model)進行改造。
下述的ReadBase類是所有數據表 Model 的基類,所有獲取數據表數據的方法都繼承或調用自getOne() 和getAll()方法,所以我們只需要改造這兩個方法。
class ReadBase { public function getOne(array $cond, $fields) { $data = $this->getReader()->select($this->getFields($fields))->from($this->getTableName())->where($cond)->queryRow(); return $this->getExchangePrice($data); } public function getAll(array $cond, $fields) { $data = $this->getReader()->select($this->getFields($fields))->from($this->getTableName())->where($cond)->queryAll(); if ($data) { foreach ($data as &$one) { $this->getExchangePrice($one); } } return $data; } }
由于涉及到價格字段名字較多,且具有不確定性,所以這里使用后綴方式匹配。為了防止一些字段命名不規范,這里引入了黑名單機制。
protected function isExchangeField($field) { $priceSuffix = array("cost", "_price"); $black = array(); $len = strlen($field) ; foreach ($priceSuffix as $suffix) { $lastPos = $len - strlen($suffix); // 非黑名單且非is_ if (!in_array($field, $black) && false === strpos($field, "is_") && $lastPos === strpos($field, $suffix) ) { return true; } } return false; }
前綴為is_的字段一般定義為標識字段,默認為非價格字段。
上述getExchangePrice()方法,用來根據地區標識轉化價格覆蓋到原價格字段,并自增以_origin后綴的人民幣價格字段。
public function getExchangePrice(&$data) { if (empty($data)) { return $data; } $originPrice = array(); foreach ($data as $field => &$value) { // 是否是價格字段 if ($this->isExchangeField($field)) { $originField = $field . "_origin"; $originPrice[$originField] = $value; // 獲取對應地區的價格 $value = $this->getExchangePrice($value); } } $data = array_merge($originPrice, $data); return $data; } public static function getExchangePrice($price) { // 獲取地區標識 $location = Location::get(); // 匯率 $exchangeRateConfig = Config::$exchangeRate; if ($location === "china") { return $price; } else if (isset($exchangeRateConfig[$location])) { $exchangeRate = $exchangeRateConfig[$location]; } else { throw new BusinessException("not found $location exchange rate"); } // 向上取值并保留兩位小數 $exchangePrice = bcmul($price, $exchangeRate, 3); return number_format(ceil($exchangePrice * 100) / 100, 2, ".", ""); }
其中,getExchangePrice()方法會調用Location::get()獲取地區標識,并根據匯率計算實時價格。
最終,商品中心改造后,得到的部分商品價格信息,如下:
# 人民幣價格10,匯率10.87 market_price: 108.7 market_price_origin: 10API系統
對于所有 API 的項目,我們只需要讓客戶端在所有的請求中增加X-Location頭即可。
GET /product/detail/1 HTTP/1.1 Request Headers X-Location: india
API 項目需在入口文件處,初始化地區標識。如下:
Location::init();商品管理系統
對于商品管理系統,我們為了方便運營操作,所有商品價格都應以人民幣。因此,我們只需要初始化地區標識為中國,如下:
Location::init(); // 地區設置為中國 Location::set("china");總結
為了實現需求很容易,但是要做到合理且快速卻不簡單。本文的實現的方案,避免了很多坑,但同時也可能又埋下了一些坑。沒有一套方案是萬能的,慢慢去優化吧!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/31097.html
摘要:主要遇到的問題如下涉及商品價格的系統眾多各上層系統調用商品價格接口繁多商品價格相關字段較多為了實現快速上線,我們在原人民幣的商品價格基礎架構上,只能進行少量且合適的改造。可行性調研改造商品中心,商品價格支持盧比。 首發于 樊浩柏科學院 假若,你是某個國內電商平臺的商品中心項目負責人。突然今天,接到了一個這樣的需求:商品在原人民幣價格的基礎架構上,須支持盧比(印度)價格。 showIm...
摘要:上周,在杭州歐美金融城創投中心啟動全球黑客馬拉松,本次活動吸引了眾多長三角開發者的關注,有多位嘉賓出席活動進行項目交流討論,現場更有幣圈大佬現場撒幣。 showImg(https://segmentfault.com/img/bVbbQCS); 上周,SegmentFault 在杭州歐美金融城 G5 創投中心啟動全球黑客馬拉松,本次活動吸引了眾多長三角開發者的關注,有多位嘉賓出席活動進...
摘要:背景比特幣說好的分叉最后卻分叉不成,如今算力又不夠,于是比特現金想篡位沒一個星期就漲了快倍,錯過這趟快車甚是后悔,于是打算寫一個可不定期推送最新消息的微信公眾號。既然是利用微信這個平臺載體,當然要熟悉微信的,遂封裝了一下。 背景:比特幣說好的segwit2x分叉最后卻分叉不成,如今算力又不夠,于是比特現金想篡位? 沒一個星期就漲了快10倍,錯過這趟快車甚是后悔,于是打算寫一個可不定期推...
摘要:很多人將這一波的上漲解讀為比特幣小牛市的到來,無論從技術層面還是從消息層面來看,比特幣都有逐步回暖的跡象。到日,關于英雄鏈網絡詐騙案被破獲的報道便鋪天蓋地地傳播開來。 摘要:不在風口上,長了翅膀的項目同樣可以起飛,價值終究會超越時間。 showImg(https://segmentfault.com/img/bVbrS0N?w=4096&h=3575); 自四月初以來,比特幣就開啟了起...
閱讀 1460·2021-11-22 14:44
閱讀 2842·2021-11-16 11:44
閱讀 3206·2021-10-13 09:40
閱讀 1979·2021-10-08 10:04
閱讀 2363·2021-09-24 10:28
閱讀 2909·2021-09-06 15:02
閱讀 2957·2019-08-30 15:52
閱讀 2392·2019-08-30 13:20