Commit 2266e9a9e680d98ebcfe13c7a7ad71e3fde58935

Authored by shuhongfan
1 parent 59c1b151

day07-智能调度之调度任务

sl-express-ms-dispatch-service/src/main/java/com/sl/ms/dispatch/mq/TransportOrderDispatchMQListener.java
1 1 package com.sl.ms.dispatch.mq;
2 2  
  3 +import cn.hutool.core.map.MapUtil;
  4 +import cn.hutool.core.util.ObjectUtil;
3 5 import cn.hutool.core.util.StrUtil;
  6 +import cn.hutool.json.JSONUtil;
  7 +import com.sl.ms.dispatch.dto.DispatchMsgDTO;
4 8 import com.sl.transport.common.constant.Constants;
5 9 import lombok.extern.slf4j.Slf4j;
6 10 import org.springframework.amqp.core.ExchangeTypes;
... ... @@ -37,7 +41,35 @@ public class TransportOrderDispatchMQListener {
37 41 // {"transportOrderId":"SL1000000000560","currentAgencyId":100280,"nextAgencyId":90001,"totalWeight":3.5,"totalVolume":2.1,"created":1652337676330}
38 42 log.info("接收到新运单的消息 >>> msg = {}", msg);
39 43  
40   - //TODO 待实现
  44 + DispatchMsgDTO dispatchMsgDTO = JSONUtil.toBean(msg, DispatchMsgDTO.class);
  45 + if (ObjectUtil.isEmpty(dispatchMsgDTO)) {
  46 + return;
  47 + }
  48 +
  49 + Long startId = dispatchMsgDTO.getCurrentAgencyId();
  50 + Long endId = dispatchMsgDTO.getNextAgencyId();
  51 + String transportOrderId = dispatchMsgDTO.getTransportOrderId();
  52 +
  53 +// 消息幂等性处理,将相同起始节点的运单存放到set结构的redis中,在相应的运单处理完成之后将其删除掉
  54 + String setRedisKey = getSetRedisKey(startId, endId);
  55 + if (Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember(setRedisKey, transportOrderId))) {
  56 +// 重复消息
  57 + return;
  58 + }
  59 +
  60 +// 存储数据到Redis,采用list结构,从左边写入数据,读取数据时从右边读取
  61 + //key => DISPATCH_LIST_CurrentAgencyId_NextAgencyId
  62 + //value => {"transportOrderId":111222, "totalVolume":0.8, "totalWeight":2.1, "created":111222223333}
  63 + String listRedisKey = getListRedisKey(startId, endId);
  64 + String value = JSONUtil.toJsonStr(MapUtil.builder()
  65 + .put("transportOrderId", transportOrderId)
  66 + .put("totalVolume", dispatchMsgDTO.getTotalVolume())
  67 + .put("totalWeight", dispatchMsgDTO.getTotalWeight())
  68 + .put("created", dispatchMsgDTO.getCreated()).build());
  69 +
  70 +// 存储到redis
  71 + stringRedisTemplate.opsForList().leftPush(listRedisKey, value);
  72 + stringRedisTemplate.opsForSet().add(setRedisKey, transportOrderId);
