Commit fd237e9c0eea84d50510063d189e62bb0d977cf3

Authored by shuhongfan
1 parent 2266e9a9

day08-智能调度之运输任务

sl-express-ms-dispatch-service/src/main/java/com/sl/ms/dispatch/job/DispatchJob.java
1 1 package com.sl.ms.dispatch.job;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.collection.ListUtil;
  5 +import cn.hutool.core.map.MapUtil;
  6 +import cn.hutool.core.util.NumberUtil;
  7 +import cn.hutool.core.util.ObjectUtil;
  8 +import cn.hutool.core.util.StrUtil;
  9 +import cn.hutool.json.JSONUtil;
  10 +import com.sl.ms.base.api.common.MQFeign;
  11 +import com.sl.ms.base.api.truck.TruckPlanFeign;
  12 +import com.sl.ms.base.domain.truck.TruckDto;
  13 +import com.sl.ms.base.domain.truck.TruckPlanDto;
  14 +import com.sl.ms.dispatch.dto.DispatchMsgDTO;
  15 +import com.sl.ms.dispatch.mq.TransportOrderDispatchMQListener;
  16 +import com.sl.transport.common.constant.Constants;
3 17 import com.xxl.job.core.context.XxlJobHelper;
4 18 import com.xxl.job.core.handler.annotation.XxlJob;
5 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.redisson.api.RLock;
  21 +import org.redisson.api.RedissonClient;
  22 +import org.springframework.beans.factory.annotation.Autowired;
  23 +import org.springframework.beans.factory.annotation.Value;
  24 +import org.springframework.data.redis.core.StringRedisTemplate;
6 25 import org.springframework.stereotype.Component;
7 26  
  27 +import java.math.BigDecimal;
  28 +import java.util.ArrayList;
  29 +import java.util.List;
  30 +import java.util.Map;
  31 +
