Просмотр исходного кода

add 定时器开启派单功能

tea 3 месяцев назад
Родитель
Сommit
2ae3c9cdf9
49 измененных файлов с 1085 добавлено и 391 удалено
  1. 22 0
      doc/sql/increment/kx_order_rider_recommend.sql
  2. 19 55
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/order/service/impl/KxStoreOrderServiceImpl.java
  3. 33 0
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/quartz/AutoDispatchQuartz.java
  4. 0 25
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/quartz/OrderQuartz.java
  5. 0 12
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/quartz/service/OrderQuartzService.java
  6. 0 74
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/quartz/service/impl/OrderQuartzServiceImpl.java
  7. 48 0
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/IKxOrderRiderRecommendService.java
  8. 19 0
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/IOrderDispatchService.java
  9. 108 0
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/impl/KxOrderRiderRecommendServiceImpl.java
  10. 127 0
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/impl/OrderDispatchServiceImpl.java
  11. 2 2
      kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/impl/SmartDispatchServiceImpl.java
  12. 2 2
      kxmall-admin/src/main/java/com/kxmall/KxmallShopApplication.java
  13. 5 21
      kxmall-app-api/src/main/java/com/kxmall/web/controller/callback/CallbackController.java
  14. 8 7
      kxmall-app-api/src/main/java/com/kxmall/web/controller/order/builder/OrderBuilder.java
  15. 5 9
      kxmall-app-api/src/main/java/com/kxmall/web/controller/order/builder/OrderConcreteBuilder.java
  16. 5 4
      kxmall-app-api/src/main/java/com/kxmall/web/controller/order/builder/OrderDirector.java
  17. 33 0
      kxmall-common/src/main/java/com/kxmall/common/enums/OrderRiderRecommendStatusEnum.java
  18. 15 15
      kxmall-common/src/main/java/com/kxmall/common/enums/RiderOrderStatusType.java
  19. 6 10
      kxmall-common/src/main/java/com/kxmall/common/exception/ServiceException.java
  20. 11 19
      kxmall-rider-api/src/main/java/com/kxmall/web/controller/task/service/impl/TaskCenterServiceImpl.java
  21. 3 4
      kxmall-rider-api/src/main/java/com/kxmall/web/controller/websocket/RiderWebSocketServer.java
  22. 16 16
      kxmall-rider-api/src/main/java/com/kxmall/web/controller/websocket/service/impl/RiderNotificationServiceImpl.java
  23. 0 32
      kxmall-system/src/main/java/com/kxmall/notify/AdminNotifyConfig.java
  24. 0 32
      kxmall-system/src/main/java/com/kxmall/notify/MemberNotifyConfig.java
  25. 64 0
      kxmall-system/src/main/java/com/kxmall/notify/NotifyConfig.java
  26. 3 3
      kxmall-system/src/main/java/com/kxmall/notify/service/AdminNotifyBizService.java
  27. 5 5
      kxmall-system/src/main/java/com/kxmall/notify/service/MemberNotifyBizService.java
  28. 17 0
      kxmall-system/src/main/java/com/kxmall/notify/service/RiderNotifyBizService.java
  29. 7 4
      kxmall-system/src/main/java/com/kxmall/notify/service/impl/UniNotifyAdminNotifyBizServiceImpl.java
  30. 4 4
      kxmall-system/src/main/java/com/kxmall/notify/service/impl/UniNotifyMemberNotifyBizServiceImpl.java
  31. 23 0
      kxmall-system/src/main/java/com/kxmall/notify/service/impl/UniNotifyRiderNotifyBizServiceImpl.java
  32. 4 1
      kxmall-system/src/main/java/com/kxmall/notify/service/impl/mock/MockAdminNotifyBizServiceImpl.java
  33. 4 2
      kxmall-system/src/main/java/com/kxmall/notify/service/impl/mock/MockMemberNotifyBizServiceImpl.java
  34. 25 0
      kxmall-system/src/main/java/com/kxmall/notify/service/impl/mock/MockRiderNotifyBizServiceImpl.java
  35. 14 18
      kxmall-system/src/main/java/com/kxmall/order/biz/OrderRiderBizService.java
  36. 2 2
      kxmall-system/src/main/java/com/kxmall/order/biz/bo/SmartDispatchParamBo.java
  37. 0 1
      kxmall-system/src/main/java/com/kxmall/order/domain/KxStoreOrder.java
  38. 2 0
      kxmall-system/src/main/java/com/kxmall/order/domain/KxStoreOrderProduct.java
  39. 1 0
      kxmall-system/src/main/java/com/kxmall/order/domain/bo/OrderRequestProductBo.java
  40. 4 1
      kxmall-system/src/main/java/com/kxmall/order/domain/vo/KxStoreOrderProductVo.java
  41. 9 2
      kxmall-system/src/main/java/com/kxmall/order/domain/vo/KxStoreOrderVo.java
  42. 71 0
      kxmall-system/src/main/java/com/kxmall/rider/domain/KxOrderRiderRecommend.java
  43. 1 2
      kxmall-system/src/main/java/com/kxmall/rider/domain/KxRiderOrder.java
  44. 63 0
      kxmall-system/src/main/java/com/kxmall/rider/domain/bo/KxOrderRiderRecommendBo.java
  45. 95 0
      kxmall-system/src/main/java/com/kxmall/rider/domain/vo/KxOrderRiderRecommendVo.java
  46. 35 0
      kxmall-system/src/main/java/com/kxmall/rider/mapper/KxOrderRiderRecommendMapper.java
  47. 39 0
      kxmall-system/src/main/resources/mapper/rider/KxOrderRiderRecommendMapper.xml
  48. 100 0
      智能派单功能实现说明.md
  49. 6 7
      需求列表.md

+ 22 - 0
doc/sql/increment/kx_order_rider_recommend.sql

@@ -0,0 +1,22 @@
+-- ----------------------------
+-- 订单师傅推荐关联表
+-- ----------------------------
+CREATE TABLE `kx_order_rider_recommend` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `order_no` varchar(50) NOT NULL COMMENT '订单编号',
+  `rider_id` bigint(20) NOT NULL COMMENT '推荐的师傅ID',
+  `rider_name` varchar(50) DEFAULT NULL COMMENT '师傅姓名',
+  `match_score` decimal(5,2) DEFAULT NULL COMMENT '匹配度分数',
+  `recommend_order` int(11) DEFAULT NULL COMMENT '推荐排序(1表示最优推荐)',
+  `status` int(11) DEFAULT '0' COMMENT '推荐状态(0-待处理, 1-已分配, 2-师傅拒绝, 3-超时)',
+  `recommend_time` datetime DEFAULT NULL COMMENT '推荐时间',
+  `process_time` datetime DEFAULT NULL COMMENT '处理时间',
+  `remark` varchar(500) DEFAULT NULL COMMENT '备注信息',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE,
+  KEY `idx_order_no` (`order_no`) USING BTREE,
+  KEY `idx_rider_id` (`rider_id`) USING BTREE,
+  KEY `idx_status` (`status`) USING BTREE,
+  KEY `idx_recommend_order` (`recommend_order`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='订单师傅推荐关联表';

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

@@ -4,7 +4,6 @@ import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.NumberUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
-import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -24,17 +23,17 @@ import com.kxmall.common.utils.StringUtils;
 import com.kxmall.coupon.domain.KxStoreCouponUser;
 import com.kxmall.coupon.mapper.KxStoreCouponUserMapper;
 import com.kxmall.executor.GlobalExecutor;
-import com.kxmall.notify.MemberNotifyBizService;
+import com.kxmall.notify.service.MemberNotifyBizService;
 import com.kxmall.order.biz.*;
 import com.kxmall.order.biz.common.DeliveryResult;
 import com.kxmall.order.domain.*;
 import com.kxmall.order.domain.bo.KxStoreOrderBo;
 import com.kxmall.order.domain.bo.OrderMessageBO;
 import com.kxmall.order.domain.bo.RiderProductBO;
+import com.kxmall.order.domain.vo.KxOrderScreenshotVo;
 import com.kxmall.order.domain.vo.KxOrderStatisticalVo;
 import com.kxmall.order.domain.vo.KxStoreOrderProductVo;
 import com.kxmall.order.domain.vo.KxStoreOrderVo;
-import com.kxmall.order.domain.vo.KxOrderScreenshotVo;
 import com.kxmall.order.mapper.*;
 import com.kxmall.rider.domain.KxRider;
 import com.kxmall.rider.domain.vo.KxRiderVo;
@@ -59,7 +58,10 @@ import org.springframework.util.CollectionUtils;
 import java.math.BigDecimal;
 import java.sql.Timestamp;
 import java.time.LocalDateTime;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
 
 /**
  * 订单Service业务层处理
@@ -109,8 +111,6 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
 
     private final DeliveryStrategyFactory deliveryStrategyFactory;
 
-    private final KxOrderDeliveryMapper orderDeliveryMapper;
-
     /**
      * 查询订单
      */
@@ -143,11 +143,11 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
 
         // 加载订单照片
         List<KxOrderScreenshotVo> photos = orderScreenshotMapper.selectVoList(
-            new LambdaQueryWrapper<KxOrderScreenshot>()
-                .eq(KxOrderScreenshot::getOrderId, kxStoreOrderVo.getId())
-                .eq(KxOrderScreenshot::getIsDeleted, 0)
-                .orderByAsc(KxOrderScreenshot::getSortOrder)
-                .orderByAsc(KxOrderScreenshot::getCreateTime)
+                new LambdaQueryWrapper<KxOrderScreenshot>()
+                        .eq(KxOrderScreenshot::getOrderId, kxStoreOrderVo.getId())
+                        .eq(KxOrderScreenshot::getIsDeleted, 0)
+                        .orderByAsc(KxOrderScreenshot::getSortOrder)
+                        .orderByAsc(KxOrderScreenshot::getCreateTime)
         );
         kxStoreOrderVo.setPhotos(photos);
 
@@ -193,8 +193,8 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
         if (StringUtils.isNoneBlank(bo.getOrderStatus())) {
             if (!"all".equals(bo.getOrderStatus())) {
                 String[] states = bo.getOrderStatus().split(",");
-                for (int i = 0; i < states.length; i++) {
-                    status.add(Integer.parseInt(states[i]));
+                for (String state : states) {
+                    status.add(Integer.parseInt(state));
                 }
                 lqw.in(KxStoreOrder::getStatus, status);
             }
@@ -231,7 +231,6 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
     }
 
     private LambdaQueryWrapper<KxStoreOrder> buildQueryWrapper(KxStoreOrderBo bo) {
-        Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<KxStoreOrder> lqw = Wrappers.lambdaQuery();
         lqw.eq(StringUtils.isNotBlank(bo.getOrderId()), KxStoreOrder::getOrderId, bo.getOrderId());
         lqw.eq(bo.getUid() != null, KxStoreOrder::getUid, bo.getUid());
@@ -344,9 +343,7 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
             throw new ServiceException("订单不存在");
         }
         // 通知会员开始配货
-        GlobalExecutor.execute(() -> {
-            memberNotifyBizService.newOrder(storeOrder);
-        });
+        GlobalExecutor.execute(() -> memberNotifyBizService.newOrder(storeOrder));
 
         return aBoolean;
     }
@@ -358,13 +355,6 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
 
     @Override
     public Boolean merchantDistribution(Long id) {
-        // 判断一下如果正在第三方配置,就不能点击系统配送,以免重复
-        Long count = orderDeliveryMapper.selectCount(new LambdaQueryWrapper<KxOrderDelivery>()
-                .eq(KxOrderDelivery::getOrderId, id)
-                .eq(KxOrderDelivery::getIsDelete, 0));
-        if (count > 0L) {
-            throw new ServiceException("已发起第三方配送,无法再次发起商家配送");
-        }
         return this.updateOrderStatus(id, OrderStatusType.WAIT_CONFIRM.getCode(), OrderStatusType.WAIT_STOCK.getCode());
     }
 
@@ -387,9 +377,7 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
         }
 
         // 通知会员完成订单
