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運營截圖相對而言比較簡單,我這邊就不貼出來了。
