6 Commits 6f7e330246 ... 1c72bc0b77

Autore SHA1 Messaggio Data
  Huanyi 1c72bc0b77 尽力没报错,bug之后再修 1 settimana fa
  Huanyi cd823b5264 Merge branch 'feature/v1' into dev/shenliang 1 settimana fa
  Huanyi 6e793c2296 处理回车 1 settimana fa
  Huanyi 4f57ae6e06 消息中心基本接入 1 settimana fa
  Huanyi c6e9e16e83 bug均已修复 1 settimana fa
  Huanyi f8d06a6ce9 整改初步结束 1 settimana fa
53 ha cambiato i file con 1498 aggiunte e 358 eliminazioni
  1. 0 14
      pom.xml
  2. 2 0
      ruoyi-api/yingpaipay-api-archieves/src/main/java/org/dromara/api/RemotePetService.java
  3. 5 0
      ruoyi-api/yingpaipay-api-archieves/src/main/java/org/dromara/api/domain/vo/RemotePetVo.java
  4. 2 0
      ruoyi-api/yingpaipay-api-fulfiller/src/main/java/org/dromara/fulfiller/api/domain/vo/RemoteFulfillerVo.java
  5. 2 0
      ruoyi-api/yingpaipay-api-order/src/main/java/org/dromara/order/api/domain/vo/RemoteSubOrderVo.java
  6. 2 1
      ruoyi-api/yingpaipay-api-order/src/main/java/org/dromara/order/api/enums/OrderLogFulfillerStepEnum.java
  7. 1 1
      ruoyi-api/yingpaipay-api-order/src/main/java/org/dromara/order/api/enums/OrderLogSystemStepEnum.java
  8. 1 0
      ruoyi-common/pom.xml
  9. 7 0
      ruoyi-common/ruoyi-common-bom/pom.xml
  10. 37 0
      ruoyi-common/yingpaipay-common-rabbitmq/pom.xml
  11. 40 0
      ruoyi-common/yingpaipay-common-rabbitmq/src/main/java/org/dromara/common/rabbitmq/config/RabbitMqConfig.java
  12. 49 0
      ruoyi-common/yingpaipay-common-rabbitmq/src/main/java/org/dromara/common/rabbitmq/core/RabbitMqProducer.java
  13. 62 0
      ruoyi-common/yingpaipay-common-rabbitmq/src/main/java/org/dromara/common/rabbitmq/message/SubOrderAnomalyMessage.java
  14. 158 0
      ruoyi-common/yingpaipay-common-rabbitmq/src/main/java/org/dromara/common/rabbitmq/message/SubOrderDispatchMessage.java
  15. 63 0
      ruoyi-common/yingpaipay-common-rabbitmq/src/main/java/org/dromara/common/rabbitmq/message/SubOrderRejectMessage.java
  16. 1 0
      ruoyi-common/yingpaipay-common-rabbitmq/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  17. 3 3
      ruoyi-example/ruoyi-test-mq/src/main/resources/application.yml
  18. 5 0
      ruoyi-modules/ruoyi-system/pom.xml
  19. 81 38
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java
  20. 23 27
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysNotice.java
  21. 30 33
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysNoticeBo.java
  22. 1 1
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysStoreBo.java
  23. 33 41
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysNoticeVo.java
  24. 77 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/OrderAnomalyListener.java
  25. 60 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/OrderDispatchListener.java
  26. 89 0
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/OrderRejectListener.java
  27. 2 2
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysNoticeMapper.java
  28. 27 27
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysNoticeService.java
  29. 41 78
      ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java
  30. 0 7
      ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml
  31. 35 4
      ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/dubbo/RemotePetServiceImpl.java
  32. 5 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/controller/FlfAnamalyController.java
  33. 2 1
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/controller/FlfFulfillerController.java
  34. 3 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/vo/FlfAnamalyVo.java
  35. 5 5
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/dubbo/RemoteFulfillerServiceImpl.java
  36. 1 0
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/IFlfAnamalyService.java
  37. 1 1
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/IFlfFulfillerService.java
  38. 122 3
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/impl/FlfAnamalyServiceImpl.java
  39. 14 16
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/impl/FlfFulfillerServiceImpl.java
  40. 26 29
      ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/impl/FlfLevelConfigServiceImpl.java
  41. 5 0
      ruoyi-modules/yingpaipay-order/pom.xml
  42. 86 0
      ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/config/OrderRabbitMqConfig.java
  43. 31 5
      ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/controller/SysSubOrderController.java
  44. 4 0
      ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/domain/bo/SysSubOrderCancelBo.java
  45. 2 0
      ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/domain/bo/SysSubOrderRejectBo.java
  46. 9 1
      ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/domain/excel/SysSubOrderListExcel.java
  47. 3 0
      ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/domain/vo/SysSubOrderListPageVo.java
  48. 2 0
      ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/domain/vo/SysSubOrderVo.java
  49. 11 2
      ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/dubbo/RemoteSubOrderServiceImpl.java
  50. 1 0
      ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/service/impl/SysOrderServiceImpl.java
  51. 191 14
      ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/service/impl/SysSubOrderServiceImpl.java
  52. 13 4
      ruoyi-visual/ruoyi-nacos/src/main/resources/application.properties
  53. 22 0
      script/sql/business/create.sql

+ 0 - 14
pom.xml

@@ -92,20 +92,6 @@
                 <nacos.ip>127.0.0.1</nacos.ip>
             </properties>
         </profile>
-        <profile>
-            <id>test</id>
-            <properties>
-                <profiles.active>test</profiles.active>
-                <nacos.server>127.0.0.1:8848</nacos.server>
-                <nacos.discovery.group>DEFAULT_GROUP</nacos.discovery.group>
-                <nacos.config.group>DEFAULT_GROUP</nacos.config.group>
-                <nacos.username>nacos</nacos.username>
-                <nacos.password>nacos</nacos.password>
-                <logstash.address>127.0.0.1:4560</logstash.address>
-                <discovery.ip>127.0.0.1</discovery.ip>
-                <nacos.ip>127.0.0.1</nacos.ip>
-            </properties>
-        </profile>
     </profiles>
 
     <!-- 依赖声明 -->

+ 2 - 0
ruoyi-api/yingpaipay-api-archieves/src/main/java/org/dromara/api/RemotePetService.java

@@ -6,4 +6,6 @@ import java.util.List;
 
 public interface RemotePetService {
     List<RemotePetVo> getByIds(List<Long> petIds);
+
+    List<RemotePetVo> getByCustomerIds(List<Long> customerIds);
 }

+ 5 - 0
ruoyi-api/yingpaipay-api-archieves/src/main/java/org/dromara/api/domain/vo/RemotePetVo.java

@@ -4,6 +4,7 @@ import lombok.Data;
 
 import java.io.Serial;
 import java.io.Serializable;
+import java.math.BigDecimal;
 
 @Data
 public class RemotePetVo implements Serializable {
@@ -19,4 +20,8 @@ public class RemotePetVo implements Serializable {
 
     private Long avatar;
 
+    private Long customerId;
+
+    private BigDecimal weight;
+
 }

+ 2 - 0
ruoyi-api/yingpaipay-api-fulfiller/src/main/java/org/dromara/fulfiller/api/domain/vo/RemoteFulfillerVo.java

@@ -22,4 +22,6 @@ public class RemoteFulfillerVo implements Serializable {
 
     private Date createTime;
 
+    private String phone;
+
 }

+ 2 - 0
ruoyi-api/yingpaipay-api-order/src/main/java/org/dromara/order/api/domain/vo/RemoteSubOrderVo.java

@@ -36,4 +36,6 @@ public class RemoteSubOrderVo implements Serializable {
 
     private Long pet;
 
+    private Long storeSite;
+
 }

+ 2 - 1
ruoyi-api/yingpaipay-api-order/src/main/java/org/dromara/order/api/enums/OrderLogFulfillerStepEnum.java

@@ -8,7 +8,8 @@ import lombok.Getter;
 @AllArgsConstructor(access = AccessLevel.PRIVATE)
 public enum OrderLogFulfillerStepEnum {
 
-    REJECT(-1, "已拒绝", "履约者 %s 拒绝接单,订单已取消"),
+    REJECT(-2, "已拒绝", "履约者 %s 拒绝接单,原因: %s"),
+    CANCEL(-1, "已拒绝", "履约者 %s 取消订单,原因: %s"),
     RECIEVED(0, "已接单", "履约者 %s 已确认接单,准备前往服务地点"),
     COMPLETED(99, "订单完成", "用户/商家已确认,服务圆满结束"),
     ;

+ 1 - 1
ruoyi-api/yingpaipay-api-order/src/main/java/org/dromara/order/api/enums/OrderLogSystemStepEnum.java

@@ -13,7 +13,7 @@ public enum OrderLogSystemStepEnum {
     RECIEVED(2, "接单成功", "履约者已确认接单"),
     ARRIVED(3, "到达服务点", "履约者已打卡"),
     FINISH(4, "订单完成", "履约者已完成"),
-    CANCEL(5, "订单取消", "订单已取消"),
+    CANCEL(5, "订单取消", "%s 取消订单,原因: %s"),
     ;
     private final Integer step;
     private final String title;

+ 1 - 0
ruoyi-common/pom.xml

@@ -46,6 +46,7 @@
         <module>ruoyi-common-sse</module>
         <module>yingpaipay-common-platform</module>
         <module>yingpaipay-common-external</module>
+        <module>yingpaipay-common-rabbitmq</module>
     </modules>
 
     <artifactId>ruoyi-common</artifactId>

+ 7 - 0
ruoyi-common/ruoyi-common-bom/pom.xml

@@ -264,6 +264,13 @@
                 <version>${revision}</version>
             </dependency>
 
+            <!-- RabbitMQ消息队列 -->
+            <dependency>
+                <groupId>org.dromara</groupId>
+                <artifactId>yingpaipay-common-rabbitmq</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
         </dependencies>
     </dependencyManagement>
 </project>

+ 37 - 0
ruoyi-common/yingpaipay-common-rabbitmq/pom.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.dromara</groupId>
+        <artifactId>ruoyi-common</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>yingpaipay-common-rabbitmq</artifactId>
+
+    <description>
+        yingpaipay-common-rabbitmq RabbitMQ消息队列服务
+    </description>
+
+    <dependencies>
+        <!-- RuoYi Common Core-->
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-core</artifactId>
+        </dependency>
+
+        <!-- Spring Boot RabbitMQ -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-amqp</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 40 - 0
ruoyi-common/yingpaipay-common-rabbitmq/src/main/java/org/dromara/common/rabbitmq/config/RabbitMqConfig.java

@@ -0,0 +1,40 @@
+package org.dromara.common.rabbitmq.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import org.dromara.common.rabbitmq.core.RabbitMqProducer;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
+import org.springframework.amqp.support.converter.MessageConverter;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * RabbitMQ配置类
+ * 此代码为AI生成
+ *
+ * @author AI
+ */
+@AutoConfiguration
+public class RabbitMqConfig {
+
+    /**
+     * 配置消息转换器,使用Jackson进行JSON序列化
+     * 此代码为AI生成
+     */
+    @Bean
+    public MessageConverter messageConverter() {
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.registerModule(new JavaTimeModule());
+        return new Jackson2JsonMessageConverter(objectMapper);
+    }
+
+    /**
+     * 注册RabbitMQ消息生产者Bean
+     * 此代码为AI生成
+     */
+    @Bean
+    public RabbitMqProducer rabbitMqProducer(RabbitTemplate rabbitTemplate) {
+        return new RabbitMqProducer(rabbitTemplate);
+    }
+}

+ 49 - 0
ruoyi-common/yingpaipay-common-rabbitmq/src/main/java/org/dromara/common/rabbitmq/core/RabbitMqProducer.java

@@ -0,0 +1,49 @@
+package org.dromara.common.rabbitmq.core;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.stereotype.Component;
+
+/**
+ * RabbitMQ消息生产者
+ * 此代码为AI生成
+ *
+ * @author AI
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class RabbitMqProducer {
+
+    private final RabbitTemplate rabbitTemplate;
+
+    /**
+     * 发送消息到指定交换机和路由键
+     * 此代码为AI生成
+     *
+     * @param exchange   交换机名称
+     * @param routingKey 路由键
+     * @param message    消息内容
+     */
+    public void sendMessage(String exchange, String routingKey, Object message) {
+        try {
+            rabbitTemplate.convertAndSend(exchange, routingKey, message);
+            log.info("RabbitMQ消息发送成功 - Exchange: {}, RoutingKey: {}, Message: {}", exchange, routingKey, message);
+        } catch (Exception e) {
+            log.error("RabbitMQ消息发送失败 - Exchange: {}, RoutingKey: {}, Message: {}", exchange, routingKey, message, e);
+            throw new RuntimeException("消息发送失败", e);
+        }
+    }
+
+    /**
+     * 发送消息到默认交换机
+     * 此代码为AI生成
+     *
+     * @param routingKey 路由键
+     * @param message    消息内容
+     */
+    public void sendMessage(String routingKey, Object message) {
+        sendMessage("", routingKey, message);
+    }
+}

+ 62 - 0
ruoyi-common/yingpaipay-common-rabbitmq/src/main/java/org/dromara/common/rabbitmq/message/SubOrderAnomalyMessage.java

@@ -0,0 +1,62 @@
+package org.dromara.common.rabbitmq.message;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 订单异常上报消息
+ * 此代码由AI生成
+ */
+@Data
+public class SubOrderAnomalyMessage implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 子订单编号
+     */
+    private String orderCode;
+
+    /**
+     * 异常ID
+     */
+    private Long anamalyId;
+
+    /**
+     * 门店站点ID
+     */
+    private Long storeSite;
+
+    /**
+     * 履约者名称
+     */
+    private String fulfillerName;
+
+    /**
+     * 异常类型
+     */
+    private String anamalyType;
+
+    /**
+     * 异常内容
+     */
+    private String content;
+
+    /**
+     * 履约者ID
+     */
+    private Long fulfillerId;
+
+    /**
+     * 所属门店名称
+     * 此代码由AI生成
+     */
+    private String storeName;
+
+    /**
+     * 异常上报提交时间(格式化后的字符串)
+     * 此代码由AI生成
+     */
+    private String submitTime;
+}

+ 158 - 0
ruoyi-common/yingpaipay-common-rabbitmq/src/main/java/org/dromara/common/rabbitmq/message/SubOrderDispatchMessage.java

@@ -0,0 +1,158 @@
+package org.dromara.common.rabbitmq.message;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 子订单派单消息
+ * 此代码为AI生成
+ *
+ * @author AI
+ */
+@Data
+public class SubOrderDispatchMessage implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 子订单ID
+     */
+    private Long id;
+
+    /**
+     * 子订单编号
+     */
+    private String code;
+
+    /**
+     * 主订单ID
+     */
+    private Long orderId;
+
+    /**
+     * 主订单编号
+     */
+    private String orderCode;
+
+    /**
+     * 门店ID
+     */
+    private Long store;
+
+    /**
+     * 门店站点ID
+     */
+    private Long storeSite;
+
+    /**
+     * 下单人
+     */
+    private Long orderPlacer;
+
+    /**
+     * 客户ID
+     */
+    private Long usrCustomer;
+
+    /**
+     * 宠物ID
+     */
+    private Long usrPet;
+
+    /**
+     * 服务ID
+     */
+    private Long service;
+
+    /**
+     * 履约者ID
+     */
+    private Long fulfiller;
+
+    /**
+     * 服务模式
+     */
+    private String mode;
+
+    /**
+     * 服务类型
+     */
+    private String type;
+
+    /**
+     * 联系人
+     */
+    private String contact;
+
+    /**
+     * 联系电话
+     */
+    private String contactPhoneNumber;
+
+    /**
+     * 团购套餐名称
+     */
+    private String groupPurchasePackageName;
+
+    /**
+     * 服务时间
+     */
+    private Date serviceTime;
+
+    /**
+     * 服务结束时间
+     */
+    private Date endServiceTime;
+
+    /**
+     * 起点编码
+     */
+    private String fromCode;
+
+    /**
+     * 起点地址
+     */
+    private String fromAddress;
+
+    /**
+     * 终点编码
+     */
+    private String toCode;
+
+    /**
+     * 终点地址
+     */
+    private String toAddress;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 租户ID
+     */
+    private String tenantId;
+
+    /**
+     * 平台ID
+     */
+    private Long platformId;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 派单人ID
+     */
+    private Long dispatcherId;
+
+    /**
+     * 派单人姓名
+     */
+    private String dispatcherName;
+}

