Commit 97b0ec4627ef5e0f208f18527777cd293fe8d9df
1 parent
71d2b279
day10-智能调度之取派件调度
Showing
3 changed files
with
564 additions
and
1 deletions
sl-express-ms-dispatch-service/src/main/java/com/sl/ms/dispatch/mq/OrderMQListener.java
| 1 | 1 | package com.sl.ms.dispatch.mq; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.convert.Convert; | |
| 5 | +import cn.hutool.core.date.DateUtil; | |
| 6 | +import cn.hutool.core.date.LocalDateTimeUtil; | |
| 7 | +import cn.hutool.json.JSONUtil; | |
| 8 | +import com.sl.ms.api.CourierFeign; | |
| 9 | +import com.sl.ms.base.api.common.MQFeign; | |
| 10 | +import com.sl.ms.work.api.PickupDispatchTaskFeign; | |
| 11 | +import com.sl.ms.work.domain.dto.CourierTaskCountDTO; | |
| 12 | +import com.sl.ms.work.domain.enums.pickupDispatchtask.PickupDispatchTaskType; | |
| 3 | 13 | import com.sl.transport.common.constant.Constants; |
| 14 | +import com.sl.transport.common.util.ObjectUtil; | |
| 15 | +import com.sl.transport.common.vo.CourierTaskMsg; | |
| 16 | +import com.sl.transport.common.vo.OrderMsg; | |
| 4 | 17 | import lombok.extern.slf4j.Slf4j; |
| 5 | 18 | import org.springframework.amqp.core.ExchangeTypes; |
| 6 | 19 | import org.springframework.amqp.rabbit.annotation.Exchange; |
| 7 | 20 | import org.springframework.amqp.rabbit.annotation.Queue; |
| 8 | 21 | import org.springframework.amqp.rabbit.annotation.QueueBinding; |
| 9 | 22 | import org.springframework.amqp.rabbit.annotation.RabbitListener; |
| 23 | +import org.springframework.beans.factory.annotation.Autowired; | |
| 10 | 24 | import org.springframework.stereotype.Component; |
| 11 | 25 | |
| 26 | +import java.time.LocalDateTime; | |
| 27 | +import java.time.temporal.ChronoUnit; | |
| 28 | +import java.util.List; | |
| 29 | +import java.util.stream.Collectors; | |
| 30 | +import java.util.stream.Stream; | |
| 31 | + | |
| 12 | 32 | /** |
| 13 | 33 | * 订单业务消息,接收到新订单后,根据快递员的负载情况,分配快递员 |
| 14 | 34 | */ |
| ... | ... | @@ -16,6 +36,16 @@ import org.springframework.stereotype.Component; |
| 16 | 36 | @Component |
| 17 | 37 | public class OrderMQListener { |
| 18 | 38 | |
| 39 | + @Autowired | |
| 40 | + private MQFeign mqFeign; | |
| 41 | + | |
| 42 | + @Autowired | |
| 43 | + private CourierFeign courierFeign; | |
| 44 | + | |
| 45 | + @Autowired | |
| 46 | + private PickupDispatchTaskFeign pickupDispatchTaskFeign; | |
| 47 | + | |
| 48 | + | |
| 19 | 49 | /** |
| 20 | 50 | * 如果有多个快递员,需要查询快递员今日的取派件数,根据此数量进行计算 |
| 21 | 51 | * 计算的逻辑:优先分配取件任务少的,取件数相同的取第一个分配 |
| ... | ... | @@ -36,6 +66,93 @@ public class OrderMQListener { |
| 36 | 66 | //{"orderId":123, "agencyId": 8001, "taskType":1, "mark":"带包装", "longitude":116.111, "latitude":39.00, "created":1654224658728, "estimatedStartTime": 1654224658728} |
| 37 | 67 | log.info("接收到订单的消息 >>> msg = {}", msg); |
| 38 | 68 | |
| 69 | + OrderMsg orderMsg = JSONUtil.toBean(msg, OrderMsg.class); | |
| 70 | + Long agencyId = orderMsg.getAgencyId(); | |
| 71 | + | |
| 72 | +// 通过快递员微服务查询 可以为发件人服务的快递员(正常上班、服务范围内) | |
| 73 | + Double longitude = orderMsg.getLongitude(); | |
| 74 | + Double latitude = orderMsg.getLatitude(); | |
| 75 | + | |
| 76 | + Long selectedCourierId = null; | |
| 77 | + List<Long> courierIds = courierFeign.queryCourierIdListByCondition( | |
| 78 | + agencyId, | |
| 79 | + longitude, | |
| 80 | + latitude, | |
| 81 | + LocalDateTimeUtil.toEpochMilli(orderMsg.getEstimatedEndTime())); | |
| 82 | + log.info("快递员微服务查出的ids:{}", courierIds); | |
| 83 | + if (CollUtil.isNotEmpty(courierIds)) { | |
| 84 | +// 选中快递员 | |
| 85 | + selectedCourierId = selectCourier(courierIds, orderMsg.getTaskType()); | |
| 86 | + log.info("根据当前任务选出的快递员ID:{}", selectedCourierId); | |
| 87 | + } | |
| 88 | + | |
| 89 | +// 发送消息 | |
| 90 | + CourierTaskMsg courierTaskMsg = CourierTaskMsg.builder() | |
| 91 | + .courierId(selectedCourierId) | |
| 92 | + .agencyId(agencyId) | |
| 93 | + .taskType(orderMsg.getTaskType()) | |
| 94 | + .orderId(orderMsg.getOrderId()) | |
| 95 | + .mark(orderMsg.getMark()) | |
| 96 | + .estimatedEndTime(orderMsg.getEstimatedEndTime()) | |
| 97 | + .created(System.currentTimeMillis()) | |
| 98 | + .build(); | |
| 99 | + | |
| 100 | +// 计算时间差 | |
| 101 | + long between = LocalDateTimeUtil.between( | |
| 102 | + LocalDateTime.now(), | |
| 103 | + orderMsg.getEstimatedEndTime(), | |
| 104 | + ChronoUnit.MINUTES | |
| 105 | + ); | |
| 106 | + | |
| 107 | + int delay = Constants.MQ.DEFAULT_DELAY; | |
| 108 | + if (between > 120 && ObjectUtil.equalsAny(orderMsg.getTaskType(), 1)) { | |
| 109 | +// 计算延时时间,单位毫秒 | |
| 110 | + LocalDateTime sendDatatTime = LocalDateTimeUtil.offset(orderMsg.getEstimatedEndTime(), -2, ChronoUnit.HOURS); | |
| 111 | + delay = Convert.toInt(LocalDateTimeUtil.between(LocalDateTime.now(), sendDatatTime, ChronoUnit.MILLIS)); | |
| 112 | + } | |
| 113 | + | |
| 39 | 114 | //TODO 待实现 |
| 115 | + this.mqFeign.sendMsg(Constants.MQ.Exchanges.PICKUP_DISPATCH_TASK_DELAYED, | |
| 116 | + Constants.MQ.RoutingKeys.PICKUP_DISPATCH_TASK_CREATE, | |
| 117 | + courierTaskMsg.toJson(), | |
| 118 | + delay); | |
| 40 | 119 | } |
| 120 | + /** | |
| 121 | + * 根据当日的任务数选取快递员 | |
| 122 | + * | |
| 123 | + * @param courierIds 快递员列个表 | |
| 124 | + * @param taskType 任务类型 | |
| 125 | + * @return 选中的快递员id | |
| 126 | + */ | |
| 127 | + private Long selectCourier(List<Long> courierIds, Integer taskType) { | |
| 128 | + if (courierIds.size() == 1) { | |
| 129 | + return courierIds.get(0); | |
| 130 | + } | |
| 131 | + | |
| 132 | + String date = DateUtil.date().toDateStr(); | |
| 133 | + List<CourierTaskCountDTO> courierTaskCountDTOS = pickupDispatchTaskFeign.findCountByCourierIds( | |
| 134 | + courierIds, | |
| 135 | + PickupDispatchTaskType.codeOf(taskType), | |
| 136 | + date); | |
| 137 | + | |
| 138 | + if (CollUtil.isEmpty(courierTaskCountDTOS)) { | |
| 139 | +// 没有查到任务数量,默认给第一个快递员分配任务 | |
| 140 | + return courierIds.get(0); | |
| 141 | + } | |
| 142 | + | |
| 143 | +// 查看任务数是否与快递员数相同,如果不相同需要补齐,设置任务数0,这样就可以确保每个快递员都能分配搭配到任务 | |
| 144 | + if (ObjectUtil.notEqual(courierIds.size(), courierTaskCountDTOS.size())) { | |
| 145 | + List<CourierTaskCountDTO> dtoList = courierIds.stream().filter(courierId -> { | |
| 146 | + int index = CollUtil.indexOf(courierTaskCountDTOS, dto -> ObjectUtil.equal(courierId, dto.getCourierId())); | |
| 147 | + return index == -1; | |
| 148 | + }) | |
| 149 | + .map(courierId -> CourierTaskCountDTO.builder().courierId(courierId).count(0L).build()) | |
| 150 | + .collect(Collectors.toList()); | |
| 151 | + courierTaskCountDTOS.addAll(dtoList); | |
| 152 | + } | |
| 153 | +// 选中任务数最小的快递员进行分配 | |
| 154 | + CollUtil.sortByProperty(courierTaskCountDTOS, "count"); | |
| 155 | + return courierTaskCountDTOS.get(0).getCourierId(); | |
| 156 | + } | |
| 157 | + | |
| 41 | 158 | } | ... | ... |
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 | +import cn.hutool.core.date.LocalDateTimeUtil; | |
| 4 | +import cn.hutool.core.util.StrUtil; | |
| 3 | 5 | import cn.hutool.json.JSONUtil; |
| 6 | +import com.sl.ms.oms.api.OrderFeign; | |
| 7 | +import com.sl.ms.oms.dto.OrderDTO; | |
| 8 | +import com.sl.ms.oms.enums.OrderStatus; | |
| 9 | +import com.sl.ms.work.domain.enums.pickupDispatchtask.PickupDispatchTaskAssignedStatus; | |
| 10 | +import com.sl.ms.work.domain.enums.pickupDispatchtask.PickupDispatchTaskSignStatus; | |
| 11 | +import com.sl.ms.work.domain.enums.pickupDispatchtask.PickupDispatchTaskStatus; | |
| 12 | +import com.sl.ms.work.domain.enums.pickupDispatchtask.PickupDispatchTaskType; | |
| 13 | +import com.sl.ms.work.entity.PickupDispatchTaskEntity; | |
| 4 | 14 | import com.sl.ms.work.service.PickupDispatchTaskService; |
| 5 | 15 | import com.sl.ms.work.service.TransportOrderService; |
| 6 | 16 | import com.sl.transport.common.constant.Constants; |
| 17 | +import com.sl.transport.common.exception.SLException; | |
| 18 | +import com.sl.transport.common.util.BeanUtil; | |
| 19 | +import com.sl.transport.common.util.ObjectUtil; | |
| 7 | 20 | import com.sl.transport.common.vo.CourierMsg; |
| 8 | 21 | import com.sl.transport.common.vo.CourierTaskMsg; |
| 9 | 22 | import lombok.extern.slf4j.Slf4j; |
| ... | ... | @@ -15,6 +28,10 @@ import org.springframework.amqp.rabbit.annotation.RabbitListener; |
| 15 | 28 | import org.springframework.beans.factory.annotation.Autowired; |
| 16 | 29 | import org.springframework.stereotype.Component; |
| 17 | 30 | |
| 31 | +import java.time.LocalDateTime; | |
| 32 | +import java.time.temporal.ChronoUnit; | |
| 33 | +import java.util.List; | |
| 34 | + | |
| 18 | 35 | /** |
| 19 | 36 | * 快递员的消息处理,该处理器处理两个消息: |
| 20 | 37 | * 1. 生成快递员取派件任务 |
| ... | ... | @@ -29,6 +46,8 @@ public class CourierMQListener { |
| 29 | 46 | |
| 30 | 47 | @Autowired |
| 31 | 48 | private TransportOrderService transportOrderService; |
| 49 | + @Autowired | |
| 50 | + private OrderFeign orderFeign; | |
| 32 | 51 | |
| 33 | 52 | |
| 34 | 53 | /** |
| ... | ... | @@ -47,8 +66,55 @@ public class CourierMQListener { |
| 47 | 66 | //解析消息 |
| 48 | 67 | CourierTaskMsg courierTaskMsg = JSONUtil.toBean(msg, CourierTaskMsg.class); |
| 49 | 68 | |
| 69 | +// 幂等性处理,判断订单对应的取派件任务是否存在,判断条件:订单号+任务状态 | |
| 70 | + List<PickupDispatchTaskEntity> list = pickupDispatchTaskService.findByOrderId( | |
| 71 | + courierTaskMsg.getOrderId(), | |
| 72 | + PickupDispatchTaskType.codeOf(courierTaskMsg.getTaskType())); | |
| 73 | + for (PickupDispatchTaskEntity pickupDispatchTaskEntity : list) { | |
| 74 | + if (pickupDispatchTaskEntity.getStatus() == PickupDispatchTaskStatus.NEW) { | |
| 75 | +// 消息重复消息 | |
| 76 | + } | |
| 77 | + } | |
| 78 | + | |
| 79 | +// 订单不存在,不进行调度 | |
| 80 | + OrderDTO orderDTO = orderFeign.findById(courierTaskMsg.getOrderId()); | |
| 81 | + if (ObjectUtil.isEmpty(orderDTO)) { | |
| 82 | + return; | |
| 83 | + } | |
| 84 | + | |
| 85 | +// 如果已经取消或者删除 则不进行调度 | |
| 86 | + if (orderDTO.getStatus().equals(OrderStatus.CANCELLED.getCode()) || | |
| 87 | + orderDTO.getStatus().equals(OrderStatus.DEL.getCode())) { | |
| 88 | + return; | |
| 89 | + } | |
| 90 | + | |
| 91 | + PickupDispatchTaskEntity pickupDispatchTask = BeanUtil.toBean(courierTaskMsg, PickupDispatchTaskEntity.class); | |
| 92 | + | |
| 93 | +// 任务类型 | |
| 94 | + pickupDispatchTask.setTaskType(PickupDispatchTaskType.codeOf(courierTaskMsg.getTaskType())); | |
| 95 | + | |
| 96 | +// 预计开始时间,结束时间向前推一个小时 | |
| 97 | + LocalDateTime estimatedStartTime = LocalDateTimeUtil.offset(pickupDispatchTask.getEstimatedEndTime(), -1, ChronoUnit.HOURS); | |
| 98 | + pickupDispatchTask.setEstimatedStartTime(estimatedStartTime); | |
| 99 | + | |
| 100 | +// 默认未签收状态 | |
| 101 | + pickupDispatchTask.setSignStatus(PickupDispatchTaskSignStatus.NOT_SIGNED); | |
| 102 | + | |
| 103 | +// 分配状态 | |
| 104 | + if (ObjectUtil.isNotEmpty(pickupDispatchTask.getCourierId())) { | |
| 105 | + pickupDispatchTask.setAssignedStatus(PickupDispatchTaskAssignedStatus.DISTRIBUTED); | |
| 106 | + } else { | |
| 107 | + pickupDispatchTask.setAssignedStatus(PickupDispatchTaskAssignedStatus.MANUAL_DISTRIBUTED); | |
| 108 | + } | |
| 109 | + | |
| 110 | + PickupDispatchTaskEntity result = pickupDispatchTaskService.saveTaskPickupDispatch(pickupDispatchTask); | |
| 111 | + if (result == null) { | |
| 112 | + //保存任务失败 | |
| 113 | + throw new SLException(StrUtil.format("快递员任务保存失败 >>> msg = {}", msg)); | |
| 114 | + } | |
| 115 | + | |
| 50 | 116 | // 订单转运单 |
| 51 | - transportOrderService.orderToTransportOrder(courierTaskMsg.getOrderId()); | |
| 117 | +// transportOrderService.orderToTransportOrder(courierTaskMsg.getOrderId()); | |
| 52 | 118 | } |
| 53 | 119 | |
| 54 | 120 | /** | ... | ... |
sl-express-ms-work-service/src/main/java/com/sl/ms/work/service/impl/PickupDispatchTaskServiceImpl.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.DateTime; | |
| 7 | +import cn.hutool.core.date.DateUtil; | |
| 8 | +import cn.hutool.core.date.LocalDateTimeUtil; | |
| 9 | +import cn.hutool.core.util.ObjectUtil; | |
| 10 | +import com.baomidou.mybatisplus.core.conditions.Wrapper; | |
| 11 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 12 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 13 | +import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |
| 14 | +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | |
| 15 | +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |
| 16 | +import com.sl.ms.oms.api.OrderFeign; | |
| 17 | +import com.sl.ms.oms.enums.OrderStatus; | |
| 18 | +import com.sl.ms.work.domain.dto.CourierTaskCountDTO; | |
| 19 | +import com.sl.ms.work.domain.dto.PickupDispatchTaskDTO; | |
| 20 | +import com.sl.ms.work.domain.dto.request.PickupDispatchTaskPageQueryDTO; | |
| 21 | +import com.sl.ms.work.domain.dto.response.PickupDispatchTaskStatisticsDTO; | |
| 22 | +import com.sl.ms.work.domain.enums.WorkExceptionEnum; | |
| 23 | +import com.sl.ms.work.domain.enums.pickupDispatchtask.*; | |
| 24 | +import com.sl.ms.work.entity.PickupDispatchTaskEntity; | |
| 25 | +import com.sl.ms.work.mapper.TaskPickupDispatchMapper; | |
| 26 | +import com.sl.ms.work.service.PickupDispatchTaskService; | |
| 27 | +import com.sl.ms.work.service.TransportOrderService; | |
| 28 | +import com.sl.transport.common.exception.SLException; | |
| 29 | +import com.sl.transport.common.util.BeanUtil; | |
| 30 | +import com.sl.transport.common.util.PageResponse; | |
| 31 | +import com.sl.transport.common.vo.OrderMsg; | |
| 32 | +import org.springframework.beans.factory.annotation.Autowired; | |
| 33 | +import org.springframework.stereotype.Service; | |
| 34 | +import org.springframework.transaction.annotation.Transactional; | |
| 35 | + | |
| 36 | +import java.time.LocalDateTime; | |
| 37 | +import java.util.Date; | |
| 38 | +import java.util.List; | |
| 39 | +import java.util.stream.Collectors; | |
| 40 | + | |
| 41 | +@Service | |
| 42 | +public class PickupDispatchTaskServiceImpl extends ServiceImpl<TaskPickupDispatchMapper, PickupDispatchTaskEntity> implements PickupDispatchTaskService { | |
| 43 | + | |
| 44 | + @Autowired | |
| 45 | + private TaskPickupDispatchMapper taskPickupDispatchMapper; | |
| 46 | + | |
| 47 | + @Autowired | |
| 48 | + private TransportOrderService transportOrderService; | |
| 49 | + | |
| 50 | + @Autowired | |
| 51 | + private OrderFeign orderFeign; | |
| 52 | + | |
| 53 | + /** | |
| 54 | + * 更新取派件状态,不允许 NEW 状态 | |
| 55 | + * | |
| 56 | + * @param pickupDispatchTaskDTO 修改的数据 | |
| 57 | + * @return 是否成功 | |
| 58 | + */ | |
| 59 | + @Override | |
| 60 | + @Transactional | |
| 61 | + public Boolean updateStatus(PickupDispatchTaskDTO pickupDispatchTaskDTO) { | |
| 62 | + WorkExceptionEnum paramError = WorkExceptionEnum.PICKUP_DISPATCH_TASK_PARAM_ERROR; | |
| 63 | + if (ObjectUtil.hasEmpty(pickupDispatchTaskDTO.getId(), pickupDispatchTaskDTO.getStatus())) { | |
| 64 | + throw new SLException("更新取派件任务状态,id或status不能为空", paramError.getCode()); | |
| 65 | + } | |
| 66 | + | |
| 67 | + PickupDispatchTaskEntity pickupDispatchTask = getById(pickupDispatchTaskDTO.getId()); | |
| 68 | + | |
| 69 | + switch (pickupDispatchTask.getStatus()) { | |
| 70 | + case NEW: | |
| 71 | + throw new SLException(WorkExceptionEnum.PICKUP_DISPATCH_TASK_STATUS_NOT_NEW); | |
| 72 | + case COMPLETED: | |
| 73 | +// 任务完成 | |
| 74 | + pickupDispatchTask.setStatus(PickupDispatchTaskStatus.COMPLETED); | |
| 75 | +// 设置完成时间 | |
| 76 | + pickupDispatchTask.setActualEndTime(LocalDateTime.now()); | |
| 77 | + | |
| 78 | + if (PickupDispatchTaskType.DISPATCH == pickupDispatchTask.getTaskType()) { | |
| 79 | +// 如果是派件任务的完成,已签收需要设置签收状态和签收人,拒收只需要设置签收状态 | |
| 80 | + if (ObjectUtil.isEmpty(pickupDispatchTaskDTO.getSignStatus())) { | |
| 81 | + throw new SLException("完成派件任务,签收状态不能为空", paramError.getCode()); | |
| 82 | + } | |
| 83 | + pickupDispatchTask.setSignStatus(pickupDispatchTaskDTO.getSignStatus()); | |
| 84 | + | |
| 85 | + if (PickupDispatchTaskSignStatus.RECEIVED == pickupDispatchTaskDTO.getSignStatus()) { | |
| 86 | + if (ObjectUtil.isEmpty(pickupDispatchTaskDTO.getSignRecipient())) { | |
| 87 | + throw new SLException("完成派件任务,签收人不能为空", paramError.getCode()); | |
| 88 | + } | |
| 89 | + pickupDispatchTask.setSignRecipient(pickupDispatchTaskDTO.getSignRecipient()); | |
| 90 | + } | |
| 91 | + } | |
| 92 | + break; | |
| 93 | + case CANCELLED: | |
| 94 | + if (ObjectUtil.isEmpty(pickupDispatchTaskDTO.getCancelReason())) { | |
| 95 | + throw new SLException("取消任务,原因不能为空", paramError.getCode()); | |
| 96 | + } | |
| 97 | + pickupDispatchTask.setStatus(PickupDispatchTaskStatus.CANCELLED); | |
| 98 | + pickupDispatchTask.setCancelReason(pickupDispatchTaskDTO.getCancelReason()); | |
| 99 | + pickupDispatchTask.setCancelReasonDescription(pickupDispatchTaskDTO.getCancelReasonDescription()); | |
| 100 | + pickupDispatchTask.setCancelTime(LocalDateTime.now()); | |
| 101 | + | |
| 102 | + if (pickupDispatchTaskDTO.getCancelReason() == PickupDispatchTaskCancelReason.RETURN_TO_AGENCY) { | |
| 103 | + //发送分配快递员派件任务的消息 | |
| 104 | + OrderMsg orderMsg = OrderMsg.builder() | |
| 105 | + .agencyId(pickupDispatchTask.getAgencyId()) | |
| 106 | + .orderId(pickupDispatchTask.getOrderId()) | |
| 107 | + .created(DateUtil.current()) | |
| 108 | + .taskType(PickupDispatchTaskType.PICKUP.getCode()) //取件任务 | |
| 109 | + .mark(pickupDispatchTask.getMark()) | |
| 110 | + .estimatedEndTime(pickupDispatchTask.getEstimatedEndTime()).build(); | |
| 111 | + //发送消息(取消任务发生在取件之前,没有运单,参数直接填入null) | |
| 112 | + transportOrderService.sendPickupDispatchTaskMsgToDispatch(null, orderMsg); | |
| 113 | + } else if (pickupDispatchTaskDTO.getCancelReason() == PickupDispatchTaskCancelReason.CANCEL_BY_USER) { | |
| 114 | + //原因是用户取消,则订单状态改为取消 | |
| 115 | + orderFeign.updateStatus(ListUtil.of(pickupDispatchTask.getOrderId()), OrderStatus.CANCELLED.getCode()); | |
| 116 | + } else { | |
| 117 | + //其他原因则关闭订单 | |
| 118 | + orderFeign.updateStatus(ListUtil.of(pickupDispatchTask.getOrderId()), OrderStatus.CLOSE.getCode()); | |
| 119 | + } | |
| 120 | + break; | |
| 121 | + default: | |
| 122 | + throw new SLException("其他未知状态,不能完成更新操作", paramError.getCode()); | |
| 123 | + } | |
| 124 | + | |
| 125 | + return updateById(pickupDispatchTask); | |
| 126 | + } | |
| 127 | + | |
| 128 | + /** | |
| 129 | + * 改派快递员 | |
| 130 | + * | |
| 131 | + * @param ids 任务id列表 | |
| 132 | + * @param originalCourierId 原快递员id | |
| 133 | + * @param targetCourierId 目标快递员id | |
| 134 | + * @return 是否成功 | |
| 135 | + */ | |
| 136 | + @Override | |
| 137 | + public Boolean updateCourierId(List<Long> ids, Long originalCourierId, Long targetCourierId) { | |
| 138 | + if (ObjectUtil.hasEmpty(ids, targetCourierId, originalCourierId)) { | |
| 139 | + throw new SLException(WorkExceptionEnum.UPDATE_COURIER_PARAM_ERROR); | |
| 140 | + } | |
| 141 | + | |
| 142 | + if (ObjectUtil.equal(originalCourierId, targetCourierId)) { | |
| 143 | + throw new SLException(WorkExceptionEnum.UPDATE_COURIER_EQUAL_PARAM_ERROR); | |
| 144 | + } | |
| 145 | + | |
| 146 | + List<PickupDispatchTaskEntity> pickupDispatchTaskEntities = listByIds(ids); | |
| 147 | + if (CollUtil.isEmpty(pickupDispatchTaskEntities)) { | |
| 148 | + throw new SLException(WorkExceptionEnum.PICKUP_DISPATCH_TASK_NOT_FOUND); | |
| 149 | + } | |
| 150 | + | |
| 151 | + pickupDispatchTaskEntities.forEach(pickupDispatchTaskEntity -> { | |
| 152 | +// 校验原快递id是否正确(本来无快递员ID的情况除外) | |
| 153 | + if (ObjectUtil.isNotEmpty(pickupDispatchTaskEntity.getCourierId()) | |
| 154 | + && ObjectUtil.notEqual(pickupDispatchTaskEntity.getCourierId(), originalCourierId)) { | |
| 155 | + throw new SLException(WorkExceptionEnum.UPDATE_COURIER_ID_PARAM_ERROR); | |
| 156 | + } | |
| 157 | + | |
| 158 | +// 更新快递员ID | |
| 159 | + pickupDispatchTaskEntity.setCourierId(targetCourierId); | |
| 160 | + | |
| 161 | +// 标识已分配状态 | |
| 162 | + pickupDispatchTaskEntity.setAssignedStatus(PickupDispatchTaskAssignedStatus.DISTRIBUTED); | |
| 163 | + }); | |
| 164 | + | |
| 165 | +// 批量更新 | |
| 166 | + List<Long> taskIds = pickupDispatchTaskEntities.stream().map(PickupDispatchTaskEntity::getId).collect(Collectors.toList()); | |
| 167 | + | |
| 168 | + LambdaUpdateWrapper<PickupDispatchTaskEntity> updateWrapper = Wrappers.<PickupDispatchTaskEntity>lambdaUpdate() | |
| 169 | + .in(PickupDispatchTaskEntity::getId, taskIds) | |
| 170 | + .set(PickupDispatchTaskEntity::getCourierId, targetCourierId) | |
| 171 | + .set(PickupDispatchTaskEntity::getAssignedStatus, PickupDispatchTaskAssignedStatus.DISTRIBUTED); | |
| 172 | + | |
| 173 | + boolean result = update(updateWrapper); | |
| 174 | + if (result) { | |
| 175 | + | |
| 176 | + } | |
| 177 | + | |
| 178 | + return result; | |
| 179 | + } | |
| 180 | + | |
| 181 | + /** | |
| 182 | + * 新增取派件任务 | |
| 183 | + * | |
| 184 | + * @param taskPickupDispatch 取派件任务信息 | |
| 185 | + * @return 取派件任务信息 | |
| 186 | + */ | |
| 187 | + @Override | |
| 188 | + public PickupDispatchTaskEntity saveTaskPickupDispatch(PickupDispatchTaskEntity taskPickupDispatch) { | |
| 189 | +// 设置任务状态为新任务 | |
| 190 | + taskPickupDispatch.setStatus(PickupDispatchTaskStatus.NEW); | |
| 191 | + boolean result = super.save(taskPickupDispatch); | |
| 192 | + | |
| 193 | + if (result) { | |
| 194 | + | |
| 195 | + return taskPickupDispatch; | |
| 196 | + } | |
| 197 | + throw new SLException(WorkExceptionEnum.PICKUP_DISPATCH_TASK_SAVE_ERROR); | |
| 198 | + } | |
| 199 | + | |
| 200 | + /** | |
| 201 | + * 分页查询取派件任务 | |
| 202 | + * | |
| 203 | + * @param dto 查询条件 | |
| 204 | + * @return 分页结果 | |
| 205 | + */ | |
| 206 | + @Override | |
| 207 | + public PageResponse<PickupDispatchTaskDTO> findByPage(PickupDispatchTaskPageQueryDTO dto) { | |
| 208 | +// 1.构造条件 | |
| 209 | + Page<PickupDispatchTaskEntity> iPage = new Page<>(dto.getPage(), dto.getPageSize()); | |
| 210 | + LambdaQueryWrapper<PickupDispatchTaskEntity> queryWrapper = Wrappers.<PickupDispatchTaskEntity>lambdaQuery() | |
| 211 | + .like(ObjectUtil.isNotEmpty(dto.getId()), PickupDispatchTaskEntity::getId, dto.getId()) | |
| 212 | + .like(ObjectUtil.isNotEmpty(dto.getOrderId()), PickupDispatchTaskEntity::getOrderId, dto.getOrderId()) | |
| 213 | + .eq(ObjectUtil.isNotEmpty(dto.getAgencyId()), PickupDispatchTaskEntity::getAgencyId, dto.getAgencyId()) | |
| 214 | + .eq(ObjectUtil.isNotEmpty(dto.getCourierId()), PickupDispatchTaskEntity::getCourierId, dto.getCourierId()) | |
| 215 | + .eq(ObjectUtil.isNotEmpty(dto.getTaskType()), PickupDispatchTaskEntity::getTaskType, dto.getTaskType()) | |
| 216 | + .eq(ObjectUtil.isNotEmpty(dto.getStatus()), PickupDispatchTaskEntity::getStatus, dto.getStatus()) | |
| 217 | + .eq(ObjectUtil.isNotEmpty(dto.getAssignedStatus()), PickupDispatchTaskEntity::getAssignedStatus, dto.getAssignedStatus()) | |
| 218 | + .eq(ObjectUtil.isNotEmpty(dto.getSignStatus()), PickupDispatchTaskEntity::getSignStatus, dto.getSignStatus()) | |
| 219 | + .eq(ObjectUtil.isNotEmpty(dto.getIsDeleted()), PickupDispatchTaskEntity::getIsDeleted, dto.getIsDeleted()) | |
| 220 | + .between(ObjectUtil.isNotEmpty(dto.getMinEstimatedEndTime()), PickupDispatchTaskEntity::getEstimatedEndTime, dto.getMinEstimatedEndTime(), dto.getMaxEstimatedEndTime()) | |
| 221 | + .between(ObjectUtil.isNotEmpty(dto.getMinActualEndTime()), PickupDispatchTaskEntity::getActualEndTime, dto.getMinActualEndTime(), dto.getMaxActualEndTime()) | |
| 222 | + .orderByDesc(PickupDispatchTaskEntity::getUpdated); | |
| 223 | + | |
| 224 | +// 2. 分页查询 | |
| 225 | + Page<PickupDispatchTaskEntity> result = super.page(iPage, queryWrapper); | |
| 226 | + | |
| 227 | +// 3.实体类转dto | |
| 228 | + return PageResponse.of(result, PickupDispatchTaskDTO.class); | |
| 229 | + } | |
| 230 | + | |
| 231 | + /** | |
| 232 | + * 按照当日快递员id列表查询每个快递员的取派件任务数 | |
| 233 | + * | |
| 234 | + * @param courierIds 快递员id列表 | |
| 235 | + * @param pickupDispatchTaskType 任务类型 | |
| 236 | + * @param date 日期,格式:yyyy-MM-dd 或 yyyyMMdd | |
| 237 | + * @return 任务数 | |
| 238 | + */ | |
| 239 | + @Override | |
| 240 | + public List<CourierTaskCountDTO> findCountByCourierIds(List<Long> courierIds, PickupDispatchTaskType pickupDispatchTaskType, String date) { | |
| 241 | + DateTime dateTime = DateUtil.parse(date); | |
| 242 | + LocalDateTime startDateTime = DateUtil.beginOfDay(dateTime).toLocalDateTime(); | |
| 243 | + LocalDateTime endDateTime = DateUtil.endOfDay(dateTime).toLocalDateTime(); | |
| 244 | + return taskPickupDispatchMapper.findCountByCourierIds(courierIds, | |
| 245 | + pickupDispatchTaskType.getCode(), | |
| 246 | + startDateTime, | |
| 247 | + endDateTime); | |
| 248 | + } | |
| 249 | + | |
| 250 | + /** | |
| 251 | + * 根据订单id查询取派件任务 | |
| 252 | + * | |
| 253 | + * @param orderId 订单id | |
| 254 | + * @param taskType 任务类型 | |
| 255 | + * @return 任务 | |
| 256 | + */ | |
| 257 | + @Override | |
| 258 | + public List<PickupDispatchTaskEntity> findByOrderId(Long orderId, PickupDispatchTaskType taskType) { | |
| 259 | + LambdaQueryWrapper<PickupDispatchTaskEntity> wrapper = Wrappers.<PickupDispatchTaskEntity>lambdaQuery() | |
| 260 | + .eq(PickupDispatchTaskEntity::getOrderId, orderId) | |
| 261 | + .eq(PickupDispatchTaskEntity::getTaskType, taskType) | |
| 262 | + .orderByAsc(PickupDispatchTaskEntity::getCreated); | |
| 263 | + return list(wrapper); | |
| 264 | + } | |
| 265 | + | |
| 266 | + @Override | |
| 267 | + public boolean deleteByIds(List<Long> ids) { | |
| 268 | + if (CollUtil.isEmpty(ids)) { | |
| 269 | + return false; | |
| 270 | + } | |
| 271 | + | |
| 272 | +// 通过Id列表构造对象列表 | |
| 273 | + List<PickupDispatchTaskEntity> list = ids.stream().map(id -> { | |
| 274 | + PickupDispatchTaskEntity pickupDispatchTaskEntity = new PickupDispatchTaskEntity(); | |
| 275 | + pickupDispatchTaskEntity.setId(id); | |
| 276 | + pickupDispatchTaskEntity.setIsDeleted(PickupDispatchTaskIsDeleted.IS_DELETED); | |
| 277 | + | |
| 278 | + return pickupDispatchTaskEntity; | |
| 279 | + }).collect(Collectors.toList()); | |
| 280 | + | |
| 281 | + return updateBatchById(list); | |
| 282 | + } | |
| 283 | + | |
| 284 | + /** | |
| 285 | + * 今日任务分类计数 | |
| 286 | + * | |
| 287 | + * @param courierId 快递员id | |
| 288 | + * @param taskType 任务类型,1为取件任务,2为派件任务 | |
| 289 | + * @param status 任务状态,1新任务,2已完成,3已取消 | |
| 290 | + * @param isDeleted 是否逻辑删除 | |
| 291 | + * @return 任务数量 | |
| 292 | + */ | |
| 293 | + @Override | |
| 294 | + public Integer todayTasksCount(Long courierId, PickupDispatchTaskType taskType, PickupDispatchTaskStatus status, PickupDispatchTaskIsDeleted isDeleted) { | |
| 295 | +// 构建查询条件 | |
| 296 | + LambdaQueryWrapper<PickupDispatchTaskEntity> queryWrapper = Wrappers.<PickupDispatchTaskEntity>lambdaQuery() | |
| 297 | + .eq(ObjectUtil.isNotEmpty(courierId), PickupDispatchTaskEntity::getCourierId, courierId) | |
| 298 | + .eq(ObjectUtil.isNotEmpty(taskType), PickupDispatchTaskEntity::getTaskType, taskType) | |
| 299 | + .eq(ObjectUtil.isNotEmpty(status), PickupDispatchTaskEntity::getStatus, status) | |
| 300 | + .eq(ObjectUtil.isNotEmpty(isDeleted), PickupDispatchTaskEntity::getIsDeleted, isDeleted); | |
| 301 | + | |
| 302 | +// 根据任务状态限定查询的日期条件 | |
| 303 | + LocalDateTime startTime = LocalDateTimeUtil.of(DateUtil.beginOfDay(new Date())); | |
| 304 | + LocalDateTime endTime = LocalDateTimeUtil.of(DateUtil.endOfDay(new Date())); | |
| 305 | + if (status == null) { | |
| 306 | +// 没有任务状态,查询任务创建时间 | |
| 307 | + queryWrapper.between(PickupDispatchTaskEntity::getCreated, startTime, endTime); | |
| 308 | + } else if (status == PickupDispatchTaskStatus.NEW) { | |
| 309 | +// 新任务状态,查询预计结束时间 | |
| 310 | + queryWrapper.between(PickupDispatchTaskEntity::getEstimatedEndTime, startTime, endTime); | |
| 311 | + } else if (status == PickupDispatchTaskStatus.COMPLETED) { | |
| 312 | +// 完成状态,查询实际完成时间 | |
| 313 | + queryWrapper.between(PickupDispatchTaskEntity::getActualEndTime, startTime, endTime); | |
| 314 | + } else if (status == PickupDispatchTaskStatus.CANCELLED) { | |
| 315 | +// 取消状态,查询取消时间 | |
| 316 | + queryWrapper.between(PickupDispatchTaskEntity::getCancelTime, startTime, endTime); | |
| 317 | + } | |
| 318 | + | |
| 319 | + | |
| 320 | + return Convert.toInt(count(queryWrapper)); | |
| 321 | + } | |
| 322 | + | |
| 323 | + /** | |
| 324 | + * 条件查询所有 | |
| 325 | + * | |
| 326 | + * @param courierId 快递员id | |
| 327 | + * @param taskType 任务类型,1为取件任务,2为派件任务 | |
| 328 | + * @param taskStatus 任务状态,1新任务,2已完成,3已取消 | |
| 329 | + * @param isDeleted 是否逻辑删除 | |
| 330 | + * @return 取派件任务列表 | |
| 331 | + */ | |
| 332 | + @Override | |
| 333 | + public List<PickupDispatchTaskDTO> findAll(Long courierId, PickupDispatchTaskType taskType, PickupDispatchTaskStatus taskStatus, PickupDispatchTaskIsDeleted isDeleted) { | |
| 334 | +// 构建查询条件 | |
| 335 | + LambdaQueryWrapper<PickupDispatchTaskEntity> queryWrapper = Wrappers.<PickupDispatchTaskEntity>lambdaQuery() | |
| 336 | + .eq(ObjectUtil.isNotEmpty(courierId), PickupDispatchTaskEntity::getCourierId, courierId) | |
| 337 | + .eq(ObjectUtil.isNotEmpty(taskType), PickupDispatchTaskEntity::getTaskType, taskType) | |
| 338 | + .eq(ObjectUtil.isNotEmpty(taskStatus), PickupDispatchTaskEntity::getStatus, taskStatus) | |
| 339 | + .eq(ObjectUtil.isNotEmpty(isDeleted), PickupDispatchTaskEntity::getIsDeleted, isDeleted); | |
| 340 | + | |
| 341 | + List<PickupDispatchTaskEntity> taskEntities = list(queryWrapper); | |
| 342 | + return BeanUtil.copyToList(taskEntities, PickupDispatchTaskDTO.class); | |
| 343 | + } | |
| 344 | + | |
| 345 | + /** | |
| 346 | + * 今日任务分类统计 | |
| 347 | + * | |
| 348 | + * @param courierId 快递员id | |
| 349 | + * @return 统计结果 | |
| 350 | + */ | |
| 351 | + @Override | |
| 352 | + public PickupDispatchTaskStatisticsDTO todayTaskStatistics(Long courierId) { | |
| 353 | + PickupDispatchTaskStatisticsDTO taskStatisticsDTO = new PickupDispatchTaskStatisticsDTO(); | |
| 354 | + | |
| 355 | +// 今日取件任务数量 | |
| 356 | + taskStatisticsDTO.setPickupNum(todayTasksCount(courierId, PickupDispatchTaskType.PICKUP, null, PickupDispatchTaskIsDeleted.NOT_DELETED)); | |
| 357 | + | |
| 358 | + //今日待取件任务数量 | |
| 359 | + taskStatisticsDTO.setNewPickUpNum(todayTasksCount(courierId, PickupDispatchTaskType.PICKUP, PickupDispatchTaskStatus.NEW, PickupDispatchTaskIsDeleted.NOT_DELETED)); | |
| 360 | + | |
| 361 | + //今日已取件任务数量 | |
| 362 | + taskStatisticsDTO.setCompletePickUpNum(todayTasksCount(courierId, PickupDispatchTaskType.PICKUP, PickupDispatchTaskStatus.COMPLETED, PickupDispatchTaskIsDeleted.NOT_DELETED)); | |
| 363 | + | |
| 364 | + //今日已取消取件任务数量 | |
| 365 | + taskStatisticsDTO.setCancelPickUpNum(todayTasksCount(courierId, PickupDispatchTaskType.PICKUP, PickupDispatchTaskStatus.CANCELLED, PickupDispatchTaskIsDeleted.NOT_DELETED)); | |
| 366 | + | |
| 367 | + //今日派件任务数量 | |
| 368 | + taskStatisticsDTO.setDispatchNum(todayTasksCount(courierId, PickupDispatchTaskType.DISPATCH, null, PickupDispatchTaskIsDeleted.NOT_DELETED)); | |
| 369 | + | |
| 370 | + //今日待派件任务数量 | |
| 371 | + taskStatisticsDTO.setNewDispatchNum(todayTasksCount(courierId, PickupDispatchTaskType.DISPATCH, PickupDispatchTaskStatus.NEW, PickupDispatchTaskIsDeleted.NOT_DELETED)); | |
| 372 | + | |
| 373 | + //今日已签收任务数量 | |
| 374 | + taskStatisticsDTO.setSignedNum(todayTasksCount(courierId, PickupDispatchTaskType.DISPATCH, PickupDispatchTaskStatus.COMPLETED, PickupDispatchTaskIsDeleted.NOT_DELETED)); | |
| 375 | + | |
| 376 | + //今日已取消派件任务数量 | |
| 377 | + taskStatisticsDTO.setCancelDispatchNum(todayTasksCount(courierId, PickupDispatchTaskType.DISPATCH, PickupDispatchTaskStatus.CANCELLED, PickupDispatchTaskIsDeleted.NOT_DELETED)); | |
| 378 | + return taskStatisticsDTO; | |
| 379 | + } | |
| 380 | +} | ... | ... |