Java生鮮電商平台-售后模塊的設計與架構
說明:任何一個的電商平台都有售后服務系統,那么對於我們這個生鮮的電商平台,售后系統需要思考以下幾個維度。
1. 買家的需求維度
說明:買家在平台上沒找到自己想要的東西,我們需要提供給他一個入口,告訴我們他有這個需求,我們進行改進。系統需要有記錄這種情況,同時也有回復客戶的情況。
2. 投訴入口
說明:有客戶性子比較急,他有問題,就會馬上打電話給客服,客服需要解答與回答,維護客戶關系。對於系統而言,需要記錄這種情況,然后分析問題與解決問題。
3. IM聊天入口
說明:客戶有時候也不想寫信息,也不想打電話,能否有一個時刻的IM聊天記錄呢?對於系統而言需要記錄這種信息,我們目前系統沒處理,采用的是微信,以及銷售人員的反饋機制。
4. 退貨問題
說明:售后系統中,退貨問題是最繁瑣的,買家存在以下兩種情況。
4.1 買家要錢不要貨。顧名思義,有些買家就是不要貨了,他需要我們退錢給他,這個配送端有一個一件退貨功能,錢退到買家的余額里面,下次可以繼續購買。
4.2 買家要貨不要錢,顧名思義,有些買家的確需要這個貨物,對於我們退錢給他,他是不接受的,因為他真的需要這種東西,你讓他再去買,客戶體驗非常差,可能 就沒有下次購物了。對於這種情況,我們用時間軸來繼續整個過程。(說明,由於這個系統設計到生鮮電商方面,其他的電商方面可能會不一樣。)
相關數據庫的設計與架構如下:
1. 買家平台建議信息表
CREATE TABLE `suggestion` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自動增加ID', `suggestion_content` varchar(1024) DEFAULT NULL COMMENT '建議內容', `suggestion_imgs` varchar(255) DEFAULT NULL COMMENT '多張圖片', `user_id` bigint(20) DEFAULT NULL COMMENT '所屬用戶ID', `create_time` datetime DEFAULT NULL COMMENT '創建時間', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=78 DEFAULT CHARSET=utf8 COMMENT='用戶對平台的建議';
說明: 平台建議表,是買家對平台的建議以及自己的需求的一個入口,可以是圖片與內容兩點。
比如說:他說我們送的菜有問題,很多爛的,那么他是需要拍圖片證明的。
2. 平台回復信息表
CREATE TABLE `suggestion_reply` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自動增加ID', `suggestion_id` bigint(20) DEFAULT NULL COMMENT '客戶的建議ID', `content` varchar(512) DEFAULT NULL COMMENT '回復的內容', `create_time` datetime DEFAULT NULL COMMENT '創建時間', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8 COMMENT='客戶建議回復信息表';
說明:作為一個平台,平台需要回復客戶的信息,買家也需要看到,當然這邊系統是不區分是買家還是賣家的,我們都是可以數據的處理的。
3. 售后系統時間軸的設計
說明:其實我們系統需要知道整個售后的過程的,比如買家什么時候發起的不要錢,要貨,然后師傅是什么時候知道這個消息的,如何進行售后的,他們會遇到什么問題,
當然這里面有很多的問題,系統可以做的事情其實是很少的,而我們需要做的是更多的事情。
CREATE TABLE `order_timeline` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自動增加ID', `item_id` bigint(20) DEFAULT NULL COMMENT '訂單項ID', `remarks` varchar(256) DEFAULT NULL COMMENT '備注', `create_time` datetime DEFAULT NULL COMMENT '創建時間', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1980 DEFAULT CHARSET=utf8 COMMENT='售后模塊,退換貨時間軸,針對的是某一個訂單項';
相關時間軸運營截圖如下:
整個業務不算復雜,需要的一種思路與解決思路的方案:
關於補貨流程:
補貨需求 業務需求: 當賣家主動點擊缺貨,則配送師傅看到這個異常訂單項,然后他有兩種選擇, 第一種補貨(有貨,他也想補或者客戶說要貨不要錢) 第二種不補貨(無貨可補,他不想補或者客戶說退錢等等) 第一種補貨業務: 1. 當配送師傅點擊已補貨,則把這個訂單項對應的金額從買家中直接扣除,前提是線上付款,如果這個訂單是線下付款,則不用處理扣款邏輯,直接修改狀態即可。同時記錄時間軸日志。 第二種不補貨業務: 2. 當師傅點擊不補貨,則這個訂單項不做任何扣款邏輯,不管線下還是線上,直接修改狀態即可,同時記錄時間軸日志。 補充說明:補貨與不補貨屬於互斥操作,即已補貨后不允許再出現不補貨,不補貨后不再允許出現補貨。按照規則來處理。
相關業務核心代碼如下:
/** * 訂單項退貨*/ @RestController @RequestMapping("/delivery") public class OrderReturnController extends BaseController { private static final Logger logger = LoggerFactory.getLogger(OrderReturnController.class); @Autowired private OrderItemService orderItemService; @Autowired private OrderReturnService orderReturnService; /** * 訂單項退貨 * */ @RequestMapping(value = "/order/return/item", method = { RequestMethod.GET, RequestMethod.POST }) public JsonResult orderReturnItem(HttpServletRequest request, HttpServletResponse response, @Param("itemId") Long itemId, @Param("deliveryId") Long deliveryId, @Param("status") int status) { try { if (itemId == null) { return new JsonResult(JsonResultCode.FAILURE, "item參數有誤", ""); } OrderItem orderItem = orderItemService.getOrderItemByItemId(itemId); if (orderItem == null) { return new JsonResult(JsonResultCode.FAILURE, "無此訂單項", ""); } String returnMsg = ""; if(status == BuyerStatus.THREE){ returnMsg = TimelineTemplate.return_MSG; }if(status == BuyerStatus.FOUR){ returnMsg = TimelineTemplate.BACK_MSG; }if(status == BuyerStatus.ZERO){ returnMsg = TimelineTemplate.OFF_MSG; } orderItemService.updateOrderItemStatus(itemId, status, deliveryId, returnMsg); return new JsonResult(JsonResultCode.SUCCESS, "操作成功", ""); } catch (Exception ex) { logger.error("[OrderReturnController][orderReturnItem] exception :", ex); return new JsonResult(JsonResultCode.FAILURE, "系統錯誤,請稍后重試", ""); } } /** * 退還列表 */ @RequestMapping(value = "/order/return/list", method = { RequestMethod.GET, RequestMethod.POST }) public JsonResult orderReturnList(HttpServletRequest request, HttpServletResponse response,@Param("deliveryId") Long deliveryId, @Param("status") int status) { try { // 組裝成為最終的列表結果 List<OrderReturnVo> listResult = new ArrayList<OrderReturnVo>(); List<OrderGoodsVo> goodsList = orderReturnService.getReturnOrderGoodsList(deliveryId,status); if (CollectionUtils.isEmpty(goodsList)) { return new JsonResult(JsonResultCode.SUCCESS, "查詢完成", listResult); } // 臨時參數,判斷時間 Map<String, List<OrderReturnEntity>> paramTimeMap = new HashMap<String, List<OrderReturnEntity>>(); // 過濾賣家 Map<String, List<OrderGoodsVo>> paramSellerMap = new HashMap<String, List<OrderGoodsVo>>(); for (OrderGoodsVo vo : goodsList) { String bestTime = DateUtil.dateToString(vo.getBestTime(), "yyyy-MM-dd"); // 時間相同 if (paramTimeMap.get(bestTime) != null) { List<OrderReturnEntity> mapOrderReturnEntity = paramTimeMap.get(bestTime); // 組裝時間 OrderReturnVo resultVo = new OrderReturnVo(); resultVo.setBestTime(bestTime); // 判斷是否是同一個賣家的 if (paramSellerMap.get(vo.getSellerName()) != null) { OrderReturnEntity entity = new OrderReturnEntity(); List<OrderGoodsVo> listVo = paramSellerMap.get(vo.getSellerName()); listVo.add(vo); entity.setListOrderGoodsVo(listVo); resultVo.setListOrderReturnEntity(mapOrderReturnEntity); }else { //不同買家 OrderReturnEntity entity = new OrderReturnEntity(); entity.setSellerName(vo.getSellerName()); List<OrderGoodsVo> listVo =new ArrayList<OrderGoodsVo>(); listVo.add(vo); entity.setListOrderGoodsVo(listVo); mapOrderReturnEntity.add(entity); paramSellerMap.put(vo.getSellerName(), listVo); } } else { // 組裝時間 OrderReturnVo resultVo = new OrderReturnVo(); resultVo.setBestTime(bestTime); OrderReturnEntity entity = new OrderReturnEntity(); entity.setSellerName(vo.getSellerName()); List<OrderGoodsVo> paramOrderGoodsVo = new ArrayList<OrderGoodsVo>(); paramOrderGoodsVo.add(vo); entity.setListOrderGoodsVo(paramOrderGoodsVo); List<OrderReturnEntity> listOrderReturnEntity = new ArrayList<OrderReturnEntity>(); listOrderReturnEntity.add(entity); resultVo.setListOrderReturnEntity(listOrderReturnEntity); listResult.add(resultVo); paramSellerMap.put(vo.getSellerName(), paramOrderGoodsVo); paramTimeMap.put(bestTime, listOrderReturnEntity); } } return new JsonResult(JsonResultCode.SUCCESS, "查詢信息成功", listResult); } catch (Exception ex) { logger.error("[OrderReturnController][orderReturnItem] exception :", ex); return new JsonResult(JsonResultCode.FAILURE, "系統錯誤,請稍后重試", ""); } }
APP運營截圖相對而言比較簡單,我這邊就不貼出來了。