41 73 }
42 74  
43 75 public String getListRedisKey(Long startId, Long endId) {
... ...
sl-express-ms-work-service/src/main/java/com/sl/ms/work/mq/CourierMQListener.java
1 1 package com.sl.ms.work.mq;
2 2  
3 3 import cn.hutool.json.JSONUtil;
  4 +import com.sl.ms.work.service.PickupDispatchTaskService;
  5 +import com.sl.ms.work.service.TransportOrderService;
4 6 import com.sl.transport.common.constant.Constants;
5 7 import com.sl.transport.common.vo.CourierMsg;
6 8 import com.sl.transport.common.vo.CourierTaskMsg;
... ... @@ -10,6 +12,7 @@ import org.springframework.amqp.rabbit.annotation.Exchange;
10 12 import org.springframework.amqp.rabbit.annotation.Queue;
11 13 import org.springframework.amqp.rabbit.annotation.QueueBinding;
12 14 import org.springframework.amqp.rabbit.annotation.RabbitListener;
  15 +import org.springframework.beans.factory.annotation.Autowired;
13 16 import org.springframework.stereotype.Component;
14 17  
15 18 /**
... ... @@ -21,6 +24,13 @@ import org.springframework.stereotype.Component;
21 24 @Slf4j
22 25 public class CourierMQListener {
23 26  
  27 + @Autowired
  28 + private PickupDispatchTaskService pickupDispatchTaskService;
  29 +
  30 + @Autowired
  31 + private TransportOrderService transportOrderService;
  32 +
  33 +
24 34 /**
25 35 * 生成快递员取派件任务
26 36 *
... ... @@ -37,7 +47,8 @@ public class CourierMQListener {
37 47 //解析消息
38 48 CourierTaskMsg courierTaskMsg = JSONUtil.toBean(msg, CourierTaskMsg.class);
39 49  
40   - //TODO 未实现具体逻辑
  50 +// 订单转运单
  51 + transportOrderService.orderToTransportOrder(courierTaskMsg.getOrderId());
41 52 }
42 53  
43 54 /**
... ...
sl-express-ms-work-service/src/main/java/com/sl/ms/work/service/impl/TransportOrderServiceImpl.java 0 → 100644
  1 +package com.sl.ms.work.service.impl;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.collection.ListUtil;
  5 +import cn.hutool.core.convert.Convert;
  6 +import cn.hutool.core.date.DateField;
  7 +import cn.hutool.core.date.DateUtil;
  8 +import cn.hutool.core.map.MapUtil;
  9 +import cn.hutool.core.text.CharSequenceUtil;
  10 +import cn.hutool.core.util.NumberUtil;
  11 +import cn.hutool.core.util.ObjectUtil;
  12 +import cn.hutool.core.util.StrUtil;
  13 +import cn.hutool.json.JSONUtil;
  14 +import com.alibaba.csp.sentinel.datasource.Converter;
  15 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  16 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  17 +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  18 +import com.sl.ms.base.api.common.MQFeign;
  19 +import com.sl.ms.oms.api.OrderFeign;
  20 +import com.sl.ms.oms.dto.OrderCargoDTO;
  21 +import com.sl.ms.oms.dto.OrderDetailDTO;
  22 +import com.sl.ms.oms.dto.OrderLocationDTO;
  23 +import com.sl.ms.transport.api.OrganFeign;
  24 +import com.sl.ms.transport.api.TransportLineFeign;
  25 +import com.sl.ms.work.domain.dto.TransportOrderDTO;
  26 +import com.sl.ms.work.domain.dto.request.TransportOrderQueryDTO;
  27 +import com.sl.ms.work.domain.dto.response.TransportOrderStatusCountDTO;
  28 +import com.sl.ms.work.domain.enums.WorkExceptionEnum;
  29 +import com.sl.ms.work.domain.enums.pickupDispatchtask.PickupDispatchTaskType;
  30 +import com.sl.ms.work.domain.enums.transportorder.TransportOrderSchedulingStatus;
  31 +import com.sl.ms.work.domain.enums.transportorder.TransportOrderStatus;
  32 +import com.sl.ms.work.entity.TransportOrderEntity;
  33 +import com.sl.ms.work.entity.TransportOrderTaskEntity;
  34 +import com.sl.ms.work.mapper.TransportOrderMapper;
  35 +import com.sl.ms.work.mapper.TransportOrderTaskMapper;
  36 +import com.sl.ms.work.service.TransportOrderService;
  37 +import com.sl.transport.common.constant.Constants;
  38 +import com.sl.transport.common.enums.IdEnum;
  39 +import com.sl.transport.common.exception.SLException;
  40 +import com.sl.transport.common.service.IdService;
  41 +import com.sl.transport.common.util.DateUtils;
  42 +import com.sl.transport.common.util.PageResponse;
  43 +import com.sl.transport.common.vo.OrderMsg;
  44 +import com.sl.transport.common.vo.TransportInfoMsg;
  45 +import com.sl.transport.common.vo.TransportOrderMsg;
  46 +import com.sl.transport.common.vo.TransportOrderStatusMsg;
  47 +import com.sl.transport.domain.OrganDTO;
  48 +import com.sl.transport.domain.TransportLineNodeDTO;
  49 +import org.springframework.beans.factory.annotation.Autowired;
  50 +import org.springframework.stereotype.Service;
  51 +import org.springframework.transaction.annotation.Transactional;
  52 +
  53 +import java.time.LocalDateTime;
  54 +import java.util.Arrays;
  55 +import java.util.Date;
  56 +import java.util.List;
  57 +import java.util.Map;
  58 +import java.util.stream.Collectors;
  59 +
  60 +@Service
  61 +public class TransportOrderServiceImpl extends
  62 + ServiceImpl<TransportOrderMapper, TransportOrderEntity> implements TransportOrderService {
  63 +
  64 +
  65 + @Autowired
  66 + private OrderFeign orderFeign;
  67 +
  68 + @Autowired
  69 + private TransportLineFeign transportLineFeign;
  70 +
  71 + @Autowired
  72 + private IdService idService;
  73 +
  74 + @Autowired
  75 + private MQFeign mqFeign;
  76 + @Autowired
  77 + private OrganFeign organFeign;
  78 +
  79 + @Override
  80 + @Transactional(rollbackFor = Exception.class)
  81 + public TransportOrderEntity orderToTransportOrder(Long orderId) {
  82 +// 幂等性校验
  83 + TransportOrderEntity transportOrderEntity = this.findByOrderId(orderId);
  84 + if (ObjectUtil.isNotEmpty(transportOrderEntity)) {
  85 + return transportOrderEntity;
  86 + }
  87 +
  88 +// 查询订单
  89 + OrderDetailDTO detailByOrderId = this.orderFeign.findDetailByOrderId(orderId);
  90 + if (ObjectUtil.isEmpty(detailByOrderId)) {
  91 + throw new SLException(WorkExceptionEnum.ORDER_NOT_FOUND);
  92 + }
  93 +
  94 +// 校验货物的重量和体积数据
  95 + OrderCargoDTO cargoDTO = detailByOrderId.getOrderDTO().getOrderCargoDto();
  96 + if (ObjectUtil.isEmpty(cargoDTO)) {
  97 + throw new SLException(WorkExceptionEnum.ORDER_CARGO_NOT_FOUND);
  98 + }
  99 +
  100 +// 检验位置信息
  101 + OrderLocationDTO orderLocationDTO = detailByOrderId.getOrderLocationDTO();
  102 + if (ObjectUtil.isEmpty(orderLocationDTO)) {
  103 + throw new SLException(WorkExceptionEnum.ORDER_LOCATION_NOT_FOUND);
  104 + }
  105 +
  106 +// 起始网点ID
  107 + Long sendAgentId = Convert.toLong(orderLocationDTO.getSendAgentId());
  108 +// 终点网点ID
  109 + Long receiveAgentId = Convert.toLong(orderLocationDTO.getReceiveAgentId());
  110 +
  111 +// 默认参与调度
  112 + boolean isDispatch = true;
  113 + TransportLineNodeDTO transportLineNodeDTO = null;
  114 + if (ObjectUtil.equal(sendAgentId, receiveAgentId)) {
  115 +// 起点终点是同一个网点,不需要规划路线,直接发消息生成派件任务即可
  116 + isDispatch = false;
  117 + } else {
  118 +// 根据起点机构规划运输路线
  119 + transportLineNodeDTO = this.transportLineFeign.queryPathByDispatchMethod(sendAgentId, receiveAgentId);
  120 + if (ObjectUtil.isEmpty(transportLineNodeDTO) || CollUtil.isEmpty(transportLineNodeDTO.getNodeList())) {
  121 + throw new SLException(WorkExceptionEnum.TRANSPORT_LINE_NOT_FOUND);
  122 + }
  123 + }
  124 +
  125 +// 创建新的运单对象
  126 + TransportOrderEntity transportOrder = new TransportOrderEntity();
  127 +// 设置ID
  128 + transportOrder.setId(idService.getId(IdEnum.TRANSPORT_ORDER));
  129 +// 订单ID
  130 + transportOrder.setOrderId(orderId);
  131 +// 起始网点ID
  132 + transportOrder.setStartAgencyId(sendAgentId);
  133 +// 终点网点ID
  134 + transportOrder.setEndAgencyId(receiveAgentId);
  135 +// 当前所在机构ID
  136 + transportOrder.setCurrentAgencyId(sendAgentId);
  137 +
  138 +
  139 + if (ObjectUtil.isNotEmpty(transportLineNodeDTO)) {
  140 + //运单状态(1.新建 2.已装车 3.运输中 4.到达终端网点 5.已签收 6.拒收)
  141 + transportOrder.setStatus(TransportOrderStatus.CREATED);
  142 + //调度状态(1.待调度2.未匹配线路3.已调度)
  143 + transportOrder.setSchedulingStatus(TransportOrderSchedulingStatus.TO_BE_SCHEDULED);
  144 + //下一个机构id
  145 + transportOrder.setNextAgencyId(transportLineNodeDTO.getNodeList().get(1).getId());
  146 + //完整的运输路线
  147 + transportOrder.setTransportLine(JSONUtil.toJsonStr(transportLineNodeDTO));
  148 + } else {
  149 +// 下个网点就是当前网点
  150 + transportOrder.setNextAgencyId(sendAgentId);
  151 + //运单状态(1.新建 2.已装车 3.运输中 4.到达终端网点 5.已签收 6.拒收)
  152 + transportOrder.setStatus(TransportOrderStatus.ARRIVED_END);
  153 + //调度状态(1.待调度2.未匹配线路3.已调度)
  154 + transportOrder.setSchedulingStatus(TransportOrderSchedulingStatus.SCHEDULED);
  155 + }
  156 +
  157 + boolean result = super.save(transportOrder);
  158 + if (result) {
  159 + if (result) {
  160 + if (isDispatch) {
  161 +// 发送消息到调度中心,进行调度
  162 + sendTransportOrderMsgToDispatch(transportOrder);
  163 + } else {
  164 + // 不需要调度 发送消息更新订单状态
  165 + this.sendUpdateStatusMsg(ListUtil.toList(transportOrder.getId()), TransportOrderStatus.ARRIVED_END);
  166 + //不需要调度,发送消息生成派件任务
  167 + this.sendDispatchTaskMsgToDispatch(transportOrder);
  168 + }
  169 +
  170 +// 发消息通知其他系统,运单已经生成
  171 + String msg = TransportOrderMsg.builder()
  172 + .id(transportOrder.getId())
  173 + .orderId(transportOrder.getOrderId())
  174 + .created(DateUtil.current())
  175 + .build().toJson();
  176 +
  177 + mqFeign.sendMsg(Constants.MQ.Exchanges.TRANSPORT_ORDER_DELAYED,
  178 + Constants.MQ.RoutingKeys.TRANSPORT_ORDER_CREATE,
  179 + msg,
  180 + Constants.MQ.NORMAL_DELAY);
  181 +
  182 + return transportOrder;
  183 + }
  184 + }
  185 + throw new SLException(WorkExceptionEnum.TRANSPORT_ORDER_SAVE_ERROR);
  186 + }
  187 +
  188 + private void sendUpdateStatusMsg(List<String> ids, TransportOrderStatus transportOrderStatus) {
  189 + String msg = TransportOrderStatusMsg.builder()
  190 + .idList(ids)
  191 + .statusName(transportOrderStatus.name())
  192 + .statusCode(transportOrderStatus.getCode())
  193 + .build()
  194 + .toJson();
  195 +
  196 +// 将状态名称写入到KEY中,方便消费者选择性的接受消息
  197 + String routingKey = Constants.MQ.RoutingKeys.TRANSPORT_ORDER_UPDATE_STATUS_PREFIX + transportOrderStatus.name();
  198 + mqFeign.sendMsg(Constants.MQ.Exchanges.TRANSPORT_ORDER_DELAYED,
  199 + routingKey,
  200 + msg,
  201 + Constants.MQ.LOW_DELAY);
  202 + }
  203 +
  204 + /**
  205 + * 发送运单消息到调度中,参与调度
  206 + */
  207 + private void sendTransportOrderMsgToDispatch(TransportOrderEntity transportOrder) {
  208 + Map<Object, Object> msg = MapUtil.builder()
  209 + .put("transportOrderId", transportOrder.getId())
  210 + .put("currentAgencyId", transportOrder.getCurrentAgencyId())
  211 + .put("nextAgencyId", transportOrder.getNextAgencyId())
  212 + .put("totalWeight", transportOrder.getTotalWeight())
  213 + .put("totalVolume", transportOrder.getTotalVolume())
  214 + .put("created", System.currentTimeMillis()).build();
  215 + String jsonMsg = JSONUtil.toJsonStr(msg);
  216 +
  217 +// 发送消息,延迟5秒,确保本地事务已经提交,可以查询到数据
  218 + mqFeign.sendMsg(Constants.MQ.Exchanges.TRANSPORT_ORDER_DELAYED,
  219 + Constants.MQ.RoutingKeys.JOIN_DISPATCH,
  220 + jsonMsg,
  221 + Constants.MQ.LOW_DELAY);
  222 + }
  223 +
  224 + /**
  225 + * 发送生成取派件任务的消息
  226 + *
  227 + * @param transportOrder 运单对象
  228 + */
  229 + private void sendDispatchTaskMsgToDispatch(TransportOrderEntity transportOrder) {
  230 + //预计完成时间,如果是中午12点到的快递,当天22点前,否则,第二天22点前
  231 + int offset = 0;
  232 + if (LocalDateTime.now().getHour() >= 12) {
  233 + offset = 1;
  234 + }
  235 + LocalDateTime estimatedEndTime = DateUtil.offsetDay(new Date(), offset)
  236 + .setField(DateField.HOUR_OF_DAY, 22)
  237 + .setField(DateField.MINUTE, 0)
  238 + .setField(DateField.SECOND, 0)
  239 + .setField(DateField.MILLISECOND, 0)
  240 + .toLocalDateTime();
  241 +
  242 +// 发送分配快递员派件任务的消息
  243 + OrderMsg orderMsg = OrderMsg.builder()
  244 + .agencyId(transportOrder.getCurrentAgencyId())
  245 + .orderId(transportOrder.getOrderId())
  246 + .created(DateUtil.current())
  247 + .taskType(PickupDispatchTaskType.DISPATCH.getCode())
  248 + .mark("系统提示:派件请与收件人电话联系。")
  249 + .estimatedEndTime(estimatedEndTime)
  250 + .build();
  251 +
  252 +// 发送消息
  253 + sendPickupDispatchTaskMsgToDispatch(transportOrder, orderMsg);
  254 + }
  255 +
  256 + /**
  257 + * 发送消息到调度中心,用于生成取派件任务
  258 + *
  259 + * @param transportOrder 运单
  260 + * @param orderMsg 消息内容
  261 + */
  262 + @Override
  263 + public void sendPickupDispatchTaskMsgToDispatch(TransportOrderEntity transportOrder, OrderMsg orderMsg) {
  264 +// 查询订单对应的位置信息
  265 + OrderLocationDTO orderLocationDTO = orderFeign.findOrderLocationByOrderId(orderMsg.getOrderId());
  266 +
  267 + //(1)运单为空:取件任务取消,取消原因为返回网点;重新调度位置取寄件人位置
  268 + //(2)运单不为空:生成的是派件任务,需要根据拒收状态判断位置是寄件人还是收件人
  269 + // 拒收:寄件人 其他:收件人
  270 + String location;
  271 + if (ObjectUtil.isEmpty(transportOrder)) {
  272 + location = orderLocationDTO.getSendLocation();
  273 + } else {
  274 + location = transportOrder.getIsRejection() ? orderLocationDTO.getSendLocation() : orderLocationDTO.getReceiveLocation();
  275 + }
  276 +
  277 + Double[] coordinate = Convert.convert(Double[].class, StrUtil.split(location, ","));
  278 + Double longitude = coordinate[0];
  279 + Double latitude = coordinate[1];
  280 +
  281 +// 设置消息中的位置信息
  282 + orderMsg.setLongitude(longitude);
  283 + orderMsg.setLatitude(latitude);
  284 +
  285 +// 发送消息,用于生产取派件任务
  286 + mqFeign.sendMsg(Constants.MQ.Exchanges.ORDER_DELAYED,
  287 + Constants.MQ.RoutingKeys.ORDER_CREATE,
  288 + orderMsg.toJson(),
  289 + Constants.MQ.NORMAL_DELAY);
  290 +
  291 + }
  292 +
  293 + /**
  294 + * 获取运单分页数据
  295 + *
  296 + * @return 运单分页数据
  297 + */
  298 + @Override
  299 + public Page<TransportOrderEntity> findByPage(TransportOrderQueryDTO transportOrderQueryDTO) {
  300 + Page<TransportOrderEntity> iPage = new Page<>(transportOrderQueryDTO.getPage(), transportOrderQueryDTO.getPageSize());
  301 +
  302 + //设置查询条件
  303 + LambdaQueryWrapper<TransportOrderEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
  304 + lambdaQueryWrapper.like(ObjectUtil.isNotEmpty(transportOrderQueryDTO.getId()), TransportOrderEntity::getId, transportOrderQueryDTO.getId());
  305 + lambdaQueryWrapper.like(ObjectUtil.isNotEmpty(transportOrderQueryDTO.getOrderId()), TransportOrderEntity::getOrderId, transportOrderQueryDTO.getOrderId());
  306 + lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(transportOrderQueryDTO.getStatus()), TransportOrderEntity::getStatus, transportOrderQueryDTO.getStatus());
  307 + lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(transportOrderQueryDTO.getSchedulingStatus()), TransportOrderEntity::getSchedulingStatus, transportOrderQueryDTO.getSchedulingStatus());
  308 +
  309 + lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(transportOrderQueryDTO.getStartAgencyId()), TransportOrderEntity::getStartAgencyId, transportOrderQueryDTO.getStartAgencyId());
  310 + lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(transportOrderQueryDTO.getEndAgencyId()), TransportOrderEntity::getEndAgencyId, transportOrderQueryDTO.getEndAgencyId());
  311 + lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(transportOrderQueryDTO.getCurrentAgencyId()), TransportOrderEntity::getCurrentAgencyId, transportOrderQueryDTO.getCurrentAgencyId());
  312 + lambdaQueryWrapper.orderByDesc(TransportOrderEntity::getCreated);
  313 +
  314 + return super.page(iPage, lambdaQueryWrapper);
  315 + }
  316 +
  317 + /**
  318 + * 通过订单id获取运单信息
  319 + *
  320 + * @param orderId 订单id
  321 + * @return 运单信息
  322 + */
  323 + @Override
  324 + public TransportOrderEntity findByOrderId(Long orderId) {
  325 + LambdaQueryWrapper<TransportOrderEntity> queryWrapper = new LambdaQueryWrapper<>();
  326 + queryWrapper.eq(TransportOrderEntity::getOrderId, orderId);
  327 + return super.getOne(queryWrapper);
  328 + }
  329 +
  330 + /**
  331 + * 通过订单id列表获取运单列表
  332 + *
  333 + * @param orderIds 订单id列表
  334 + * @return 运单列表
  335 + */
  336 + @Override
  337 + public List<TransportOrderEntity> findByOrderIds(Long[] orderIds) {
  338 + LambdaQueryWrapper<TransportOrderEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
  339 + lambdaQueryWrapper.in(TransportOrderEntity::getOrderId, orderIds);
  340 + return super.list(lambdaQueryWrapper);
  341 + }
  342 +
  343 + /**
  344 + * 通过运单id列表获取运单列表
  345 + *
  346 + * @param ids 订单id列表
  347 + * @return 运单列表
  348 + */
  349 + @Override
  350 + public List<TransportOrderEntity> findByIds(String[] ids) {
  351 + LambdaQueryWrapper<TransportOrderEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
  352 + lambdaQueryWrapper.in(TransportOrderEntity::getId, ids);
  353 + return super.list(lambdaQueryWrapper);
  354 + }
  355 +
  356 + /**
  357 + * 根据运单号搜索运单
  358 + *
  359 + * @param id 运单号
  360 + * @return 运单列表
  361 + */
  362 + @Override
  363 + public List<TransportOrderEntity> searchById(String id) {
  364 + LambdaQueryWrapper<TransportOrderEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
  365 + lambdaQueryWrapper.like(TransportOrderEntity::getId, id);
  366 + return super.list(lambdaQueryWrapper);
  367 + }
  368 +
  369 + /**
  370 + * 修改运单状态
  371 + * ● 如果更新运单为拒收状态,需要将快递退回去,也就是原路返回
  372 + * ● 在更新状态时,需要同步更新物流信息,通过发送消息的方式完成(先TODO,后面实现)
  373 + * ● 更新状态后需要通知其他系统(消息通知)
  374 + *
  375 + * @param ids 运单id列表
  376 + * @param transportOrderStatus 修改的状态
  377 + * @return 是否成功
  378 + */
  379 + @Override
  380 + public boolean updateStatus(List<String> ids, TransportOrderStatus transportOrderStatus) {
  381 + if (CollUtil.isEmpty(ids)) {
  382 + return false;
  383 + }
  384 +
  385 + if (TransportOrderStatus.CREATED == transportOrderStatus) {
  386 +// 修改订单状态不能为新建状态
  387 + throw new SLException(WorkExceptionEnum.TRANSPORT_ORDER_STATUS_NOT_CREATED);
  388 + }
  389 +
  390 + List<TransportOrderEntity> transportOrderEntityList;
  391 +
  392 +// 判断是否为拒收状态,如果是拒收状态需要重新查询路线,将包裹逆向回去
  393 + if (TransportOrderStatus.REJECTED == transportOrderStatus) {
  394 +// 查询运单列表
  395 + transportOrderEntityList = super.listByIds(ids);
  396 + for (TransportOrderEntity transportOrder : transportOrderEntityList) {
  397 +// 设置为拒收状态
  398 + transportOrder.setIsRejection(true);
  399 +// 根据起始机构规划运输路线,这里要将起点和终点互换
  400 + Long sendAgentId = transportOrder.getEndAgencyId();
  401 + Long receiveAgentId = transportOrder.getStartAgencyId();
  402 +
  403 +// 默认参与调度
  404 + boolean isDispatch = true;
  405 + if (ObjectUtil.equal(sendAgentId, receiveAgentId)) {
  406 +// 相同节点,无需调度,直接删除派件任务
  407 + isDispatch = false;
  408 + } else {
  409 + TransportLineNodeDTO transportLineNodeDTO = transportLineFeign.queryPathByDispatchMethod(sendAgentId, receiveAgentId);
  410 + if (ObjectUtil.hasEmpty(transportLineNodeDTO, transportLineNodeDTO.getNodeList())) {
  411 + throw new SLException(WorkExceptionEnum.TRANSPORT_LINE_NOT_FOUND);
  412 + }
  413 +
  414 +// 删除掉第一个机构,你想回去第一个节点就是当前所有在节点
  415 + transportLineNodeDTO.getNodeList().remove(0);
  416 + transportOrder.setSchedulingStatus(TransportOrderSchedulingStatus.TO_BE_SCHEDULED);
  417 +// 当前所在机构ID
  418 + transportOrder.setCurrentAgencyId(sendAgentId);
  419 +// 下一个机构ID
  420 + transportOrder.setNextAgencyId(transportLineNodeDTO.getNodeList().get(0).getId());
  421 +
  422 +// 获取到原有节点信息
  423 + TransportLineNodeDTO transportLineNode = JSONUtil.toBean(transportOrder.getTransportLine(), TransportLineNodeDTO.class);
  424 +// 将逆向节点追加到节点列表中
  425 + transportLineNode.getNodeList().addAll(transportLineNodeDTO.getNodeList());
  426 +// 合并版本
  427 + transportLineNode.setCost(NumberUtil.add(transportLineNode.getCost(), transportLineNodeDTO.getCost()));
  428 + transportOrder.setTransportLine(JSONUtil.toJsonStr(transportLineNode));
  429 + }
  430 +
  431 + transportOrder.setStatus(TransportOrderStatus.REJECTED);
  432 +
  433 + if (isDispatch) {
  434 +// 发送消息参与调度
  435 + sendTransportOrderMsgToDispatch(transportOrder);
  436 + } else {
  437 +// 不需要调度,发送消息生成派件任务
  438 + transportOrder.setStatus(TransportOrderStatus.ARRIVED_END);
  439 + sendDispatchTaskMsgToDispatch(transportOrder);
  440 + }
  441 + }
  442 + } else {
  443 +// 根据ID列表封装成运单对象列表
  444 + transportOrderEntityList = ids.stream().map(id -> {
  445 +// 获取将发往目的地的机构
  446 + Long nextAgencyId = getById(id).getNextAgencyId();
  447 + OrganDTO organDTO = organFeign.queryById(nextAgencyId);
  448 +
  449 +// 构建消息实体类
  450 + String info = CharSequenceUtil.format("快件发往【{}】", organDTO.getName());
  451 + String transportInfoMsg = TransportInfoMsg.builder()
  452 + .transportOrderId(id)//运单id
  453 + .status("运送中")//消息状态
  454 + .info(info)//消息详情
  455 + .created(DateUtil.current())//创建时间
  456 + .build().toJson();
  457 +
  458 +// 发送运单跟踪消息
  459 + mqFeign.sendMsg(
  460 + Constants.MQ.Exchanges.TRANSPORT_INFO,
  461 + Constants.MQ.RoutingKeys.TRANSPORT_INFO_APPEND,
  462 + transportInfoMsg);
  463 +
  464 +// 封装运单对象
  465 + TransportOrderEntity transportOrderEntity = new TransportOrderEntity();
  466 + transportOrderEntity.setId(id);
  467 + transportOrderEntity.setStatus(transportOrderStatus);
  468 + return transportOrderEntity;
  469 + }).collect(Collectors.toList());
  470 + }
  471 +
  472 +// 批量更新数据
  473 + boolean result = super.updateBatchById(transportOrderEntityList);
  474 +
  475 +// 发消息通知其他系统运单状态的变化
  476 + sendUpdateStatusMsg(ids, transportOrderStatus);
  477 +
  478 + return result;
  479 + }
  480 +
  481 + @Override
  482 + public boolean updateByTaskId(Long taskId) {
  483 + return false;
  484 + }
  485 +
  486 + /**
  487 + * 统计各个状态的数量
  488 + *
  489 + * @return 状态数量数据
  490 + */
  491 + @Override
  492 + public List<TransportOrderStatusCountDTO> findStatusCount() {
  493 +// 将所有的枚举状态放到集合中
  494 + List<TransportOrderStatusCountDTO> statusCountList = Arrays.stream(TransportOrderStatus.values())
  495 + .map(transportOrderStatus -> TransportOrderStatusCountDTO.builder()
  496 + .status(transportOrderStatus)
  497 + .statusCode(transportOrderStatus.getCode())
  498 + .count(0L)
  499 + .build())
  500 + .collect(Collectors.toList());
  501 +
  502 +// 将数量值放入到集合中,如果没有的数量为0
  503 + List<TransportOrderStatusCountDTO> statusCount = baseMapper.findStatusCount();
  504 + for (TransportOrderStatusCountDTO transportOrderStatusCountDTO : statusCountList) {
  505 + for (TransportOrderStatusCountDTO orderStatusCountDTO : statusCount) {
  506 + if (ObjectUtil.equal(transportOrderStatusCountDTO.getStatusCode(), orderStatusCountDTO.getStatusCode())) {
  507 + transportOrderStatusCountDTO.setCount(orderStatusCountDTO.getCount());
  508 + break;
  509 + }
  510 + }
  511 + }
  512 + return statusCount;
  513 + }
  514 +
  515 + @Override
  516 + public PageResponse<TransportOrderDTO> pageQueryByTaskId(Integer page, Integer pageSize, String taskId, String transportOrderId) {
  517 + return null;
  518 + }
  519 +}
... ...