-        GlobalExecutor.execute(() -> {
-            memberNotifyBizService.completeOrder(kxStoreOrder);
-        });
+        GlobalExecutor.execute(() -> memberNotifyBizService.completeOrder(kxStoreOrder));
 
         return true;
     }
@@ -433,14 +421,6 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
                 throw new ServiceException("订单状态更新异常,请检查当前订单真实状态!");
             }
 
-            // 判断一下如果正在第三方配置,就不能点击系统配送,以免重复
-            Long count = orderDeliveryMapper.selectCount(new LambdaQueryWrapper<KxOrderDelivery>()
-                    .eq(KxOrderDelivery::getOrderId, orderNo)
-                    .eq(KxOrderDelivery::getIsDelete, 0));
-            if (count > 0L) {
-                throw new ServiceException("已发起第三方配送,无法再次发起系统配送");
-            }
-
             OrderMessageBO orderMessageBO = new OrderMessageBO();
             orderMessageBO.setOrderId(orderDO.getId());
             orderMessageBO.setOrderNo(orderDO.getOrderId());
@@ -667,19 +647,8 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
             throw new ServiceException("订单状态更新失败");
         }
 
-        // 8. 记录配送信息到配送关联表
-        KxOrderDelivery orderDelivery = new KxOrderDelivery();
-        orderDelivery.setOrderId(orderVo.getOrderId());
-        orderDelivery.setDeliveryType(defaultConfig.getDeliveryType());
-        orderDelivery.setDeliveryId(result.getDeliveryId());
-        orderDelivery.setDeliveryStatus(result.getStatus());
-        orderDelivery.setDeliveryInfo(JSON.toJSONString(result.getOriginalData()));
-        orderDeliveryMapper.insert(orderDelivery);
-
         // 9. 通知用户
-        GlobalExecutor.execute(() -> {
-            memberNotifyBizService.deliveryOrder(orderVo, result);
-        });
+        GlobalExecutor.execute(() -> memberNotifyBizService.deliveryOrder(orderVo, result));
 
         return true;
     }