8 32 /**
9 33 * 调度运输任务
10 34 */
... ... @@ -12,6 +36,28 @@ import org.springframework.stereotype.Component;
12 36 @Slf4j
13 37 public class DispatchJob {
14 38  
  39 + @Autowired
  40 + private TransportOrderDispatchMQListener transportOrderDispatchMQListener;
  41 +
  42 + @Autowired
  43 + private StringRedisTemplate stringRedisTemplate;
  44 +
  45 + @Autowired
  46 + private RedissonClient redissonClient;
  47 +
  48 + @Autowired
  49 + private TruckPlanFeign truckPlanFeign;
  50 +
  51 + @Autowired
  52 + private MQFeign mqFeign;
  53 +
  54 + @Value("${sl.volume.ratio:0.95}")
  55 + private Double volumeRatio;
  56 +
  57 + @Value("${sl.weight.ratio:0.95}")
  58 + private Double weightRatio;
  59 +
  60 +
15 61 /**
16 62 * 分片广播方式处理运单,生成运输任务
17 63 */
... ... @@ -21,6 +67,166 @@ public class DispatchJob {
21 67 int shardIndex = XxlJobHelper.getShardIndex();
22 68 int shardTotal = XxlJobHelper.getShardTotal();
23 69  
24   - //TODO 待实现
  70 +// 根据分片参数 2小时内并且可用状态车辆
  71 + List<TruckPlanDto> truckPlanDtoList = truckPlanFeign.pullUnassignedPlan(shardTotal, shardIndex);
  72 + if (CollUtil.isEmpty(truckPlanDtoList)) {
  73 + return;
  74 + }
  75 +
  76 +// 对每一个车辆都进行处理
  77 +// 为了相同目的地的运单尽可能的分配在一个运输任务中,所有需要在读取数据时进行锁定
  78 +// 一辆车处理完成后再开始下一个车辆处理
  79 +// 在这里,使用redis的分布式锁实现
  80 + for (TruckPlanDto truckPlanDto : truckPlanDtoList) {
  81 +// 校验车辆计划对象
  82 + if (ObjectUtil.hasEmpty(truckPlanDto.getStartOrganId(), truckPlanDto.getEndOrganId(),
  83 + truckPlanDto.getTransportTripsId(), truckPlanDto.getId())) {
  84 + log.error("车辆计划对象数据不符合要求,truckPlanDto->{}", truckPlanDto);
  85 + continue;
  86 + }
  87 +
  88 +// 根据该车辆的开始、结束结构ID,来确定要处理的运单数据集合
  89 + Long startOrganId = truckPlanDto.getStartOrganId();
  90 + Long endOrganId = truckPlanDto.getEndOrganId();
  91 + String redisKey = transportOrderDispatchMQListener.getListRedisKey(startOrganId, endOrganId);
  92 + String lockRedisKey = Constants.LOCKS.DISPATCH_LOCK_PREFIX + redisKey;
  93 +
  94 +// 获取锁
  95 + RLock lock = redissonClient.getFairLock(lockRedisKey);
  96 + ArrayList<DispatchMsgDTO> dispatchMsgDTOList = new ArrayList<>();
  97 + try {
  98 +// 锁定,一直等等锁,一定要获取到锁,因为查询到车辆的调度状态设置为:已分配
  99 + lock.lock();
  100 +// 计算车辆运力、合并运单
  101 + executeTransportTask(redisKey, truckPlanDto.getTruckDto(), dispatchMsgDTOList);
  102 + } finally {
  103 + lock.unlock();
  104 + }
  105 + createTransportTask(truckPlanDto, startOrganId, endOrganId, dispatchMsgDTOList);
  106 + }
  107 + //发送消息通过车辆已经完成调度
  108 + completeTruckPlan(truckPlanDtoList);
25 109 }
26   -}
  110 +
  111 + /**
  112 + * 运单处理
  113 + *
  114 + * @param redisKey
  115 + * @param truckDto
  116 + * @param dispatchMsgDTOList
  117 + */
  118 + private void executeTransportTask(String redisKey, TruckDto truckDto, List<DispatchMsgDTO> dispatchMsgDTOList) {
  119 + String redisData = stringRedisTemplate.opsForList().rightPop(redisKey);
  120 + if (StrUtil.isEmptyIfStr(redisData)) {
  121 +// 车辆没有运单需要运输
  122 + return;
  123 + }
  124 +
  125 + DispatchMsgDTO dispatchMsgDTO = JSONUtil.toBean(redisData, DispatchMsgDTO.class);
  126 +
  127 +// 计算该车辆已经分配的运单,是否超出其能力,载重或体积超出,需要将新拿到的运单加进去后进行比较
  128 + BigDecimal totalWeight = NumberUtil.add(NumberUtil.toBigDecimal(
  129 + dispatchMsgDTOList
  130 + .stream()
  131 + .mapToDouble(DispatchMsgDTO::getTotalWeight)
  132 + .sum()
  133 + ), dispatchMsgDTO.getTotalWeight());
  134 +
  135 + BigDecimal totalVolume = NumberUtil.add(NumberUtil.toBigDecimal(dispatchMsgDTOList
  136 + .stream()
  137 + .mapToDouble(DispatchMsgDTO::getTotalVolume)
  138 + .sum()),
  139 + dispatchMsgDTO.getTotalVolume());
  140 +
  141 +// 车辆最大的容积和载重要留有余量,否则可能会超重或装不下
  142 + BigDecimal maxAllowAbleLoad = NumberUtil.mul(truckDto.getAllowableLoad(), weightRatio);
  143 + BigDecimal maxAllowAbleVolume = NumberUtil.mul(truckDto.getAllowableVolume(), volumeRatio);
  144 +
  145 + if (NumberUtil.isGreaterOrEqual(totalVolume, maxAllowAbleVolume) ||
  146 + NumberUtil.isGreaterOrEqual(totalWeight, maxAllowAbleLoad)) {
  147 +// 超出车辆运力,需要取货的运单再放回去,放到最右边,以便保证运单处理的顺序
  148 + stringRedisTemplate.opsForList().rightPush(redisKey, redisData);
  149 + return;
  150 + }
  151 +
  152 +// 没有超出运力,讲该运单加入到集合中
  153 + dispatchMsgDTOList.add(dispatchMsgDTO);
  154 +
  155 +// 递归处理运单
  156 + executeTransportTask(redisKey, truckDto, dispatchMsgDTOList);
  157 + }
  158 +
  159 + /**
  160 + * 消息通知生成运输任务
  161 + *
  162 + * @param truckPlanDto
  163 + * @param startOrganId
  164 + * @param endOrganId
  165 + * @param dispatchMsgDTOList
  166 + */
  167 + private void createTransportTask(TruckPlanDto truckPlanDto, Long startOrganId, Long endOrganId, List<DispatchMsgDTO> dispatchMsgDTOList) {
  168 + //将运单合并的结果以消息的方式发送出去
  169 + //key-> 车辆id,value -> 运单id列表
  170 + //{"driverId":123, "truckPlanId":456, "truckId":1210114964812075008,"totalVolume":4.2,"endOrganId":90001,"totalWeight":7,"transportOrderIdList":[320733749248,420733749248],"startOrganId":100280}
  171 + List<String> transportOrderIdList = CollUtil.getFieldValues(dispatchMsgDTOList, "transportOrderIdList", String.class);
  172 +
  173 +// 司机列表确保不为null
  174 + List<Long> driverIds = CollUtil.isNotEmpty(truckPlanDto.getDriverIds()) ? truckPlanDto.getDriverIds() : ListUtil.empty();
  175 +
  176 + Map<Object, Object> msgResult = MapUtil.builder()
  177 + .put("truckId", truckPlanDto.getTruckId()) //车辆id
  178 + .put("driverIds", driverIds) //司机id
  179 + .put("truckPlanId", truckPlanDto.getId()) //车辆计划id
  180 + .put("transportTripsId", truckPlanDto.getTransportTripsId()) //车次id
  181 + .put("startOrganId", startOrganId) //开始机构id
  182 + .put("endOrganId", endOrganId) //结束机构id
  183 +// 运单id列表
  184 + .put("transportOrderIdList", transportOrderIdList)
  185 +// 总重量
  186 + .put("totalWeight",
  187 + dispatchMsgDTOList.stream()
  188 + .mapToDouble(DispatchMsgDTO::getTotalWeight)
  189 + .sum()
  190 + )
  191 +// 总体积
  192 + .put("totalVolume",
  193 + dispatchMsgDTOList.stream()
  194 + .mapToDouble(DispatchMsgDTO::getTotalVolume)
  195 + .sum())
  196 + .build();
  197 +
  198 +// 发送消息
  199 + String jsonMsg = JSONUtil.toJsonStr(msgResult);
  200 + mqFeign.sendMsg(Constants.MQ.Exchanges.TRANSPORT_TASK,
  201 + Constants.MQ.RoutingKeys.TRANSPORT_TASK_CREATE,
  202 + jsonMsg);
  203 +
  204 + if (CollUtil.isNotEmpty(transportOrderIdList)) {
  205 +// 删除redis中set存储的运单数据
  206 + String setRedisKey = transportOrderDispatchMQListener.getSetRedisKey(startOrganId, endOrganId);
  207 + stringRedisTemplate.opsForSet().remove(setRedisKey, transportOrderIdList.toArray());
  208 + }
  209 +
  210 + }
  211 +
  212 + /**
  213 + * 消息通知完成车辆计划
  214 + * private void completeTruckPlan(List<TruckPlanDto> truckDtoList) {
  215 + * @param truckDtoList
  216 + */
  217 + private void completeTruckPlan(List<TruckPlanDto> truckDtoList) {
  218 + //{"ids":[1,2,3], "created":123456}
  219 + Map<Object, Object> msg = MapUtil.builder()
  220 + .put("ids", CollUtil.getFieldValues(truckDtoList, "id", Long.class))
  221 + .put("created", System.currentTimeMillis())
  222 + .build();
  223 +
  224 + String jsonMsg = JSONUtil.toJsonStr(msg);
  225 +
  226 +// 发送消息
  227 + mqFeign.sendMsg(Constants.MQ.Exchanges.TRUCK_PLAN,
  228 + Constants.MQ.RoutingKeys.TRUCK_PLAN_COMPLETE,
  229 + jsonMsg);
  230 + }
  231 +
  232 +}