+ 63 - 0
ruoyi-common/yingpaipay-common-rabbitmq/src/main/java/org/dromara/common/rabbitmq/message/SubOrderRejectMessage.java

@@ -0,0 +1,63 @@
+package org.dromara.common.rabbitmq.message;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 履约者拒单/取消消息
+ * 此代码由AI生成
+ */
+@Data
+public class SubOrderRejectMessage implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 子订单编号
+     */
+    private String orderCode;
+
+    /**
+     * 订单ID
+     */
+    private Long orderId;
+
+    /**
+     * 门店站点ID
+     */
+    private Long storeSite;
+
+    /**
+     * 履约者名称
+     */
+    private String fulfillerName;
+
+    /**
+     * 拒单/取消原因
+     */
+    private String rejectReason;
+
+    /**
+     * 履约者ID
+     */
+    private Long fulfillerId;
+
+    /**
+     * 所属门店名称
+     * 此代码由AI生成
+     */
+    private String storeName;
+
+    /**
+     * 取消时间(格式化后的字符串)
+     * 此代码由AI生成
+     */
+    private String cancelTime;
+
+    /**
+     * 操作类型标识:REJECT=拒单, CANCEL=取消
+     * 此代码由AI生成
+     */
+    private String actionType;
+}

+ 1 - 0
ruoyi-common/yingpaipay-common-rabbitmq/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -0,0 +1 @@
+org.dromara.common.rabbitmq.config.RabbitMqConfig

+ 3 - 3
ruoyi-example/ruoyi-test-mq/src/main/resources/application.yml

@@ -1,5 +1,5 @@
 server:
-  port: 9402
+  port: 6007
 
 # Spring
 spring:
@@ -15,8 +15,8 @@ spring:
   rabbitmq:
     host: localhost
     port: 5672
-    username: guest
-    password: guest
+    username: huanyi
+    password: 12345
     publisher-returns: true
     publisher-confirm-type: correlated
 

+ 5 - 0
ruoyi-modules/ruoyi-system/pom.xml

@@ -110,6 +110,11 @@
             <artifactId>yingpaipay-api-service</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>yingpaipay-common-rabbitmq</artifactId>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 81 - 38
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java

@@ -2,26 +2,29 @@ package org.dromara.system.controller.system;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
 import lombok.RequiredArgsConstructor;
-import org.apache.dubbo.config.annotation.DubboReference;
 import org.dromara.common.core.domain.R;
-import org.dromara.common.core.service.DictService;
+import org.dromara.common.excel.utils.ExcelUtil;
 import org.dromara.common.idempotent.annotation.RepeatSubmit;
-import org.dromara.common.web.core.BaseController;
 import org.dromara.common.log.annotation.Log;
 import org.dromara.common.log.enums.BusinessType;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
-import org.dromara.resource.api.RemoteMessageService;
+import org.dromara.common.web.core.BaseController;
 import org.dromara.system.domain.bo.SysNoticeBo;
 import org.dromara.system.domain.vo.SysNoticeVo;
 import org.dromara.system.service.ISysNoticeService;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.util.Arrays;
+import java.util.List;
+
 /**
- * 公告 信息操作处理
+ * 系统通知信息 信息操作处理
  *
- * @author Lion Li
+ * @author ruoyi
  */
 @Validated
 @RequiredArgsConstructor
