發(fā)布時間:2023-03-20 18:12:00
通過閱讀本文,您將了解到一個售后系統(tǒng)應(yīng)該具備的一些能力、在整個上下游系統(tǒng)中的定位、基本的系統(tǒng)架構(gòu),以及針對售后業(yè)務(wù)場景中常見問題的解決方案。
一、核心價值
京東到家售后系統(tǒng)作為逆向流,強依賴京東到家業(yè)務(wù)域,目前涵蓋了:退款、退貨、換貨、維修等四大類場景,并且為用戶與商家提供申訴、仲裁場景支持,為計費與結(jié)算系統(tǒng)提供逆向金額數(shù)據(jù)支持。
售后系統(tǒng)業(yè)務(wù)結(jié)構(gòu):
售后系統(tǒng)上下游依賴:
二、系統(tǒng)架構(gòu)
售后系統(tǒng)使用的就是基礎(chǔ)的三層架構(gòu)。應(yīng)用層有不同身份的三個端入口,服務(wù)層提供了一些業(yè)務(wù)支持和數(shù)據(jù)支持,數(shù)據(jù)層目前使用到了MySQL和Redis以及ElasticSearch。當(dāng)然還有一些中間件使用,比如rpc框架,zk配置中心,worker分布式定時任務(wù),jmq消息。還有完善的基礎(chǔ)設(shè)施,統(tǒng)一監(jiān)控和日志采集。
三、業(yè)務(wù)形態(tài)
當(dāng)正向訂單履約完成后,如訂單中商品有缺件、錯件、質(zhì)量等問題可以發(fā)起售后申請。目前申請售后支持用戶端、商家端、到家客服發(fā)起。用戶端申請需要根據(jù)不同責(zé)任方分配到商家或者客服審核。商家端只能選擇商家責(zé)任原因申請售后,然后自動審核通過??头脩羯暾埵酆蠛陀脩舳艘恢?,流轉(zhuǎn)到商家或客服審核。
用戶端申請售后流程:
1. 申請售后
1)多端操作并發(fā)場景下問題
2)售后商品拆分信息如何獲取
在正向訂單履約完成后一定的時效內(nèi),可以通過用戶端,商家端,運營端基于訂單中商品選擇性申請售后。當(dāng)接收到一個售后單提交申請,售后這邊會依賴訂單數(shù)據(jù),拆分?jǐn)?shù)據(jù)來構(gòu)建售后單詳情數(shù)據(jù)。那么對于多端申請售后入口,我們怎么能保證訂單中商品不會被重復(fù)申請呢?申請時我們使用了redis分布式鎖。
售后申請場景下分布式鎖需要注意點:
?、俨煌娜肟谑褂孟嗤膋ey,這里我們通過前綴加訂單號來區(qū)分,來保證對同一訂單加鎖。
?、诩尤脒^期時間,比如第一個申請獲取到鎖,如果釋放鎖異常,這里只需要等到超時時間自動過期,防止死鎖。
?、鄣却i時間,同一個訂單多個入口同時申請售后,如果獲取不到鎖就進(jìn)入等待,直到獲取到鎖或者等待超時后退出。
④使用uuid來保證token唯一性,每次都釋放自己當(dāng)前請求鎖。
我們保證了同一時間只能有一個訂單下的售后能夠申請,接下來就是組裝售后單詳情數(shù)據(jù)。一個完整的售后單數(shù)據(jù)來源于訂單詳情和拆分詳情。
通過從訂單詳情中取用戶基礎(chǔ)信息,訂單信息,商家門店信息來保存到售后單主表中。根據(jù)申請選擇的商品skuid從訂單商品詳情中獲取對應(yīng)商品基礎(chǔ)信息保存到售后商品表中。接下來就是比較重要的售后商品拆分信息,這個數(shù)據(jù)來源于拆分系統(tǒng)。先了解下拆分?jǐn)?shù)據(jù)結(jié)構(gòu):
可以看到,拆分系統(tǒng)會根據(jù)訂單中所有商品把金額拆分到每一件商品上,并且通過num_下標(biāo)來區(qū)分。當(dāng)選擇訂單中某個商品發(fā)起售后我們是怎么去找到這個商品對應(yīng)的拆分信息呢?
我們通過sku_promotionType(商品+促銷類型)來區(qū)分不同的商品拆分信息,然后通過記錄num商品下標(biāo)來確定找到哪一個商品。
比如下面的場景:
假設(shè)訂單中購買了3個正價A商品,1個促銷A商品。
?、俚谝淮紊暾堃粋€正價A售后。這時售后系統(tǒng)會記錄一個售后單,對應(yīng)售后詳情為商品A。從拆分獲取sku_A_正價_num0信息并記錄到售后商品拆分詳情表。
?、谠偕暾堃粋€正價A和一個促銷A售后。這里售后會發(fā)現(xiàn)此訂單已申請過一個正價A,記錄的是sku_A_正價_num0。這時就會去取拆分的 sku_A_正價_num1這條數(shù)據(jù)。
?、鄣诙紊暾埵酆髮?yīng)一個新售后單,商品詳情記錄為sku_A_正價,sku_A_促銷。商品拆分記錄數(shù)據(jù)為:sku_A_正價_num1,sku_A_促銷_num0。
初步了解了售后商品獲取對應(yīng)拆分?jǐn)?shù)據(jù)的邏輯,這時如果同一個訂單中購買了相同促銷的A商品,但是價格不一樣怎么辦呢?按照上面獲取邏輯,獲取的售后商品金額就會出現(xiàn)多退或者少退情況。
比如下面的捆綁促銷:
A+B捆綁銷售,A金額3元。A+C捆綁銷售,此時A金額2元。這時拆分的數(shù)據(jù)結(jié)構(gòu)為:sku_A_捆綁_num0價格3元,sku_A_捆綁_num0價格2元。此時如果兩個A都申請了售后,我們再按照sku_promotionType去獲取拆分那么永遠(yuǎn)獲取的都是第一個的金額。因此針對這種特殊的促銷場景,我們在原有獲取拆分維度基礎(chǔ)上又增加了一個價格。
區(qū)分維度:sku_promotionType_price(商品+促銷類型+價格)
上面的方案可以滿足各種不同促銷場景的售后,但是針對稱重退差訂單申請售后還會適用么?
稱重退差訂單含義:當(dāng)正向訂單揀貨時,商家發(fā)現(xiàn)實際揀貨的稱重品和售賣規(guī)格有誤差,此時可以發(fā)起退差單把差額的錢退給用戶。之后訂單正常履約,訂單完成后用戶也可以申請售后。此時再申請售后退給用戶的錢就應(yīng)該是減去退差后的部分。
比如下面的場景:
假設(shè)一個訂單中買了2個原價A+1個促銷價A,原價3元,促銷價2元,整單共8元。揀貨時發(fā)現(xiàn)A商品實際重量比標(biāo)重少,退差1元,此時退差單中會記錄商品A退差金額,退差重量。這時選擇正價A發(fā)起售后申請,售后系統(tǒng)就需要根據(jù)實際重量獲取退差商品金額,然后計算實際退款金額。這時我們又在原來的基礎(chǔ)上增加了一個重量維度。
sku_promotionType_price_weight(商品+促銷類型+價格+重量)
系統(tǒng)都是為了業(yè)務(wù)來服務(wù)的,隨著業(yè)務(wù)變更場景的增多,我們的架構(gòu)也在演變。目前所有的計算拆分邏輯都封裝成統(tǒng)一方法,統(tǒng)一入口,未來再增加不同促銷,或者其他業(yè)務(wù)都可以很友好的支持。
2. 審核售后
1)多條件復(fù)雜查詢性能問題
當(dāng)售后單申請成功后,會根據(jù)審核方分配給商家或者客服審核。這里涉及到兩個列表查詢,一個是運營端客服使用,一個是商家端根據(jù)商家賬號權(quán)限來展示可操作的售后單列表。最初我們的售后單表數(shù)據(jù)并不是很大,隨著業(yè)務(wù)品類擴增以及用戶量的增加遇到了一些問題。
?、贁?shù)據(jù)庫頻繁報警,慢SQL,影響其他業(yè)務(wù)
?、谏碳疫\營反饋售后單列表查詢過慢,影響審核效率。
通過分析慢SQL日志,我們根據(jù)查詢字段增加索引來提高查詢速率。由于支持各種查詢場景過多,目前主表中已經(jīng)建立了20多個索引。而且基于業(yè)務(wù)的發(fā)展需要支持查詢的時間區(qū)間也會更長。主表的數(shù)據(jù)量一直在增長,還是會遇到查詢性能問題,過多的索引對于售后單流程中變化更新也有一定的影響。
因為ES是基于倒排索引實現(xiàn)的搜索,配合分詞器在文本模糊搜索上表現(xiàn)比較好,使用的業(yè)務(wù)場景廣泛,因此我們考慮把售后單數(shù)據(jù)同步到ES中,列表查詢走ES。
基于我們目的是為了解決查詢問題,每次操作業(yè)務(wù)都會根據(jù)主鍵再查詢一次mysql庫詳情,數(shù)據(jù)遷移同步方案如下:
①存量數(shù)據(jù)如何同步?
首先增加一個開關(guān)來控制操作是走mysql還是es。先關(guān)閉開關(guān)然后通過批量同步接口,根據(jù)主鍵id范圍區(qū)間查詢把存量數(shù)據(jù)分批同步到ES中。打開開關(guān),這時如果有新的售后單數(shù)據(jù),通過MQ異步同步到ES中,同時把開關(guān)打開前產(chǎn)生的一部分?jǐn)?shù)據(jù)同步到ES中。最后再通過count總數(shù)校驗下數(shù)據(jù)是否全部同步。
②如何保證數(shù)據(jù)同步一致性?
涉及到同步數(shù)據(jù),難免就會有數(shù)據(jù)不一致問題。從售后單申請到售后單狀態(tài)變更,提交事務(wù)后每個節(jié)點都會發(fā)送一個需要同步的MQ消息。接收到消息后通過主鍵id查詢mysql獲取售后單詳情。然后全量字段同步到ES中。這樣不管先消費哪個節(jié)點的MQ,同步的數(shù)據(jù)都是實時查詢的數(shù)據(jù)庫,以此來保證每次同步的數(shù)據(jù)都是當(dāng)時最新數(shù)據(jù)。
③數(shù)據(jù)延遲怎么處理?
MQ消費有延時,就有可能造成ES和mysql中數(shù)據(jù)狀態(tài)不一致問題。我們只是為了解決查詢性能問題,因此所有復(fù)雜查詢都是查的ES數(shù)據(jù),但當(dāng)商家或者客服操作售后單時會根據(jù)主鍵查詢mysql售后單詳情,然后執(zhí)行審核操作。針對所有的業(yè)務(wù)操作后端也增加了前置狀態(tài)校驗,來屏蔽這種數(shù)據(jù)延時帶來的問題。
沒有最好的方案,只有最適用自己業(yè)務(wù)的方案。當(dāng)然現(xiàn)在也有一些工具類插件可以支持不同的同步方案,比如cancel基于binlog的同步以及CloudCanal。我們的目的是為了解決查詢效率問題,因此選擇了上面的同步方案。
3. 售后退貨
1)合單召喚物流配送方案
退貨退款售后單,商家或平臺審核通過后,需要退回訂單中貨物。這里就需要與達(dá)達(dá)交互,召喚配送員走逆向取件流程。在創(chuàng)建運單召喚達(dá)達(dá)配送前售后這邊會有一個合單邏輯。
?、俸蠁嗡枷?/p>
訂單完成后申請售后可以分多次申請,每次可以選擇不同數(shù)量的商品。如果用戶同一個訂單中商品分多次售后都申請為退貨,那么在售后單審核通過后這些售后的商品都需要配送員送回商家。這里為了提升用戶多次退貨體驗,也同時為了節(jié)約配送成本。因此就需要有一個合單邏輯,同一訂單下的售后單退貨只需召喚一次物流配送即可。
?、诤蠁芜壿?/p>
合單worker定時掃描待召喚物流的售后單,當(dāng)?shù)竭_(dá)用戶預(yù)計取件開始時間前10分鐘就會觸發(fā)需要合單的任務(wù)。合單任務(wù)會根據(jù)訂單號獲取此訂單下所有需合單的售后單,然后獲取預(yù)計取件開始時間最近的售后單。依據(jù)最近上門取件開始時間來創(chuàng)建物流運單。
?、蹌?chuàng)建運單
創(chuàng)建運單前需要前置狀態(tài)校驗,只處理待退貨售后單。然后組裝訂單下用戶基本信息,需要合單的所有售后單商品信息以及累計重量,創(chuàng)建運單。運單接口根據(jù)訂單號做冪等處理,重復(fù)調(diào)用會返回相同的運單號。
④接收結(jié)果
通過監(jiān)聽運單狀態(tài)消息,來同步更新配送員信息。
?、莓惓V卦?/p>
針對合單任務(wù)失敗數(shù)據(jù),記錄失敗標(biāo)識,等待下次合單worker執(zhí)行。記錄失敗次數(shù),如果超過失敗最大次數(shù),跳過合單并預(yù)警處理。避免一直合單失敗的數(shù)據(jù)影響正常合單業(yè)務(wù)數(shù)據(jù)。
4. 售后退款
1)退款準(zhǔn)確性問題
通過上面的流程圖了解了售后單審核退款到退款結(jié)束的一個過程。那么我們都做了哪些來保證審核退款的售后單金額是正確的呢?
①增加分布式鎖
商家角色審核退款可以通過商家中心、商家端APP、系統(tǒng)對接接口。同時客服端也可以通過運營平臺審核退款。
因為這里也涉及多端操作,所以這里的鎖主要為了防止重復(fù)審核退款。
審核退款時已經(jīng)確定是售后單維度,每個售后單只能審核退款一次,所以這里的key維度是售后單維度。并且獲取不到鎖直接拋出失敗,提示業(yè)務(wù)異常。
?、趩涡猩唐泛戏ㄐ孕r?/p>
為什么要做單行商品合法性校驗?zāi)??可以看下下面這個場景:
假設(shè)當(dāng)前訂單購買了1個A商品和2個B商品,A商品單價10元,B商品單價15元,整單金額40元。申請售后接口參數(shù)為:
skuList:[{"skuCount":1,"skuName":"skuA","procotionType":"1"},{"skuCount":1,"skuName":"skuA","promotionType":"1"}]
系統(tǒng)對接的商家通過到家開放平臺發(fā)布的售后接口創(chuàng)建售后單,由于開放平臺入口面對的是所有商家,每個商家系統(tǒng)對接能力不一樣,可以看出訂單中只買了1個A商品,但是傳了兩遍。正常我們的做法是解析入?yún)ist,然后校驗每一行商品的合法性。查詢當(dāng)前訂單已申請商品個數(shù),以及訂單中總商品個數(shù),然后與當(dāng)前審核售后單商品個數(shù)做比較。但是循環(huán)比較等于比較了兩次,每次個數(shù)都是1。而且由于2個商品A總額小于訂單總額,所以即使有后面的臺賬總額校驗,還是會造成多退情況。因此這里需要根據(jù)當(dāng)前申請商品總數(shù)加已申請此商品總數(shù)與訂單中商品總數(shù)做校驗。
?、塾唵闻_賬金額校驗
訂單臺賬金額校驗,是最后一道校驗,校驗的維度不同,是獲取每一項支付明細(xì)剩余可退金額。校驗當(dāng)前要退售后單金額與臺賬余額比較,必須小于等于臺賬余額。
?、墚惒酵丝罱Y(jié)果
審核退款后,通過異步接收退款mq來更新退款狀態(tài)。退款成功通知下游依賴系統(tǒng)。
總結(jié)
逆向售后的業(yè)務(wù)是依賴于正向訂單的,隨著正向單不同場景玩法的增加,售后需要支持的場景也在增多,我們也在不斷的迭代進(jìn)步。在這當(dāng)中也遇到了一些需要解決和完善的問題,比如售后系統(tǒng)沒有自己的網(wǎng)關(guān),這樣會造成業(yè)務(wù)邏輯維護(hù)多處,業(yè)務(wù)不閉環(huán)。整個售后業(yè)務(wù)中各種不同場景下邏輯配置都不同,我們也在規(guī)劃通過模板引擎配置做到智能化。最后也非常歡迎大家留言交流,共同進(jìn)步。
今天的分享就到這里了,想了解更多關(guān)于十大京東代運營公司、京東代運營等內(nèi)容,敬請關(guān)注火蝠電商官網(wǎng)。
本站部分文章及圖片來自互聯(lián)網(wǎng)及其他公眾平臺,版權(quán)歸原作者,如有侵權(quán)請聯(lián)系qq:1248031689,我們會在第一時間刪除!