Browse Source

add 管理端 选择师傅列表

tea 4 weeks ago
parent
commit
f2e1a2fa7e
20 changed files with 252 additions and 129 deletions
  1. 7 0
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/order/service/impl/KxStoreOrderServiceImpl.java
  2. 5 3
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/KxRiderController.java
  3. 2 2
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/config/SmartDispatchConfig.java
  4. 6 9
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/IKxRiderService.java
  5. 3 10
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/ISmartDispatchService.java
  6. 35 51
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/impl/KxRiderServiceImpl.java
  7. 3 3
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/impl/OrderDispatchServiceImpl.java
  8. 79 44
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/impl/SmartDispatchServiceImpl.java
  9. 1 1
      kxmall-admin/src/main/java/com/kxmall/KxmallShopApplication.java
  10. 1 1
      kxmall-rider-api/src/main/java/com/kxmall/web/controller/websocket/service/impl/RiderNotificationServiceImpl.java
  11. 1 0
      kxmall-system/src/main/java/com/kxmall/notify/service/RiderNotifyBizService.java
  12. 5 0
      kxmall-system/src/main/java/com/kxmall/notify/service/impl/UniNotifyRiderNotifyBizServiceImpl.java
  13. 6 0
      kxmall-system/src/main/java/com/kxmall/notify/service/impl/mock/MockRiderNotifyBizServiceImpl.java
  14. 12 0
      kxmall-system/src/main/java/com/kxmall/order/biz/bo/SmartDispatchParamBo.java
  15. 18 0
      kxmall-system/src/main/java/com/kxmall/rider/domain/bo/ConflictTimeRiderBo.java
  16. 38 0
      kxmall-system/src/main/java/com/kxmall/rider/domain/vo/DispatchRiderVo.java
  17. 2 2
      kxmall-system/src/main/java/com/kxmall/rider/domain/vo/KxRiderVo.java
  18. 12 1
      kxmall-system/src/main/java/com/kxmall/rider/mapper/KxRiderOrderMapper.java
  19. 14 0
      kxmall-system/src/main/resources/mapper/rider/KxRiderOrderMapper.xml
  20. 2 2
      需求列表.md

+ 7 - 0
kxmall-admin-api/src/main/java/com/kxmall/web/controller/order/service/impl/KxStoreOrderServiceImpl.java

@@ -24,6 +24,7 @@ import com.kxmall.coupon.domain.KxStoreCouponUser;
 import com.kxmall.coupon.mapper.KxStoreCouponUserMapper;
 import com.kxmall.executor.GlobalExecutor;
 import com.kxmall.notify.service.MemberNotifyBizService;
+import com.kxmall.notify.service.RiderNotifyBizService;
 import com.kxmall.order.biz.*;
 import com.kxmall.order.biz.common.DeliveryResult;
 import com.kxmall.order.domain.*;
@@ -111,6 +112,8 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
 
     private final DeliveryStrategyFactory deliveryStrategyFactory;
 
+    private final RiderNotifyBizService riderNotifyBizService;
+
     /**
      * 查询订单
      */