27 233 \ No newline at end of file
... ...
sl-express-ms-work-service/src/main/java/com/sl/ms/work/entity/TransportTaskEntity.java
... ... @@ -5,6 +5,7 @@ import com.sl.ms.work.domain.enums.transporttask.TransportTaskAssignedStatus;
5 5 import com.sl.ms.work.domain.enums.transporttask.TransportTaskLoadingStatus;
6 6 import com.sl.ms.work.domain.enums.transporttask.TransportTaskStatus;
7 7 import com.sl.transport.common.entity.BaseEntity;
  8 +import lombok.Builder;
8 9 import lombok.Data;
9 10 import lombok.EqualsAndHashCode;
10 11 import lombok.experimental.Accessors;
... ... @@ -18,6 +19,7 @@ import java.time.LocalDateTime;
18 19 @EqualsAndHashCode(callSuper = false)
19 20 @Accessors(chain = true)
20 21 @TableName("sl_transport_task")
  22 +@Builder
21 23 public class TransportTaskEntity extends BaseEntity {
22 24  
23 25 private static final long serialVersionUID = 1L;
... ...
sl-express-ms-work-service/src/main/java/com/sl/ms/work/mq/TransportTaskMQListener.java 0 → 100644
  1 +package com.sl.ms.work.mq;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.convert.Convert;
  5 +import cn.hutool.core.util.ObjectUtil;
  6 +import cn.hutool.json.JSONArray;
  7 +import cn.hutool.json.JSONObject;
  8 +import cn.hutool.json.JSONUtil;
  9 +import com.sl.ms.base.api.truck.TruckPlanFeign;
  10 +import com.sl.ms.base.domain.truck.TruckPlanDto;
  11 +import com.sl.ms.driver.api.DriverJobFeign;
  12 +import com.sl.ms.transport.api.TransportLineFeign;
  13 +import com.sl.ms.work.domain.enums.transportorder.TransportOrderSchedulingStatus;
  14 +import com.sl.ms.work.domain.enums.transporttask.TransportTaskAssignedStatus;
  15 +import com.sl.ms.work.domain.enums.transporttask.TransportTaskLoadingStatus;
  16 +import com.sl.ms.work.domain.enums.transporttask.TransportTaskStatus;
  17 +import com.sl.ms.work.entity.TransportOrderEntity;
  18 +import com.sl.ms.work.entity.TransportOrderTaskEntity;
  19 +import com.sl.ms.work.entity.TransportTaskEntity;
  20 +import com.sl.ms.work.service.TransportOrderService;
  21 +import com.sl.ms.work.service.TransportOrderTaskService;
  22 +import com.sl.ms.work.service.TransportTaskService;
  23 +import com.sl.transport.common.constant.Constants;
  24 +import com.sl.transport.common.util.PageResponse;
  25 +import com.sl.transport.domain.TransportLineDTO;
  26 +import com.sl.transport.domain.TransportLineSearchDTO;
  27 +import lombok.extern.slf4j.Slf4j;
  28 +import org.springframework.amqp.core.ExchangeTypes;
  29 +import org.springframework.amqp.rabbit.annotation.Exchange;
  30 +import org.springframework.amqp.rabbit.annotation.Queue;
  31 +import org.springframework.amqp.rabbit.annotation.QueueBinding;
  32 +import org.springframework.amqp.rabbit.annotation.RabbitListener;
  33 +import org.springframework.beans.factory.annotation.Autowired;
  34 +import org.springframework.stereotype.Component;
  35 +import org.springframework.transaction.annotation.Transactional;
  36 +
  37 +import java.util.List;
  38 +import java.util.stream.Collectors;
  39 +
  40 +@Component
  41 +@Slf4j
  42 +public class TransportTaskMQListener {
  43 +
  44 + @Autowired
  45 + private DriverJobFeign driverJobFeign;
  46 +
  47 + @Autowired
  48 + private TruckPlanFeign truckPlanFeign;
  49 +
  50 + @Autowired
  51 + private TransportLineFeign transportLineFeign;
  52 +
  53 + @Autowired
  54 + private TransportTaskService transportTaskService;
  55 +
  56 + @Autowired
  57 + private TransportOrderTaskService transportOrderTaskService;
  58 +
  59 + @Autowired
  60 + private TransportOrderService transportOrderService;
  61 +
  62 + @RabbitListener(bindings = @QueueBinding(
  63 + value = @Queue(name = Constants.MQ.Queues.WORK_TRANSPORT_TASK_CREATE),
  64 + exchange = @Exchange(name = Constants.MQ.Exchanges.TRANSPORT_TASK, type = ExchangeTypes.TOPIC),
  65 + key = Constants.MQ.RoutingKeys.TRANSPORT_TASK_CREATE
  66 + ))
  67 + public void listenTransportTaskMsg(String msg) {
  68 + //解析消息 {"driverIds":[123,345], "truckPlanId":456, "truckId":1210114964812075008,"totalVolume":4.2,"endOrganId":90001,"totalWeight":7,"transportOrderIdList":[320733749248,420733749248],"startOrganId":100280}
  69 + JSONObject jsonObject = JSONUtil.parseObj(msg);
  70 +
  71 +// 获取到司机ID列表
  72 + List<Long> driverIds = CollUtil.getFieldValues(jsonObject, "driverIds", Long.class);
  73 +
  74 +// 分配状态
  75 + TransportTaskAssignedStatus assignedStatus = CollUtil.isEmpty(driverIds) ? TransportTaskAssignedStatus.MANUAL_DISTRIBUTED : TransportTaskAssignedStatus.DISTRIBUTED;
  76 +
  77 +// 创建运输任务
  78 + Long transportTaskId = createTransportTask(jsonObject, assignedStatus);
  79 +
  80 + if (CollUtil.isEmpty(driverIds)) {
  81 + log.info("生成司机作业单,司机列表为空,需要手动设置司机作业单->msg={}", msg);
  82 + return;
  83 + }
  84 +
  85 +// 生成司机作业单
  86 + driverIds.forEach(driverId -> driverJobFeign.createDriverJob(transportTaskId, driverId));
  87 + }
  88 +
  89 + /**
  90 + * 生成运输任务
  91 + *
  92 + * @param jsonObject
  93 + * @param assignedStatus
  94 + * @return
  95 + */
  96 + @Transactional
  97 + public Long createTransportTask(JSONObject jsonObject, TransportTaskAssignedStatus assignedStatus) {
  98 + //根据车辆计划id查询预计发车时间和预计到达时间
  99 + Long truckPlanId = jsonObject.getLong("truckPlanId");
  100 + TruckPlanDto truckPlanDto = truckPlanFeign.findById(truckPlanId);
  101 +
  102 +// 创建运输任务
  103 + TransportTaskEntity transportTaskEntity = TransportTaskEntity.builder()
  104 + .truckPlanId(jsonObject.getLong("truckPlanId"))
  105 + .truckId(jsonObject.getLong("truckId"))
  106 + .startAgencyId(jsonObject.getLong("startOrganId"))
  107 + .endAgencyId(jsonObject.getLong("endOrganId"))
  108 + .transportTripsId(jsonObject.getLong("transportTripsId"))
  109 + .assignedStatus(assignedStatus)
  110 + .planDepartureTime(truckPlanDto.getPlanDepartureTime())
  111 + .planArrivalTime(truckPlanDto.getPlanArrivalTime())
  112 + .status(TransportTaskStatus.PENDING)
  113 + .build();
  114 +
  115 + if (CollUtil.isEmpty(jsonObject.getJSONArray("transportOrderIdList"))) {
  116 + transportTaskEntity.setLoadingStatus(TransportTaskLoadingStatus.EMPTY);
  117 + } else {
  118 + transportTaskEntity.setLoadingStatus(TransportTaskLoadingStatus.FULL);
  119 + }
  120 +
  121 +// 查询线路距离
  122 + TransportLineSearchDTO transportLineSearchDTO = new TransportLineSearchDTO();
  123 + transportLineSearchDTO.setPage(1);
  124 + transportLineSearchDTO.setPageSize(1);
  125 + transportLineSearchDTO.setStartOrganId(transportTaskEntity.getStartAgencyId());
  126 + transportLineSearchDTO.setEndOrganId(transportTaskEntity.getEndAgencyId());
  127 +
  128 + PageResponse<TransportLineDTO> transportLineDTOPageResponse = transportLineFeign.queryPageList(transportLineSearchDTO);
  129 + TransportLineDTO transportLineDTO = CollUtil.getFirst(transportLineDTOPageResponse.getItems());
  130 + if (ObjectUtil.isNotEmpty(transportLineDTO)) {
  131 +// 设置距离
  132 + transportTaskEntity.setDistance(transportLineDTO.getDistance());
  133 + }
  134 +
  135 +// 保存数据
  136 + transportTaskService.save(transportTaskEntity);
  137 +
  138 +// 创建运输任务与运单之间的关系
  139 + createTransportOrderTask(transportTaskEntity.getId(), jsonObject);
  140 + return transportTaskEntity.getId();
  141 + }
  142 +
  143 + /**
  144 + * 创建运输任务与运单之间的关系
  145 + *
  146 + * @param transportTaskId
  147 + * @param jsonObject
  148 + */
  149 + private void createTransportOrderTask(final Long transportTaskId, final JSONObject jsonObject) {
  150 + JSONArray transportOrderIdList = jsonObject.getJSONArray("transportOrderIdList");
  151 + if (CollUtil.isEmpty(transportOrderIdList)) {
  152 + return;
  153 + }
  154 +
  155 +// 将运单ID列表转成运单实体列表
  156 + List<TransportOrderTaskEntity> resultList = transportOrderIdList.stream()
  157 + .map(orderId -> {
  158 + TransportOrderTaskEntity transportOrderTaskEntity = new TransportOrderTaskEntity();
  159 + transportOrderTaskEntity.setTransportTaskId(transportTaskId);
  160 + transportOrderTaskEntity.setTransportOrderId(Convert.toStr(orderId));
  161 + return transportOrderTaskEntity;
  162 + })
  163 + .collect(Collectors.toList());
  164 +
  165 +// 批量保存运输任务与运单的关联表
  166 + transportOrderTaskService.batchSaveTransportOrder(resultList);
  167 +
  168 +// 批量标记运单为以调度状态
  169 + List<TransportOrderEntity> list = transportOrderIdList.stream().map(orderId -> {
  170 + TransportOrderEntity transportOrderEntity = new TransportOrderEntity();
  171 + transportOrderEntity.setId(Convert.toStr(orderId));
  172 +// 状态设置为已调度
  173 + transportOrderEntity.setSchedulingStatus(TransportOrderSchedulingStatus.SCHEDULED);
  174 + return transportOrderEntity;
  175 + }).collect(Collectors.toList());
  176 +
  177 + transportOrderService.updateBatchById(list);
  178 + }
  179 +}
0 180 \ No newline at end of file
... ...
sl-express-ms-work-service/src/main/java/com/sl/ms/work/service/impl/TransportOrderServiceImpl.java
1 1 package com.sl.ms.work.service.impl;
2 2  
  3 +import cn.hutool.core.bean.BeanUtil;
3 4 import cn.hutool.core.collection.CollUtil;
4 5 import cn.hutool.core.collection.ListUtil;
5 6 import cn.hutool.core.convert.Convert;
... ... @@ -10,6 +11,8 @@ import cn.hutool.core.text.CharSequenceUtil;
10 11 import cn.hutool.core.util.NumberUtil;
11 12 import cn.hutool.core.util.ObjectUtil;
12 13 import cn.hutool.core.util.StrUtil;
  14 +import cn.hutool.json.JSONArray;
  15 +import cn.hutool.json.JSONObject;
13 16 import cn.hutool.json.JSONUtil;
14 17 import com.alibaba.csp.sentinel.datasource.Converter;
15 18 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
... ... @@ -34,6 +37,7 @@ import com.sl.ms.work.entity.TransportOrderTaskEntity;
34 37 import com.sl.ms.work.mapper.TransportOrderMapper;
35 38 import com.sl.ms.work.mapper.TransportOrderTaskMapper;
36 39 import com.sl.ms.work.service.TransportOrderService;
  40 +import com.sl.ms.work.service.TransportTaskService;
37 41 import com.sl.transport.common.constant.Constants;
38 42 import com.sl.transport.common.enums.IdEnum;
39 43 import com.sl.transport.common.exception.SLException;
... ... @@ -75,6 +79,11 @@ public class TransportOrderServiceImpl extends
75 79 private MQFeign mqFeign;
76 80 @Autowired
77 81 private OrganFeign organFeign;
  82 + @Autowired
  83 + private TransportOrderTaskMapper transportOrderTaskMapper;
  84 +
  85 + @Autowired
  86 + private TransportTaskService transportTaskService;
78 87  
79 88 @Override
80 89 @Transactional(rollbackFor = Exception.class)
... ... @@ -480,9 +489,87 @@ public class TransportOrderServiceImpl extends
480 489  
481 490 @Override
482 491 public boolean updateByTaskId(Long taskId) {
483   - return false;
484   - }
  492 +// 通过运输任务查询运单ID列表
  493 + List<String> transportOrderIdList = transportTaskService.queryTransportOrderIdListById(taskId);
  494 + if (CollUtil.isEmpty(transportOrderIdList)) {
  495 + return false;
  496 + }
  497 +
  498 +// 查询运单列表
  499 + List<TransportOrderEntity> transportOrderList = super.listByIds(transportOrderIdList);
  500 + for (TransportOrderEntity transportOrder : transportOrderList) {
  501 +// 获取将发往目的地的机构
  502 + OrganDTO organDTO = organFeign.queryById(transportOrder.getNextAgencyId());
  503 +
  504 +// 构建消息实体类
  505 + String info = CharSequenceUtil.format("快件到达【{}】", organDTO.getName());
  506 + String transportInfoMsg = TransportInfoMsg.builder()
  507 + .transportOrderId(transportOrder.getId())
  508 + .status("运送中")
  509 + .info(info)
  510 + .created(DateUtil.current())
  511 + .build().toJson();
  512 +
  513 +// 发送运单跟踪信息
  514 + mqFeign.sendMsg(Constants.MQ.Exchanges.TRANSPORT_INFO,
  515 + Constants.MQ.RoutingKeys.TRANSPORT_INFO_APPEND,
  516 + transportInfoMsg);
  517 +
  518 +// 设置当前所在机构ID为下一个机构ID
  519 + transportOrder.setCurrentAgencyId(transportOrder.getNextAgencyId());
  520 +
  521 +// 解析完整的运输链路,找到下一个机构ID
  522 + String transportLine = transportOrder.getTransportLine();
  523 + JSONObject jsonObject = JSONUtil.parseObj(transportLine);
  524 + Long nextAgencyId = 0L;
  525 + JSONArray nodeList = jsonObject.getJSONArray("nodeList");
  526 +
  527 + //这里反向循环主要是考虑到拒收的情况,路线中会存在相同的节点,始终可以查找到后面的节点
  528 + //正常:A B C D E ,拒收:A B C D E D C B A
  529 + for (int i = nodeList.size() - 1; i >= 0; i--) {
  530 + JSONObject node = (JSONObject) nodeList.get(i);
  531 + Long agencyId = node.getLong("bid");
  532 + if (ObjectUtil.equal(agencyId, transportOrder.getCurrentAgencyId())) {
  533 + if (i == nodeList.size() - 1) {
  534 +// 已经是最后一个节点了,也就是最后一个机构了
  535 + nextAgencyId = agencyId;
  536 + transportOrder.setStatus(TransportOrderStatus.ARRIVED_END);
  537 +
  538 +// 发送消息更新状态
  539 + sendUpdateStatusMsg(
  540 + ListUtil.toList(transportOrder.getId()),
  541 + TransportOrderStatus.ARRIVED_END
  542 + );
  543 + } else {
  544 +// 后面还有节点
  545 + nextAgencyId = ((JSONObject) nodeList.get(i + 1)).getLong("bid");
  546 +// 设置运单状态为待调度
  547 + transportOrder.setSchedulingStatus(TransportOrderSchedulingStatus.TO_BE_SCHEDULED);
  548 + }
  549 + break;
  550 + }
  551 + }
485 552  
  553 +// 设置下一个节点ID
  554 + transportOrder.setNextAgencyId(nextAgencyId);
  555 +
  556 +// 如果运单没有到达终点,需要发送消息到运单调度的交换机中
  557 +// 如果已经到达最终网点,需要发送消息,进行分配快递员作业
  558 + if (ObjectUtil.notEqual(
  559 + transportOrder.getStatus(),
  560 + TransportOrderStatus.ARRIVED_END
  561 + )) {
  562 + sendTransportOrderMsgToDispatch(transportOrder);
  563 + } else {
  564 +// 发送消息生产派件任务
  565 + sendDispatchTaskMsgToDispatch(transportOrder);
  566 + }
  567 + }
  568 +
  569 +// 批量更新运单
  570 + return updateBatchById(transportOrderList);
  571 + }
  572 +
