portal下单流程

portal是面向货主的一个门户网站。客户可以在portal上新增订单。现在要实现的就是portal新建的订单(包括出库单和入库单),能同步到OMS系统和WMS系统。

一、最初的方案:

OMS已经有一套对接ERP系统的接口,包括接收入库单和出库单。可谓前人种树后人乘凉,原本是按照约定参数拼起来直接调调就可以了。可是呢,测试着测试着发现荆棘满坑啊。

portal点击确认下单(出库单或者入库单),直接调用oms的接单接口(oms保存订单,同时推送订单到wms)。这种方式由于使用到分布式事务,太耗时,执行时长能到到30s以上,调用时总是出现超时发生熔断。

显而易见,此情此景简单的拿来主义是不行了,开始使用MQ,拆。

二、改进版本一:业务拆分,portal下单,不再直接调用接口,而是发送消息,消息主题:oms_receive_order。

1.增加portal订单状态:已下单、接单成功、接单失败。

当portal点击确认下单后,修改状态:已下单。

2.消费方OmsReceiveOrderConsumer:调用oms接单接口(oms保存订单,同时推送订单到wms)。当wms订单保存成功后,发消息回写接单成功,消息主题:storage_order_receive。

3.消费方StorageOrderReceiveConsumer消费消息,修改portal出库单状态(已接单或者接单失败)。

这样改进,portal订单下单操作不在超时,能立即响应。但是依旧不能保存wms订单,和上面原因一致。

继续拆分。

三、改进版本二:继续拆分。

1. portal确认下单,发送消息到MQ,主题:storage_portal_to_oms。

当portal点击确认下单后,修改状态:已下单。
2. 消费方StoragePortalToOmsConsumer消费消息,调用oms业务接口:生成订单接口。

oms生成订单接口:发消息到MQ(主题:storage_oms_to_wms),修改oms订单状态:已下发。

3. 消费方StorageOmsToWmsConsumer消费消息。

3.1 调用wms业务接口:生成wms订单。

3.1 消费方,起个线程更改portal订单状态。

使用线程池:ExecutorService executorService = Executors.newFixedThreadPool(10);

3.2 wms生成订单失败或者portal修改订单失败,会发消息到钉钉。

四、测试消息发送

1.测试工具:postman

本地启动mq服务,请求地址:127.0.0.1:8004/satMq/mqProduce

post请求body:{
    "mqTypeEnum": "STORAGE_PORTAL_TO_OMS",
    "code": "6",
    "data": {"updateUser":0,"state":4,"customerOrderNo":"CKYWHS1201907310003"}
}

StoragePortalToOmsConsumer消费方就能接收到消息,进行消费。

 

2. 可以使用xxlmessage 页面管理工具,进行消息测试。

xxlmessage联调地址:http://192.168.173.21:8070/message

可以添加一条消息:消息主题、消息分组:default;消息数据;状态:NEW,点击保存就可以发送消息。

也可以重发消息,将发送失败的消息,状态改成NEW,就可以重发消息。

五、附录: 贴一下关键代码

5.1. 生产者

//通知oms接单
        MqDTO mqDTO = new MqDTO();
        mqDTO.setMqTypeEnum(STORAGE_PORTAL_TO_OMS);
        mqDTO.setData(JSONObject.toJSONString(receiveOrderPortalDTO));

        if (!mqProducerApi.produer(mqDTO)){
		distributedLocker.unlock(String.valueOf(LockTypeEnum.STORAGE_SYNC_OMS.getCode()) + id);
            throw new BaseException("oms入库单接单失败");
        }

 

5.2.消费者

@Service
@Slf4j
@MqConsumer(topic = "storage_portal_to_oms")
public class StoragePortalToOmsConsumer implements IMqConsumer{
    @Resource
    ReceiveOrderApi receiveOrderApi;

    /** 入库单,portal推送入库单到oms*/
    @Override
    public MqResult consume(String data) {
        ReceiveOrderPortalDTO dto = JSONObject.parseObject(data,ReceiveOrderPortalDTO.class);
        BaseResultVo vo = receiveOrderApi.receiveOrderInsert(dto);
        if (vo.getStatus() == BaseResultVo.SUCCESS_CODE){
            return MqResult.SUCCESS;
        }
        return MqResult.FAIL;
    }
}

 

 5.3 oms订单同步到wms

  // 收货订单直接同步到wms
           BaseResultVo vo =  asnApi.syncOmsOrder(asnDTO);
           Callable<BaseResultVo> c = new Callable<BaseResultVo>() {
               @Override
               public BaseResultVo call() throws Exception {
                   JSONObject json = new JSONObject();
                   if (BaseResultVo.SUCCESS_CODE.equals(vo.getStatus())) {
                       //接单成功
                       json.put("status", OrderEnum.StorageOrderStatusEnum.order_received.getId());
                   } else {
                       //接单失败
                       json.put("status", OrderEnum.StorageOrderStatusEnum.fail_received.getId());
                   }

                   json.put("customerOrderNo", asnDTO.getCustomerOrderNo());
                   json.put("updateUser", 0);
                   BaseResultVo resultVo = ptStorageOrderApi.updateStatus(json.toJSONString());
                   return resultVo;
               }

           };

           Future<BaseResultVo> ft = executorService.submit(c);
           Long startTime = System.currentTimeMillis();
           while (true) {
               if (System.currentTimeMillis() - startTime > 5000) {
                   executorService.shutdown();
                   //接单失败,发送钉钉
                   dingDingApi.sendDingMsgForService("storage_oms_to_wms 消费超时", "15967103436");
                   log.error("storage_oms_to_wms 消费超时:" + asnDTO.getCustomerOrderNo());
                   return MqResult.FAIL;
               }

               if (ft.isDone()){
                   BaseResultVo resultVo = ft.get();
                   if(resultVo.getStatus() == BaseResultVo.ERROR_CODE){
                       //接单失败,发送钉钉
                       dingDingApi.sendDingMsgForService("storage_oms_to_wms 接单失败", "15967103436");
                       return MqResult.FAIL;
                   }
                   return MqResult.SUCCESS;
               }
           }

5.4 更新订单状态时,使用数据库状态机做幂等。

int updateStatusCallBack(@Param("outboundNo") String outboundNo, @Param("state")  Integer state, @Param("updateUser") Long updateUser);
<update id="updateStatusCallBack">
		update pt_storage_order
		set update_user = #{updateUser,jdbcType=BIGINT},update_time=now(),
		status = (case when status=2 and #{status} in (3,5) then #{status,jdbcType=INTEGER}
		when status=3 and #{status}=4 then 4
		when status=4 and #{status}=6 then 6
		else status end)
		where storage_no= #{storageNo,jdbcType=VARCHAR}
	</update>

出库单流程同上。

附上流程图:

上面略去了失败,发送钉钉消息的流程。

待考虑:

1. xxl消息失败告警:支持以Topic粒度监控消息,存在失败消息时主动推送告警邮件;默认提供邮件方式失败告警,同时预留扩展接口,可方面的扩展短信、钉钉等告警方式;

目前是按照消息的维度,程序中主动调用接口发送钉钉消息。

但是没有配置消息的重试次数, 即失败了就不再重试。目前的失败补偿策略就是发送钉钉消息,人工干预。不知道将来是否考虑增加重试,譬如衰减方式重试。

2. 订单处理的过程中多次用到了 一级单位和二级单位的转换。portal、oms、wms三个系统都有涉及。是否能一个地方转,其他都不做处理。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值