@@ -399,6 +402,10 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
 
     @Override
     public Boolean distributeOrder(String orderNo, Long riderId) {
+        return riderNotifyBizService.dispatchOrder();
+    }
+
+    public Boolean distributeOrder1(String orderNo, Long riderId) {
         boolean result = false;
         try {
             List<KxStoreOrder> storeOrders = baseMapper.selectList(new QueryWrapper<KxStoreOrder>().eq("order_id", orderNo));

+ 5 - 3
kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/KxRiderController.java

@@ -13,6 +13,7 @@ import com.kxmall.common.enums.BusinessType;
 import com.kxmall.common.utils.poi.ExcelUtil;
 import com.kxmall.rider.domain.KxRider;
 import com.kxmall.rider.domain.bo.KxRiderBo;
+import com.kxmall.rider.domain.vo.DispatchRiderVo;
 import com.kxmall.rider.domain.vo.KxRiderVo;
 import com.kxmall.web.controller.rider.service.IKxRiderService;
 import lombok.RequiredArgsConstructor;
@@ -44,11 +45,12 @@ public class KxRiderController extends BaseController {
     /**
      * 查询配送列表
      */
-    @GetMapping("/getRiderByStorageId")
-    public R<List<KxRiderVo>> getRiderByStorageId(Long storageId) {
-        return R.ok(iKxRiderService.getRiderByStorageId(storageId));
+    @GetMapping("/getRiderByOrderId")
+    public R<List<DispatchRiderVo>> getRiderByOrderId(@RequestParam("orderId") Long orderId) {
+        return R.ok(iKxRiderService.getRiderByOrderId(orderId));
     }
 
+
     // 获取骑手银行信息(保留兼容)
     @GetMapping("/bankInfo/{riderId}")
     public R<Map<String, Object>> getRiderBankInfo(@PathVariable Long riderId) {

+ 2 - 2
kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/config/SmartDispatchConfig.java

@@ -30,7 +30,7 @@ public class SmartDispatchConfig {
     /**
      * 距离权重 (0.0-1.0)
      */
-    private double distanceWeight = 0.3;
+    private double distanceWeight = 0.1;
 
     /**
      * 评分权重 (0.0-1.0)
@@ -40,7 +40,7 @@ public class SmartDispatchConfig {
     /**
      * 经验权重 (0.0-1.0)
      */
-    private double experienceWeight = 0.1;
+    private double experienceWeight = 0.3;
 
     /**
      * 最低匹配度阈值,低于此分数不推荐

+ 6 - 9
kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/IKxRiderService.java

@@ -4,6 +4,7 @@ import com.kxmall.common.core.domain.PageQuery;
 import com.kxmall.common.core.page.TableDataInfo;
 import com.kxmall.rider.domain.KxRider;
 import com.kxmall.rider.domain.bo.KxRiderBo;
+import com.kxmall.rider.domain.vo.DispatchRiderVo;
 import com.kxmall.rider.domain.vo.KxRiderVo;
 
 import java.util.Collection;
@@ -47,15 +48,6 @@ public interface IKxRiderService {
      */
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
 
-    /**
-     * 查询骑手列表
-     *
-     * @param storageId
-     * @return
-     */
-    List<KxRiderVo> getRiderByStorageId(Long storageId);
-
-
     /**
      * 配送员状态批量更新为正常
      *
@@ -110,4 +102,9 @@ public interface IKxRiderService {
      * @return
      */
     Boolean resetRiderPassword(Long riderId);
+
+    /**
+     * 通过订单查询可服务师傅列表
+     */
+    List<DispatchRiderVo> getRiderByOrderId(Long orderId);
 }

+ 3 - 10
kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/ISmartDispatchService.java

@@ -1,7 +1,8 @@
 package com.kxmall.web.controller.rider.service;
 
-import com.kxmall.rider.domain.vo.KxRiderVo;
 import com.kxmall.order.biz.bo.SmartDispatchParamBo;
+import com.kxmall.rider.domain.vo.DispatchRiderVo;
+import com.kxmall.rider.domain.vo.KxRiderVo;
 
 import java.util.List;
 
@@ -19,15 +20,7 @@ public interface ISmartDispatchService {
      * @param dispatchParam 智能派单参数
      * @return 推荐的师傅列表,按推荐度排序
      */
-    List<KxRiderVo> recommendRiders(SmartDispatchParamBo dispatchParam);
-
-    /**
-     * 自动选择最佳师傅
-     * 
-     * @param dispatchParam 智能派单参数
-     * @return 最佳师傅,如果没有合适的返回null
-     */
-    KxRiderVo selectBestRider(SmartDispatchParamBo dispatchParam);
+    List<DispatchRiderVo> recommendRiders(SmartDispatchParamBo dispatchParam);
 
     /**
      * 计算师傅与订单的匹配度

+ 35 - 51
kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/impl/KxRiderServiceImpl.java

@@ -1,6 +1,7 @@
 package com.kxmall.web.controller.rider.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -13,27 +14,31 @@ import com.kxmall.common.enums.RiderWorkStateType;
 import com.kxmall.common.exception.ServiceException;
 import com.kxmall.common.utils.StringUtils;
 import com.kxmall.common.utils.file.FileUtils;
+import com.kxmall.order.biz.bo.SmartDispatchParamBo;
+import com.kxmall.order.domain.KxStoreOrderProduct;
+import com.kxmall.order.domain.vo.KxStoreOrderVo;
+import com.kxmall.order.mapper.KxStoreOrderMapper;
+import com.kxmall.order.mapper.KxStoreOrderProductMapper;
 import com.kxmall.rider.domain.KxRider;
 import com.kxmall.rider.domain.KxRiderAuthAttachment;
 import com.kxmall.rider.domain.KxRiderCycle;
 import com.kxmall.rider.domain.KxRiderExt;
 import com.kxmall.rider.domain.bo.KxRiderBo;
+import com.kxmall.rider.domain.vo.DispatchRiderVo;
 import com.kxmall.rider.domain.vo.KxRiderVo;
 import com.kxmall.rider.mapper.KxRiderCycleMapper;
 import com.kxmall.rider.mapper.KxRiderExtMapper;
 import com.kxmall.rider.mapper.KxRiderMapper;
-import com.kxmall.storage.domain.KxStorage;
-import com.kxmall.storage.mapper.KxStorageMapper;
 import com.kxmall.system.service.ISysConfigService;
 import com.kxmall.web.controller.rider.service.IKxRiderAuthAttachmentService;
 import com.kxmall.web.controller.rider.service.IKxRiderService;
+import com.kxmall.web.controller.rider.service.ISmartDispatchService;
 import com.kxmall.wechat.WxMpConfiguration;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
 import org.apache.commons.codec.digest.Md5Crypt;
 import org.apache.commons.collections.CollectionUtils;
-import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.ObjectUtils;
@@ -60,14 +65,18 @@ public class KxRiderServiceImpl implements IKxRiderService {
 
     private final KxRiderCycleMapper riderCycleMapper;
 
-    private final KxStorageMapper storageMapper;
-
     private final ISysConfigService configService;
 
     private final IKxRiderAuthAttachmentService attachmentService;
 
     private final KxRiderExtMapper riderExtMapper;
 
+    private final ISmartDispatchService smartDispatchService;
+
+    private final KxStoreOrderMapper storeOrderMapper;
+
+    private final KxStoreOrderProductMapper storeOrderProductMapper;
+
     // 初始密码
     private static final String ININT_PASSWORD = "123456";
 
@@ -82,7 +91,7 @@ public class KxRiderServiceImpl implements IKxRiderService {
             QueryWrapper<KxRiderCycle> wrapper = new QueryWrapper<>();
             wrapper.eq("rider_id", id);
             List<KxRiderCycle> riderCycles = riderCycleMapper.selectList(wrapper);
-            if (riderCycles != null && riderCycles.size() > 0) {
+            if (CollUtil.isNotEmpty(riderCycles)) {
                 riderVo.setWeekNumberIds(riderCycles.stream().map(KxRiderCycle::getWeekNumber).collect(Collectors.toList()));
             }
             
@@ -274,51 +283,6 @@ public class KxRiderServiceImpl implements IKxRiderService {
         return baseMapper.deleteBatchIds(ids) > 0;
     }
 
-    @Override
-    public List<KxRiderVo> getRiderByStorageId(Long storageId) {
-
-        QueryWrapper<KxRider> wrapper = new QueryWrapper<>();
-        if (storageId != null) {
-            wrapper.eq("storage_id", storageId);
-        }
-        KxStorage kxStorage = storageMapper.selectById(storageId);
-        wrapper.eq("state", RiderStatusType.NOMRAL.getCode());
-        wrapper.eq("work_state", RiderWorkStateType.WORKING.getCode());
-        int state = RiderStatusType.NOMRAL.getCode();
-        int workState = RiderWorkStateType.WORKING.getCode();
-        //过滤不在工作区间
-        Date now = new Date();
-        SimpleDateFormat format = new SimpleDateFormat("HH:mm");
-        String nowDate = format.format(now);
-        List<KxRider> riderDOList = baseMapper.selectListByWorkTime(storageId, state, workState, getWeekOfDate(new Date()));
-        if (CollectionUtils.isEmpty(riderDOList)) {
-            throw new ServiceException("不存在工作中的骑手");
-        }
-        List<KxRider> collect = riderDOList.stream().filter(x -> {
-            try {
-                return isEffectiveDate(nowDate, x.getDeliveryStart(), x.getDeliveryEnd());
-            } catch (ParseException e) {
-                log.warn(e.getMessage(), e);
-            }
-            return false;
-        }).collect(Collectors.toList());
-        if (CollectionUtils.isEmpty(collect)) {
-            throw new ServiceException("不存在工作中的骑手", 500);
-        }
-        List<KxRiderVo> riderDOS = new ArrayList<>();
-        for (KxRider riderDO : collect) {
-            KxRiderVo riderDTO = new KxRiderVo();
-            BeanUtils.copyProperties(riderDO, riderDTO);
-            riderDTO.setAutomatic(kxStorage.getAutomatic());
-            riderDOS.add(riderDTO);
-        }
-        //需要自动分配骑手
-        if (kxStorage.getAutomatic() == 1) {
-            Optional<KxRiderVo> any = riderDOS.stream().findAny();
-            riderDOS = Collections.singletonList(any.get());
-        }
-        return riderDOS;
-    }
 
 
     /**
@@ -470,4 +434,24 @@ public class KxRiderServiceImpl implements IKxRiderService {
         update.setUpdateTime(new Date());
         return baseMapper.updateById(update) > 0;
     }
+
+    @Override
+    public List<DispatchRiderVo> getRiderByOrderId(Long orderId) {
+        KxStoreOrderVo kxStoreOrderVo = storeOrderMapper.selectVoById(orderId);
+
+        LambdaQueryWrapper<KxStoreOrderProduct> storeOrderProductLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        storeOrderProductLambdaQueryWrapper.eq(KxStoreOrderProduct::getOrderId, kxStoreOrderVo.getId());
+        List<KxStoreOrderProduct> storeOrderProductList = storeOrderProductMapper.selectList(storeOrderProductLambdaQueryWrapper);
+        if (CollUtil.isEmpty(storeOrderProductList)) {
+            throw new ServiceException("订单商品为空,订单号:" + kxStoreOrderVo.getOrderId());
+        }
+
+        List<String> keywords = storeOrderProductList.stream()
+                .map(storeOrderProduct -> storeOrderProduct.getKeyword().split(","))
+                .flatMap(Arrays::stream)
+                .collect(Collectors.toList());
+
+        SmartDispatchParamBo dispatchParam = new SmartDispatchParamBo(kxStoreOrderVo, keywords);
+        return smartDispatchService.recommendRiders(dispatchParam);
+    }
 }

+ 3 - 3
kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/impl/OrderDispatchServiceImpl.java

@@ -14,7 +14,7 @@ import com.kxmall.order.domain.KxStoreOrderProduct;
 import com.kxmall.order.domain.vo.KxStoreOrderVo;
 import com.kxmall.order.mapper.KxStoreOrderProductMapper;
 import com.kxmall.rider.domain.KxOrderRiderRecommend;
-import com.kxmall.rider.domain.vo.KxRiderVo;
+import com.kxmall.rider.domain.vo.DispatchRiderVo;
 import com.kxmall.rider.mapper.KxOrderRiderRecommendMapper;
 import com.kxmall.web.controller.rider.service.IOrderDispatchService;
 import com.kxmall.web.controller.rider.service.ISmartDispatchService;
@@ -57,7 +57,7 @@ public class OrderDispatchServiceImpl implements IOrderDispatchService {
         waitingOrderMap.forEach((orderNo, storeOrder) -> {
             try {
                 dispatchOrder(storeOrder);
-            }catch (Exception e) {
+            } catch (Exception e) {
                 log.error("处理订单 {} 派单时发生异常: {}", orderNo, e.getMessage(), e);
             }
         });
@@ -68,7 +68,7 @@ public class OrderDispatchServiceImpl implements IOrderDispatchService {
         // 构建智能派单参数
         SmartDispatchParamBo dispatchParam = buildDispatchParam(order);
         // 获取推荐师傅列表
-        List<KxRiderVo> recommendedRiderList = smartDispatchService.recommendRiders(dispatchParam);
+        List<DispatchRiderVo> recommendedRiderList = smartDispatchService.recommendRiders(dispatchParam);
 
         if (CollUtil.isEmpty(recommendedRiderList)) {
             log.info("订单 {} 没有找到合适的师傅", order.getOrderId());

+ 79 - 44
kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/impl/SmartDispatchServiceImpl.java

@@ -1,17 +1,22 @@
 package com.kxmall.web.controller.rider.service.impl;
 
+import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.LocalDateTimeUtil;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.kxmall.common.enums.RiderStatusType;
 import com.kxmall.common.enums.RiderWorkStateType;
+import com.kxmall.order.biz.bo.SmartDispatchParamBo;
 import com.kxmall.rider.domain.KxRider;
 import com.kxmall.rider.domain.KxRiderExt;
+import com.kxmall.rider.domain.bo.ConflictTimeRiderBo;
+import com.kxmall.rider.domain.vo.DispatchRiderVo;
 import com.kxmall.rider.domain.vo.KxRiderVo;
 import com.kxmall.rider.mapper.KxRiderExtMapper;
 import com.kxmall.rider.mapper.KxRiderMapper;
+import com.kxmall.rider.mapper.KxRiderOrderMapper;
 import com.kxmall.web.controller.rider.config.SmartDispatchConfig;
-import com.kxmall.order.biz.bo.SmartDispatchParamBo;
 import com.kxmall.web.controller.rider.service.ISmartDispatchService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -21,7 +26,8 @@ import org.springframework.stereotype.Service;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.text.ParseException;
-import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -39,25 +45,43 @@ public class SmartDispatchServiceImpl implements ISmartDispatchService {
 
     private final KxRiderMapper riderMapper;
     private final KxRiderExtMapper riderExtMapper;
+    private final KxRiderOrderMapper riderOrderMapper;
     private final SmartDispatchConfig smartDispatchConfig;
 
     @Override
-    public List<KxRiderVo> recommendRiders(SmartDispatchParamBo dispatchParam) {
+    public List<DispatchRiderVo> recommendRiders(SmartDispatchParamBo dispatchParam) {
         // 获取可用的师傅列表
-        List<KxRiderVo> availableRiders = getAvailableRiders();
-
+        List<KxRiderVo> availableRiders = getAvailableRiders(dispatchParam);
         if (CollUtil.isEmpty(availableRiders)) {
             log.warn("没有可用的师傅,orderNo: {}", dispatchParam.getOrderNo());
             return new ArrayList<>();
         }
 
+        List<ConflictTimeRiderBo> timeConflictRider = riderOrderMapper.selectRiderOrdersByTimeRange(dispatchParam.getPredictTime());
+        Map<Long, List<LocalDateTime>> conflictMap = timeConflictRider.stream()
+                .collect(
+                        Collectors.groupingBy(
+                                ConflictTimeRiderBo::getRiderId,
+                                Collectors.mapping(ConflictTimeRiderBo::getConflictTime, Collectors.toList()
+                                )
+                        )
+                );
+
         // 计算匹配度并排序
-        List<KxRiderVo> rankedRiders = availableRiders.stream()
-                .peek(rider -> {
-                    Double matchScore = calculateMatchScore(rider, dispatchParam);
-                    rider.setMatchScore(matchScore);
+        List<DispatchRiderVo> rankedRiders = availableRiders.stream()
+                .map(rider -> {
+                    DispatchRiderVo dispatchRiderVo = BeanUtil.toBean(rider, DispatchRiderVo.class);
+                    if (conflictMap.containsKey(rider.getId())) {
+                        List<LocalDateTime> ls = conflictMap.get(rider.getId());
+                        dispatchRiderVo.setConflictTime(ls);
+                    } else {
+                        Double matchScore = calculateMatchScore(rider, dispatchParam);
+                        dispatchRiderVo.setMatchScore(BigDecimal.valueOf(matchScore));
+                    }
+
+                    return dispatchRiderVo;
                 })
-                .sorted((r1, r2) -> Double.compare(r2.getMatchScore(), r1.getMatchScore()))
+                .sorted(Comparator.comparing(DispatchRiderVo::getMatchScore).reversed())
                 .collect(Collectors.toList());
 
         log.debug("为订单 {} 推荐了 {} 个师傅,最佳匹配度: {}",
@@ -67,29 +91,6 @@ public class SmartDispatchServiceImpl implements ISmartDispatchService {
         return rankedRiders;
     }
 
-    @Override
-    public KxRiderVo selectBestRider(SmartDispatchParamBo dispatchParam) {
-        List<KxRiderVo> recommendedRiders = recommendRiders(dispatchParam);
-
-        if (CollUtil.isEmpty(recommendedRiders)) {
-            return null;
-        }
-
-        KxRiderVo bestRider = recommendedRiders.get(0);
-
-        // 根据紧急程度调整阈值
-        double threshold = getMatchThreshold(dispatchParam.getUrgencyLevel());
-
-        if (bestRider.getMatchScore() >= threshold) {
-            log.info("自动选择师傅: {} (ID: {}), 匹配度: {}, 阈值: {}",
-                    bestRider.getName(), bestRider.getId(), bestRider.getMatchScore(), threshold);
-            return bestRider;
-        }
-
-        log.warn("最佳师傅匹配度不足{}分: {}, 不进行自动派单", threshold, bestRider.getMatchScore());
-        return null;
-    }
-
     @Override
     public Double calculateMatchScore(KxRiderVo rider, SmartDispatchParamBo dispatchParam) {
         try {
@@ -103,7 +104,7 @@ public class SmartDispatchServiceImpl implements ISmartDispatchService {
             double totalScore = (serviceTypeScore * smartDispatchConfig.getServiceTypeWeight()
                     + distanceScore * smartDispatchConfig.getDistanceWeight()
                     + ratingScore * smartDispatchConfig.getRatingWeight()
-                    + experienceScore * smartDispatchConfig.getMinMatchScore()) * urgencyMultiplier;
+                    + experienceScore * smartDispatchConfig.getExperienceWeight()) * urgencyMultiplier;
 
             log.debug("师傅 {} 匹配度: 服务={}, 距离={}, 评分={}, 经验={}, 紧急倍数={}, 总分={}",
                     rider.getName(), serviceTypeScore, distanceScore, ratingScore, experienceScore, urgencyMultiplier, totalScore);
@@ -278,11 +279,8 @@ public class SmartDispatchServiceImpl implements ISmartDispatchService {
     /**
      * 获取可用的师傅列表
      */
-    private List<KxRiderVo> getAvailableRiders() {
+    private List<KxRiderVo> getAvailableRiders(SmartDispatchParamBo dispatchParam) {
         // 获取当前时间和星期
-        Date now = new Date();
-        SimpleDateFormat format = new SimpleDateFormat("HH:mm");
-        String nowTime = format.format(now);
 
         // 查询在工作时间范围内的师傅
         int state = RiderStatusType.NOMRAL.getCode();
@@ -291,6 +289,12 @@ public class SmartDispatchServiceImpl implements ISmartDispatchService {
         LambdaQueryWrapper<KxRider> lqw = new LambdaQueryWrapper<>();
         lqw.eq(KxRider::getState, state)
                 .eq(KxRider::getWorkState, workState);
+
+        // 如果指定了仓库ID,按仓库过滤师傅
+        if (dispatchParam.getStorageId() != null) {
+            lqw.eq(KxRider::getStorageId, dispatchParam.getStorageId());
+        }
+
         List<KxRider> riderList = riderMapper.selectList(lqw);
 
         if (CollUtil.isEmpty(riderList)) {
@@ -301,7 +305,7 @@ public class SmartDispatchServiceImpl implements ISmartDispatchService {
         return riderList.stream()
                 .filter(rider -> {
                     try {
-                        return isEffectiveDate(nowTime, rider.getDeliveryStart(), rider.getDeliveryEnd());
+                        return isEffectiveDate(dispatchParam.getPredictTime(), rider.getDeliveryStart(), rider.getDeliveryEnd());
                     } catch (ParseException e) {
                         log.warn("解析师傅{}工作时间失败: {}", rider.getName(), e.getMessage());
                         return false;
@@ -334,16 +338,47 @@ public class SmartDispatchServiceImpl implements ISmartDispatchService {
     /**
      * 判断当前时间是否在工作时间区间内
      */
-    private boolean isEffectiveDate(String nowDate, String startDate, String endDate) throws ParseException {
+    private boolean isEffectiveDate(LocalDateTime nowDate, String startDate, String endDate) throws ParseException {
         String format = "HH:mm";
         if (StrUtil.isBlank(startDate) || StrUtil.isBlank(endDate)) {
             return false;
         }
+        if (endDate.equals("24:00")) {
+            endDate = "23:59";
+        }
+
+        LocalDate localDate = nowDate.toLocalDate();
+        LocalDateTime s = LocalDateTime.of(localDate, LocalDateTimeUtil.parse(startDate, format).toLocalTime());
+        LocalDateTime e = LocalDateTime.of(localDate, LocalDateTimeUtil.parse(endDate, format).toLocalTime());
+        return LocalDateTimeUtil.isIn(nowDate, s, e);
+    }
 
-        Date startTime = new SimpleDateFormat(format).parse(startDate);
-        Date endTime = new SimpleDateFormat(format).parse(endDate);
-        Date currentTime = new SimpleDateFormat(format).parse(nowDate);
+    /**
+     * 计算两个时间段的重叠时间(分钟)
+     *
+     * @param start1 时间段1开始时间
+     * @param end1   时间段1结束时间
+     * @param start2 时间段2开始时间
+     * @param end2   时间段2结束时间
+     * @return 重叠时间(分钟)
+     */
+    private long calculateTimeOverlap(Date start1, Date end1, Date start2, Date end2) {
+        // 确保时间顺序正确
+        Date actualStart1 = start1.before(end1) ? start1 : end1;
+        Date actualEnd1 = start1.before(end1) ? end1 : start1;
+        Date actualStart2 = start2.before(end2) ? start2 : end2;
+        Date actualEnd2 = start2.before(end2) ? end2 : start2;
+
+        // 计算重叠时间
+        Date overlapStart = actualStart1.after(actualStart2) ? actualStart1 : actualStart2;
+        Date overlapEnd = actualEnd1.before(actualEnd2) ? actualEnd1 : actualEnd2;
+
+        // 如果没有重叠,返回0
+        if (overlapStart.after(overlapEnd) || overlapStart.equals(overlapEnd)) {
+            return 0;
+        }
 
-        return currentTime.getTime() >= startTime.getTime() && currentTime.getTime() <= endTime.getTime();
+        // 计算重叠时间(分钟)
+        return (overlapEnd.getTime() - overlapStart.getTime()) / (1000 * 60);
     }
 }

+ 1 - 1
kxmall-admin/src/main/java/com/kxmall/KxmallShopApplication.java

@@ -22,7 +22,7 @@ public class KxmallShopApplication {
         application.run(args);
         System.out.println("(♥◠‿◠)ノ゙  ============   ლ(´ڡ`ლ)゙");
         System.out.println("(♥◠‿◠)ノ゙  ============   ლ(´ڡ`ლ)゙");
-        System.out.println("(♥◠‿◠)ノ゙  =服务启动成功=  ლ(´ڡ`ლ)゙");
+        System.out.println("(♥◠‿◠)ノ゙   服务启动成功   ლ(´ڡ`ლ)゙");
         System.out.println("(♥◠‿◠)ノ゙  ============   ლ(´ڡ`ლ)゙");
         System.out.println("(♥◠‿◠)ノ゙  ============   ლ(´ڡ`ლ)゙");
     }

+ 1 - 1
kxmall-rider-api/src/main/java/com/kxmall/web/controller/websocket/service/impl/RiderNotificationServiceImpl.java

@@ -47,7 +47,7 @@ public class RiderNotificationServiceImpl implements RiderNotificationService {
 
         // 发送通知
         for (KxRiderVo rider : onlineRiders) {
-            notification.setMatchScore(rider.getMatchScore());
+            // notification.setMatchScore(rider.getMatchScore());
             sendOrderNotification(String.valueOf(rider.getId()), notification);
         }
 

+ 1 - 0
kxmall-system/src/main/java/com/kxmall/notify/service/RiderNotifyBizService.java

@@ -14,4 +14,5 @@ public interface RiderNotifyBizService {
 
     void newOrder(List<KxOrderRiderRecommend> recommendList);
 
+    boolean dispatchOrder();
 }

+ 5 - 0
kxmall-system/src/main/java/com/kxmall/notify/service/impl/UniNotifyRiderNotifyBizServiceImpl.java

@@ -20,4 +20,9 @@ public class UniNotifyRiderNotifyBizServiceImpl implements RiderNotifyBizService
     public void newOrder(List<KxOrderRiderRecommend> recommendList) {
 
     }
+
+    @Override
+    public boolean dispatchOrder() {
+        return true;
+    }
 }

+ 6 - 0
kxmall-system/src/main/java/com/kxmall/notify/service/impl/mock/MockRiderNotifyBizServiceImpl.java

@@ -22,4 +22,10 @@ public class MockRiderNotifyBizServiceImpl implements RiderNotifyBizService {
     public void newOrder(List<KxOrderRiderRecommend> recommendList) {
         log.info("[师傅 mock通知 有新的订单] 派单通知:" + JSONObject.toJSONString(recommendList));
     }
+
+    @Override
+    public boolean dispatchOrder() {
+        log.info("[师傅 mock通知 管理员为你指派了新订单] 派单通知");
+        return true;
+    }
 }

+ 12 - 0
kxmall-system/src/main/java/com/kxmall/order/biz/bo/SmartDispatchParamBo.java

@@ -6,6 +6,8 @@ import lombok.ToString;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.util.List;
 
 /**
@@ -52,10 +54,20 @@ public class SmartDispatchParamBo implements Serializable {
      */
     private Integer urgencyLevel;
 
+    /**
+     * 订单服务开始时间
+     */
+    private LocalDateTime predictTime;
+
     public SmartDispatchParamBo(KxStoreOrderVo order, List<String> serviceKeywords) {
         this.setOrderNo(order.getOrderId());
         this.setLongitude(order.getLongitude());
         this.setLatitude(order.getLatitude());
+        this.setStorageId(order.getStoreId());
+
+        // 设置服务时间
+        LocalDateTime predictTime = order.getPredictDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+        this.setPredictTime(predictTime);
 
         int urgencyLevel = 1;
         if (order.getUrgentFee() != null) {

+ 18 - 0
kxmall-system/src/main/java/com/kxmall/rider/domain/bo/ConflictTimeRiderBo.java

@@ -0,0 +1,18 @@
+package com.kxmall.rider.domain.bo;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author tea
+ * @date 2025/9/12
+ */
+@Data
+public class ConflictTimeRiderBo {
+
+    private Long riderId;
+
+    private LocalDateTime conflictTime;
+
+}

+ 38 - 0
kxmall-system/src/main/java/com/kxmall/rider/domain/vo/DispatchRiderVo.java

@@ -0,0 +1,38 @@
+package com.kxmall.rider.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+
+/**
+ * 配送视图对象 kx_rider
+ *
+ * @author kxmall
+ * @date 2023-09-21
+ */
+@Data
+public class DispatchRiderVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 配送员主键ID
+     */
+    private Long id;
+
+    /**
+     * 配送员名字
+     */
+    private String name;
+
+    private BigDecimal matchScore;
+
+    /**
+     * 冲突时间
+     */
+    private List<LocalDateTime> conflictTime;
+}

+ 2 - 2
kxmall-system/src/main/java/com/kxmall/rider/domain/vo/KxRiderVo.java

@@ -5,6 +5,7 @@ import com.alibaba.excel.annotation.ExcelProperty;
 import com.kxmall.rider.domain.KxRiderAuthAttachment;
 import lombok.Data;
 
+import java.io.Serializable;
 import java.math.BigDecimal;
 import java.util.Date;
 import java.util.List;
@@ -18,7 +19,7 @@ import java.util.List;
  */
 @Data
 @ExcelIgnoreUnannotated
-public class KxRiderVo {
+public class KxRiderVo implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
@@ -154,5 +155,4 @@ public class KxRiderVo {
 
     private String idCardNumber;
 
-    private Double matchScore;
 }

+ 12 - 1
kxmall-system/src/main/java/com/kxmall/rider/mapper/KxRiderOrderMapper.java

@@ -3,10 +3,11 @@ package com.kxmall.rider.mapper;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.toolkit.Constants;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.kxmall.common.core.mapper.BaseMapperPlus;
 import com.kxmall.common.enums.RiderOrderStatusType;
 import com.kxmall.rider.domain.KxRiderOrder;
+import com.kxmall.rider.domain.bo.ConflictTimeRiderBo;
 import com.kxmall.rider.domain.vo.KxRiderOrderVo;
-import com.kxmall.common.core.mapper.BaseMapperPlus;
 import com.kxmall.rider.domain.vo.RiderStatisticalDTO;
 import org.apache.ibatis.annotations.Param;
 import org.springframework.stereotype.Repository;
@@ -37,6 +38,16 @@ public interface KxRiderOrderMapper extends BaseMapperPlus<KxRiderOrderMapper, K
 
     BigDecimal selectSumIncome(@Param("riderId") Long riderId);
 
+    /**
+     * 查询师傅在指定时间范围内的已接订单
+     * 
+     * @param riderId 师傅ID
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return 师傅已接订单列表
+     */
+    List<ConflictTimeRiderBo> selectRiderOrdersByTimeRange(@Param("predictDate") LocalDateTime predictDate);
+
     Page<KxRiderOrderVo> selectVoPageList(@Param("page") Page<Object> build, @Param(Constants.WRAPPER) Wrapper<KxRiderOrder> kxRiderOrderWrapper);
 
     List<KxRiderOrder> selectFinishedOrderListByLastDay(@Param("status")RiderOrderStatusType status, @Param("time") LocalDateTime time);

+ 14 - 0
kxmall-system/src/main/resources/mapper/rider/KxRiderOrderMapper.xml

@@ -53,6 +53,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
           and status = 4
     </select>
 
+    <select id="selectRiderOrdersByTimeRange" resultType="com.kxmall.rider.domain.bo.ConflictTimeRiderBo">
+        SELECT
+            ro.rider_id, so.predict_date conflictTime
+        FROM
+            kx_rider_order ro JOIN kx_store_order so ON ro.order_id = so.id
+        WHERE
+            so.predict_date IS NOT NULL
+            AND so.predict_date BETWEEN
+                DATE_SUB(#{predictDate}, INTERVAL 1 hour)
+            AND DATE_ADD(#{predictDate}, INTERVAL 1 hour)
+            AND ro.status = 0
+            and so.status = 14;
+    </select>
 
 
     <select id="selectVoPageList" resultType="com.kxmall.rider.domain.vo.KxRiderOrderVo">
@@ -65,4 +78,5 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectFinishedOrderListByLastDay" resultType="com.kxmall.rider.domain.KxRiderOrder">
         select * from kx_rider_order force index (idx_status_and_finish_time) where status = #{status.code} and finish_time between DATE_SUB(#{time}, INTERVAL 1 DAY) and #{time}
     </select>
+
 </mapper>

+ 2 - 2
需求列表.md

@@ -18,7 +18,7 @@
 - [x] 订单售后-重新服务(*确认需求) xx
 
 # 接单/派单-admin
-- [ ] 系统自动派单(沟通派单逻辑)
+- [x] 系统自动派单(沟通派单逻辑)
 - [ ] 派单后 地址导航
 
 # 订单服务中
@@ -34,7 +34,7 @@
 
 # 订单列表-admin
 - [ ] 手动派单
-- [ ] 手动派单-理由
+- [x] 手动派单-理由  xx
 - [x] 订单评价
 - [x] 售后订单 - 判定责任方 - 退款或补偿