486 573 /**
487 574 * 统计各个状态的数量
488 575 *
... ... @@ -512,8 +599,34 @@ public class TransportOrderServiceImpl extends
512 599 return statusCount;
513 600 }
514 601  
  602 + /**
  603 + * 根据运输任务id分页查询运单信息
  604 + *
  605 + * @param page 页码
  606 + * @param pageSize 页面大小
  607 + * @param taskId 运输任务id
  608 + * @param transportOrderId 运单id
  609 + * @return 运单对象分页数据
  610 + */
515 611 @Override
516 612 public PageResponse<TransportOrderDTO> pageQueryByTaskId(Integer page, Integer pageSize, String taskId, String transportOrderId) {
517   - return null;
  613 +// 构建分页查询条件
  614 + Page<TransportOrderTaskEntity> transportOrderTaskEntityPage = new Page<>(page, pageSize);
  615 + LambdaQueryWrapper<TransportOrderTaskEntity> queryWrapper = new LambdaQueryWrapper<>();
  616 + queryWrapper.eq(ObjectUtil.isNotEmpty(transportOrderId), TransportOrderTaskEntity::getTransportTaskId, taskId)
  617 + .like(ObjectUtil.isNotEmpty(transportOrderId), TransportOrderTaskEntity::getTransportOrderId, transportOrderId)
  618 + .orderByDesc(TransportOrderTaskEntity::getCreated);
  619 +
  620 +// 根据运输任务ID、运单ID查询运输任务与运单关联关系表
  621 + Page<TransportOrderTaskEntity> pageResult = transportOrderTaskMapper.selectPage(transportOrderTaskEntityPage, queryWrapper);
  622 + if (CollUtil.isEmpty(pageResult.getRecords())) {
  623 + return new PageResponse<>(pageResult);
  624 + }
  625 +
  626 +// 根据运单ID查询运单,并转化为DTO
  627 + List<String> transportOrderIds = pageResult.getRecords().stream().map(TransportOrderTaskEntity::getTransportOrderId).collect(Collectors.toList());
  628 + List<TransportOrderEntity> entities = baseMapper.selectBatchIds(transportOrderIds);
  629 +
  630 + return PageResponse.of(BeanUtil.copyToList(entities, TransportOrderDTO.class));
518 631 }
519 632 }
... ...