Commit fd237e9c0eea84d50510063d189e62bb0d977cf3
1 parent
2266e9a9
day08-智能调度之运输任务
Showing
4 changed files
with
505 additions
and
5 deletions
sl-express-ms-dispatch-service/src/main/java/com/sl/ms/dispatch/job/DispatchJob.java
| 1 | package com.sl.ms.dispatch.job; | 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 | import com.xxl.job.core.context.XxlJobHelper; | 17 | import com.xxl.job.core.context.XxlJobHelper; |
| 4 | import com.xxl.job.core.handler.annotation.XxlJob; | 18 | import com.xxl.job.core.handler.annotation.XxlJob; |
| 5 | import lombok.extern.slf4j.Slf4j; | 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 | import org.springframework.stereotype.Component; | 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,6 +36,28 @@ import org.springframework.stereotype.Component; | ||
| 12 | @Slf4j | 36 | @Slf4j |
| 13 | public class DispatchJob { | 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,6 +67,166 @@ public class DispatchJob { | ||
| 21 | int shardIndex = XxlJobHelper.getShardIndex(); | 67 | int shardIndex = XxlJobHelper.getShardIndex(); |
| 22 | int shardTotal = XxlJobHelper.getShardTotal(); | 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 | \ No newline at end of file | 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,6 +5,7 @@ import com.sl.ms.work.domain.enums.transporttask.TransportTaskAssignedStatus; | ||
| 5 | import com.sl.ms.work.domain.enums.transporttask.TransportTaskLoadingStatus; | 5 | import com.sl.ms.work.domain.enums.transporttask.TransportTaskLoadingStatus; |
| 6 | import com.sl.ms.work.domain.enums.transporttask.TransportTaskStatus; | 6 | import com.sl.ms.work.domain.enums.transporttask.TransportTaskStatus; |
| 7 | import com.sl.transport.common.entity.BaseEntity; | 7 | import com.sl.transport.common.entity.BaseEntity; |
| 8 | +import lombok.Builder; | ||
| 8 | import lombok.Data; | 9 | import lombok.Data; |
| 9 | import lombok.EqualsAndHashCode; | 10 | import lombok.EqualsAndHashCode; |
| 10 | import lombok.experimental.Accessors; | 11 | import lombok.experimental.Accessors; |
| @@ -18,6 +19,7 @@ import java.time.LocalDateTime; | @@ -18,6 +19,7 @@ import java.time.LocalDateTime; | ||
| 18 | @EqualsAndHashCode(callSuper = false) | 19 | @EqualsAndHashCode(callSuper = false) |
| 19 | @Accessors(chain = true) | 20 | @Accessors(chain = true) |
| 20 | @TableName("sl_transport_task") | 21 | @TableName("sl_transport_task") |
| 22 | +@Builder | ||
| 21 | public class TransportTaskEntity extends BaseEntity { | 23 | public class TransportTaskEntity extends BaseEntity { |
| 22 | 24 | ||
| 23 | private static final long serialVersionUID = 1L; | 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 | \ No newline at end of file | 180 | \ No newline at end of file |
sl-express-ms-work-service/src/main/java/com/sl/ms/work/service/impl/TransportOrderServiceImpl.java
| 1 | package com.sl.ms.work.service.impl; | 1 | package com.sl.ms.work.service.impl; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.bean.BeanUtil; | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 4 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.collection.ListUtil; | 5 | import cn.hutool.core.collection.ListUtil; |
| 5 | import cn.hutool.core.convert.Convert; | 6 | import cn.hutool.core.convert.Convert; |
| @@ -10,6 +11,8 @@ import cn.hutool.core.text.CharSequenceUtil; | @@ -10,6 +11,8 @@ import cn.hutool.core.text.CharSequenceUtil; | ||
| 10 | import cn.hutool.core.util.NumberUtil; | 11 | import cn.hutool.core.util.NumberUtil; |
| 11 | import cn.hutool.core.util.ObjectUtil; | 12 | import cn.hutool.core.util.ObjectUtil; |
| 12 | import cn.hutool.core.util.StrUtil; | 13 | import cn.hutool.core.util.StrUtil; |
| 14 | +import cn.hutool.json.JSONArray; | ||
| 15 | +import cn.hutool.json.JSONObject; | ||
| 13 | import cn.hutool.json.JSONUtil; | 16 | import cn.hutool.json.JSONUtil; |
| 14 | import com.alibaba.csp.sentinel.datasource.Converter; | 17 | import com.alibaba.csp.sentinel.datasource.Converter; |
| 15 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | 18 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| @@ -34,6 +37,7 @@ import com.sl.ms.work.entity.TransportOrderTaskEntity; | @@ -34,6 +37,7 @@ import com.sl.ms.work.entity.TransportOrderTaskEntity; | ||
| 34 | import com.sl.ms.work.mapper.TransportOrderMapper; | 37 | import com.sl.ms.work.mapper.TransportOrderMapper; |
| 35 | import com.sl.ms.work.mapper.TransportOrderTaskMapper; | 38 | import com.sl.ms.work.mapper.TransportOrderTaskMapper; |
| 36 | import com.sl.ms.work.service.TransportOrderService; | 39 | import com.sl.ms.work.service.TransportOrderService; |
| 40 | +import com.sl.ms.work.service.TransportTaskService; | ||
| 37 | import com.sl.transport.common.constant.Constants; | 41 | import com.sl.transport.common.constant.Constants; |
| 38 | import com.sl.transport.common.enums.IdEnum; | 42 | import com.sl.transport.common.enums.IdEnum; |
| 39 | import com.sl.transport.common.exception.SLException; | 43 | import com.sl.transport.common.exception.SLException; |
| @@ -75,6 +79,11 @@ public class TransportOrderServiceImpl extends | @@ -75,6 +79,11 @@ public class TransportOrderServiceImpl extends | ||
| 75 | private MQFeign mqFeign; | 79 | private MQFeign mqFeign; |
| 76 | @Autowired | 80 | @Autowired |
| 77 | private OrganFeign organFeign; | 81 | private OrganFeign organFeign; |
| 82 | + @Autowired | ||
| 83 | + private TransportOrderTaskMapper transportOrderTaskMapper; | ||
| 84 | + | ||
| 85 | + @Autowired | ||
| 86 | + private TransportTaskService transportTaskService; | ||
| 78 | 87 | ||
| 79 | @Override | 88 | @Override |
| 80 | @Transactional(rollbackFor = Exception.class) | 89 | @Transactional(rollbackFor = Exception.class) |
| @@ -480,9 +489,87 @@ public class TransportOrderServiceImpl extends | @@ -480,9 +489,87 @@ public class TransportOrderServiceImpl extends | ||
| 480 | 489 | ||
| 481 | @Override | 490 | @Override |
| 482 | public boolean updateByTaskId(Long taskId) { | 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,8 +599,34 @@ public class TransportOrderServiceImpl extends | ||
| 512 | return statusCount; | 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 | @Override | 611 | @Override |
| 516 | public PageResponse<TransportOrderDTO> pageQueryByTaskId(Integer page, Integer pageSize, String taskId, String transportOrderId) { | 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 | } |