@@ -768,7 +737,7 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
     /**
      * 退回优惠券
      *
-     * @param order
+     * @param order order
      */
     private void returnCoupon(KxStoreOrderVo order) {
 
@@ -789,7 +758,7 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
     /**
      * 库存退回
      *
-     * @param order
+     * @param order order
      */
     private void returnStock(KxStoreOrderVo order) {
         for (KxStoreOrderProductVo orderProductVo : order.getProductList()) {
@@ -803,7 +772,7 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
      * \
      * 退回积分
      *
-     * @param order
+     * @param order order
      */
     private void returnIntegral(KxStoreOrderVo order) {
 
@@ -833,11 +802,6 @@ public class KxStoreOrderServiceImpl implements IKxStoreOrderService {
 
     /**
      * 更新状态
-     *
-     * @param id
-     * @param newStatus
-     * @param oldStatus
-     * @return
      */
     private Boolean updateOrderStatus(Long id, Integer newStatus, Integer oldStatus) {
         try {

+ 33 - 0
kxmall-admin-api/src/main/java/com/kxmall/web/controller/quartz/AutoDispatchQuartz.java

@@ -0,0 +1,33 @@
+package com.kxmall.web.controller.quartz;
+
+import com.kxmall.web.controller.rider.service.IOrderDispatchService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+/**
+ * 自动派单定时任务
+ *
+ * @author kxmall
+ * @date 2025-09-11
+ */
+@Component
+@Slf4j
+@RequiredArgsConstructor
+public class AutoDispatchQuartz {
+
+    private final IOrderDispatchService orderDispatchService;
+
+    /**
+     * 自动扫描等待状态的订单进行智能派单
+     * 每3分钟执行一次
+     */
+    @Scheduled(cron = "0 */3 * * * ?")
+    public void autoDispatchWaitingOrders() {
+        log.debug("开始执行自动派单定时任务");
+        orderDispatchService.scanAndDispatchWaitingOrders();
+
+    }
+
+}

+ 0 - 25
kxmall-admin-api/src/main/java/com/kxmall/web/controller/quartz/OrderQuartz.java

@@ -1,25 +0,0 @@
-package com.kxmall.web.controller.quartz;
-
-import com.kxmall.web.controller.quartz.service.OrderQuartzService;
-import lombok.RequiredArgsConstructor;
-import org.springframework.scheduling.annotation.EnableScheduling;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-/**
- *
- * @author tea
- * @date 2025/9/7
- */
-@Component
-@EnableScheduling
-@RequiredArgsConstructor
-public class OrderQuartz {
-
-    private final OrderQuartzService orderQuartzService;
-
-    @Scheduled(fixedRate = 60 * 1000)
-    public void smartDispatch() {
-        orderQuartzService.smartDispatch();
-    }
-}

+ 0 - 12
kxmall-admin-api/src/main/java/com/kxmall/web/controller/quartz/service/OrderQuartzService.java

@@ -1,12 +0,0 @@
-package com.kxmall.web.controller.quartz.service;
-
-/**
- *
- * @author tea
- * @date 2025/9/7
- */
-public interface OrderQuartzService {
-
-    void smartDispatch();
-
-}

+ 0 - 74
kxmall-admin-api/src/main/java/com/kxmall/web/controller/quartz/service/impl/OrderQuartzServiceImpl.java

@@ -1,74 +0,0 @@
-package com.kxmall.web.controller.quartz.service.impl;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.map.MapUtil;
-import com.kxmall.common.constant.CacheConstants;
-import com.kxmall.common.service.RiderNotificationService;
-import com.kxmall.common.utils.redis.RedisUtils;
-import com.kxmall.order.biz.bo.SmartDispatchParamBo;
-import com.kxmall.rider.domain.vo.KxRiderVo;
-import com.kxmall.web.controller.quartz.service.OrderQuartzService;
-import com.kxmall.web.controller.rider.service.ISmartDispatchService;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-/**
- *
- * @author tea
- * @date 2025/9/7
- */
-@Service
-@Slf4j
-@RequiredArgsConstructor
-public class OrderQuartzServiceImpl implements OrderQuartzService {
-
-    private final ISmartDispatchService smartDispatchService;
-    private final RiderNotificationService riderNotificationService;
-
-    @Override
-    public void smartDispatch() {
-        Map<String, SmartDispatchParamBo> waitDispatchOrderMap = RedisUtils.getCacheMap(CacheConstants.WAIT_DISPATCH_ORDERS);
-
-        if (MapUtil.isNotEmpty(waitDispatchOrderMap)) {
-            waitDispatchOrderMap.values().parallelStream().forEach(this::processOrderDispatch);
-        }
-    }
-
-    /**
-     * 处理单个订单的智能派单
-     */
-    private void processOrderDispatch(SmartDispatchParamBo smartDispatchParamBo) {
-        try {
-            // 获取推荐骑手列表
-            List<KxRiderVo> recommendedRiders = smartDispatchService.recommendRiders(smartDispatchParamBo);
-
-            if (CollUtil.isEmpty(recommendedRiders)) {
-                log.warn("订单{}没有找到合适的骑手", smartDispatchParamBo.getOrderNo());
-                return;
-            }
-
-            List<String> riderIds = recommendedRiders.stream()
-                    .map(rider -> String.valueOf(rider.getId()))
-                    .collect(Collectors.toList());
-
-            List<Double> matchScores = recommendedRiders.stream()
-                    .map(KxRiderVo::getMatchScore)
-                    .collect(Collectors.toList());
-
-            riderNotificationService.sendNewOrderNotification(
-                    smartDispatchParamBo, riderIds, matchScores);
-
-            log.info("订单{}智能派单完成,推荐{}个骑手",
-                    smartDispatchParamBo.getOrderNo(), recommendedRiders.size());
-
-        } catch (Exception e) {
-            log.error("处理订单{}智能派单失败", smartDispatchParamBo.getOrderNo(), e);
-        }
-    }
-}

+ 48 - 0
kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/IKxOrderRiderRecommendService.java

@@ -0,0 +1,48 @@
+package com.kxmall.web.controller.rider.service;
+
+import com.kxmall.common.core.domain.PageQuery;
+import com.kxmall.common.core.page.TableDataInfo;
+import com.kxmall.rider.domain.bo.KxOrderRiderRecommendBo;
+import com.kxmall.rider.domain.vo.KxOrderRiderRecommendVo;
+
+import java.util.List;
+
+/**
+ * 订单师傅推荐关联表Service接口
+ *
+ * @author kxmall
+ * @date 2025-09-11
+ */
+public interface IKxOrderRiderRecommendService {
+
+    /**
+     * 查询订单师傅推荐关联表
+     */
+    KxOrderRiderRecommendVo queryById(Long id);
+
+    /**
+     * 查询订单师傅推荐关联表列表
+     */
+    TableDataInfo<KxOrderRiderRecommendVo> queryPageList(KxOrderRiderRecommendBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询订单师傅推荐关联表列表
+     */
+    List<KxOrderRiderRecommendVo> queryList(KxOrderRiderRecommendBo bo);
+
+    /**
+     * 根据订单号查询推荐记录
+     */
+    List<KxOrderRiderRecommendVo> queryByOrderNo(String orderNo);
+
+    /**
+     * 新增订单师傅推荐关联表
+     */
+    Boolean insertByBo(KxOrderRiderRecommendBo bo);
+
+    /**
+     * 修改订单师傅推荐关联表
+     */
+    Boolean updateByBo(KxOrderRiderRecommendBo bo);
+
+}

+ 19 - 0
kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/IOrderDispatchService.java

@@ -0,0 +1,19 @@
+package com.kxmall.web.controller.rider.service;
+
+import com.kxmall.order.domain.vo.KxStoreOrderVo;
+
+/**
+ * 订单派单服务接口
+ *
+ * @author kxmall
+ * @date 2025-09-11
+ */
+public interface IOrderDispatchService {
+
+    /**
+     * 扫描等待状态的订单并进行智能派单
+     */
+    void scanAndDispatchWaitingOrders();
+
+    void dispatchOrder(KxStoreOrderVo order);
+}

+ 108 - 0
kxmall-admin-api/src/main/java/com/kxmall/web/controller/rider/service/impl/KxOrderRiderRecommendServiceImpl.java

@@ -0,0 +1,108 @@
+package com.kxmall.web.controller.rider.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.kxmall.common.core.domain.PageQuery;
+import com.kxmall.common.core.page.TableDataInfo;
+import com.kxmall.common.utils.StringUtils;
+import com.kxmall.rider.domain.KxOrderRiderRecommend;
+import com.kxmall.rider.domain.bo.KxOrderRiderRecommendBo;
+import com.kxmall.rider.domain.vo.KxOrderRiderRecommendVo;
+import com.kxmall.rider.mapper.KxOrderRiderRecommendMapper;
+import com.kxmall.web.controller.rider.service.IKxOrderRiderRecommendService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 订单师傅推荐关联表Service业务层处理
+ *
+ * @author kxmall
+ * @date 2025-09-11
+ */
+@RequiredArgsConstructor
+@Service
+public class KxOrderRiderRecommendServiceImpl implements IKxOrderRiderRecommendService {
+
+    private final KxOrderRiderRecommendMapper baseMapper;
+
+    /**
+     * 查询订单师傅推荐关联表
+     */
+    @Override
+    public KxOrderRiderRecommendVo queryById(Long id){
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 查询订单师傅推荐关联表列表
+     */
+    @Override
+    public TableDataInfo<KxOrderRiderRecommendVo> queryPageList(KxOrderRiderRecommendBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<KxOrderRiderRecommend> lqw = buildQueryWrapper(bo);
+        Page<KxOrderRiderRecommendVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询订单师傅推荐关联表列表
+     */
+    @Override
+    public List<KxOrderRiderRecommendVo> queryList(KxOrderRiderRecommendBo bo) {
+        LambdaQueryWrapper<KxOrderRiderRecommend> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    /**
+     * 根据订单号查询推荐记录
+     */
+    @Override
+    public List<KxOrderRiderRecommendVo> queryByOrderNo(String orderNo) {
+        if (StrUtil.isBlank(orderNo)) {
+            return new ArrayList<>();
+        }
+        
+        List<KxOrderRiderRecommend> recommendList = baseMapper.selectByOrderNo(orderNo);
+        return BeanUtil.copyToList(recommendList, KxOrderRiderRecommendVo.class);
+    }
+
+    private LambdaQueryWrapper<KxOrderRiderRecommend> buildQueryWrapper(KxOrderRiderRecommendBo bo) {
+        LambdaQueryWrapper<KxOrderRiderRecommend> lqw = Wrappers.lambdaQuery();
+        lqw.like(StringUtils.isNotBlank(bo.getOrderNo()), KxOrderRiderRecommend::getOrderNo, bo.getOrderNo());
+        lqw.eq(bo.getRiderId() != null, KxOrderRiderRecommend::getRiderId, bo.getRiderId());
+        lqw.like(StringUtils.isNotBlank(bo.getRiderName()), KxOrderRiderRecommend::getRiderName, bo.getRiderName());
+        lqw.eq(bo.getMatchScore() != null, KxOrderRiderRecommend::getMatchScore, bo.getMatchScore());
+        lqw.eq(bo.getStatus() != null, KxOrderRiderRecommend::getStatus, bo.getStatus());
+        lqw.like(StringUtils.isNotBlank(bo.getRemark()), KxOrderRiderRecommend::getRemark, bo.getRemark());
+        lqw.orderByDesc(KxOrderRiderRecommend::getCreateTime);
+        return lqw;
+    }
+
+    /**
+     * 新增订单师傅推荐关联表
+     */
+    @Override
+    public Boolean insertByBo(KxOrderRiderRecommendBo bo) {
+        KxOrderRiderRecommend add = BeanUtil.toBean(bo, KxOrderRiderRecommend.class);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改订单师傅推荐关联表
+     */
+    @Override
+    public Boolean updateByBo(KxOrderRiderRecommendBo bo) {
+        KxOrderRiderRecommend update = BeanUtil.toBean(bo, KxOrderRiderRecommend.class);
+        return baseMapper.updateById(update) > 0;
+    }
+
+}

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

@@ -0,0 +1,127 @@
+package com.kxmall.web.controller.rider.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.kxmall.common.constant.CacheConstants;
+import com.kxmall.common.enums.OrderRiderRecommendStatusEnum;
+import com.kxmall.common.exception.ServiceException;
+import com.kxmall.common.utils.redis.RedisUtils;
+import com.kxmall.executor.GlobalExecutor;
+import com.kxmall.notify.service.RiderNotifyBizService;
+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.KxStoreOrderProductMapper;
+import com.kxmall.rider.domain.KxOrderRiderRecommend;
+import com.kxmall.rider.domain.vo.KxRiderVo;
+import com.kxmall.rider.mapper.KxOrderRiderRecommendMapper;
+import com.kxmall.web.controller.rider.service.IOrderDispatchService;
+import com.kxmall.web.controller.rider.service.ISmartDispatchService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 订单派单服务实现类
+ *
+ * @author kxmall
+ * @date 2025-09-11
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class OrderDispatchServiceImpl implements IOrderDispatchService {
+
+    private final KxOrderRiderRecommendMapper orderRiderRecommendMapper;
+    private final ISmartDispatchService smartDispatchService;
+    private final KxStoreOrderProductMapper storeOrderProductMapper;
+    private final RiderNotifyBizService riderNotifyBizService;
+
+    @Override
+    public void scanAndDispatchWaitingOrders() {
+        // 查询所有等待状态的订单
+        Map<String, KxStoreOrderVo> waitingOrderMap = RedisUtils.getCacheMap(CacheConstants.WAIT_DISPATCH_ORDERS);
+
+        if (MapUtil.isEmpty(waitingOrderMap)) {
+            return;
+        }
+
+        waitingOrderMap.forEach((orderNo, storeOrder) -> {
+            try {
+                dispatchOrder(storeOrder);
+            }catch (Exception e) {
+                log.error("处理订单 {} 派单时发生异常: {}", orderNo, e.getMessage(), e);
+            }
+        });
+    }
+
+    @Override
+    public void dispatchOrder(KxStoreOrderVo order) {
+        // 构建智能派单参数
+        SmartDispatchParamBo dispatchParam = buildDispatchParam(order);
+        // 获取推荐师傅列表
+        List<KxRiderVo> recommendedRiderList = smartDispatchService.recommendRiders(dispatchParam);
+
+        if (CollUtil.isEmpty(recommendedRiderList)) {
+            log.info("订单 {} 没有找到合适的师傅", order.getOrderId());
+            return;
+        }
+
+        // 过滤已派过单的
+        List<KxOrderRiderRecommend> alreadyDispaycherList = orderRiderRecommendMapper.selectByOrderNo(order.getOrderId());
+        List<Long> alreadyDispaycherRiderIdList = alreadyDispaycherList.stream().map(KxOrderRiderRecommend::getId).collect(Collectors.toList());
+
+        LocalDateTime now = LocalDateTime.now();
+        // 保存推荐记录
+        List<KxOrderRiderRecommend> recommendList = recommendedRiderList.stream()
+                .filter(rider -> !alreadyDispaycherRiderIdList.contains(rider.getId()))
+                .map(rider -> {
+                    KxOrderRiderRecommend recommend = new KxOrderRiderRecommend();
+                    recommend.setOrderId(order.getId());
+                    recommend.setOrderNo(order.getOrderId());
+                    recommend.setRiderId(rider.getId());
+                    recommend.setRiderName(rider.getName());
+                    recommend.setMatchScore(new BigDecimal(rider.getMatchScore().toString()));
+                    recommend.setStatus(OrderRiderRecommendStatusEnum.WAITING.getStatus()); // 待处理
+                    recommend.setCreateTime(now);
+                    recommend.setUpdateTime(now);
+                    return recommend;
+                }).collect(Collectors.toList());
+
+        // 批量插入推荐记录
+        orderRiderRecommendMapper.insertBatch(recommendList);
+
+        // 发送公众号通知
+        GlobalExecutor.execute(() -> riderNotifyBizService.newOrder(recommendList));
+    }
+
+    /**
+     * 构建智能派单参数
+     */
+    private SmartDispatchParamBo buildDispatchParam(KxStoreOrderVo order) {
+        LambdaQueryWrapper<KxStoreOrderProduct> storeOrderProductLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        storeOrderProductLambdaQueryWrapper.eq(KxStoreOrderProduct::getOrderId, order.getOrderId());
+        List<KxStoreOrderProduct> storeOrderProductList = storeOrderProductMapper.selectList(storeOrderProductLambdaQueryWrapper);
+        if (CollUtil.isEmpty(storeOrderProductList)) {
+            throw new ServiceException("订单商品为空,订单号:" + order.getOrderId());
+        }
+
+        List<String> keywords = storeOrderProductList.stream()
+                .map(storeOrderProduct -> storeOrderProduct.getKeyword().split(","))
+                .flatMap(Arrays::stream)
+                .collect(Collectors.toList());
+
+        SmartDispatchParamBo dispatchParam = new SmartDispatchParamBo(order, keywords);
+        dispatchParam.setStorageId(order.getStoreId());
+        return dispatchParam;
+    }
+
+}

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

@@ -47,7 +47,7 @@ public class SmartDispatchServiceImpl implements ISmartDispatchService {
         List<KxRiderVo> availableRiders = getAvailableRiders();
 
         if (CollUtil.isEmpty(availableRiders)) {
-            log.warn("没有可用的师傅,storageId: {}", dispatchParam.getStorageId());
+            log.warn("没有可用的师傅,orderNo: {}", dispatchParam.getOrderNo());
             return new ArrayList<>();
         }
 
@@ -60,7 +60,7 @@ public class SmartDispatchServiceImpl implements ISmartDispatchService {
                 .sorted((r1, r2) -> Double.compare(r2.getMatchScore(), r1.getMatchScore()))
                 .collect(Collectors.toList());
 
-        log.info("为订单 {} 推荐了 {} 个师傅,最佳匹配度: {}",
+        log.debug("为订单 {} 推荐了 {} 个师傅,最佳匹配度: {}",
                 dispatchParam.getOrderNo(), rankedRiders.size(),
                 rankedRiders.isEmpty() ? 0 : rankedRiders.get(0).getMatchScore());
 

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

@@ -11,7 +11,7 @@ import org.springframework.boot.context.metrics.buffering.BufferingApplicationSt
  * @author kxmall
  */
 
-@SpringBootApplication
+@SpringBootApplication(scanBasePackages = {"com.kxmall", "cn.hutool"})
 @MapperScan("com.kxmall.**.mapper")
 public class KxmallShopApplication {
 
@@ -22,7 +22,7 @@ public class KxmallShopApplication {
         application.run(args);
         System.out.println("(♥◠‿◠)ノ゙  ============   ლ(´ڡ`ლ)゙");
         System.out.println("(♥◠‿◠)ノ゙  ============   ლ(´ڡ`ლ)゙");
-        System.out.println("(♥◠‿◠)ノ゙  kxmall启动成功   ლ(´ڡ`ლ)゙");
+        System.out.println("(♥◠‿◠)ノ゙  =服务启动成功=  ლ(´ڡ`ლ)゙");
         System.out.println("(♥◠‿◠)ノ゙  ============   ლ(´ڡ`ლ)゙");
         System.out.println("(♥◠‿◠)ノ゙  ============   ლ(´ڡ`ლ)゙");
     }

+ 5 - 21
kxmall-app-api/src/main/java/com/kxmall/web/controller/callback/CallbackController.java

@@ -1,7 +1,6 @@
 package com.kxmall.web.controller.callback;
 
 import cn.dev33.satoken.annotation.SaIgnore;
-import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.ListUtil;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -15,17 +14,14 @@ import com.kxmall.common.enums.PayMethodEnum;
 import com.kxmall.common.utils.redis.RedisUtils;
 import com.kxmall.executor.GlobalExecutor;
 import com.kxmall.group.mapper.KxGroupShopMapper;
-import com.kxmall.notify.AdminNotifyBizService;
+import com.kxmall.notify.service.AdminNotifyBizService;
 import com.kxmall.order.biz.OrderBizService;
-import com.kxmall.order.biz.bo.SmartDispatchParamBo;
 import com.kxmall.order.domain.KxStoreOrder;
 import com.kxmall.order.domain.KxStoreOrderProduct;
 import com.kxmall.order.domain.vo.KxStoreOrderProductVo;
 import com.kxmall.order.domain.vo.KxStoreOrderVo;
-import com.kxmall.order.mapper.KxStoreOrderMapper;
 import com.kxmall.order.mapper.KxStoreOrderProductMapper;
 import com.kxmall.print.AdminPrintBizService;
-import com.kxmall.product.domain.vo.KxStoreProductVo;
 import com.kxmall.product.mapper.KxStoreProductMapper;
 import com.kxmall.web.controller.order.service.IKxAppOrderService;
 import com.kxmall.wechat.WxPayConfiguration;
@@ -41,7 +37,6 @@ import org.springframework.web.bind.annotation.RestController;
 import java.math.BigDecimal;
 import java.util.Date;
 import java.util.List;
-import java.util.stream.Collectors;
 
 /**
  * @author admin
@@ -67,23 +62,15 @@ public class CallbackController {
     private AdminNotifyBizService adminNotifyBizService;
     @Autowired
     private AdminPrintBizService adminPrintBizService;
-    @Autowired
-    private KxStoreOrderMapper kxStoreOrderMapper;
 
     @RequestMapping("/wxpay")
     @SaIgnore
     @Transactional(rollbackFor = Exception.class)
-    public Object wxpay(@RequestBody String body) throws Exception {
+    public Object wxPay(@RequestBody String body) {
 //        ============微信支付回调代码 prod 开始============
         WxPayOrderNotifyResult result;
         try {
-            WxPayService wxPayService = WxPayConfiguration.getPayService(PayMethodEnum.H5);
-            if (wxPayService == null) {
-                wxPayService = WxPayConfiguration.getPayService(PayMethodEnum.MINI);
-            }
-            if (wxPayService == null) {
-                wxPayService = WxPayConfiguration.getPayService(PayMethodEnum.APP);
-            }
+            WxPayService wxPayService = WxPayConfiguration.getPayService(PayMethodEnum.MINI);
             result = wxPayService.parseOrderNotifyResult(body);
         } catch (WxPayException e) {
             logger.error("[微信解析回调请求] 异常", e);
@@ -152,10 +139,7 @@ public class CallbackController {
         });
 
 
-        List<String> keyWords = storeProductMapper.selectVoBatchIds(productIds).stream().map(KxStoreProductVo::getKeyword).collect(Collectors.toList());
-        SmartDispatchParamBo smartDispatchParamBo = new SmartDispatchParamBo(BeanUtil.toBean(order, KxStoreOrder.class), keyWords);
-        RedisUtils.setCacheMapValue(CacheConstants.WAIT_DISPATCH_ORDERS, order.getId().toString(), smartDispatchParamBo);
-
+        RedisUtils.setCacheMapValue(CacheConstants.WAIT_DISPATCH_ORDERS, order.getId().toString(), order);
 
         // 通知管理员发货
         GlobalExecutor.execute(() -> {
@@ -169,7 +153,7 @@ public class CallbackController {
 
     @RequestMapping("/balancePay")
     @Transactional(rollbackFor = Exception.class)
-    public Object balancePay(String orderNo) throws Exception {
+    public Object balancePay(String orderNo) {
 
 
         List<KxStoreOrderVo> KxStoreOrderList = appOrderService.selectListVoByWrapper(

+ 8 - 7
kxmall-app-api/src/main/java/com/kxmall/web/controller/order/builder/OrderBuilder.java

@@ -4,14 +4,12 @@ package com.kxmall.web.controller.order.builder;
 import com.kxmall.order.domain.KxStoreOrder;
 import com.kxmall.order.domain.bo.OrderPriceBo;
 import com.kxmall.order.domain.bo.OrderRequestBo;
-import com.kxmall.order.domain.bo.OrderRequestProductBo;
-
-import java.util.List;
 
 /**
- * @description: 抽象建造者
- * @author: fy
- * @date: 2020/03/13 13:04
+ * 抽象建造者
+ *
+ * @author tea
+ * @date 2025/9/12
  **/
 public abstract class OrderBuilder {
 
@@ -57,14 +55,17 @@ public abstract class OrderBuilder {
 
     /**
      * 余额回调
+     *
      * @param orderDO
      */
     public abstract void buildCallBackHandlePart(KxStoreOrder orderDO);
+
     /**
      * 积分回调
+     *
      * @param orderDO
      */
     public abstract void buildCallBackHandlePointsPart(KxStoreOrder orderDO);
 
-    public abstract void addOrderToPool(List<OrderRequestProductBo> productList, KxStoreOrder orderDO);
+    public abstract void addOrderToPool(KxStoreOrder orderDO);
 }

+ 5 - 9
kxmall-app-api/src/main/java/com/kxmall/web/controller/order/builder/OrderConcreteBuilder.java

@@ -19,8 +19,7 @@ import com.kxmall.group.biz.GroupShopBizService;
 import com.kxmall.group.domain.KxGroupShopProduct;
 import com.kxmall.group.domain.vo.KxGroupShopVo;
 import com.kxmall.group.mapper.KxGroupShopMapper;
-import com.kxmall.notify.AdminNotifyBizService;
-import com.kxmall.order.biz.bo.SmartDispatchParamBo;
+import com.kxmall.notify.service.AdminNotifyBizService;
 import com.kxmall.order.domain.KxStoreCart;
 import com.kxmall.order.domain.KxStoreOrder;
 import com.kxmall.order.domain.KxStoreOrderProduct;
@@ -488,15 +487,15 @@ public class OrderConcreteBuilder extends OrderBuilder {
     @Override
     public void buildOrderSkuHandlePart(KxStoreOrder orderDO, OrderPriceBo orderPriceDTO, OrderRequestBo orderRequest) {
         KxStoreProductVo productVo;
-        KxStoreOrderProduct storeOrderProduct;
         Date now = new Date();
         Map<Long, KxStoreProductVo> productIdDTOMap = orderPriceDTO.getProductIdDTOMap();
         List<KxStoreOrderProduct> orderSkuDOList = new ArrayList<>();
         List<OrderRequestProductBo> skuList = orderRequest.getProductList();
         for (OrderRequestProductBo orderRequestKxStoreProduct : skuList) {
-            storeOrderProduct = new KxStoreOrderProduct();
+            KxStoreOrderProduct storeOrderProduct = new KxStoreOrderProduct();
             productVo = productIdDTOMap.get(orderRequestKxStoreProduct.getProductId());
             storeOrderProduct.setBarCode(productVo.getBarCode());
+            storeOrderProduct.setKeyword(orderRequestKxStoreProduct.getKeyword());
             storeOrderProduct.setProductTitle(productVo.getStoreName());
             storeOrderProduct.setUnitName(productVo.getUnitName());
             storeOrderProduct.setProductAttrTitle(productVo.getStoreName());
@@ -691,11 +690,8 @@ public class OrderConcreteBuilder extends OrderBuilder {
     }
 
     @Override
-    public void addOrderToPool(List<OrderRequestProductBo> productList, KxStoreOrder orderDO) {
-        List<Long> productIds = productList.stream().map(OrderRequestProductBo::getProductId).collect(Collectors.toList());
-        List<String> keyWords = productMapper.selectVoBatchIds(productIds).stream().map(KxStoreProductVo::getKeyword).collect(Collectors.toList());
-        SmartDispatchParamBo smartDispatchParamBo = new SmartDispatchParamBo(orderDO, keyWords);
-        RedisUtils.setCacheMapValue(CacheConstants.WAIT_DISPATCH_ORDERS, orderDO.getId().toString(), smartDispatchParamBo);
+    public void addOrderToPool(KxStoreOrder orderDO) {
+        RedisUtils.setCacheMapValue(CacheConstants.WAIT_DISPATCH_ORDERS, orderDO.getId().toString(), orderDO);
     }
 
 }

+ 5 - 4
kxmall-app-api/src/main/java/com/kxmall/web/controller/order/builder/OrderDirector.java

@@ -6,9 +6,10 @@ import com.kxmall.order.domain.bo.OrderPriceBo;
 import com.kxmall.order.domain.bo.OrderRequestBo;
 
 /**
- * @description: 指挥者
- * @author: fy
- * @date: 2020/03/13 13:05
+ * 指挥者
+ *
+ * @author kxmall
+ * @date 2023-09-21
  **/
 public class OrderDirector {
 
@@ -34,7 +35,7 @@ public class OrderDirector {
         // 余额回调
         if (PayChannelType.BALANCE.getCode().equals(orderRequest.getPayType())) {
             builder.buildCallBackHandlePart(orderDO);
-            builder.addOrderToPool(orderRequest.getProductList(), orderDO);
+            builder.addOrderToPool(orderDO);
         }
         // 积分回调
         if (PayChannelType.INTEGRAL.getCode().equals(orderRequest.getPayType())) {

+ 33 - 0
kxmall-common/src/main/java/com/kxmall/common/enums/OrderRiderRecommendStatusEnum.java

@@ -0,0 +1,33 @@
+package com.kxmall.common.enums;
+
+import com.kxmall.common.exception.ServiceException;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ *
+ * @author tea
+ * @date 2025/9/11
+ */
+@AllArgsConstructor
+@Getter
+public enum OrderRiderRecommendStatusEnum {
+
+    // 推荐状态(0-待处理, 1-已分配, 2-师傅拒绝, 3-超时)
+    WAITING(0, "待处理"),
+    ALLOCATED(1, "已分配"),
+    REFUSED(2, "师傅拒绝"),
+    TIMEOUT(3, "超时");
+
+    private final Integer status;
+    private final String msg;
+
+    public static OrderRiderRecommendStatusEnum getByStatus(Integer status) {
+        return Arrays.stream(OrderRiderRecommendStatusEnum.values())
+                .filter(value -> value.status.equals(status))
+                .findFirst().orElseThrow(() -> new ServiceException("订单派单未找到对应的状态:" + status));
+    }
+
+}

+ 15 - 15
kxmall-common/src/main/java/com/kxmall/common/enums/RiderOrderStatusType.java

@@ -1,10 +1,14 @@
 package com.kxmall.common.enums;
 
+import lombok.Getter;
+
 /**
- * @description: 配送状态
- * @author: kxmall
- * @date: 2020/03/02 17:08
- **/
+ * 配送状态
+ *
+ * @author tea
+ * @date 2025/9/12
+ */
+@Getter
 public enum RiderOrderStatusType {
 
     WAITING(0, "待取货"),
@@ -12,10 +16,13 @@ public enum RiderOrderStatusType {
     TIMEOUT(2, "超时"),
     ABNORMAL(3, "配送异常"),
     COMPLETED(4, "已完成"),
-    RIDERERROR(5, "被更改分配");
-    private Integer code;
+    RIDER_CHANGE(5, "被更改分配"),
+    ALLOCATED(6, "已分配"),
+    CANCELED(7, "已取消"),
+    ;
+    private final Integer code;
 
-    private String msg;
+    private final String msg;
 
     RiderOrderStatusType(Integer code, String msg) {
         this.code = code;
@@ -23,7 +30,7 @@ public enum RiderOrderStatusType {
     }
 
 
-    public static RiderOrderStatusType getBycode(Integer code) {
+    public static RiderOrderStatusType getByCode(Integer code) {
         for (RiderOrderStatusType orderCode : values()) {
             if (orderCode.getCode().equals(code)) {
                 return orderCode;
@@ -32,11 +39,4 @@ public enum RiderOrderStatusType {
         return null;
     }
 
-    public Integer getCode() {
-        return code;
-    }
-
-    public String getMsg() {
-        return msg;
-    }
 }

+ 6 - 10
kxmall-common/src/main/java/com/kxmall/common/exception/ServiceException.java

@@ -1,16 +1,22 @@
 package com.kxmall.common.exception;
 
+import lombok.Getter;
+import lombok.Setter;
+
 /**
  * 业务异常
  *
  * @author kxmall
  */
+@Getter
+@Setter
 public final class ServiceException extends RuntimeException {
     private static final long serialVersionUID = 1L;
 
     /**
      * 错误码
      */
+    @Getter
     private Integer code;
 
     /**
@@ -20,8 +26,6 @@ public final class ServiceException extends RuntimeException {
 
     /**
      * 错误明细,内部调试错误
-     * <p>
-     * 和 {@link CommonResult#getDetailMessage()} 一致的设计
      */
     private String detailMessage;
 
@@ -40,19 +44,11 @@ public final class ServiceException extends RuntimeException {
         this.code = code;
     }
 
-    public String getDetailMessage() {
-        return detailMessage;
-    }
-
     @Override
     public String getMessage() {
         return message;
     }
 
-    public Integer getCode() {
-        return code;
-    }
-
     public ServiceException setMessage(String message) {
         this.message = message;
         return this;

+ 11 - 19
kxmall-rider-api/src/main/java/com/kxmall/web/controller/task/service/impl/TaskCenterServiceImpl.java

@@ -43,39 +43,31 @@ import java.util.concurrent.locks.Lock;
 import java.util.stream.Collectors;
 
 /**
- * @description: 配送中心业务接口
- * @author: kxmall
- * @date: 2020/03/01 19:34
+ * 配送中心业务接口
+ *
+ * @author tea
+ * @date 2025/9/12
  **/
 @Service
 public class TaskCenterServiceImpl implements TaskCenterService {
 
 
+    private static final Logger logger = LoggerFactory.getLogger(TaskCenterServiceImpl.class);
+    private static final Integer RIDER_ORDER_LOCK_WAITING_TIME = 30;
+    private static final String RIDER_ORDER_STATUS_LOCK = "RIDER_ORDER_STATUS_LOCK";
+    @Resource
+    OrderRiderBizService orderRiderBizService;
     @Resource
     private KxRiderItemMapper riderSpuMapper;
-
     @Resource
     private KxRiderOrderMapper riderOrderMapper;
-
-
     @Resource
     private KxRiderMapper riderMapper;
-
     @Resource
     private KxRiderWalletTransactionMapper riderWalletTransactionMapper;
-
-    @Resource
-    OrderRiderBizService orderRiderBizService;
-
     @Resource
     private KxOrderScreenshotMapper kxOrderScreenshotMapper;
 
-
-    private static final Logger logger = LoggerFactory.getLogger(TaskCenterServiceImpl.class);
-
-    private static final Integer RIDER_ORDER_LOCK_WAITING_TIME = 30;
-    private static final String RIDER_ORDER_STATUS_LOCK = "RIDER_ORDER_STATUS_LOCK";
-
     @Override
     public TableDataInfo<KxRiderOrderVo> list(Integer status, Integer page, Integer limit, Long riderId) throws ServiceException {
         if (ObjectUtils.isEmpty(status)) {
@@ -279,7 +271,7 @@ public class TaskCenterServiceImpl implements TaskCenterService {
      */
     private String generateTransactionNo() {
         // 示例:时间戳+随机数,实际可根据业务需求调整
-        return "T" + System.currentTimeMillis() + (int)(Math.random() * 1000);
+        return "T" + System.currentTimeMillis() + (int) (Math.random() * 1000);
     }
 
     @Override
@@ -328,7 +320,7 @@ public class TaskCenterServiceImpl implements TaskCenterService {
 
     @Override
     public String reportLocation(KxRiderVo riderVo) {
-        Map<String,BigDecimal> dataSet = new HashMap<>();
+        Map<String, BigDecimal> dataSet = new HashMap<>();
         dataSet.put("latitude", riderVo.getLatitude());
         dataSet.put("longitude", riderVo.getLongitude());
         RedisUtils.setCacheObject(String.valueOf(riderVo.getId()), dataSet);

+ 3 - 4
kxmall-rider-api/src/main/java/com/kxmall/web/controller/websocket/RiderWebSocketServer.java

@@ -3,11 +3,9 @@ package com.kxmall.web.controller.websocket;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
 
 import javax.websocket.*;
 import javax.websocket.server.PathParam;
-import javax.websocket.server.ServerEndpoint;
 import java.io.IOException;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -19,9 +17,10 @@ import java.util.concurrent.atomic.AtomicInteger;
  * @author kxmall
  * @date 2025-01-08
  */
-@Component
+// @Component
+@SuppressWarnings("all")
 @Slf4j
-@ServerEndpoint("/websocket/rider/{riderId}")
+// @ServerEndpoint("/websocket/rider/{riderId}")
 public class RiderWebSocketServer {
 
     /** 静态变量,用来记录当前在线连接数 */

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

@@ -121,22 +121,22 @@ public class RiderNotificationServiceImpl implements RiderNotificationService {
      */
     private OrderNotificationDTO createOrderNotification(SmartDispatchParamBo dispatchParam) {
         OrderNotificationDTO notification = new OrderNotificationDTO();
-        notification.setType(OrderNotificationDTO.Type.NEW_ORDER);
-        notification.setOrderId(dispatchParam.getOrderId());
-        notification.setOrderNo(dispatchParam.getOrderNo());
-        notification.setStoreName(dispatchParam.getStoreName());
-        notification.setFreightPrice(dispatchParam.getFreightPrice());
-        notification.setTotalAmount(dispatchParam.getTotalAmount());
-        notification.setConsignee(dispatchParam.getConsignee());
-        notification.setPhone(dispatchParam.getPhone());
-        notification.setAddress(dispatchParam.getAddress());
-        notification.setLongitude(dispatchParam.getLongitude());
-        notification.setLatitude(dispatchParam.getLatitude());
-        notification.setPredictTime(dispatchParam.getPredictTime());
-        notification.setUrgencyLevel(dispatchParam.getUrgencyLevel());
-        notification.setCreateTime(new Date());
-        notification.setRemark(dispatchParam.getRemark());
-        
+        // notification.setType(OrderNotificationDTO.Type.NEW_ORDER);
+        // notification.setOrderId(dispatchParam.getOrderId());
+        // notification.setOrderNo(dispatchParam.getOrderNo());
+        // notification.setStoreName(dispatchParam.getStoreName());
+        // notification.setFreightPrice(dispatchParam.getFreightPrice());
+        // notification.setTotalAmount(dispatchParam.getTotalAmount());
+        // notification.setConsignee(dispatchParam.getConsignee());
+        // notification.setPhone(dispatchParam.getPhone());
+        // notification.setAddress(dispatchParam.getAddress());
+        // notification.setLongitude(dispatchParam.getLongitude());
+        // notification.setLatitude(dispatchParam.getLatitude());
+        // notification.setPredictTime(dispatchParam.getPredictTime());
+        // notification.setUrgencyLevel(dispatchParam.getUrgencyLevel());
+        // notification.setCreateTime(new Date());
+        // notification.setRemark(dispatchParam.getRemark());
+        //
         // 服务关键词转换为字符串
         if (CollUtil.isNotEmpty(dispatchParam.getServiceKeywords())) {
             notification.setServiceKeywords(String.join(",", dispatchParam.getServiceKeywords()));

+ 0 - 32
kxmall-system/src/main/java/com/kxmall/notify/AdminNotifyConfig.java

@@ -1,32 +0,0 @@
-package com.kxmall.notify;
-
-import com.kxmall.system.service.ISysConfigService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * Description:
- * User: admin
- * Date: 2022/12/27
- * Time: 20:15
- */
-@Configuration
-public class AdminNotifyConfig {
-
-    @Autowired
-    private ISysConfigService configService;
-
-    @Bean
-    public AdminNotifyBizService adminNotifyBizService() {
-        String enable = configService.selectConfigByKey("uninotify");
-        if ("mock".equalsIgnoreCase(enable)) {
-            return new MockAdminNotifyBizServiceImpl();
-        } else if ("uninotify".equalsIgnoreCase(enable)) {
-            return new UniNotifyAdminNotifyBizServiceImpl();
-        } else {
-            return new MockAdminNotifyBizServiceImpl();
-        }
-    }
-
-}

+ 0 - 32
kxmall-system/src/main/java/com/kxmall/notify/MemberNotifyConfig.java

@@ -1,32 +0,0 @@
-package com.kxmall.notify;
-
-import com.kxmall.system.service.ISysConfigService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * Description:
- * User: admin
- * Date: 2022/12/27
- * Time: 20:15
- */
-@Configuration
-public class MemberNotifyConfig {
-
-    @Autowired
-    private ISysConfigService configService;
-
-    @Bean
-    public MemberNotifyBizService memberNotifyBizService() {
-        String enable = configService.selectConfigByKey("uninotify");
-        if ("mock".equalsIgnoreCase(enable)) {
-            return new MockMemberNotifyBizServiceImpl();
-        } else if ("uninotify".equalsIgnoreCase(enable)) {
-            return new UniNotifyMemberNotifyBizServiceImpl();
-        } else {
-            return new MockMemberNotifyBizServiceImpl();
-        }
-    }
-
-}

+ 64 - 0
kxmall-system/src/main/java/com/kxmall/notify/NotifyConfig.java

@@ -0,0 +1,64 @@
+package com.kxmall.notify;
+
+import com.kxmall.common.utils.spring.SpringUtils;
+import com.kxmall.notify.service.AdminNotifyBizService;
+import com.kxmall.notify.service.MemberNotifyBizService;
+import com.kxmall.notify.service.RiderNotifyBizService;
+import com.kxmall.notify.service.impl.UniNotifyAdminNotifyBizServiceImpl;
+import com.kxmall.notify.service.impl.UniNotifyMemberNotifyBizServiceImpl;
+import com.kxmall.notify.service.impl.UniNotifyRiderNotifyBizServiceImpl;
+import com.kxmall.notify.service.impl.mock.MockAdminNotifyBizServiceImpl;
+import com.kxmall.notify.service.impl.mock.MockMemberNotifyBizServiceImpl;
+import com.kxmall.notify.service.impl.mock.MockRiderNotifyBizServiceImpl;
+import com.kxmall.system.service.ISysConfigService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ *
+ * @author tea
+ * @date 2025/9/12
+ */
+@Configuration
+@RequiredArgsConstructor
+public class NotifyConfig {
+
+    private final ISysConfigService configService;
+
+    @Bean
+    public AdminNotifyBizService adminNotifyBizService() {
+        String enable = queryNotifyType();
+        if ("uninotify".equalsIgnoreCase(enable)) {
+            return SpringUtils.getBean(UniNotifyAdminNotifyBizServiceImpl.class);
+        } else {
+            return SpringUtils.getBean(MockAdminNotifyBizServiceImpl.class);
+        }
+    }
+
+    @Bean
+    public MemberNotifyBizService memberNotifyBizService() {
+        String enable = queryNotifyType();
+        if ("uninotify".equalsIgnoreCase(enable)) {
+            return SpringUtils.getBean(UniNotifyMemberNotifyBizServiceImpl.class);
+        } else {
+            return SpringUtils.getBean(MockMemberNotifyBizServiceImpl.class);
+        }
+    }
+
+    @Bean
+    public RiderNotifyBizService riderNotifyBizService() {
+        String enable = queryNotifyType();
+        if ("uninotify".equalsIgnoreCase(enable)) {
+            return SpringUtils.getBean(UniNotifyRiderNotifyBizServiceImpl.class);
+        } else {
+            return SpringUtils.getBean(MockRiderNotifyBizServiceImpl.class);
+        }
+    }
+
+    private String queryNotifyType() {
+        return configService.selectConfigByKey("uninotify");
+    }
+
+
+}

+ 3 - 3
kxmall-system/src/main/java/com/kxmall/notify/AdminNotifyBizService.java → kxmall-system/src/main/java/com/kxmall/notify/service/AdminNotifyBizService.java

@@ -1,4 +1,4 @@
-package com.kxmall.notify;
+package com.kxmall.notify.service;
 
 import com.kxmall.order.domain.bo.OrderMessageBO;
 import com.kxmall.order.domain.vo.KxStoreOrderVo;
@@ -11,9 +11,9 @@ import com.kxmall.order.domain.vo.KxStoreOrderVo;
  */
 public interface AdminNotifyBizService {
 
-    public void newOrder(KxStoreOrderVo kxStoreOrder);
+    void newOrder(KxStoreOrderVo kxStoreOrder);
 
-    public void refundOrder(KxStoreOrderVo kxStoreOrder);
+    void refundOrder(KxStoreOrderVo kxStoreOrder);
 
     void newRiderOrder(OrderMessageBO orderMessageBO);
 }

+ 5 - 5
kxmall-system/src/main/java/com/kxmall/notify/MemberNotifyBizService.java → kxmall-system/src/main/java/com/kxmall/notify/service/MemberNotifyBizService.java

@@ -1,4 +1,4 @@
-package com.kxmall.notify;
+package com.kxmall.notify.service;
 
 import com.kxmall.order.biz.common.DeliveryResult;
 import com.kxmall.order.domain.vo.KxStoreOrderVo;
@@ -11,11 +11,11 @@ import com.kxmall.order.domain.vo.KxStoreOrderVo;
  */
 public interface MemberNotifyBizService {
 
-    public void newOrder(KxStoreOrderVo kxStoreOrder);
+    void newOrder(KxStoreOrderVo kxStoreOrder);
 
-    public void refundOrder(KxStoreOrderVo kxStoreOrder);
+    void refundOrder(KxStoreOrderVo kxStoreOrder);
 
-    public void completeOrder(KxStoreOrderVo kxStoreOrder);
+    void completeOrder(KxStoreOrderVo kxStoreOrder);
 
-    public void deliveryOrder(KxStoreOrderVo orderVo, DeliveryResult result);
+    void deliveryOrder(KxStoreOrderVo orderVo, DeliveryResult result);
 }

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

@@ -0,0 +1,17 @@
+package com.kxmall.notify.service;
+
+import com.kxmall.rider.domain.KxOrderRiderRecommend;
+
+import java.util.List;
+
+/**
+ * Description: 管理员通知相关接口,意为:发货、退款审核等需要通知管理员即时处理。此业务不应该影响主流业务,不应该抛异常,并且可以异步执行
+ * User: admin
+ * Date: 2019/12/27
+ * Time: 16:15
+ */
+public interface RiderNotifyBizService {
+
+    void newOrder(List<KxOrderRiderRecommend> recommendList);
+
+}

+ 7 - 4
kxmall-system/src/main/java/com/kxmall/notify/UniNotifyAdminNotifyBizServiceImpl.java → kxmall-system/src/main/java/com/kxmall/notify/service/impl/UniNotifyAdminNotifyBizServiceImpl.java

@@ -1,14 +1,16 @@
-package com.kxmall.notify;
+package com.kxmall.notify.service.impl;
 
 import cn.hutool.core.date.DateUtil;
 import com.kxmall.common.event.TemplateBean;
 import com.kxmall.common.event.TemplateEvent;
 import com.kxmall.common.event.TemplateListenEnum;
+import com.kxmall.notify.service.AdminNotifyBizService;
 import com.kxmall.order.domain.bo.OrderMessageBO;
 import com.kxmall.order.domain.vo.KxStoreOrderVo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
 import java.util.Date;
@@ -19,6 +21,7 @@ import java.util.Date;
  * Date: 2022/12/27
  * Time: 16:19
  */
+@Service
 public class UniNotifyAdminNotifyBizServiceImpl implements AdminNotifyBizService {
 
 
@@ -30,13 +33,13 @@ public class UniNotifyAdminNotifyBizServiceImpl implements AdminNotifyBizService
     @Override
     public void newOrder(KxStoreOrderVo storeOrder) {
         try {
-            //公众号通知
+            // 公众号通知
             TemplateBean templateBean = TemplateBean.builder()
                     .orderId(storeOrder.getOrderId())
                     .name(storeOrder.getUserPhone())
                     .templateType(TemplateListenEnum.TYPE_1.getValue())
                     .time(DateUtil.formatDateTime(new Date()))
-                    .price(storeOrder.getPayChannel() + "")
+                    .price(storeOrder.getPayChannel())
                     .storeId(storeOrder.getStoreId())
                     .build();
             publisher.publishEvent(new TemplateEvent(this, templateBean));
@@ -52,7 +55,7 @@ public class UniNotifyAdminNotifyBizServiceImpl implements AdminNotifyBizService
 
     @Override
     public void newRiderOrder(OrderMessageBO orderMessageBO) {
-        //公众号通知
+        // 公众号通知
         TemplateBean templateBean = TemplateBean.builder()
                 .orderId(orderMessageBO.getOrderNo())
                 .templateType(TemplateListenEnum.TYPE_2.getValue())

+ 4 - 4
kxmall-system/src/main/java/com/kxmall/notify/UniNotifyMemberNotifyBizServiceImpl.java → kxmall-system/src/main/java/com/kxmall/notify/service/impl/UniNotifyMemberNotifyBizServiceImpl.java

@@ -1,16 +1,17 @@
-package com.kxmall.notify;
+package com.kxmall.notify.service.impl;
 
 import com.kxmall.common.constant.MessageTemplateConstants;
 import com.kxmall.common.enums.OrderStatusType;
 import com.kxmall.common.utils.DateUtils;
+import com.kxmall.notify.service.MemberNotifyBizService;
 import com.kxmall.order.biz.common.DeliveryResult;
 import com.kxmall.order.domain.vo.KxStoreOrderVo;
 import com.kxmall.wechat.domain.vo.SocialWxaSubscribeMessageSendReqVo;
 import com.kxmall.wechat.service.MiniTemplateService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
 
@@ -22,12 +23,11 @@ import static com.kxmall.common.utils.DateUtils.YYYY_MM_DD_HH_MM_SS;
  * Date: 2022/12/27
  * Time: 16:19
  */
+@Service
 public class UniNotifyMemberNotifyBizServiceImpl implements MemberNotifyBizService {
 
-
     private static final Logger logger = LoggerFactory.getLogger(UniNotifyMemberNotifyBizServiceImpl.class);
 
-
     @Resource
     private MiniTemplateService miniTemplateService;
 

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

@@ -0,0 +1,23 @@
+package com.kxmall.notify.service.impl;
+
+import com.kxmall.notify.service.RiderNotifyBizService;
+import com.kxmall.rider.domain.KxOrderRiderRecommend;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ *
+ * @author tea
+ * @date 2025/9/12
+ */
+@Slf4j
+@Service
+public class UniNotifyRiderNotifyBizServiceImpl implements RiderNotifyBizService {
+
+    @Override
+    public void newOrder(List<KxOrderRiderRecommend> recommendList) {
+
+    }
+}

+ 4 - 1
kxmall-system/src/main/java/com/kxmall/notify/MockAdminNotifyBizServiceImpl.java → kxmall-system/src/main/java/com/kxmall/notify/service/impl/mock/MockAdminNotifyBizServiceImpl.java

@@ -1,10 +1,12 @@
-package com.kxmall.notify;
+package com.kxmall.notify.service.impl.mock;
 
 import com.alibaba.fastjson.JSONObject;
+import com.kxmall.notify.service.AdminNotifyBizService;
 import com.kxmall.order.domain.bo.OrderMessageBO;
 import com.kxmall.order.domain.vo.KxStoreOrderVo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
 
 /**
  * Description: 为了能够正常启动demo的mock实现
@@ -12,6 +14,7 @@ import org.slf4j.LoggerFactory;
  * Date: 2022/12/27
  * Time: 16:22
  */
+@Service
 public class MockAdminNotifyBizServiceImpl implements AdminNotifyBizService {
 
     private static final Logger logger = LoggerFactory.getLogger(MockAdminNotifyBizServiceImpl.class);

+ 4 - 2
kxmall-system/src/main/java/com/kxmall/notify/MockMemberNotifyBizServiceImpl.java → kxmall-system/src/main/java/com/kxmall/notify/service/impl/mock/MockMemberNotifyBizServiceImpl.java

@@ -1,11 +1,12 @@
-package com.kxmall.notify;
+package com.kxmall.notify.service.impl.mock;
 
 import com.alibaba.fastjson.JSONObject;
+import com.kxmall.notify.service.MemberNotifyBizService;
 import com.kxmall.order.biz.common.DeliveryResult;
-import com.kxmall.order.domain.bo.OrderMessageBO;
 import com.kxmall.order.domain.vo.KxStoreOrderVo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
 
 /**
  * Description: 为了能够正常启动demo的mock实现
@@ -13,6 +14,7 @@ import org.slf4j.LoggerFactory;
  * Date: 2022/12/27
  * Time: 16:22
  */
+@Service
 public class MockMemberNotifyBizServiceImpl implements MemberNotifyBizService {
 
     private static final Logger logger = LoggerFactory.getLogger(MockMemberNotifyBizServiceImpl.class);

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

@@ -0,0 +1,25 @@
+package com.kxmall.notify.service.impl.mock;
+
+import com.alibaba.fastjson.JSONObject;
+import com.kxmall.notify.service.RiderNotifyBizService;
+import com.kxmall.rider.domain.KxOrderRiderRecommend;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * Description: 为了能够正常启动demo的mock实现
+ * User: admin
+ * Date: 2022/12/27
+ * Time: 16:22
+ */
+@Slf4j
+@Service
+public class MockRiderNotifyBizServiceImpl implements RiderNotifyBizService {
+
+    @Override
+    public void newOrder(List<KxOrderRiderRecommend> recommendList) {
+        log.info("[师傅 mock通知 有新的订单] 派单通知:" + JSONObject.toJSONString(recommendList));
+    }
+}

+ 14 - 18
kxmall-system/src/main/java/com/kxmall/order/biz/OrderRiderBizService.java

@@ -1,5 +1,6 @@
 package com.kxmall.order.biz;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.kxmall.common.enums.OrderStatusType;
@@ -7,8 +8,8 @@ import com.kxmall.common.enums.RiderOrderStatusType;
 import com.kxmall.common.exception.ServiceException;
 import com.kxmall.common.utils.redis.RedisUtils;
 import com.kxmall.executor.GlobalExecutor;
-import com.kxmall.notify.AdminNotifyBizService;
-import com.kxmall.notify.MemberNotifyBizService;
+import com.kxmall.notify.service.AdminNotifyBizService;
+import com.kxmall.notify.service.MemberNotifyBizService;
 import com.kxmall.order.domain.KxStoreOrder;
 import com.kxmall.order.domain.bo.OrderMessageBO;
 import com.kxmall.order.domain.bo.RiderMessageBO;
@@ -25,7 +26,6 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.StringUtils;
 
 import javax.annotation.Resource;
 import java.util.ArrayList;
@@ -83,23 +83,23 @@ public class OrderRiderBizService {
             try {
                 boolean isLocked = lock.tryLock(RIDER_ORDER_LOCK_WAITING_TIME, TimeUnit.SECONDS);
                 if (isLocked) {
-                    if (StringUtils.isEmpty(orderNo)) {
+                    if (StrUtil.isEmpty(orderNo)) {
                         throw new ServiceException("配送订单信息缺少");
                     }
                     // 检查订单是否已经被分配过,如果已经分配,这个改变已分配的状态为‘已经被更改配送’
                     QueryWrapper<KxRiderOrder> wrapper = new QueryWrapper<>();
                     wrapper.eq("order_no", orderNo);
-                    wrapper.notIn("status", RiderOrderStatusType.COMPLETED.getCode(), RiderOrderStatusType.RIDERERROR.getCode());
+                    wrapper.notIn("status", RiderOrderStatusType.COMPLETED.getCode(), RiderOrderStatusType.RIDER_CHANGE.getCode());
                     List<KxRiderOrder> riderOrderDOList = riderOrderMapper.selectList(wrapper);
-                    if (riderOrderDOList != null && riderOrderDOList.size() > 0 && !riderOrderDOList.get(0).getRiderId().equals(orderMessageBO.getRiderId())) {
+                    if (CollUtil.isNotEmpty(riderOrderDOList) && !riderOrderDOList.get(0).getRiderId().equals(orderMessageBO.getRiderId())) {
                         // 订单重新分配了配送员,修改就得订单状态;
                         KxRiderOrder riderOrderDO = riderOrderDOList.get(0);
                         riderOrderDO.setUpdateTime(new Date());
-                        riderOrderDO.setStatus(RiderOrderStatusType.RIDERERROR.getCode());
+                        riderOrderDO.setStatus(RiderOrderStatusType.RIDER_CHANGE.getCode());
                         riderOrderMapper.updateById(riderOrderDO);
                         // 重新分配的记录
                         return persistenceOrderRiderMessage(orderMessageBO);
-                    } else if (riderOrderDOList != null && riderOrderDOList.size() > 0 && riderOrderDOList.get(0).getRiderId().equals(orderMessageBO.getRiderId())) {
+                    } else if (CollUtil.isNotEmpty(riderOrderDOList) && riderOrderDOList.get(0).getRiderId().equals(orderMessageBO.getRiderId())) {
                         // 还是原来的订单消息,不做处理
                         // TODO 可能修改了订单信息也要更新
                         return true;
@@ -131,7 +131,7 @@ public class OrderRiderBizService {
         riderOrderDO.setStatus(RiderOrderStatusType.WAITING.getCode());
         if (riderOrderMapper.insert(riderOrderDO) > 0) {
             List<RiderProductBO> riderSpuBOList = orderMessageBO.getRiderSpuBOList();
-            if (riderSpuBOList != null && riderSpuBOList.size() > 0) {
+            if (CollUtil.isNotEmpty(riderSpuBOList)) {
                 KxRiderItem riderSpuDO;
                 Long riderOrderDOId = riderOrderDO.getId();
                 List<KxRiderItem> riderSpuDOList = new ArrayList<>();
@@ -153,12 +153,10 @@ public class OrderRiderBizService {
     /**
      * 发送订单消息给客户
      *
-     * @param orderMessageBO
+     * @param orderMessageBO bo
      */
     private void sendRiderOrder(OrderMessageBO orderMessageBO) {
-        GlobalExecutor.execute(() -> {
-            adminNotifyBizService.newRiderOrder(orderMessageBO);
-        });
+        GlobalExecutor.execute(() -> adminNotifyBizService.newRiderOrder(orderMessageBO));
     }
 
 
@@ -169,7 +167,7 @@ public class OrderRiderBizService {
      * @param riderOrderStatusType 配送状态
      * @param riderId              配送员主键ID
      * @param errorMsg             配送异常【如果有】
-     * @throws ServiceException
+     * @throws ServiceException ServiceException
      */
     @Transactional(rollbackFor = Exception.class)
     public void sendRiderMessageBusiness(String orderNo, RiderOrderStatusType riderOrderStatusType, Long riderId, String errorMsg) throws ServiceException {
@@ -187,7 +185,7 @@ public class OrderRiderBizService {
     private void orderRiderMeaageInPut(RiderMessageBO riderMessageBO) {
         try {
             if (riderMessageBO != null) {
-                switch (Objects.requireNonNull(RiderOrderStatusType.getBycode(riderMessageBO.getOrderRiderStatus()))) {
+                switch (Objects.requireNonNull(RiderOrderStatusType.getByCode(riderMessageBO.getOrderRiderStatus()))) {
                     case DISPENSE:
                         // 1.订单配送
                         orderBizService.updateOrderStatus(riderMessageBO.getOrderNo(), OrderStatusType.WAIT_CONFIRM.getCode());
@@ -211,9 +209,7 @@ public class OrderRiderBizService {
                         // 5.分配佣金
                         orderBizService.backOrderBrokerage(storeOrder);
                         //通知会员完成订单
-                        GlobalExecutor.execute(() -> {
-                            memberNotifyBizService.completeOrder(storeOrder);
-                        });
+                        GlobalExecutor.execute(() -> memberNotifyBizService.completeOrder(storeOrder));
                         break;
                     default:
                         throw new ServiceException("配送订单消息保存失败");

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

@@ -1,6 +1,6 @@
 package com.kxmall.order.biz.bo;
 
-import com.kxmall.order.domain.KxStoreOrder;
+import com.kxmall.order.domain.vo.KxStoreOrderVo;
 import lombok.Data;
 import lombok.ToString;
 
@@ -52,7 +52,7 @@ public class SmartDispatchParamBo implements Serializable {
      */
     private Integer urgencyLevel;
 
-    public SmartDispatchParamBo(KxStoreOrder order, List<String> serviceKeywords) {
+    public SmartDispatchParamBo(KxStoreOrderVo order, List<String> serviceKeywords) {
         this.setOrderNo(order.getOrderId());
         this.setLongitude(order.getLongitude());
         this.setLatitude(order.getLatitude());

+ 0 - 1
kxmall-system/src/main/java/com/kxmall/order/domain/KxStoreOrder.java

@@ -259,7 +259,6 @@ public class KxStoreOrder extends BaseEntity {
 
     private String exceptionReason;
 
-
     /**
      * 经度
      */

+ 2 - 0
kxmall-system/src/main/java/com/kxmall/order/domain/KxStoreOrderProduct.java

@@ -34,6 +34,8 @@ public class KxStoreOrderProduct {
      */
     private String orderNo;
 
+    private String keyword;
+
     /**
      * 产品名
      */

+ 1 - 0
kxmall-system/src/main/java/com/kxmall/order/domain/bo/OrderRequestProductBo.java

@@ -26,4 +26,5 @@ public class OrderRequestProductBo {
 
     private BigDecimal giveIntegral;
 
+    private String keyword;
 }

+ 4 - 1
kxmall-system/src/main/java/com/kxmall/order/domain/vo/KxStoreOrderProductVo.java

@@ -3,11 +3,12 @@ package com.kxmall.order.domain.vo;
 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
 import lombok.Data;
 
+import java.io.Serializable;
 import java.math.BigDecimal;
 
 @Data
 @ExcelIgnoreUnannotated
-public class KxStoreOrderProductVo {
+public class KxStoreOrderProductVo implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
@@ -33,6 +34,8 @@ public class KxStoreOrderProductVo {
      */
     private String orderNo;
 
+    private String keyword;
+
     /**
      * 产品名
      */

+ 9 - 2
kxmall-system/src/main/java/com/kxmall/order/domain/vo/KxStoreOrderVo.java

@@ -5,11 +5,11 @@ import com.alibaba.excel.annotation.ExcelProperty;
 import com.kxmall.common.annotation.ExcelDictFormat;
 import com.kxmall.common.convert.ExcelDictConvert;
 import com.kxmall.group.domain.vo.KxGroupShopVo;
-import com.kxmall.order.domain.vo.KxOrderScreenshotVo;
 import com.kxmall.rider.domain.vo.KxRiderOrderVo;
 import com.kxmall.user.domain.vo.KxUserVo;
 import lombok.Data;
 
+import java.io.Serializable;
 import java.math.BigDecimal;
 import java.util.Date;
 import java.util.List;
@@ -23,7 +23,7 @@ import java.util.List;
  */
 @Data
 @ExcelIgnoreUnannotated
-public class KxStoreOrderVo {
+public class KxStoreOrderVo implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
@@ -401,11 +401,18 @@ public class KxStoreOrderVo {
      * 经度
      */
     private BigDecimal longitude;
+
     /**
      * 维度
      */
     private BigDecimal latitude;
 
+    /**
+     * 加急费用
+     */
+    private BigDecimal urgentFee;
+
+
     /**
      * 订单照片列表
      */

+ 71 - 0
kxmall-system/src/main/java/com/kxmall/rider/domain/KxOrderRiderRecommend.java

@@ -0,0 +1,71 @@
+package com.kxmall.rider.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 订单师傅推荐关联表对象 kx_order_rider_recommend
+ *
+ * @author kxmall
+ * @date 2025-09-11
+ */
+@Data
+@TableName("kx_order_rider_recommend")
+public class KxOrderRiderRecommend implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    private Long orderId;
+
+    /**
+     * 订单编号
+     */
+    private String orderNo;
+
+    /**
+     * 推荐的师傅ID
+     */
+    private Long riderId;
+
+    /**
+     * 师傅姓名
+     */
+    private String riderName;
+
+    /**
+     * 匹配度分数
+     */
+    private BigDecimal matchScore;
+
+    /**
+     * 推荐状态(0-待处理, 1-已分配, 2-师傅拒绝, 3-超时)
+     */
+    private Integer status;
+
+    /**
+     * 备注信息
+     */
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+
+}

+ 1 - 2
kxmall-system/src/main/java/com/kxmall/rider/domain/KxRiderOrder.java

@@ -2,7 +2,6 @@ package com.kxmall.rider.domain;
 
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Builder;
 import lombok.Data;
 
 import java.io.Serializable;
@@ -33,7 +32,7 @@ public class KxRiderOrder implements Serializable {
      *     TIMEOUT(2, "超时"),
      *     ABNORMAL(3, "配送异常"),
      *     COMPLETED(4, "已完成"),
-     *     RIDERERROR(5, "被更改分配");
+     *     RIDER_CHANGE(5, "被更改分配");
      */
     private Integer status;
     /**

+ 63 - 0
kxmall-system/src/main/java/com/kxmall/rider/domain/bo/KxOrderRiderRecommendBo.java

@@ -0,0 +1,63 @@
+package com.kxmall.rider.domain.bo;
+
+import com.kxmall.common.core.domain.BaseEntity;
+import com.kxmall.common.core.validate.AddGroup;
+import com.kxmall.common.core.validate.EditGroup;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+
+/**
+ * 订单师傅推荐关联表业务对象 kx_order_rider_recommend
+ *
+ * @author kxmall
+ * @date 2025-09-11
+ */
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class KxOrderRiderRecommendBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    @NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 订单编号
+     */
+    @NotBlank(message = "订单编号不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String orderNo;
+
+    /**
+     * 推荐的师傅ID
+     */
+    @NotNull(message = "推荐的师傅ID不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long riderId;
+
+    /**
+     * 师傅姓名
+     */
+    private String riderName;
+
+    /**
+     * 匹配度分数
+     */
+    private BigDecimal matchScore;
+
+
+    /**
+     * 推荐状态(0-待处理, 1-已分配, 2-师傅拒绝, 3-超时)
+     */
+    private Integer status;
+
+    /**
+     * 备注信息
+     */
+    private String remark;
+
+}

+ 95 - 0
kxmall-system/src/main/java/com/kxmall/rider/domain/vo/KxOrderRiderRecommendVo.java

@@ -0,0 +1,95 @@
+package com.kxmall.rider.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 订单师傅推荐关联表视图对象 kx_order_rider_recommend
+ *
+ * @author kxmall
+ * @date 2025-09-11
+ */
+@Data
+@ExcelIgnoreUnannotated
+public class KxOrderRiderRecommendVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @ExcelProperty(value = "主键ID")
+    private Long id;
+
+    /**
+     * 订单编号
+     */
+    @ExcelProperty(value = "订单编号")
+    private String orderNo;
+
+    /**
+     * 推荐的师傅ID
+     */
+    @ExcelProperty(value = "推荐的师傅ID")
+    private Long riderId;
+
+    /**
+     * 师傅姓名
+     */
+    @ExcelProperty(value = "师傅姓名")
+    private String riderName;
+
+    /**
+     * 匹配度分数
+     */
+    @ExcelProperty(value = "匹配度分数")
+    private BigDecimal matchScore;
+
+    /**
+     * 推荐排序(1表示最优推荐)
+     */
+    @ExcelProperty(value = "推荐排序")
+    private Integer recommendOrder;
+
+    /**
+     * 推荐状态(0-待处理, 1-已分配, 2-师傅拒绝, 3-超时)
+     */
+    @ExcelProperty(value = "推荐状态")
+    private Integer status;
+
+    /**
+     * 推荐时间
+     */
+    @ExcelProperty(value = "推荐时间")
+    private Date recommendTime;
+
+    /**
+     * 处理时间
+     */
+    @ExcelProperty(value = "处理时间")
+    private Date processTime;
+
+    /**
+     * 备注信息
+     */
+    @ExcelProperty(value = "备注信息")
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
+
+    /**
+     * 更新时间
+     */
+    @ExcelProperty(value = "更新时间")
+    private Date updateTime;
+
+}

+ 35 - 0
kxmall-system/src/main/java/com/kxmall/rider/mapper/KxOrderRiderRecommendMapper.java

@@ -0,0 +1,35 @@
+package com.kxmall.rider.mapper;
+
+import com.kxmall.rider.domain.KxOrderRiderRecommend;
+import com.kxmall.rider.domain.vo.KxOrderRiderRecommendVo;
+import com.kxmall.common.core.mapper.BaseMapperPlus;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * 订单师傅推荐关联表Mapper接口
+ *
+ * @author kxmall
+ * @date 2025-09-11
+ */
+@Repository
+public interface KxOrderRiderRecommendMapper extends BaseMapperPlus<KxOrderRiderRecommendMapper, KxOrderRiderRecommend, KxOrderRiderRecommendVo> {
+
+    /**
+     * 根据订单编号查询推荐记录
+     */
+    List<KxOrderRiderRecommend> selectByOrderNo(@Param("orderNo") String orderNo);
+
+    /**
+     * 根据订单编号删除推荐记录
+     */
+    int deleteByOrderNo(@Param("orderNo") String orderNo);
+
+    /**
+     * 更新推荐状态
+     */
+    int updateRecommendStatus(@Param("id") Long id, @Param("status") Integer status);
+
+}

+ 39 - 0
kxmall-system/src/main/resources/mapper/rider/KxOrderRiderRecommendMapper.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.kxmall.rider.mapper.KxOrderRiderRecommendMapper">
+
+    <resultMap type="KxOrderRiderRecommend" id="KxOrderRiderRecommendResult">
+        <result property="id"    column="id"    />
+        <result property="orderNo"    column="order_no"    />
+        <result property="riderId"    column="rider_id"    />
+        <result property="riderName"    column="rider_name"    />
+        <result property="matchScore"    column="match_score"    />
+        <result property="status"    column="status"    />
+        <result property="remark"    column="remark"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+    </resultMap>
+
+    <sql id="selectKxOrderRiderRecommendVo">
+        select id, order_no, rider_id, rider_name, match_score, status, remark, create_time, update_time from kx_order_rider_recommend
+    </sql>
+
+    <select id="selectByOrderNo" parameterType="String" resultMap="KxOrderRiderRecommendResult">
+        <include refid="selectKxOrderRiderRecommendVo"/>
+        where order_no = #{orderNo}
+        order by recommend_order asc
+    </select>
+
+    <delete id="deleteByOrderNo" parameterType="String">
+        delete from kx_order_rider_recommend where order_no = #{orderNo}
+    </delete>
+
+    <update id="updateRecommendStatus">
+        update kx_order_rider_recommend 
+        set status = #{status}, update_time = now()
+        where id = #{id}
+    </update>
+
+</mapper>

+ 100 - 0
智能派单功能实现说明.md

@@ -0,0 +1,100 @@
+# 智能派单功能实现说明
+
+## 功能概述
+
+实现了自动扫描 `WAITING` 状态的订单,通过 `ISmartDispatchService.recommendRiders` 方法进行智能派单,并将订单和推荐的师傅关联保存到中间表中。
+
+## 核心功能
+
+### 1. 自动扫描和派单
+- **定时任务**: `AutoDispatchQuartz` 每5分钟自动扫描等待状态的订单
+- **派单服务**: `OrderDispatchServiceImpl` 实现智能派单逻辑
+- **状态管理**: 订单状态从 `WAITING(0)` 更新为 `ALLOCATED(6)`
+
+### 2. 中间表设计
+- **表名**: `kx_order_rider_recommend`
+- **主要字段**:
+  - `order_no`: 订单编号
+  - `rider_id`: 推荐的师傅ID
+  - `rider_name`: 师傅姓名
+  - `match_score`: 匹配度分数
+  - `recommend_order`: 推荐排序(1表示最优推荐)
+  - `status`: 推荐状态(0-待处理, 1-已分配, 2-师傅拒绝, 3-超时)
+
+### 3. 管理功能
+- **Controller**: `KxOrderRiderRecommendController` 提供CRUD操作
+- **手动派单**: 支持手动触发指定订单的派单功能
+- **查询功能**: 根据订单号查询推荐的师傅列表
+
+## 主要类文件
+
+### 实体类和数据访问
+1. `KxOrderRiderRecommend.java` - 中间表实体类
+2. `KxOrderRiderRecommendVo.java` - 视图对象
+3. `KxOrderRiderRecommendBo.java` - 业务对象
+4. `KxOrderRiderRecommendMapper.java` - 数据访问接口
+5. `KxOrderRiderRecommendMapper.xml` - MyBatis映射文件
+
+### 业务服务
+1. `IOrderDispatchService.java` - 派单服务接口
+2. `OrderDispatchServiceImpl.java` - 派单服务实现
+3. `IKxOrderRiderRecommendService.java` - 推荐记录服务接口
+4. `KxOrderRiderRecommendServiceImpl.java` - 推荐记录服务实现
+
+### 控制器和定时任务
+1. `KxOrderRiderRecommendController.java` - 管理控制器
+2. `AutoDispatchQuartz.java` - 自动派单定时任务
+
+### 数据库脚本
+1. `kx_order_rider_recommend.sql` - 中间表创建脚本
+
+## 工作流程
+
+1. **定时扫描**: 每5分钟扫描状态为 `WAITING` 的订单
+2. **参数构建**: 根据订单信息构建 `SmartDispatchParamBo` 参数
+3. **智能推荐**: 调用 `ISmartDispatchService.recommendRiders` 获取推荐师傅
+4. **保存关联**: 将推荐结果批量插入到中间表
+5. **状态更新**: 更新订单状态为 `ALLOCATED`
+
+## 配置说明
+
+### 定时任务配置
+- **执行频率**: 每5分钟执行一次 (`0 */5 * * * ?`)
+- **可以根据业务需要调整执行频率**
+
+### 推荐策略
+- **匹配度计算**: 基于服务类型、距离、评分、经验等因素
+- **紧急程度**: 根据配送费判断订单紧急程度
+- **服务关键词**: 目前使用默认关键词,可扩展为根据商品类型动态设置
+
+## 扩展点
+
+1. **服务关键词优化**: 可以根据订单商品类型动态设置服务关键词
+2. **推荐策略调整**: 可以根据业务需求调整匹配度计算权重
+3. **通知机制**: 可以加入师傅推送通知功能
+4. **监控报警**: 可以加入派单失败的监控和报警机制
+
+## 使用说明
+
+### 自动派单
+系统会自动运行,无需人工干预。
+
+### 手动派单
+可以通过以下API手动触发订单派单:
+```
+POST /rider/orderRiderRecommend/dispatch/{orderNo}
+```
+
+### 查询推荐记录
+可以通过以下API查询订单的推荐师傅:
+```
+GET /rider/orderRiderRecommend/listByOrderNo/{orderNo}
+```
+
+## 注意事项
+
+1. **数据库表**: 需要先执行 `kx_order_rider_recommend.sql` 创建中间表
+2. **依赖关系**: 依赖现有的 `ISmartDispatchService` 智能派单服务
+3. **事务处理**: 派单过程使用事务确保数据一致性
+4. **错误处理**: 包含完善的异常处理和日志记录
+5. **幂等性**: 对于已有推荐记录的订单会跳过处理,避免重复派单

+ 6 - 7
需求列表.md

@@ -4,7 +4,7 @@
 - [x] 设置服务半径(如 5 公里内接单)、擅长服务类型,系统优先推送匹配订单 关联派单业务
 
 # 服务商品
-- [ ] 商品分类 查询条件(按销量、价格、评分筛选) xx
+- [x] 商品分类 查询条件(按销量、价格、评分筛选) xx
 
 # 商品下单
 - [x] 订单选择时间,可上传照片
@@ -15,19 +15,19 @@
 - [ ] 订单内置聊天(*确认需求)
 - [x] 拨打师傅电话
 - [x] 评价添加师傅
-- [ ] 订单售后-重新服务(*确认需求) xx
+- [x] 订单售后-重新服务(*确认需求) xx
 
 # 接单/派单-admin
 - [ ] 系统自动派单(沟通派单逻辑)
 - [ ] 派单后 地址导航
 
 # 订单服务中
-- [ ] 上传照片 xx
-- [ ] 实际费用 xx
-- [ ] 费用划分(师傅-平台)  xx
+- [x] 上传照片 xx
+- [x] 实际费用 xx
+- [x] 费用划分(师傅-平台)  xx
 
 # 佣金比例-admin
-- [ ] 佣金比例(*沟通业务) xx
+- [x] 佣金比例(*沟通业务) xx
 
 # 师傅列表-admin
 - [x] 师傅等级 关联佣金比例 
@@ -52,6 +52,5 @@
 
 # 系统bug
 - [x] 订单客户信息有误
-- [ ] 师傅测订单状态异常
 - [x] 退出登录清除余额等信息
 - [x] 订单唤起支付但是未支付,系统取消后支付 !!!