@@ -30,63 +33,103 @@ import org.springframework.web.bind.annotation.*;
 public class SysNoticeController extends BaseController {
 
     private final ISysNoticeService noticeService;
-    private final DictService dictService;
-
-    @DubboReference
-    private RemoteMessageService remoteMessageService;
 
     /**
-     * 获取通知公告列表
+     * 获取系统通知信息列表
      */
+    @SaCheckPermission("system:notice:list")
     @GetMapping("/list")
-    public TableDataInfo<SysNoticeVo> list(SysNoticeBo notice, PageQuery pageQuery) {
-        return noticeService.selectPageNoticeList(notice, pageQuery);
+    public TableDataInfo<SysNoticeVo> list(SysNoticeBo bo, PageQuery pageQuery) {
+        return noticeService.selectPageNoticeList(bo, pageQuery);
+    }
+
+    /**
+     * 获取我的通知列表
+     */
+    @GetMapping("/myList")
+    public TableDataInfo<SysNoticeVo> myList(SysNoticeBo bo, PageQuery pageQuery) {
+        bo.setReceiver(org.dromara.common.satoken.utils.LoginHelper.getUserId());
+        return noticeService.selectPageNoticeList(bo, pageQuery);
+    }
+
+    /**
+     * 标记消息为已读
+     */
+    @Log(title = "系统通知信息", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping("/read/{id}")
+    public R<Void> read(@PathVariable Long id) {
+        SysNoticeBo bo = new SysNoticeBo();
+        bo.setId(id);
+        bo.setReadFlag(true);
+        bo.setReadTime(new java.util.Date());
+        return toAjax(noticeService.updateNotice(bo));
+    }
+
+    /**
+     * 标记所有未读消息为已读
+     */
+    @Log(title = "系统通知信息", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping("/readAll")
+    public R<Void> readAll() {
+        return toAjax(noticeService.readAllNotice(org.dromara.common.satoken.utils.LoginHelper.getUserId()));
+    }
+
+    /**
+     * 导出系统通知信息列表
+     */
+    @SaCheckPermission("system:notice:export")
+    @Log(title = "系统通知信息", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(SysNoticeBo bo, HttpServletResponse response) {
+        List<SysNoticeVo> list = noticeService.selectNoticeList(bo);
+        ExcelUtil.exportExcel(list, "系统通知信息", SysNoticeVo.class, response);
     }
 
     /**
-     * 根据通知公告编号获取详细信息
+     * 根据系统通知信息ID获取详细信息
      *
-     * @param noticeId 公告ID
+     * @param id 系统通知信息ID
      */
-    @GetMapping(value = "/{noticeId}")
-    public R<SysNoticeVo> getInfo(@PathVariable Long noticeId) {
-        return R.ok(noticeService.selectNoticeById(noticeId));
+    @SaCheckPermission("system:notice:query")
+    @GetMapping(value = "/{id}")
+    public R<SysNoticeVo> getInfo(@PathVariable Long id) {
+        return R.ok(noticeService.selectNoticeById(id));
     }
 
     /**
-     * 新增通知公告
+     * 新增系统通知信息
      */
-    @Log(title = "通知公告", businessType = BusinessType.INSERT)
+    @SaCheckPermission("system:notice:add")
+    @Log(title = "系统通知信息", businessType = BusinessType.INSERT)
     @RepeatSubmit()
     @PostMapping
-    public R<Void> add(@Validated @RequestBody SysNoticeBo notice) {
-        int rows = noticeService.insertNotice(notice);
-        if (rows <= 0) {
-            return R.fail();
-        }
-        String type = dictService.getDictLabel("sys_notice_type", notice.getNoticeType());
-        remoteMessageService.publishAll("[" + type + "] " + notice.getNoticeTitle());
-        return R.ok();
+    public R<Void> add(@Validated @RequestBody SysNoticeBo bo) {
+        return toAjax(noticeService.insertNotice(bo));
     }
 
     /**
-     * 修改通知公告
+     * 修改系统通知信息
      */
-    @Log(title = "通知公告", businessType = BusinessType.UPDATE)
+    @SaCheckPermission("system:notice:edit")
+    @Log(title = "系统通知信息", businessType = BusinessType.UPDATE)
     @RepeatSubmit()
     @PutMapping
-    public R<Void> edit(@Validated @RequestBody SysNoticeBo notice) {
-        return toAjax(noticeService.updateNotice(notice));
+    public R<Void> edit(@Validated @RequestBody SysNoticeBo bo) {
+        return toAjax(noticeService.updateNotice(bo));
     }
 
     /**
-     * 删除通知公告
+     * 删除系统通知信息
      *
-     * @param noticeIds 公告ID串
+     * @param ids 系统通知信息ID串
      */
-    @Log(title = "通知公告", businessType = BusinessType.DELETE)
-    @DeleteMapping("/{noticeIds}")
-    public R<Void> remove(@PathVariable Long[] noticeIds) {
-        return toAjax(noticeService.deleteNoticeByIds(noticeIds));
+    @SaCheckPermission("system:notice:remove")
+    @Log(title = "系统通知信息", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@PathVariable Long[] ids) {
+        return toAjax(noticeService.deleteNoticeByIds(Arrays.asList(ids)));
     }
+
 }

+ 23 - 27
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysNotice.java

@@ -4,48 +4,44 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
 import org.dromara.common.tenant.core.TenantEntity;
 
+import java.util.Date;
 
 /**
- * 通知公告表 sys_notice
+ * 系统通知信息表 sys_notice
  *
- * @author Lion Li
+ * @author ruoyi
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
 @TableName("sys_notice")
-public class SysNotice extends TenantEntity {
+public class SysNotice extends BaseEntity {
 
-    /**
-     * 公告ID
-     */
-    @TableId(value = "notice_id")
-    private Long noticeId;
+    @TableId(value = "id")
+    private Long id;
 
-    /**
-     * 公告标题
-     */
-    private String noticeTitle;
+    private Integer senderType;
 
-    /**
-     * 公告类型(1通知 2公告)
-     */
-    private String noticeType;
+    private Long sender;
 
-    /**
-     * 公告内容
-     */
-    private String noticeContent;
+    private Integer type;
 
-    /**
-     * 公告状态(0正常 1关闭)
-     */
-    private String status;
+    private String title;
+
+    private String content;
+
+    private Integer receiverType;
+
+    private Long receiver;
+
+    private Boolean readFlag;
+
+    private Date readTime;
 
     /**
-     * 备注
+     * 业务关联ID
      */
-    private String remark;
-
+    private Long businessId;
 }

+ 30 - 33
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysNoticeBo.java

@@ -2,60 +2,57 @@ package org.dromara.system.domain.bo;
 
 import io.github.linpeilie.annotations.AutoMapper;
 import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
-import org.dromara.common.core.xss.Xss;
 import org.dromara.common.mybatis.core.domain.BaseEntity;
 import org.dromara.system.domain.SysNotice;
 
+import java.util.Date;
+
 /**
- * 通知公告业务对象 sys_notice
+ * 系统通知信息业务对象 sys_notice
  *
- * @author Michelle.Chung
+ * @author ruoyi
  */
-
 @Data
 @EqualsAndHashCode(callSuper = true)
 @AutoMapper(target = SysNotice.class, reverseConvertGenerate = false)
 public class SysNoticeBo extends BaseEntity {
 
-    /**
-     * 公告ID
-     */
-    private Long noticeId;
+    private Long id;
 
-    /**
-     * 公告标题
-     */
-    @Xss(message = "公告标题不能包含脚本字符")
-    @NotBlank(message = "公告标题不能为空")
-    @Size(min = 0, max = 50, message = "公告标题不能超过{max}个字符")
-    private String noticeTitle;
+    @NotNull(message = "发送方类型不能为空")
+    private Integer senderType;
 
-    /**
-     * 公告类型(1通知 2公告)
-     */
-    private String noticeType;
+    @NotNull(message = "发送方不能为空")
+    private Long sender;
 
-    /**
-     * 公告内容
-     */
-    private String noticeContent;
+    @NotNull(message = "消息类型不能为空")
+    private Integer type;
 
-    /**
-     * 公告状态(0正常 1关闭)
-     */
-    private String status;
+    @NotBlank(message = "标题不能为空")
+    @Size(max = 255, message = "标题长度不能超过{max}个字符")
+    private String title;
 
-    /**
-     * 备注
-     */
-    private String remark;
+    @NotBlank(message = "内容不能为空")
+    @Size(max = 2560, message = "内容长度不能超过{max}个字符")
+    private String content;
+
+    @NotNull(message = "接收方类型不能为空")
+    private Integer receiverType;
+
+    @NotNull(message = "接收方不能为空")
+    private Long receiver;
+
+    private Boolean readFlag;
+
+    private Date readTime;
 
     /**
-     * 创建人名称
+     * 业务关联ID
      */
-    private String createByName;
+    private Long businessId;
 
 }

+ 1 - 1
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysStoreBo.java

@@ -39,7 +39,7 @@ public class SysStoreBo extends TenantEntity {
     /**
      * 营业执照
      */
-    @NotNull(message = "营业执照不能为空", groups = {AddGroup.class, EditGroup.class})
+//    @NotNull(message = "营业执照不能为空", groups = {AddGroup.class, EditGroup.class})
     private Long businessLicense;
 
     /**

+ 33 - 41
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysNoticeVo.java

@@ -1,73 +1,65 @@
 package org.dromara.system.domain.vo;
 
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
 import io.github.linpeilie.annotations.AutoMapper;
 import lombok.Data;
-import org.dromara.common.translation.annotation.Translation;
-import org.dromara.common.translation.constant.TransConstant;
 import org.dromara.system.domain.SysNotice;
 
 import java.io.Serial;
 import java.io.Serializable;
 import java.util.Date;
 
-
-
 /**
- * 通知公告视图对象 sys_notice
+ * 系统通知信息视图对象 sys_notice
  *
- * @author Michelle.Chung
+ * @author ruoyi
  */
 @Data
+@ExcelIgnoreUnannotated
 @AutoMapper(target = SysNotice.class)
 public class SysNoticeVo implements Serializable {
 
     @Serial
     private static final long serialVersionUID = 1L;
 
-    /**
-     * 公告ID
-     */
-    private Long noticeId;
+    @ExcelProperty(value = "序号")
+    private Long id;
 
-    /**
-     * 公告标题
-     */
-    private String noticeTitle;
+    @ExcelProperty(value = "发送方类型")
+    private Integer senderType;
 
-    /**
-     * 公告类型(1通知 2公告)
-     */
-    private String noticeType;
+    @ExcelProperty(value = "发送方")
+    private Long sender;
 
-    /**
-     * 公告内容
-     */
-    private String noticeContent;
+    @ExcelProperty(value = "消息类型")
+    private Integer type;
 
-    /**
-     * 公告状态(0正常 1关闭)
-     */
-    private String status;
+    @ExcelProperty(value = "标题")
+    private String title;
 
-    /**
-     * 备注
-     */
-    private String remark;
+    @ExcelProperty(value = "内容")
+    private String content;
 
-    /**
-     * 创建者
-     */
-    private Long createBy;
+    @ExcelProperty(value = "接收方类型")
+    private Integer receiverType;
 
-    /**
-     * 创建人名称
-     */
-    @Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy")
-    private String createByName;
+    @ExcelProperty(value = "接收方")
+    private Long receiver;
+
+    @ExcelProperty(value = "已读标识")
+    private Boolean readFlag;
+
+    @ExcelProperty(value = "阅读时间")
+    private Date readTime;
+
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
 
     /**
-     * 创建时间
+     * 业务关联ID
      */
-    private Date createTime;
+    @ExcelProperty(value = "业务关联ID")
+    private Long businessId;
 
 }

+ 77 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/OrderAnomalyListener.java

@@ -0,0 +1,77 @@
+package org.dromara.system.listener;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.rabbitmq.message.SubOrderAnomalyMessage;
+import org.dromara.system.domain.SysUserAreaStation;
+import org.dromara.system.domain.bo.SysNoticeBo;
+import org.dromara.system.mapper.SysUserAreaStationMapper;
+import org.dromara.system.service.ISysNoticeService;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 订单异常上报消息监听器
+ * 此代码由AI生成
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class OrderAnomalyListener {
+
+    private final ISysNoticeService sysNoticeService;
+    private final SysUserAreaStationMapper sysUserAreaStationMapper;
+
+    @RabbitListener(queues = "order.notification.anomaly.queue")
+    public void handleOrderAnomaly(SubOrderAnomalyMessage message) {
+        log.info("=== 收到订单异常上报消息 ===");
+        log.info("单号: {}", message.getOrderCode());
+        log.info("上报者: {}", message.getFulfillerName());
+        log.info("异常类型: {}", message.getAnamalyType());
+
+        try {
+            if (message.getStoreSite() == null) {
+                return;
+            }
+            // 查找对应区域站长的用户(接收方规则同拒单通知)
+            List<SysUserAreaStation> users = sysUserAreaStationMapper.selectList(
+                Wrappers.<SysUserAreaStation>lambdaQuery()
+                    .in(SysUserAreaStation::getAreaStationId, Arrays.asList(message.getStoreSite(), 0L))
+            );
+
+            if (users != null && !users.isEmpty()) {
+                // 此代码由AI生成 - 修改通知格式为:每个字段换行显示
+                String title = "订单异常上报通知";
+                String submitTime = message.getSubmitTime() != null ? message.getSubmitTime() : "未知时间";
+                String storeName = message.getStoreName() != null ? message.getStoreName() : "未知门店";
+                String content = "履约者:" + message.getFulfillerName()
+                    + "\n提交时间:" + submitTime
+                    + "\n异常原因:" + message.getContent()
+                    + "\n所属门店:" + storeName;
+
+                for (SysUserAreaStation user : users) {
+                     SysNoticeBo notice = new SysNoticeBo();
+                     // senderType 2为履约者
+                     notice.setSenderType(2);
+                     notice.setSender(message.getFulfillerId() != null ? message.getFulfillerId() : 0L);
+                     // receiverType 1为平台用户
+                     notice.setReceiverType(1);
+                     notice.setReceiver(user.getSysUserId());
+                     // 消息类型 2代表异常上报
+                     notice.setType(2);
+                     notice.setTitle(title);
+                     notice.setContent(content);
+                     notice.setBusinessId(message.getAnamalyId());
+
+                     sysNoticeService.insertNotice(notice);
+                }
+            }
+        } catch (Exception e) {
+            log.error("发送订单异常上报通知失败", e);
+        }
+    }
+}

+ 60 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/OrderDispatchListener.java

@@ -0,0 +1,60 @@
+package org.dromara.system.listener;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.rabbitmq.message.SubOrderDispatchMessage;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.stereotype.Component;
+
+import org.dromara.system.service.ISysNoticeService;
+import org.dromara.system.domain.bo.SysNoticeBo;
+
+/**
+ * 订单派单消息监听器
+ * 此代码为AI生成
+ *
+ * @author AI
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class OrderDispatchListener {
+
+    private final ISysNoticeService sysNoticeService;
+
+    /**
+     * 监听订单派单消息
+     * 此代码为AI生成
+     *
+     * @param message 子订单派单消息
+     */
+    @RabbitListener(queues = "order.notification.queue")
+    public void handleOrderDispatch(SubOrderDispatchMessage message) {
+        
+        /*
+        try {
+            SysNoticeBo notice = new SysNoticeBo();
+            // 发送方类型 1为后台用户
+            notice.setSenderType(1);
+            notice.setSender(message.getDispatcherId() != null ? message.getDispatcherId() : 0L);
+            // 接收方类型 2为履约者
+            notice.setReceiverType(2);
+            notice.setReceiver(message.getFulfiller());
+            // 消息类型 0代表派单通知
+            notice.setType(0);
+            notice.setTitle("新订单待接单");
+            
+            String senderName = message.getDispatcherName() != null ? message.getDispatcherName() : "系统管理员";
+            notice.setContent("派单人:" + senderName
+                + "\n订单号:" + message.getCode()
+                + "\n请您及时接单");
+            
+            sysNoticeService.insertNotice(notice);
+            log.info("已成功发送系统通知:派单通知");
+        } catch (Exception e) {
+            log.error("发送派单通知失败", e);
+        }
+        */
+        log.info("派单通知功能暂时禁用,不进行通知入库");
+    }
+}

+ 89 - 0
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/OrderRejectListener.java

@@ -0,0 +1,89 @@
+package org.dromara.system.listener;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.rabbitmq.message.SubOrderRejectMessage;
+import org.dromara.system.domain.SysUserAreaStation;
+import org.dromara.system.domain.bo.SysNoticeBo;
+import org.dromara.system.mapper.SysUserAreaStationMapper;
+import org.dromara.system.service.ISysNoticeService;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 订单拒单/取消消息监听器
+ * 此代码由AI生成
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class OrderRejectListener {
+
+    private final ISysNoticeService sysNoticeService;
+    private final SysUserAreaStationMapper sysUserAreaStationMapper;
+
+    @RabbitListener(queues = "order.notification.reject.queue")
+    public void handleOrderReject(SubOrderRejectMessage message) {
+        log.info("=== 收到拒单/取消消息 ===");
+        log.info("单号: {}", message.getOrderCode());
+        log.info("履约者: {}", message.getFulfillerName());
+        log.info("原因: {}", message.getRejectReason());
+        log.info("操作类型: {}", message.getActionType());
+
+        try {
+            if (message.getStoreSite() == null) {
+                return;
+            }
+            List<SysUserAreaStation> users = sysUserAreaStationMapper.selectList(
+                Wrappers.<SysUserAreaStation>lambdaQuery()
+                    .in(SysUserAreaStation::getAreaStationId, Arrays.asList(message.getStoreSite(), 0L))
+            );
+
+            if (users != null && !users.isEmpty()) {
+                String cancelTime = message.getCancelTime() != null ? message.getCancelTime() : "未知时间";
+                String storeName = message.getStoreName() != null ? message.getStoreName() : "未知门店";
+
+                // 此代码由AI生成 - 根据actionType区分拒单和取消,每个字段换行显示
+                boolean isReject = "REJECT".equals(message.getActionType());
+                String title;
+                String content;
+                if (isReject) {
+                    title = "履约者拒绝接单通知";
+                    content = "履约者:" + message.getFulfillerName()
+                        + "\n拒单时间:" + cancelTime
+                        + "\n拒单原因:" + message.getRejectReason()
+                        + "\n所属门店:" + storeName;
+                } else {
+                    title = "履约者取消订单通知";
+                    content = "履约者:" + message.getFulfillerName()
+                        + "\n取消时间:" + cancelTime
+                        + "\n取消原因:" + message.getRejectReason()
+                        + "\n所属门店:" + storeName;
+                }
+
+                for (SysUserAreaStation user : users) {
+                     SysNoticeBo notice = new SysNoticeBo();
+                     // senderType 2为履约者
+                     notice.setSenderType(2);
+                     notice.setSender(message.getFulfillerId() != null ? message.getFulfillerId() : 0L);
+                     // receiverType 1为平台用户
+                     notice.setReceiverType(1);
+                     notice.setReceiver(user.getSysUserId());
+                     // 消息类型 1代表拒单/取消通知
+                     notice.setType(1);
+                     notice.setTitle(title);
+                     notice.setContent(content);
+                     notice.setBusinessId(message.getOrderId());
+
+                     sysNoticeService.insertNotice(notice);
+                }
+            }
+        } catch (Exception e) {
+            log.error("发送拒单/取消订单通知失败", e);
+        }
+    }
+}

+ 2 - 2
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysNoticeMapper.java

@@ -5,9 +5,9 @@ import org.dromara.system.domain.SysNotice;
 import org.dromara.system.domain.vo.SysNoticeVo;
 
 /**
- * 通知公告表 数据层
+ * 系统通知信息 数据层
  *
- * @author Lion Li
+ * @author ruoyi
  */
 public interface SysNoticeMapper extends BaseMapperPlus<SysNotice, SysNoticeVo> {
 

+ 27 - 27
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysNoticeService.java

@@ -8,66 +8,66 @@ import org.dromara.system.domain.vo.SysNoticeVo;
 import java.util.List;
 
 /**
- * 公告 服务层
+ * 系统通知信息 服务层
  *
- * @author Lion Li
+ * @author ruoyi
  */
 public interface ISysNoticeService {
 
     /**
-     * 分页查询通知公告列表
+     * 分页查询系统通知信息列表
      *
-     * @param notice    查询条件
+     * @param bo        查询条件
      * @param pageQuery 分页参数
-     * @return 通知公告分页列表
+     * @return 系统通知信息分页列表
      */
-    TableDataInfo<SysNoticeVo> selectPageNoticeList(SysNoticeBo notice, PageQuery pageQuery);
+    TableDataInfo<SysNoticeVo> selectPageNoticeList(SysNoticeBo bo, PageQuery pageQuery);
 
     /**
-     * 查询公告信息
+     * 查询系统通知信息列表
      *
-     * @param noticeId 公告ID
-     * @return 公告信息
+     * @param bo 查询条件
+     * @return 系统通知信息列表
      */
-    SysNoticeVo selectNoticeById(Long noticeId);
+    List<SysNoticeVo> selectNoticeList(SysNoticeBo bo);
 
     /**
-     * 查询公告列表
+     * 查询系统通知信息
      *
-     * @param notice 公告信息
-     * @return 公告集合
+     * @param id 系统通知信息ID
+     * @return 系统通知信息
      */
-    List<SysNoticeVo> selectNoticeList(SysNoticeBo notice);
+    SysNoticeVo selectNoticeById(Long id);
 
     /**
-     * 新增公告
+     * 新增系统通知信息
      *
-     * @param bo 公告信息
+     * @param bo 系统通知信息
      * @return 结果
      */
-    int insertNotice(SysNoticeBo bo);
+    Boolean insertNotice(SysNoticeBo bo);
 
     /**
-     * 修改公告
+     * 修改系统通知信息
      *
-     * @param bo 公告信息
+     * @param bo 系统通知信息
      * @return 结果
      */
-    int updateNotice(SysNoticeBo bo);
+    Boolean updateNotice(SysNoticeBo bo);
 
     /**
-     * 删除公告信息
+     * 批量删除系统通知信息
      *
-     * @param noticeId 公告ID
+     * @param ids 需要删除的系统通知信息ID
      * @return 结果
      */
-    int deleteNoticeById(Long noticeId);
+    Boolean deleteNoticeByIds(List<Long> ids);
 
     /**
-     * 批量删除公告信息
-     *
-     * @param noticeIds 需要删除的公告ID
+     * 标记所有未读消息为已读
+     * @param receiver 接收方
      * @return 结果
      */
-    int deleteNoticeByIds(Long[] noticeIds);
+    Boolean readAllNotice(Long receiver);
+
 }

+ 41 - 78
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java

@@ -5,127 +5,90 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.utils.MapstructUtils;
-import org.dromara.common.core.utils.ObjectUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.system.domain.SysNotice;
-import org.dromara.system.domain.SysUser;
 import org.dromara.system.domain.bo.SysNoticeBo;
 import org.dromara.system.domain.vo.SysNoticeVo;
-import org.dromara.system.domain.vo.SysUserVo;
 import org.dromara.system.mapper.SysNoticeMapper;
-import org.dromara.system.mapper.SysUserMapper;
 import org.dromara.system.service.ISysNoticeService;
 import org.springframework.stereotype.Service;
 
-import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 
 /**
- * 公告 服务层实现
+ * 系统通知信息 服务层实现
  *
- * @author Lion Li
+ * @author ruoyi
  */
 @RequiredArgsConstructor
 @Service
 public class SysNoticeServiceImpl implements ISysNoticeService {
 
     private final SysNoticeMapper baseMapper;
-    private final SysUserMapper userMapper;
 
-    /**
-     * 分页查询通知公告列表
-     *
-     * @param notice    查询条件
-     * @param pageQuery 分页参数
-     * @return 通知公告分页列表
-     */
     @Override
-    public TableDataInfo<SysNoticeVo> selectPageNoticeList(SysNoticeBo notice, PageQuery pageQuery) {
-        LambdaQueryWrapper<SysNotice> lqw = buildQueryWrapper(notice);
+    public TableDataInfo<SysNoticeVo> selectPageNoticeList(SysNoticeBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<SysNotice> lqw = buildQueryWrapper(bo);
         Page<SysNoticeVo> page = baseMapper.selectVoPage(pageQuery.build(), lqw);
         return TableDataInfo.build(page);
     }
 
-    /**
-     * 查询公告信息
-     *
-     * @param noticeId 公告ID
-     * @return 公告信息
-     */
     @Override
-    public SysNoticeVo selectNoticeById(Long noticeId) {
-        return baseMapper.selectVoById(noticeId);
-    }
-
-    /**
-     * 查询公告列表
-     *
-     * @param notice 公告信息
-     * @return 公告集合
-     */
-    @Override
-    public List<SysNoticeVo> selectNoticeList(SysNoticeBo notice) {
-        LambdaQueryWrapper<SysNotice> lqw = buildQueryWrapper(notice);
+    public List<SysNoticeVo> selectNoticeList(SysNoticeBo bo) {
+        LambdaQueryWrapper<SysNotice> lqw = buildQueryWrapper(bo);
         return baseMapper.selectVoList(lqw);
     }
 
-    private LambdaQueryWrapper<SysNotice> buildQueryWrapper(SysNoticeBo bo) {
-        LambdaQueryWrapper<SysNotice> lqw = Wrappers.lambdaQuery();
-        lqw.like(StringUtils.isNotBlank(bo.getNoticeTitle()), SysNotice::getNoticeTitle, bo.getNoticeTitle());
-        lqw.eq(StringUtils.isNotBlank(bo.getNoticeType()), SysNotice::getNoticeType, bo.getNoticeType());
-        if (StringUtils.isNotBlank(bo.getCreateByName())) {
-            SysUserVo sysUser = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, bo.getCreateByName()));
-            lqw.eq(SysNotice::getCreateBy, ObjectUtils.notNullGetter(sysUser, SysUserVo::getUserId));
-        }
-        lqw.orderByAsc(SysNotice::getNoticeId);
-        return lqw;
+    @Override
+    public SysNoticeVo selectNoticeById(Long id) {
+        return baseMapper.selectVoById(id);
     }
 
-    /**
-     * 新增公告
-     *
-     * @param bo 公告信息
-     * @return 结果
-     */
     @Override
-    public int insertNotice(SysNoticeBo bo) {
+    public Boolean insertNotice(SysNoticeBo bo) {
         SysNotice notice = MapstructUtils.convert(bo, SysNotice.class);
-        return baseMapper.insert(notice);
+        return baseMapper.insert(notice) > 0;
     }
 
-    /**
-     * 修改公告
-     *
-     * @param bo 公告信息
-     * @return 结果
-     */
     @Override
-    public int updateNotice(SysNoticeBo bo) {
+    public Boolean updateNotice(SysNoticeBo bo) {
         SysNotice notice = MapstructUtils.convert(bo, SysNotice.class);
-        return baseMapper.updateById(notice);
+        return baseMapper.updateById(notice) > 0;
     }
 
-    /**
-     * 删除公告对象
-     *
-     * @param noticeId 公告ID
-     * @return 结果
-     */
     @Override
-    public int deleteNoticeById(Long noticeId) {
-        return baseMapper.deleteById(noticeId);
+    public Boolean deleteNoticeByIds(List<Long> ids) {
+        return baseMapper.deleteByIds(ids) > 0;
     }
 
-    /**
-     * 批量删除公告信息
-     *
-     * @param noticeIds 需要删除的公告ID
-     * @return 结果
-     */
     @Override
-    public int deleteNoticeByIds(Long[] noticeIds) {
-        return baseMapper.deleteByIds(Arrays.asList(noticeIds));
+    public Boolean readAllNotice(Long receiver) {
+        SysNotice notice = new SysNotice();
+        notice.setReadFlag(true);
+        notice.setReadTime(new java.util.Date());
+        LambdaQueryWrapper<SysNotice> lqw = Wrappers.lambdaQuery();
+        lqw.eq(SysNotice::getReceiver, receiver);
+        lqw.eq(SysNotice::getReadFlag, false);
+        return baseMapper.update(notice, lqw) > 0;
     }
+
+    private LambdaQueryWrapper<SysNotice> buildQueryWrapper(SysNoticeBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<SysNotice> lqw = Wrappers.lambdaQuery();
+        lqw.eq(bo.getSenderType() != null, SysNotice::getSenderType, bo.getSenderType());
+        lqw.eq(bo.getSender() != null, SysNotice::getSender, bo.getSender());
+        lqw.eq(bo.getType() != null, SysNotice::getType, bo.getType());
+        lqw.like(StringUtils.isNotBlank(bo.getTitle()), SysNotice::getTitle, bo.getTitle());
+        lqw.eq(bo.getReceiverType() != null, SysNotice::getReceiverType, bo.getReceiverType());
+        lqw.eq(bo.getReceiver() != null, SysNotice::getReceiver, bo.getReceiver());
+        lqw.eq(bo.getReadFlag() != null, SysNotice::getReadFlag, bo.getReadFlag());
+        lqw.between(params.get("beginTime") != null && params.get("endTime") != null,
+            SysNotice::getCreateTime, params.get("beginTime"), params.get("endTime"));
+        lqw.orderByDesc(SysNotice::getId);
+        return lqw;
+    }
+
 }

+ 0 - 7
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml

@@ -1,7 +0,0 @@
-<?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="org.dromara.system.mapper.SysNoticeMapper">
-
-</mapper>

+ 35 - 4
ruoyi-modules/yingpaipay-archieves/src/main/java/org/dromara/archieves/dubbo/RemotePetServiceImpl.java

@@ -12,6 +12,7 @@ import org.dromara.common.mybatis.utils.WrapperUtils;
 import org.springframework.stereotype.Service;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 @RequiredArgsConstructor
@@ -25,13 +26,40 @@ public class RemotePetServiceImpl implements RemotePetService {
     @Override
     public List<RemotePetVo> getByIds(List<Long> petIds) {
 
+        if (petIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+
         List<RemotePetVo> vos = new ArrayList<>();
 
-        List<UsrPet> pets = baseMapper.selectList(
-            Wrappers.lambdaQuery(UsrPet.class)
+        List<UsrPet> pets = baseMapper.selectList(Wrappers.lambdaQuery(UsrPet.class)
                 .select(UsrPet::getId, UsrPet::getName, UsrPet::getBreed, UsrPet::getAvatar)
-                .in(UsrPet::getId, WrapperUtils.convertIds(petIds))
-        );
+                .in(UsrPet::getId, petIds));
+
+        for (UsrPet pet : pets) {
+            RemotePetVo vo = new RemotePetVo();
+            vo.setId(pet.getId());
+            vo.setName(pet.getName());
+            vo.setBreed(pet.getBreed());
+            vo.setAvatar(pet.getAvatar());
+            vos.add(vo);
+        }
+
+        return vos;
+    }
+
+    @Override
+    public List<RemotePetVo> getByCustomerIds(List<Long> customerIds) {
+
+        if (customerIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        List<RemotePetVo> vos = new ArrayList<>();
+
+        List<UsrPet> pets = baseMapper.selectList(Wrappers.lambdaQuery(UsrPet.class)
+                .select(UsrPet::getId, UsrPet::getName, UsrPet::getBreed, UsrPet::getAvatar, UsrPet::getUserId, UsrPet::getWeight)
+                .in(UsrPet::getUserId, customerIds));
 
         for (UsrPet pet : pets) {
             RemotePetVo vo = new RemotePetVo();
@@ -39,9 +67,12 @@ public class RemotePetServiceImpl implements RemotePetService {
             vo.setName(pet.getName());
             vo.setBreed(pet.getBreed());
             vo.setAvatar(pet.getAvatar());
+            vo.setCustomerId(pet.getUserId());
+            vo.setWeight(pet.getWeight());
             vos.add(vo);
         }
 
         return vos;
+
     }
 }

+ 5 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/controller/FlfAnamalyController.java

@@ -56,4 +56,9 @@ public class FlfAnamalyController extends BaseController {
         return R.ok(flfAnamalyService.getByOrderId(orderId));
     }
 
+    @GetMapping("/getInfo")
+    public R<FlfAnamalyVo> getInfo(@RequestParam Long id) {
+        return R.ok(flfAnamalyService.getById(id));
+    }
+
 }

+ 2 - 1
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/controller/FlfFulfillerController.java

@@ -327,9 +327,10 @@ public class FlfFulfillerController extends BaseController {
     public TableDataInfo<FlfFulfillerOnOrderVo> pageOnOrder(
         @RequestParam(name = "content", required = false) String content,
         @RequestParam Long service,
+        @RequestParam Long orderId,
         PageQuery pageQuery
     ) {
-        return fulfillerService.pageOnOrder(service, content, pageQuery);
+        return fulfillerService.pageOnOrder(service, orderId, content, pageQuery);
     }
 
     @GetMapping("/listAllOnDispatch")

+ 3 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/domain/vo/FlfAnamalyVo.java

@@ -51,6 +51,9 @@ public class FlfAnamalyVo implements Serializable {
     private String orderCode;
     private Long orderId;
 
+    private Long store;
+    private String storeName;
+
     /**
      * 异常类型
      */

+ 5 - 5
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/dubbo/RemoteFulfillerServiceImpl.java

@@ -1,5 +1,6 @@
 package org.dromara.fulfiller.dubbo;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -57,10 +58,8 @@ public class RemoteFulfillerServiceImpl implements RemoteFulfillerService {
 
     @Override
     public FulfillerLoginUser getFulfillerByPhone(String phone) throws UserException {
-        FlfFulfiller fulfiller = fulfillerMapper.selectOne(
-            new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<FlfFulfiller>()
-                .eq(FlfFulfiller::getPhone, phone)
-        );
+        FlfFulfiller fulfiller = fulfillerMapper.selectOne(new LambdaQueryWrapper<FlfFulfiller>()
+                .eq(FlfFulfiller::getPhone, phone));
 
         if (fulfiller == null) {
             log.info("登录用户:{} 不存在.", phone);
@@ -102,13 +101,14 @@ public class RemoteFulfillerServiceImpl implements RemoteFulfillerService {
         List<RemoteFulfillerVo> vos = new ArrayList<>();
         fulfillerMapper.selectList(
             Wrappers.lambdaQuery(FlfFulfiller.class)
-                .select(FlfFulfiller::getId, FlfFulfiller::getName, FlfFulfiller::getStatus)
+                .select(FlfFulfiller::getId, FlfFulfiller::getName, FlfFulfiller::getStatus, FlfFulfiller::getPhone)
                 .in(FlfFulfiller::getId, fulfillerIds)
         ).forEach(e -> {
             RemoteFulfillerVo vo = new RemoteFulfillerVo();
             vo.setId(e.getId());
             vo.setName(e.getName());
             vo.setStatus(e.getStatus());
+            vo.setPhone(e.getPhone());
             vos.add(vo);
         });
         return vos;

+ 1 - 0
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/IFlfAnamalyService.java

@@ -20,4 +20,5 @@ public interface IFlfAnamalyService {
     List<FlfAnamalyOnOrderVo> getByOrderId(Long orderId);
 
     boolean delete(Long id);
+    FlfAnamalyVo getById(Long id);
 }

+ 1 - 1
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/IFlfFulfillerService.java

@@ -128,7 +128,7 @@ public interface IFlfFulfillerService {
      */
     Boolean updateAuthInfoByUserId(Long userId, java.util.Map<String, String> params);
 
-    TableDataInfo<FlfFulfillerOnOrderVo> pageOnOrder(Long service, String content, PageQuery pageQuery);
+    TableDataInfo<FlfFulfillerOnOrderVo> pageOnOrder(Long service, Long orderId, String content, PageQuery pageQuery);
 
     List<FlfFulfillerOnOrderVo> listAllOnOrder();
 

+ 122 - 3
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/impl/FlfAnamalyServiceImpl.java

@@ -11,6 +11,8 @@ import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.mybatis.utils.WrapperUtils;
 import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.rabbitmq.core.RabbitMqProducer;
+import org.dromara.common.rabbitmq.message.SubOrderAnomalyMessage;
 import org.dromara.fulfiller.domain.FlfAnamaly;
 import org.dromara.fulfiller.domain.FlfFulfiller;
 import org.dromara.fulfiller.domain.bo.*;
@@ -22,7 +24,9 @@ import org.dromara.order.api.RemoteSubOrderService;
 import org.dromara.order.api.domain.vo.RemoteSubOrderVo;
 import org.dromara.resource.api.RemoteFileService;
 import org.dromara.resource.api.domain.RemoteFile;
+import org.dromara.system.api.RemoteStoreService;
 import org.dromara.system.api.RemoteUserService;
+import org.dromara.system.api.domain.vo.RemoteStoreVo;
 import org.dromara.system.api.domain.vo.RemoteUserVo;
 import org.dromara.fulfiller.domain.vo.FlfAnamalyOnOrderVo;
 import org.springframework.stereotype.Service;
@@ -36,6 +40,7 @@ public class FlfAnamalyServiceImpl implements IFlfAnamalyService {
 
     private final FlfAnamalyMapper baseMapper;
     private final FlfFulfillerMapper flfFulfillerMapper;
+    private final RabbitMqProducer rabbitMqProducer;
 
     @DubboReference
     private final RemoteSubOrderService remoteSubOrderService;
@@ -43,6 +48,8 @@ public class FlfAnamalyServiceImpl implements IFlfAnamalyService {
     private final RemoteFileService remoteFileService;
     @DubboReference
     private final RemoteUserService remoteUserService;
+    @DubboReference
+    private final RemoteStoreService remoteStoreService;
 
     @Override
     public TableDataInfo<FlfAnamalyVo> list(FlfAnamalyPageBo bo, PageQuery pageQuery) {
@@ -74,6 +81,7 @@ public class FlfAnamalyServiceImpl implements IFlfAnamalyService {
         Map<Long, FlfFulfiller> fulfillerMap = new HashMap<>();
         Map<Long, RemoteSubOrderVo> orderMap = new HashMap<>();
         Map<Long, RemoteUserVo> auditorMap = new HashMap<>();
+        Map<Long, RemoteStoreVo> storeMap = new HashMap<>();
         page.getRecords().forEach(e -> {
             fulfillerIds.add(e.getFulfiller());
             if (StringUtils.isNotBlank(e.getPhotos())) {
@@ -89,7 +97,12 @@ public class FlfAnamalyServiceImpl implements IFlfAnamalyService {
             remoteFileService.selectByIds(photoIds).forEach(e -> photoMap.put(e.getOssId(), e));
         }
         if (!orderIds.isEmpty()) {
-            remoteSubOrderService.getByIds(orderIds).forEach(e -> orderMap.put(e.getId(), e));
+            List<Long> storeIds = new ArrayList<>();
+            remoteSubOrderService.getByIds(orderIds).forEach(e -> {
+                orderMap.put(e.getId(), e);
+                storeIds.add(e.getStore());
+            });
+            remoteStoreService.getByIds(storeIds).forEach(e -> storeMap.put(e.getId(), e));
         }
         if (!auditorIds.isEmpty()) {
             remoteUserService.getByIds(auditorIds).forEach(e -> auditorMap.put(e.getUserId(), e));
@@ -117,6 +130,11 @@ public class FlfAnamalyServiceImpl implements IFlfAnamalyService {
             RemoteSubOrderVo order = orderMap.get(e.getOrderId());
             if (order != null) {
                 e.setOrderCode(order.getCode());
+                RemoteStoreVo store = storeMap.get(order.getStore());
+                if (store != null) {
+                    e.setStore(store.getId());
+                    e.setStoreName(store.getName());
+                }
             }
             RemoteUserVo auditor = auditorMap.get(e.getAuditor());
             if (auditor != null) {
@@ -143,7 +161,58 @@ public class FlfAnamalyServiceImpl implements IFlfAnamalyService {
         add.setStatus(bo.getStatus());
         add.setTenantId(vo.getTenantId());
 
-        return baseMapper.insert(add) > 0;
+        boolean success = baseMapper.insert(add) > 0;
+        if (success) {
+            // 发送异常上报通知
+            sendAnomalyNotification(add, vo);
+        }
+        return success;
+    }
+
+    /**
+     * 发送异常上报通知消息
+     * 此代码由AI生成
+     */
+    private void sendAnomalyNotification(FlfAnamaly anamaly, RemoteSubOrderVo orderVo) {
+        try {
+            SubOrderAnomalyMessage message = new SubOrderAnomalyMessage();
+            message.setOrderCode(orderVo.getCode());
+            message.setAnamalyId(anamaly.getId());
+            message.setStoreSite(orderVo.getStoreSite());
+            message.setFulfillerId(anamaly.getFulfiller());
+            message.setAnamalyType(anamaly.getType());
+            message.setContent(anamaly.getContent());
+
+            // 获取履约者姓名
+            FlfFulfiller fulfiller = flfFulfillerMapper.selectById(anamaly.getFulfiller());
+            if (fulfiller != null) {
+                message.setFulfillerName(fulfiller.getName());
+            }
+
+            // 此代码由AI生成 - 获取门店名称
+            if (orderVo.getStore() != null) {
+                List<RemoteStoreVo> stores = remoteStoreService.getByIds(List.of(orderVo.getStore()));
+                if (stores != null && !stores.isEmpty()) {
+                    message.setStoreName(stores.get(0).getName());
+                } else {
+                    message.setStoreName("未知门店");
+                }
+            } else {
+                message.setStoreName("未知门店");
+            }
+
+            // 此代码由AI生成 - 设置提交时间
+            message.setSubmitTime(new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date()));
+
+            // 发送消息到交换机
+            rabbitMqProducer.sendMessage(
+                "order.exchange",
+                "order.notification.anomaly",
+                message
+            );
+        } catch (Exception e) {
+            // 消息发送失败不影响主业务逻辑
+        }
     }
 
     @Override
@@ -171,7 +240,12 @@ public class FlfAnamalyServiceImpl implements IFlfAnamalyService {
         RemoteSubOrderVo vo = remoteSubOrderService.getById(bo.getOrderId());
         anamaly.setTenantId(vo.getTenantId());
 
-        return baseMapper.insert(anamaly) > 0;
+        boolean success = baseMapper.insert(anamaly) > 0;
+        if (success) {
+            // 发送异常上报通知
+            sendAnomalyNotification(anamaly, vo);
+        }
+        return success;
     }
 
     @Override
@@ -236,4 +310,49 @@ public class FlfAnamalyServiceImpl implements IFlfAnamalyService {
     public boolean delete(Long id) {
         return baseMapper.deleteById(id) > 0;
     }
+
+    @Override
+    public FlfAnamalyVo getById(Long id) {
+        FlfAnamalyVo vo = baseMapper.selectVoById(id);
+        if (vo == null) {
+            return null;
+        }
+
+        // 补全信息
+        if (vo.getFulfiller() != null) {
+            FlfFulfiller fulfiller = flfFulfillerMapper.selectById(vo.getFulfiller());
+            if (fulfiller != null) {
+                vo.setFulfillerName(fulfiller.getName());
+                vo.setFulfillerPhone(fulfiller.getPhone());
+            }
+        }
+
+        if (vo.getAuditor() != null) {
+            List<RemoteUserVo> users = remoteUserService.getByIds(List.of(vo.getAuditor()));
+            if (!users.isEmpty()) {
+                vo.setAuditorName(users.get(0).getUserName());
+            }
+        }
+
+        if (StringUtils.isNotBlank(vo.getPhotos())) {
+            List<Long> photoIds = Arrays.stream(vo.getPhotos().split(",")).map(Long::valueOf).toList();
+            List<String> photoUrls = new ArrayList<>();
+            remoteFileService.selectByIds(photoIds).forEach(e -> photoUrls.add(e.getUrl()));
+            vo.setPhotosUrls(photoUrls);
+        }
+
+        if (vo.getOrderId() != null) {
+            RemoteSubOrderVo order = remoteSubOrderService.getById(vo.getOrderId());
+            if (order != null) {
+                vo.setOrderCode(order.getCode());
+                RemoteStoreVo store = remoteStoreService.getByIds(List.of(order.getStore())).get(0);
+                if (store != null) {
+                    vo.setStore(store.getId());
+                    vo.setStoreName(store.getName());
+                }
+            }
+        }
+
+        return vo;
+    }
 }

+ 14 - 16
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/impl/FlfFulfillerServiceImpl.java

@@ -23,10 +23,7 @@ import org.dromara.fulfiller.constants.FlfRewardTargetConstant;
 import org.dromara.common.core.constant.TagTargetConstants;
 import org.dromara.fulfiller.domain.vo.FlfFulfillerOnOrderVo;
 import org.dromara.fulfiller.domain.vo.FlfFulfilllerListByNameAndPhoneNumberVo;
-import org.dromara.fulfiller.enums.AuditStatusEnum;
-import org.dromara.fulfiller.enums.AuditTypeEnum;
-import org.dromara.fulfiller.enums.FlfBalanceBizTypeEnum;
-import org.dromara.fulfiller.enums.FlfPointsBizTypeEnum;
+import org.dromara.fulfiller.enums.*;
 import org.dromara.order.api.RemoteSubOrderLogService;
 import org.dromara.order.api.RemoteSubOrderService;
 import org.dromara.order.api.domain.vo.RemoteSubOrderLogCountVo;
@@ -605,18 +602,22 @@ public class FlfFulfillerServiceImpl implements IFlfFulfillerService {
     }
 
     @Override
-    public TableDataInfo<FlfFulfillerOnOrderVo> pageOnOrder(Long service, String content, PageQuery pageQuery) {
+    public TableDataInfo<FlfFulfillerOnOrderVo> pageOnOrder(Long service, Long orderId, String content, PageQuery pageQuery) {
+
+        RemoteSubOrderVo subOrder = remoteSubOrderService.getById(orderId);
 
         Page<FlfFulfiller> page = baseMapper.selectPage(
             pageQuery.build(),
             Wrappers.lambdaQuery(FlfFulfiller.class)
                 .select(FlfFulfiller::getId, FlfFulfiller::getAvatar, FlfFulfiller::getPhone, FlfFulfiller::getName, FlfFulfiller::getStatus, FlfFulfiller::getServiceTypes, FlfFulfiller::getGender)
+                .eq(FlfFulfiller::getStationId, subOrder.getStoreSite())
                 .orderByDesc(FlfFulfiller::getId)
                 .and(StringUtils.isNotBlank(content), wrapper -> wrapper
                     .like(FlfFulfiller::getName, content).or().like(FlfFulfiller::getPhone, content)
                 )
-                .apply(DataBaseHelper.findInSet(service, "service_types"))
-        );
+                .ne(FlfFulfiller::getStatus, FlfFulfillerStatusEnum.DISABLED.getValue())
+                .apply(DataBaseHelper.findInSet(service, "service_types")));
+
         List<Long> fulfillerIds = new ArrayList<>();
         List<Long> avatarIds = new ArrayList<>();
         page.getRecords().forEach(e -> {
@@ -627,11 +628,10 @@ public class FlfFulfillerServiceImpl implements IFlfFulfillerService {
         });
         Map<Long, List<Long>> tagMap = new HashMap<>();
         Map<Long, RemoteFile> avatarMap = new HashMap<>();
-        tagRelMapper.selectList(
-            Wrappers.lambdaQuery(SysTagRel.class)
+        tagRelMapper.selectList(Wrappers.lambdaQuery(SysTagRel.class)
                 .eq(SysTagRel::getTargetType, TagTargetConstants.FULFILLER)
-                .in(SysTagRel::getTargetId, WrapperUtils.convertIds(fulfillerIds))
-        ).forEach(e -> tagMap.computeIfAbsent(e.getTargetId(), k -> new ArrayList<>()).add(e.getTagId()));
+                .in(SysTagRel::getTargetId, WrapperUtils.convertIds(fulfillerIds)))
+            .forEach(e -> tagMap.computeIfAbsent(e.getTargetId(), k -> new ArrayList<>()).add(e.getTagId()));
 
         remoteFileService.selectByIds(avatarIds).forEach(e -> avatarMap.put(e.getOssId(), e));
 
@@ -641,11 +641,9 @@ public class FlfFulfillerServiceImpl implements IFlfFulfillerService {
             FlfFulfillerOnOrderVo vo = new FlfFulfillerOnOrderVo();
             vo.setId(e.getId());
             vo.setName(e.getName());
-            vo.setAvatar(
-                Optional.ofNullable(e.getAvatar())
+            vo.setAvatar(Optional.ofNullable(e.getAvatar())
                     .map(id -> avatarMap.get(Long.parseLong(id)).getUrl())
-                    .orElse(null)
-            );
+                    .orElse(null));
             vo.setPhone(e.getPhone());
             vo.setTags(tagMap.get(e.getId()));
             vo.setStatus(e.getStatus());
@@ -705,7 +703,7 @@ public class FlfFulfillerServiceImpl implements IFlfFulfillerService {
     @Override
     public List<FlfFulfillerOnOrderVo> listAllOnDispatch(Long service) {
 
-        List<FlfFulfiller> flfFulfillers = baseMapper.selectList();
+        List<FlfFulfiller> flfFulfillers = baseMapper.selectList(Wrappers.lambdaQuery(FlfFulfiller.class).ne(FlfFulfiller::getStatus, FlfFulfillerStatusEnum.DISABLED.getValue()));
 
         List<Long> fulfillerIds = new ArrayList<>();
         List<Long> avatarIds = new ArrayList<>();

+ 26 - 29
ruoyi-modules/yingpaipay-fulfiller/src/main/java/org/dromara/fulfiller/service/impl/FlfLevelConfigServiceImpl.java

@@ -53,9 +53,8 @@ public class FlfLevelConfigServiceImpl implements IFlfLevelConfigService {
     public boolean insertByBo(FlfLevelConfigBo bo) {
 
         FlfLevelConfig config = MapstructUtils.convert(bo, FlfLevelConfig.class);
-        boolean configFlag = baseMapper.insert(config) == 0;
-        if (configFlag) {
-            throw new RuntimeException("新增配置信息");
+        if (baseMapper.insert(config) == 0) {
+            throw new RuntimeException("新增配置信息失败");
         }
 
         List<FlfLevelConfigRights> relations = new ArrayList<>();
@@ -65,9 +64,11 @@ public class FlfLevelConfigServiceImpl implements IFlfLevelConfigService {
             right.setRightsId(e);
             relations.add(right);
         });
-        boolean relationFlag = levelConfigRightsMapper.insertBatch(relations);
-        if (!relationFlag) {
-            throw new RuntimeException("新增关系失败");
+        if (!relations.isEmpty()) {
+            boolean relationFlag = levelConfigRightsMapper.insertBatch(relations);
+            if (!relationFlag) {
+                throw new RuntimeException("新增关系失败");
+            }
         }
 
         return true;
@@ -78,26 +79,25 @@ public class FlfLevelConfigServiceImpl implements IFlfLevelConfigService {
     @Override
     public boolean updateByBo(FlfLevelConfigBo bo) {
         FlfLevelConfig config = MapstructUtils.convert(bo, FlfLevelConfig.class);
-        boolean configFlag = baseMapper.updateById(config) == 0;
-        if (configFlag) {
-            throw new RuntimeException("修改配置信息");
+        if (baseMapper.updateById(config) == 0) {
+            throw new RuntimeException("修改配置信息失败");
         }
 
-        boolean deleteFlag = levelConfigRightsMapper.delete(Wrappers.lambdaQuery(FlfLevelConfigRights.class).eq(FlfLevelConfigRights::getConfigId, bo.getId())) == 0;
-        if (deleteFlag) {
-            throw new RuntimeException("删除关系失败");
-        }
+        // 先删除原有关系,即使原本没有关系返回0也是正常的
+        levelConfigRightsMapper.delete(Wrappers.lambdaQuery(FlfLevelConfigRights.class).eq(FlfLevelConfigRights::getConfigId, bo.getId()));
 
         List<FlfLevelConfigRights> relations = new ArrayList<>();
-        bo.getRights().forEach(e -> {
-            FlfLevelConfigRights right = new FlfLevelConfigRights();
-            right.setConfigId(config.getId());
-            right.setRightsId(e);
-            relations.add(right);
-        });
-        boolean relationFlag = levelConfigRightsMapper.insertBatch(relations);
-        if (!relationFlag) {
-            throw new RuntimeException("新增关系失败");
+        if (bo.getRights() != null && !bo.getRights().isEmpty()) {
+            bo.getRights().forEach(e -> {
+                FlfLevelConfigRights right = new FlfLevelConfigRights();
+                right.setConfigId(config.getId());
+                right.setRightsId(e);
+                relations.add(right);
+            });
+            boolean relationFlag = levelConfigRightsMapper.insertBatch(relations);
+            if (!relationFlag) {
+                throw new RuntimeException("新增关系失败");
+            }
         }
 
         return true;
@@ -108,15 +108,12 @@ public class FlfLevelConfigServiceImpl implements IFlfLevelConfigService {
     @Override
     public boolean deleteById(Long id) {
 
-        boolean configFlag = baseMapper.deleteById(id) == 0;
-        if (configFlag) {
-            throw new RuntimeException("删除配置信息");
+        if (baseMapper.deleteById(id) == 0) {
+            throw new RuntimeException("删除配置信息失败");
         }
 
-        boolean deleteFlag = levelConfigRightsMapper.delete(Wrappers.lambdaQuery(FlfLevelConfigRights.class).eq(FlfLevelConfigRights::getConfigId, id)) == 0;
-        if (deleteFlag) {
-            throw new RuntimeException("删除关系失败");
-        }
+        // 即使没有关系返回0也是正常的
+        levelConfigRightsMapper.delete(Wrappers.lambdaQuery(FlfLevelConfigRights.class).eq(FlfLevelConfigRights::getConfigId, id));
 
         return true;
     }

+ 5 - 0
ruoyi-modules/yingpaipay-order/pom.xml

@@ -84,6 +84,11 @@
             <artifactId>ruoyi-common-encrypt</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>yingpaipay-common-rabbitmq</artifactId>
+        </dependency>
+
         <!-- RuoYi Api System -->
         <dependency>
             <groupId>org.dromara</groupId>

+ 86 - 0
ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/config/OrderRabbitMqConfig.java

@@ -0,0 +1,86 @@
+package org.dromara.order.config;
+
+import org.springframework.amqp.core.Binding;
+import org.springframework.amqp.core.BindingBuilder;
+import org.springframework.amqp.core.DirectExchange;
+import org.springframework.amqp.core.Queue;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 订单服务RabbitMQ配置
+ * 此代码为AI生成
+ *
+ * @author AI
+ */
+@Configuration
+public class OrderRabbitMqConfig {
+
+    /**
+     * 订单交换机名称
+     */
+    public static final String ORDER_EXCHANGE = "order.exchange";
+
+    /**
+     * 订单队列名称
+     */
+    public static final String ORDER_QUEUE = "order.notification.queue";
+
+    /**
+     * 订单路由键
+     */
+    public static final String ORDER_ROUTING_KEY = "order.notification";
+
+    public static final String ORDER_REJECT_QUEUE = "order.notification.reject.queue";
+    public static final String ORDER_REJECT_ROUTING_KEY = "order.notification.reject";
+
+    public static final String ORDER_ANOMALY_QUEUE = "order.notification.anomaly.queue";
+    public static final String ORDER_ANOMALY_ROUTING_KEY = "order.notification.anomaly";
+
+    /**
+     * 声明订单交换机
+     * 此代码为AI生成
+     */
+    @Bean
+    public DirectExchange orderExchange() {
+        return new DirectExchange(ORDER_EXCHANGE, true, false);
+    }
+
+    /**
+     * 声明订单队列
+     * 此代码为AI生成
+     */
+    @Bean
+    public Queue orderQueue() {
+        return new Queue(ORDER_QUEUE, true);
+    }
+
+    /**
+     * 绑定队列到交换机
+     * 此代码为AI生成
+     */
+    @Bean
+    public Binding orderBinding(Queue orderQueue, DirectExchange orderExchange) {
+        return BindingBuilder.bind(orderQueue).to(orderExchange).with(ORDER_ROUTING_KEY);
+    }
+
+    @Bean
+    public Queue orderRejectQueue() {
+        return new Queue(ORDER_REJECT_QUEUE, true);
+    }
+
+    @Bean
+    public Binding orderRejectBinding(Queue orderRejectQueue, DirectExchange orderExchange) {
+        return BindingBuilder.bind(orderRejectQueue).to(orderExchange).with(ORDER_REJECT_ROUTING_KEY);
+    }
+
+    @Bean
+    public Queue orderAnomalyQueue() {
+        return new Queue(ORDER_ANOMALY_QUEUE, true);
+    }
+
+    @Bean
+    public Binding orderAnomalyBinding(Queue orderAnomalyQueue, DirectExchange orderExchange) {
+        return BindingBuilder.bind(orderAnomalyQueue).to(orderExchange).with(ORDER_ANOMALY_ROUTING_KEY);
+    }
+}

+ 31 - 5
ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/controller/SysSubOrderController.java

@@ -4,7 +4,10 @@ import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.NotNull;
 import lombok.RequiredArgsConstructor;
 import org.apache.dubbo.config.annotation.DubboReference;
+import org.dromara.api.RemoteCustomerService;
 import org.dromara.api.RemotePetService;
+import org.dromara.api.domain.vo.RemoteCustomerVo;
+import org.dromara.api.domain.vo.RemotePetVo;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.utils.DateUtils;
 import org.dromara.common.excel.utils.ExcelUtil;
@@ -42,6 +45,10 @@ public class SysSubOrderController extends BaseController {
     private final RemoteSysServiceService remoteSysServiceService;
     @DubboReference
     private final RemoteAreaStationService remoteAreaStationService;
+    @DubboReference
+    private final RemoteCustomerService remoteCustomerService;
+    @DubboReference
+    private final RemotePetService remotePetService;
 
     @GetMapping("/list")
     public TableDataInfo<SysSubOrderListPageVo> list(SysSubOrderListPageBo bo, PageQuery pageQuery) {
@@ -61,7 +68,7 @@ public class SysSubOrderController extends BaseController {
 
     @RepeatSubmit()
     @PutMapping("/cancel")
-    public R<Void> cancel(@RequestBody SysSubOrderCancelBo bo) {
+    public R<Void> cancel(@Validated @RequestBody SysSubOrderCancelBo bo) {
         return toAjax(subOrderService.cancel(bo));
     }
 
@@ -117,7 +124,7 @@ public class SysSubOrderController extends BaseController {
 
     @RepeatSubmit()
     @PutMapping("/reject")
-    public R<Void> reject(@RequestBody SysSubOrderRejectBo bo) {
+    public R<Void> reject(@Validated @RequestBody SysSubOrderRejectBo bo) {
         return toAjax(subOrderService.reject(bo));
     }
 
@@ -151,10 +158,23 @@ public class SysSubOrderController extends BaseController {
     public void export(SysSubOrderListPageBo bo, HttpServletResponse response) {
         List<SysSubOrderListPageVo> list = subOrderService.listExcel(bo);
 
+        List<Long> customerIds = new ArrayList<>();
+
+        list.forEach(e -> {
+            customerIds.add(e.getCustomer());
+        });
+
         Map<Long, RemoteSysServiceVo> serviceMap = new HashMap<>();
         Map<Long, RemoteAreaStationVo> areaStationMap = new HashMap<>();
+        Map<Long, List<RemotePetVo>> customerPetMap = new HashMap<>();
+        Map<Long, RemotePetVo> petMap = new HashMap<>();
         remoteSysServiceService.listAll().forEach(e -> serviceMap.put(e.getId(), e));
         remoteAreaStationService.listAll().forEach(e -> areaStationMap.put(e.getId(), e));
+        List<RemotePetVo> pets = remotePetService.getByCustomerIds(customerIds);
+        pets.forEach(e -> {
+            petMap.put(e.getId(), e);
+            customerPetMap.computeIfAbsent(e.getCustomerId(), k -> new ArrayList<>()).add(e);
+        });
 
         List<SysSubOrderListExcel> excels = list.stream()
             .map(e -> {
@@ -163,15 +183,16 @@ public class SysSubOrderController extends BaseController {
                 excel.setCode(e.getCode());
                 excel.setService(e.getService());
                 RemoteSysServiceVo service = serviceMap.get(e.getService());
-                excel.setServiceName(service.getName());
+                excel.setServiceName(service != null ? service.getName() : "未知服务");
                 excel.setServiceTime(Optional.ofNullable(e.getServiceTime()).map(DateUtils::formatDateTime).orElse("-"));
                 excel.setToAddress(e.getToAddress());
-                excel.setMode(service.getMode().equals(ServiceModeEnum.PICK_UP.getValue()) ? (e.getMode() == 1 ? "往返" : "单程") : "-");
+                excel.setMode(service != null && service.getMode().equals(ServiceModeEnum.PICK_UP.getValue()) ? (e.getMode() == 1 ? "往返" : "单程") : "-");
                 excel.setType(OrderTypeEnum.getLabel(e.getType()));
                 excel.setPetName(e.getPetName());
                 excel.setPetBreed(e.getPetBreed());
                 excel.setCustomerName(e.getCustomerName());
-                excel.setSiteName(areaStationMap.get(e.getSite()).getName());
+                RemoteAreaStationVo site = areaStationMap.get(e.getSite());
+                excel.setSiteName(site != null ? site.getName() : "未知站点");
                 excel.setStoreName(e.getStoreName());
                 excel.setPlacerUsername(e.getPlacerUsername());
                 excel.setCreateTime(Optional.ofNullable(e.getCreateTime()).map(DateUtils::formatDateTime).orElse("-"));
@@ -180,6 +201,11 @@ public class SysSubOrderController extends BaseController {
                 excel.setFulfillmentCommission(Optional.ofNullable(e.getFulfillmentCommission())
                         .map(fulfillmentCommission -> new BigDecimal(fulfillmentCommission).multiply(new BigDecimal("0.01")))
                         .orElse(null));
+                excel.setCustomerPhone(e.getCustomerPhone());
+                RemotePetVo pet = petMap.get(e.getPet());
+                excel.setPetWeight(pet != null ? pet.getWeight() : null);
+                excel.setCustomerPetCount(customerPetMap.getOrDefault(e.getCustomer(), new ArrayList<>()).size());
+                excel.setRemark(e.getRemark());
                 return excel;
             }).toList();
         ExcelUtil.exportExcel(excels, "订单列表", SysSubOrderListExcel.class, response);

+ 4 - 0
ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/domain/bo/SysSubOrderCancelBo.java

@@ -1,5 +1,6 @@
 package org.dromara.order.domain.bo;
 
+import jakarta.validation.constraints.NotBlank;
 import lombok.Data;
 
 @Data
@@ -7,4 +8,7 @@ public class SysSubOrderCancelBo {
 
     private Long orderId;
 
+    @NotBlank(message = "必须填写拒绝原因")
+    private String reason;
+
 }

+ 2 - 0
ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/domain/bo/SysSubOrderRejectBo.java

@@ -1,5 +1,6 @@
 package org.dromara.order.domain.bo;
 
+import jakarta.validation.constraints.NotBlank;
 import lombok.Data;
 
 @Data
@@ -7,6 +8,7 @@ public class SysSubOrderRejectBo {
 
     private Long orderId;
 
+    @NotBlank(message = "必须填写拒绝原因")
     private String rejectReason;
 
 }

+ 9 - 1
ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/domain/excel/SysSubOrderListExcel.java

@@ -7,7 +7,6 @@ import lombok.Data;
 import java.io.Serial;
 import java.io.Serializable;
 import java.math.BigDecimal;
-import java.util.Date;
 
 @Data
 @ExcelIgnoreUnannotated
@@ -69,4 +68,13 @@ public class SysSubOrderListExcel implements Serializable {
     @ExcelProperty(value = "服务费")
     private BigDecimal fulfillmentCommission;
 
+    @ExcelProperty(value = "用户联系电话")
+    private String customerPhone;
+    @ExcelProperty(value = "用户宠物数量")
+    private Integer customerPetCount;
+    @ExcelProperty(value = "宠物重量")
+    private BigDecimal petWeight;
+    @ExcelProperty(value = "备注")
+    private String remark;
+
 }

+ 3 - 0
ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/domain/vo/SysSubOrderListPageVo.java

@@ -39,6 +39,8 @@ public class SysSubOrderListPageVo implements Serializable {
     @Translation(type = TransConstant.CUSTOMER_ID_TO_NAME, mapper = "customer")
     private String customerName;
 
+    private String customerPhone;
+
     private Long site;
 
     private Long store;
@@ -62,6 +64,7 @@ public class SysSubOrderListPageVo implements Serializable {
     private String fulfillerStatus;
 
     private Long fulfillmentCommission;
+    private String fulfillerPhone;
 
     private String remark;
 

+ 2 - 0
ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/domain/vo/SysSubOrderVo.java

@@ -135,4 +135,6 @@ public class  SysSubOrderVo implements Serializable {
 
     private Date createTime;
 
+    private String remark;
+
 }

+ 11 - 2
ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/dubbo/RemoteSubOrderServiceImpl.java

@@ -52,7 +52,7 @@ public class RemoteSubOrderServiceImpl implements RemoteSubOrderService {
     @Override
     public RemoteSubOrderVo getIdByCode(String code) {
         SysSubOrder subOrder = baseMapper.selectOne(Wrappers.lambdaQuery(SysSubOrder.class)
-            .select(SysSubOrder::getId, SysSubOrder::getTenantId)
+            .select(SysSubOrder::getId, SysSubOrder::getTenantId, SysSubOrder::getCode, SysSubOrder::getStore, SysSubOrder::getStoreSite)
             .eq(SysSubOrder::getCode, code)
             .last("LIMIT 1"));
 
@@ -63,6 +63,10 @@ public class RemoteSubOrderServiceImpl implements RemoteSubOrderService {
         RemoteSubOrderVo vo = new RemoteSubOrderVo();
         vo.setId(subOrder.getId());
         vo.setTenantId(subOrder.getTenantId());
+        vo.setCode(subOrder.getCode());
+        // 此代码由AI生成 - 补充门店相关字段
+        vo.setStore(subOrder.getStore());
+        vo.setStoreSite(subOrder.getStoreSite());
 
         return vo;
     }
@@ -77,12 +81,13 @@ public class RemoteSubOrderServiceImpl implements RemoteSubOrderService {
         List<RemoteSubOrderVo> vos = new ArrayList<>();
 
         baseMapper.selectList(Wrappers.lambdaQuery(SysSubOrder.class)
-                .select(SysSubOrder::getId, SysSubOrder::getCode)
+                .select(SysSubOrder::getId, SysSubOrder::getCode, SysSubOrder::getStore)
                 .in(SysSubOrder::getId, orderIds))
             .forEach(e -> {
                 RemoteSubOrderVo vo = new RemoteSubOrderVo();
                 vo.setId(e.getId());
                 vo.setCode(e.getCode());
+                vo.setStore(e.getStore());
                 vos.add(vo);
             });
 
@@ -121,6 +126,10 @@ public class RemoteSubOrderServiceImpl implements RemoteSubOrderService {
         RemoteSubOrderVo vo = new RemoteSubOrderVo();
         vo.setId(order.getId());
         vo.setTenantId(order.getTenantId());
+        vo.setStoreSite(order.getStoreSite());
+        vo.setCode(order.getCode());
+        // 此代码由AI生成 - 补充门店ID字段
+        vo.setStore(order.getStore());
 
         return vo;
     }

+ 1 - 0
ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/service/impl/SysOrderServiceImpl.java

@@ -23,6 +23,7 @@ import org.dromara.order.mapper.SysOrderMapper;
 import org.dromara.order.mapper.SysSubOrderLogMapper;
 import org.dromara.order.mapper.SysSubOrderMapper;
 import org.dromara.order.service.ISysOrderService;
+import org.dromara.common.rabbitmq.core.RabbitMqProducer;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 

+ 191 - 14
ruoyi-modules/yingpaipay-order/src/main/java/org/dromara/order/service/impl/SysSubOrderServiceImpl.java

@@ -20,17 +20,18 @@ import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.fulfiller.api.RemoteFulfillerService;
 import org.dromara.fulfiller.api.domain.vo.RemoteFulfillerVo;
 import org.dromara.fulfiller.api.model.FulfillerLoginUser;
-import org.dromara.order.api.enums.OrderLogActionerTypeEnum;
-import org.dromara.order.api.enums.OrderLogSystemStepEnum;
-import org.dromara.order.api.enums.OrderLogTypeEnum;
-import org.dromara.order.api.enums.OrderStatusEnum;
+import org.dromara.order.api.enums.*;
+import org.dromara.order.config.OrderRabbitMqConfig;
 import org.dromara.order.domain.SysSubOrder;
 import org.dromara.order.domain.SysSubOrderLog;
 import org.dromara.order.domain.bo.*;
 import org.dromara.order.domain.vo.*;
+import org.dromara.common.rabbitmq.message.SubOrderDispatchMessage;
+import org.dromara.common.rabbitmq.message.SubOrderRejectMessage;
 import org.dromara.order.mapper.SysSubOrderLogMapper;
 import org.dromara.order.mapper.SysSubOrderMapper;
 import org.dromara.order.service.ISysSubOrderService;
+import org.dromara.common.rabbitmq.core.RabbitMqProducer;
 import org.dromara.resource.api.RemoteFileService;
 import org.dromara.resource.api.domain.RemoteFile;
 import org.dromara.system.api.RemoteAreaStationService;
@@ -40,9 +41,11 @@ import org.dromara.system.api.RemoteUserService;
 import org.dromara.system.api.domain.vo.RemoteStoreVo;
 import org.dromara.system.api.domain.vo.RemoteUserVo;
 import org.dromara.system.api.model.LoginUser;
+import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.stream.Collectors;
@@ -51,6 +54,9 @@ import java.util.stream.Collectors;
 @RequiredArgsConstructor
 public class SysSubOrderServiceImpl implements ISysSubOrderService {
 
+    // 此代码为AI生成
+    private final RabbitMqProducer rabbitMqProducer;
+
     private final SysSubOrderMapper baseMapper;
     private final SysSubOrderLogMapper subOrderLogMapper;
 
@@ -83,8 +89,8 @@ public class SysSubOrderServiceImpl implements ISysSubOrderService {
             orderWrapper.and(w -> w
                 .in(SysSubOrder::getTenantId, tenantIds.isEmpty() ? List.of(TenantConstants.DEFAULT_TENANT_ID) : tenantIds).or()
                 .in(SysSubOrder::getOrderPlacer, WrapperUtils.convertIds(userIds)).or()
-                .in(SysSubOrder::getUsrCustomer, WrapperUtils.convertIds(customerIds))
-            );
+                .in(SysSubOrder::getUsrCustomer, WrapperUtils.convertIds(customerIds)).or()
+                .like(SysSubOrder::getCode, bo.getContent()));
         }
 
         orderWrapper.in(SysSubOrder::getStoreSite, WrapperUtils.convertIds(remoteAreaStationService.listByUserId(LoginHelper.getUserId())));
@@ -147,6 +153,7 @@ public class SysSubOrderServiceImpl implements ISysSubOrderService {
             if (fulfiller != null) {
                 vo.setFulfillerName(fulfiller.getName());
                 vo.setFulfillerStatus(fulfiller.getStatus());
+                vo.setFulfillerPhone(fulfiller.getPhone());
             }
             vo.setFulfillmentCommission(e.getFulfillmentCommission());
             vo.setRemark(e.getRemark());
@@ -192,6 +199,28 @@ public class SysSubOrderServiceImpl implements ISysSubOrderService {
             throw new RuntimeException("记录日志失败");
         }
 
+        // 此代码为AI生成 - 派单时发送RabbitMQ消息用于后续消息通知 (暂时禁用)
+        /*
+        try {
+            SubOrderDispatchMessage message = new SubOrderDispatchMessage();
+            BeanUtils.copyProperties(subOrder, message);
+
+            // 补全派单人信息
+            message.setDispatcherId(LoginHelper.getUserId());
+            if (LoginHelper.getLoginUser() != null) {
+                message.setDispatcherName(LoginHelper.getLoginUser().getNickname());
+            }
+
+            rabbitMqProducer.sendMessage(
+                OrderRabbitMqConfig.ORDER_EXCHANGE,
+                OrderRabbitMqConfig.ORDER_ROUTING_KEY,
+                message
+            );
+        } catch (Exception e) {
+            // 消息发送失败不影响派单,仅记录日志
+        }
+        */
+
         return true;
     }
 
@@ -210,15 +239,17 @@ public class SysSubOrderServiceImpl implements ISysSubOrderService {
         SysSubOrderLog log = new SysSubOrderLog();
 
         LoginUser loginUser = LoginHelper.getLoginUser();
-        if (loginUser.getUserType().equals(UserType.FULFILLER_USER.getUserType())) {
+        // 此代码由AI生成 - 判断是否为履约者取消
+        boolean isFulfillerCancel = loginUser.getUserType().equals(UserType.FULFILLER_USER.getUserType());
+        if (isFulfillerCancel) {
             log.setSubOrderId(subOrder.getId());
             log.setActioner(LoginHelper.getUserId());
             log.setActionerType(OrderLogActionerTypeEnum.FULFILLER.getValue());
             log.setLogType(OrderLogTypeEnum.FULFILLER.getValue());
-            org.dromara.order.api.enums.OrderLogFulfillerStepEnum step = org.dromara.order.api.enums.OrderLogFulfillerStepEnum.REJECT;
+            OrderLogFulfillerStepEnum step = OrderLogFulfillerStepEnum.REJECT;
             log.setStep(step.getStep());
             log.setTitle(step.getTitle());
-            log.setContent(String.format(step.getContent(), loginUser.getNickname()));
+            log.setContent(String.format(step.getContent(), loginUser.getNickname(), bo.getReason()));
             log.setTenantId(subOrder.getTenantId());
         } else {
             log.setSubOrderId(subOrder.getId());
@@ -228,7 +259,7 @@ public class SysSubOrderServiceImpl implements ISysSubOrderService {
             OrderLogSystemStepEnum step = OrderLogSystemStepEnum.CANCEL;
             log.setStep(step.getStep());
             log.setTitle(step.getTitle());
-            log.setContent(step.getContent());
+            log.setContent(String.format(step.getContent(), loginUser.getNickname(), bo.getReason()));
             log.setTenantId(subOrder.getTenantId());
         }
 
@@ -237,14 +268,134 @@ public class SysSubOrderServiceImpl implements ISysSubOrderService {
             throw new RuntimeException("日志追加失败");
         }
 
+        // 此代码由AI生成 - 履约者取消订单时发送消息通知
+        if (isFulfillerCancel) {
+            try {
+                SubOrderRejectMessage msg = new SubOrderRejectMessage();
+                msg.setOrderCode(subOrder.getCode());
+                msg.setOrderId(subOrder.getId());
+                msg.setStoreSite(subOrder.getStoreSite());
+                msg.setFulfillerId(LoginHelper.getUserId());
+                msg.setFulfillerName(loginUser.getNickname());
+                msg.setRejectReason(bo.getReason());
+                // 获取门店名称
+                String storeName = remoteStoreService.getNameById(subOrder.getStore());
+                msg.setStoreName(storeName != null ? storeName : "未知门店");
+                // 设置取消时间
+                msg.setCancelTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+                // 此代码由AI生成 - 标识为取消操作
+                msg.setActionType("CANCEL");
+
+                rabbitMqProducer.sendMessage(
+                    OrderRabbitMqConfig.ORDER_EXCHANGE,
+                    OrderRabbitMqConfig.ORDER_REJECT_ROUTING_KEY,
+                    msg
+                );
+            } catch (Exception e) {
+                // 消息发送失败不影响主流程
+            }
+        }
+
         return true;
     }
 
     @Override
     public SysSubOrderVo getInfo(Long id) {
-        return baseMapper.selectVoById(id);
+        SysSubOrderVo vo = baseMapper.selectVoById(id);
+        if (vo == null) {
+            return null;
+        }
+        return vo;
     }
 
+//    @Override
+//    public TableDataInfo<SysSubOrderListPageVo> listOnMerchant(SysSubOrderListPageBo bo, PageQuery pageQuery) {
+//
+//        LambdaQueryWrapper<SysSubOrder> orderWrapper = Wrappers.lambdaQuery(SysSubOrder.class);
+//
+//        if (StringUtils.isNotBlank(bo.getContent())) {
+//            List<Long> storeIds = remoteStoreService.selectIdsByName(bo.getContent());
+//            List<Long> userIds = remoteUserService.selectUserIdsByName(bo.getContent());
+//            List<Long> customerIds = remoteCustomerService.selectIdsByName(bo.getContent());
+//            orderWrapper.and(w -> w
+//                .in(SysSubOrder::getStore, WrapperUtils.convertIds(storeIds)).or()
+//                .in(SysSubOrder::getOrderPlacer, WrapperUtils.convertIds(userIds)).or()
+//                .in(SysSubOrder::getUsrCustomer, WrapperUtils.convertIds(customerIds))
+//            );
+//
+//        }
+//
+//        Page<SysSubOrder> page = baseMapper.selectPage(
+//            pageQuery.build(),
+//            orderWrapper.orderByDesc(SysSubOrder::getId)
+//                .eq(bo.getStatus() != null, SysSubOrder::getStatus, bo.getStatus())
+//                .eq(bo.getService() != null, SysSubOrder::getService, bo.getService())
+//        );
+//
+//        List<Long> petIds = new ArrayList<>();
+//        List<Long> storeIds = new ArrayList<>();
+//        List<Long> customerIds = new ArrayList<>();
+//        List<Long> userIds = new ArrayList<>();
+//        List<Long> fulfillerIds = new ArrayList<>();
+//        page.getRecords().forEach(e -> {
+//            petIds.add(e.getUsrPet());
+//            customerIds.add(e.getUsrCustomer());
+//            storeIds.add(e.getStore());
+//            userIds.add(e.getOrderPlacer());
+//            if (e.getFulfiller() != null) {
+//                fulfillerIds.add(e.getFulfiller());
+//            }
+//        });
+//        Map<Long, RemotePetVo> petMap = new HashMap<>();
+//        Map<Long, RemoteStoreVo> storeMap = new HashMap<>();
+//        Map<Long, RemoteCustomerVo> customerMap = new HashMap<>();
+//        Map<Long, RemoteUserVo> userMap = new HashMap<>();
+//        Map<Long, RemoteFulfillerVo> fulfillerMap = new HashMap<>();
+//        remotePetService.getByIds(petIds).forEach(e -> petMap.put(e.getId(), e));
+//        remoteStoreService.getByIds(storeIds).forEach(e -> storeMap.put(e.getId(), e));
+//        remoteUserService.getByIds(userIds).forEach(e -> userMap.put(e.getUserId(), e));
+//        remoteCustomerService.getByIds(customerIds).forEach(e -> customerMap.put(e.getId(), e));
+//        remoteFulfillerService.getByIds(fulfillerIds).forEach(e -> fulfillerMap.put(e.getId(), e));
+//
+//        return TableDataInfo.build(page.convert(e -> {
+//            SysSubOrderListPageVo vo = new SysSubOrderListPageVo();
+//            vo.setId(e.getId());
+//            vo.setCode(e.getCode());
+//            vo.setService(e.getService());
+//            vo.setServiceTime(e.getServiceTime());
+//            vo.setMode(e.getMode());
+//            vo.setType(e.getType());
+//            vo.setPet(e.getUsrPet());
+//            RemotePetVo pet = petMap.get(e.getUsrPet());
+//            vo.setPetName(pet.getName());
+//            vo.setPetBreed(pet.getBreed());
+//            vo.setCustomer(e.getUsrCustomer());
+//            RemoteCustomerVo customer = customerMap.get(e.getUsrCustomer());
+//            vo.setCustomerName(customer.getName());
+//            vo.setToAddress(e.getToAddress());
+//            vo.setSite(e.getStoreSite());
+//            vo.setStore(e.getStore());
+//            RemoteStoreVo store = storeMap.get(e.getStore());
+//            vo.setStoreName(store.getName());
+//            vo.setPlacer(e.getOrderPlacer());
+//            RemoteUserVo placer = userMap.get(e.getOrderPlacer());
+//            vo.setPlacerUsername(placer.getUserName());
+//            vo.setCreateTime(e.getCreateTime());
+//            vo.setStatus(e.getStatus());
+//            vo.setFulfiller(e.getFulfiller());
+//            RemoteFulfillerVo fulfiller = fulfillerMap.get(e.getFulfiller());
+//            if (fulfiller != null) {
+//                vo.setFulfillerName(fulfiller.getName());
+//                vo.setFulfillerStatus(fulfiller.getStatus());
+//            }
+//            vo.setRemark(e.getRemark());
+//            vo.setNursingSummary(e.getNursingSummary());
+//            vo.setNursingSummaryTime(e.getNursingSummaryTime());
+//            return vo;
+//        }));
+//
+//    }
+
     @Override
     public TableDataInfo<SysSubOrderPendingAcceptPageVo> listPendingAccept(SysSubOrderPendingAcceptPageBo bo, PageQuery pageQuery) {
 
@@ -545,7 +696,6 @@ public class SysSubOrderServiceImpl implements ISysSubOrderService {
             vo.setToAddress(e.getToAddress());
             vo.setRemark(e.getRemark());
             vo.setStatus(e.getStatus());
-            vo.setServiceFlag(e.getServiceFlag());
             return vo;
         }));
     }
@@ -653,16 +803,42 @@ public class SysSubOrderServiceImpl implements ISysSubOrderService {
         log.setActioner(LoginHelper.getUserId());
         log.setActionerType(OrderLogActionerTypeEnum.FULFILLER.getValue());
         log.setLogType(OrderLogTypeEnum.FULFILLER.getValue());
-        org.dromara.order.api.enums.OrderLogFulfillerStepEnum step = org.dromara.order.api.enums.OrderLogFulfillerStepEnum.REJECT;
+        OrderLogFulfillerStepEnum step = OrderLogFulfillerStepEnum.REJECT;
         log.setStep(step.getStep());
         log.setTitle(step.getTitle());
-        log.setContent(bo.getRejectReason());
+        log.setContent(String.format(step.getContent(), LoginHelper.getLoginUser().getNickname(), bo.getRejectReason()));
         log.setTenantId(subOrder.getTenantId());
         boolean logFlag = subOrderLogMapper.insert(log) == 0;
         if (logFlag) {
             throw new RuntimeException("日志新增失败");
         }
 
+        // 此代码由AI生成 - 发送拒单消息(包含门店名称和取消时间)
+        try {
+            SubOrderRejectMessage msg = new SubOrderRejectMessage();
+            msg.setOrderCode(subOrder.getCode());
+            msg.setOrderId(subOrder.getId());
+            msg.setStoreSite(subOrder.getStoreSite());
+            msg.setFulfillerId(LoginHelper.getUserId());
+            msg.setFulfillerName(LoginHelper.getLoginUser().getNickname());
+            msg.setRejectReason(bo.getRejectReason());
+            // 获取门店名称
+            String storeName = remoteStoreService.getNameById(subOrder.getStore());
+            msg.setStoreName(storeName != null ? storeName : "未知门店");
+            // 设置取消时间
+            msg.setCancelTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+            // 此代码由AI生成 - 标识为拒单操作
+            msg.setActionType("REJECT");
+
+            rabbitMqProducer.sendMessage(
+                OrderRabbitMqConfig.ORDER_EXCHANGE,
+                OrderRabbitMqConfig.ORDER_REJECT_ROUTING_KEY,
+                msg
+            );
+        } catch (Exception e) {
+            // 忽略发送异常,以不影响主流程
+        }
+
         return true;
     }
 
@@ -899,6 +1075,7 @@ public class SysSubOrderServiceImpl implements ISysSubOrderService {
             RemoteCustomerVo customer = customerMap.get(e.getUsrCustomer());
             if (customer != null) {
                 vo.setCustomerName(customer.getName());
+                vo.setCustomerPhone(customer.getPhone());
             }
             vo.setToAddress(e.getToAddress());
             vo.setSite(e.getStoreSite());

+ 13 - 4
ruoyi-visual/ruoyi-nacos/src/main/resources/application.properties

@@ -40,13 +40,22 @@ spring.sql.init.platform=mysql
 db.num=1
 
 ### Connect URL of DB:
-#db.url.0=jdbc:mysql://116.62.136.107:4563/pet_system_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
-#db.user.0=pet_system_config
-#db.password.0=tfry6h6yrztNTw3e
+## Development
+
+# Local
 db.url.0=jdbc:mysql://127.0.0.1:3306/pet_system_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
 db.user.0=root
 db.password.0=1234
-# db.password.0=Yr7777777
+
+# Test
+#db.url.0=jdbc:mysql://116.62.136.107:4563/pet_system_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
+#db.user.0=pet_system_config
+#db.password.0=tfry6h6yrztNTw3e
+
+## Production
+#db.url.0=jdbc:mysql://127.0.0.1:3306/pet_system_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
+#db.user.0=root
+#db.password.0=Yr7777777
 
 ### the maximum retry times for push
 nacos.config.push.maxRetryTime=50

+ 22 - 0
script/sql/business/create.sql

@@ -390,3 +390,25 @@ CREATE TABLE `pet_system`.`sys_customer_service_setting`
     `qr_code`                bigint COMMENT '二维码',
     `enterprise_wechat_link` varchar(512) COMMENT '企业微信链接'
 ) ENGINE = innoDB COMMENT = '客服配置信息表';
+
+CREATE TABLE pet_system.sys_notice
+(
+    id            bigint PRIMARY KEY NOT NULL COMMENT '序号',
+    sender_type   tinyint            NOT NULL COMMENT '发送方类型',
+    sender        bigint             NOT NULL COMMENT '发送方',
+    send_flag     boolean            NOT NULL DEFAULT false COMMENT '发送标识',
+    send_time     datetime COMMENT '发送时间',
+    type          tinyint            NOT NULL COMMENT '消息类型',
+    title         varchar(255)       NOT NULL COMMENT '标题',
+    content       varchar(2560)      NOT NULL COMMENT '内容',
+    receiver_type tinyint            NOT NULL COMMENT '接收方类型',
+    receiver      bigint             NOT NULL COMMENT '接收方',
+    read_flag     boolean            NOT NULL DEFAULT false COMMENT '已读标识',
+    read_time     datetime COMMENT '阅读时间',
+    del_flag      char(1)                     DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)',
+    create_dept   bigint(20) COMMENT '创建部门',
+    create_by     bigint(20) COMMENT '创建者',
+    create_time   datetime COMMENT '创建时间',
+    update_by     bigint(20) COMMENT '更新者',
+    update_time   datetime COMMENT '更新时间'
+) ENGINE = innoDB COMMENT = '系统通知信息表';