qxy преди 3 месеца
родител
ревизия
022d79ffe0
променени са 100 файла, в които са добавени 20359 реда и са изтрити 0 реда
  1. 318 0
      alien-branch-lawyer/pom.xml
  2. 21 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/AlienLawyerApplication.java
  3. 49 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/AsyncConfig.java
  4. 386 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/BaseRedisService.java
  5. 69 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/DruidConfig.java
  6. 61 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/MyBatisFieldHandler.java
  7. 13 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/MyBatisPlusPageConfig.java
  8. 33 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/RedisKeyExpirationConfig.java
  9. 57 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/SwaggerConfig.java
  10. 49 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/TokenInfoArgumentResolver.java
  11. 16 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/WebConfig.java
  12. 23 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/WebSocketConfig.java
  13. 241 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/WebSocketProcess.java
  14. 288 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/AiAutoReview.java
  15. 105 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/AliController.java
  16. 130 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/CommentAppealController.java
  17. 66 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/FileUploadController.java
  18. 217 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawFirmController.java
  19. 202 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawFirmReconciliationController.java
  20. 183 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerAiInteractionLogController.java
  21. 113 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerChatMessageController.java
  22. 178 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerChatSessionController.java
  23. 258 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerClientConsultationOrderController.java
  24. 129 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerCommonQuestionController.java
  25. 454 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerConsultationOrderController.java
  26. 115 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerConsultationReviewController.java
  27. 145 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerExpertiseAreaController.java
  28. 107 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerImgController.java
  29. 150 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerLegalProblemScenarioController.java
  30. 150 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerNoticeController.java
  31. 113 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerPaymentTransactionController.java
  32. 114 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerServiceAreaController.java
  33. 96 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerStatisticsController.java
  34. 424 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerStoreCommentController.java
  35. 347 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserController.java
  36. 129 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserLogInController.java
  37. 94 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserSearchHistoryController.java
  38. 298 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserViolationController.java
  39. 302 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/OrderReviewController.java
  40. 92 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/PaymentController.java
  41. 215 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/ReviewCommentController.java
  42. 43 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/TestController.java
  43. 12 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/feign/AlienSecondFeign.java
  44. 39 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/feign/AlienStoreFeign.java
  45. 90 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/listener/RedisKeyExpirationHandler.java
  46. 46 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/listener/RedisKeyExpirationListener.java
  47. 61 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/payment/PaymentStrategy.java
  48. 64 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/payment/PaymentStrategyFactory.java
  49. 602 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/payment/impl/AlipayPaymentStrategyImpl.java
  50. 1265 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/payment/impl/WeChatPaymentStrategyImpl.java
  51. 24 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/AiUserAuditTaskService.java
  52. 76 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/CommentAppealService.java
  53. 16 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawFirmPaymentService.java
  54. 149 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawFirmReconciliationService.java
  55. 131 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawFirmService.java
  56. 88 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerAiInteractionLogService.java
  57. 63 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerChatMessageService.java
  58. 103 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerChatSessionService.java
  59. 109 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerClientConsultationOrderService.java
  60. 73 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerCommonQuestionService.java
  61. 159 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerConsultationOrderService.java
  62. 63 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerConsultationReviewService.java
  63. 70 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerExpertiseAreaService.java
  64. 61 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerImgService.java
  65. 56 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerLegalProblemScenarioService.java
  66. 50 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerNoticeService.java
  67. 53 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerPaymentTransactionService.java
  68. 39 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerServiceAreaService.java
  69. 31 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerStatisticsService.java
  70. 29 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerUserLogInService.java
  71. 79 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerUserSearchHistoryService.java
  72. 199 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerUserService.java
  73. 87 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerUserViolationService.java
  74. 84 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/OrderExpirationService.java
  75. 146 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/OrderReviewService.java
  76. 17 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/RefundRecordService.java
  77. 100 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/ReviewCommentService.java
  78. 17 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/StoreAliPayErrorLogService.java
  79. 17 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/StoreAliPayRefundLogService.java
  80. 269 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/StoreCommentService.java
  81. 73 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/StoreImgService.java
  82. 11 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/TestService.java
  83. 620 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/AiUserAuditTaskServiceImpl.java
  84. 581 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/CommentAppealServiceImpl.java
  85. 34 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawFirmPaymentServiceImpl.java
  86. 330 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawFirmReconciliationServiceImpl.java
  87. 1403 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawFirmServiceImpl.java
  88. 187 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerAiInteractionLogServiceImpl.java
  89. 95 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerChatMessageServiceImpl.java
  90. 245 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerChatSessionServiceImpl.java
  91. 1855 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerClientConsultationOrderServiceImpl.java
  92. 117 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerCommonQuestionServiceImpl.java
  93. 2368 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerConsultationOrderServiceImpl.java
  94. 94 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerConsultationReviewServiceImpl.java
  95. 162 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerExpertiseAreaServiceImpl.java
  96. 140 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerImgServiceImpl.java
  97. 199 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerLegalProblemScenarioServiceImpl.java
  98. 477 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerNoticeServiceImpl.java
  99. 82 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerPaymentTransactionServiceImpl.java
  100. 56 0
      alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerServiceAreaServiceImpl.java

+ 318 - 0
alien-branch-lawyer/pom.xml

@@ -17,4 +17,322 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
 
+    <dependencies>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.fasterxml.jackson.core</groupId>
+                    <artifactId>jackson-databind</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+        </dependency>
+
+        <!-- mybatis-plus代码生成器 Start -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-generator</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>jsqlparser</artifactId>
+                    <groupId>com.github.jsqlparser</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>mybatis</artifactId>
+                    <groupId>org.mybatis</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>mybatis-spring</artifactId>
+                    <groupId>org.mybatis</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-annotation</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-extension</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-core</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <!-- mybatisPlus Freemarker 模版引擎 -->
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+        </dependency>
+        <!-- mybatis-plus代码生成器 End -->
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+
+        <!--Swagger Start-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>swagger-annotations</artifactId>
+                    <groupId>io.swagger</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>swagger-models</artifactId>
+                    <groupId>io.swagger</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-models</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>swagger-bootstrap-ui</artifactId>
+        </dependency>
+        <!--Swagger End-->
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <!--token-->
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alipay.sdk</groupId>
+            <artifactId>alipay-sdk-java</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>dysmsapi20170525</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.netease.yidun</groupId>
+            <artifactId>yidun-java-sdk</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>shop.alien</groupId>
+            <artifactId>alien-service-entity</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>shop.alien</groupId>
+            <artifactId>alien-service-util</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>shop.alien</groupId>
+            <artifactId>alien-service-config</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+        </dependency>
+
+        <!--允许你使用流(Flux和Mono)来处理异步、非阻塞的数据-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-webflux</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>4.0.3</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>4.0.3</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>4.0.3</version>
+            <scope>compile</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <version>3.1.2</version> <!-- 使用最新版本 -->
+                <executions>
+                    <execution>
+                        <id>copy-dependencies</id>
+                        <phase>prepare-package</phase> <!-- 在package之前执行 -->
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${project.build.directory}/lib</outputDirectory> <!-- 指定输出目录 -->
+                            <includeScope>runtime</includeScope> <!-- 仅包含运行时依赖 -->
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>shop.alien.lawyer.AlienLawyerApplication</mainClass>
+                    <layout>ZIP</layout>
+                    <!--                    <includes>-->
+                    <!--                        <include>-->
+                    <!--                            <groupId>nothing</groupId>-->
+                    <!--                            <artifactId>nothing</artifactId>-->
+                    <!--                        </include>-->
+                    <!--                    </includes>-->
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>8</source>
+                    <target>8</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
 </project>

+ 21 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/AlienLawyerApplication.java

@@ -0,0 +1,21 @@
+package shop.alien.lawyer;
+
+import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.ComponentScan;
+
+@ComponentScan({"shop.alien.lawyer.*", "shop.alien.util.*", "shop.alien.config.http", "shop.alien.config.properties"})
+@EnableSwaggerBootstrapUI
+@MapperScan({"shop.alien.mapper"})
+@SpringBootApplication
+@EnableFeignClients(basePackages = "shop.alien.lawyer.feign")
+public class AlienLawyerApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(AlienLawyerApplication.class, args);
+    }
+
+}

+ 49 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/AsyncConfig.java

@@ -0,0 +1,49 @@
+package shop.alien.lawyer.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 异步任务配置类
+ * 用于配置异步任务的线程池
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Configuration
+@EnableAsync
+public class AsyncConfig {
+
+    /**
+     * 配置异步任务执行器
+     * 用于执行AI审核等异步任务
+     *
+     * @return 异步任务执行器
+     */
+    @Bean(name = "lawyerTaskExecutor")
+    public Executor taskExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        // 核心线程数
+        executor.setCorePoolSize(5);
+        // 最大线程数
+        executor.setMaxPoolSize(10);
+        // 队列容量
+        executor.setQueueCapacity(100);
+        // 线程名前缀
+        executor.setThreadNamePrefix("async-task-lawyer-");
+        // 拒绝策略:调用者运行策略
+        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+        // 等待所有任务结束后再关闭线程池
+        executor.setWaitForTasksToCompleteOnShutdown(true);
+        // 等待时间
+        executor.setAwaitTerminationSeconds(60);
+        executor.initialize();
+        return executor;
+    }
+}
+

+ 386 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/BaseRedisService.java

@@ -0,0 +1,386 @@
+package shop.alien.lawyer.config;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.geo.*;
+import org.springframework.data.redis.connection.RedisGeoCommands;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.data.redis.core.script.RedisScript;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author ssk
+ * @version 1.0
+ * @date 2022/3/14 15:14
+ */
+@Component
+@RequiredArgsConstructor
+public class BaseRedisService {
+
+    @Autowired
+    private StringRedisTemplate stringRedisTemplate;
+
+    /**
+     * 添加List值, 向右
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setListRight(String key, String value) {
+        stringRedisTemplate.opsForList().rightPush(key, value);
+    }
+
+    /**
+     * 取出并删除列表中的所有元素(原子操作)
+     */
+    public List<String> popBatchFromList(String key) {
+        String luaScript =
+                "local elements = redis.call('LRANGE', KEYS[1], 0, -1) " +
+                        "redis.call('DEL', KEYS[1]) " +
+                        "return elements ";
+
+        RedisScript<List> script = RedisScript.of(luaScript, List.class);
+        return stringRedisTemplate.execute(script, Collections.singletonList(key));
+    }
+
+    private static final String SAVE_OR_OVERWRITE_SCRIPT =
+            "if redis.call('EXISTS', KEYS[1]) == 1 then " +
+                    "   redis.call('DEL', KEYS[1]) " +
+                    "end " +
+                    "return redis.call('RPUSH', KEYS[1], unpack(ARGV))";
+
+    /**
+     * 如果key不存在则存入,存在则覆盖
+     * @param key
+     * @param values
+     */
+    public void setSaveOrOverwriteScriptList(String key, List<String> values) {
+
+        // 创建DefaultRedisScript对象
+        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
+        redisScript.setScriptText(SAVE_OR_OVERWRITE_SCRIPT);
+        redisScript.setResultType(Long.class);
+
+        // 执行脚本
+        stringRedisTemplate.execute(
+                redisScript,
+                Collections.singletonList(key),
+                values.toArray()
+        );
+    }
+
+    /**
+     * 判断是否存在指定key
+     * @param key
+     * @return
+     */
+    public boolean hasKey(String key) {
+        Boolean exists = stringRedisTemplate.hasKey(key);
+        return exists != null && exists;
+    }
+
+    /**
+     * 从List中删除所有匹配的字符串
+     * @param key Redis key
+     * @param value 要删除的值
+     * @return 删除的元素数量
+     */
+    public Long removeAllOccurrences(String key, String value) {
+        return stringRedisTemplate.opsForList().remove(key, 0, value);
+    }
+
+    /**
+     * 添加List, 所有
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setList(String key, List<String> value) {
+        stringRedisTemplate.opsForList().rightPushAll(key, value);
+    }
+
+    /**
+     * 从List左边取值并删除
+     *
+     * @param key 键
+     * @return 删除的值
+     */
+    public String getListLeft(String key) {
+        return stringRedisTemplate.opsForList().leftPop(key);
+    }
+
+    /**
+     * 获取List所有
+     *
+     * @param key 键
+     * @return List<String>
+     */
+    public List<String> getList(String key) {
+        return stringRedisTemplate.opsForList().range(key, 0, -1);
+    }
+
+
+    /**
+     * 添加Set
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setSetList(String key, String value) {
+        stringRedisTemplate.opsForSet().add(key, value);
+    }
+
+    /**
+     * 获取Set
+     *
+     * @param key 键
+     * @return Set<String>
+     */
+    public Set<String> getSetList(String key) {
+        return stringRedisTemplate.opsForSet().members(key);
+    }
+
+    /**
+     * 添加String值, 不设置过期时间
+     *
+     * @param key   键
+     * @param value 值
+     */
+    public void setString(String key, String value) {
+        set(key, value, null);
+    }
+
+    /**
+     * 添加String值, 并设置过期时间
+     *
+     * @param key     键
+     * @param value   值
+     * @param timeOut 时长(秒)
+     */
+    public void setString(String key, String value, Long timeOut) {
+        set(key, value, timeOut);
+    }
+
+    /**
+     * 设置超时时间
+     *
+     * @param key     键
+     * @param timeOut 时长(秒)
+     */
+    public void setTimeOut(String key, Long timeOut) {
+        stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 传入object对象
+     *
+     * @param key     键
+     * @param value   值
+     * @param timeOut 时长(秒)
+     */
+    private void set(String key, Object value, Long timeOut) {
+        if (value != null) {
+            if (value instanceof String) {
+                String setValue = (String) value;
+                stringRedisTemplate.opsForValue().set(key, setValue);
+            }
+            //设置有效期
+            if (timeOut != null) {
+                stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
+            }
+        }
+    }
+
+    /**
+     * 使用key查找redis信息
+     *
+     * @param key 键
+     * @return
+     */
+    public String getString(String key) {
+        return stringRedisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 使用key删除redis信息
+     *
+     * @param key 键
+     */
+    public void delete(String key) {
+        stringRedisTemplate.delete(key);
+    }
+
+
+    /**
+     * 添加地理信息
+     *
+     * @param point
+     * @param content
+     * @param type
+     * @return
+     */
+    public Long inGeolocation(Point point, String content, String type) {
+        return stringRedisTemplate.opsForGeo().add(type, point, content);
+    }
+
+
+    /**
+     * 计算两个位置距离
+     *
+     * @param content
+     * @param contentII
+     * @param type
+     * @return
+     */
+    public Distance computeDistance(String content, String contentII, String type) {
+        return stringRedisTemplate.opsForGeo().distance(type, content, contentII, Metrics.KILOMETERS);
+
+    }
+
+    /**
+     * 获取成员经纬度
+     *
+     * @param contentArray
+     * @return
+     */
+    public List<Point> positions(String type, String... contentArray) {
+        return stringRedisTemplate.opsForGeo().position(type, contentArray);
+    }
+
+
+    /**
+     * 根据content查询附近商家
+     *
+     * @param content
+     * @param distance 搜索半径(单位:千米)
+     * @param count    获取几条
+     * @param type     name or 主键
+     * @return
+     */
+    public GeoResults<RedisGeoCommands.GeoLocation<String>> radius(String content, double distance, long count, String type) {
+
+        return stringRedisTemplate.opsForGeo()
+                .radius(type,
+                        content,
+                        new Distance(distance, Metrics.KILOMETERS),
+                        RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
+                                .includeDistance()   // 包含距离信息
+                                .includeCoordinates()// 包含坐标信息
+                                .sortAscending()     // 按距离升序
+                                .limit(count));   // 限制返回数量
+    }
+
+    /**
+     * 根据坐标查询附近商家
+     *
+     * @param point    经度 纬度
+     * @param distance 搜索半径(单位:千米)
+     * @param count    最大返回数量
+     * @return 附近商家地理信息结果集
+     */
+    public GeoResults<RedisGeoCommands.GeoLocation<String>> radius(Point point, double distance, long count, String type) {
+        return stringRedisTemplate.opsForGeo()
+                .radius(type,
+                        new Circle(point, new Distance(distance, Metrics.KILOMETERS)),
+                        RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
+                                .includeDistance()
+                                .includeCoordinates()
+                                .sortAscending()
+                                .limit(count));
+    }
+
+
+    /**
+     * 删除GEO类型中的成员
+     *
+     * @param type    GEO数据对应的键名(对应你代码中的type参数)
+     * @param members 要删除的成员名称
+     * @return 实际删除的成员数量
+     */
+    public Long removeGeoMember(String type, String... members) {
+        return stringRedisTemplate.opsForZSet().remove(type, (Object[]) members);
+    }
+
+    /**
+     * 获取分布式锁
+     * @param lockKey 锁的标识
+     * @return 锁的value值,用于释放锁时验证,null表示获取失败
+     */
+    public String lock(String lockKey) {
+        return lock(lockKey, 30000, 10000);
+    }
+
+    /**
+     * 获取分布式锁
+     * @param lockKey 锁的标识
+     * @param expireTime 锁的过期时间(毫秒)
+     * @param acquireTimeout 获取锁的超时时间(毫秒)
+     * @return 锁的value值,用于释放锁时验证,null表示获取失败
+     */
+    public String lock(String lockKey, long expireTime, long acquireTimeout) {
+        // 生成唯一标识,用于释放锁时验证
+        String identifier = UUID.randomUUID().toString();
+        // 完整的锁键
+        String lockKeyWithPrefix = "inventory:lock:" + lockKey;
+        // 超时时间戳
+        long endTime = System.currentTimeMillis() + acquireTimeout;
+
+        while (System.currentTimeMillis() < endTime) {
+            // 尝试获取锁:SET NX PX
+            Boolean success = stringRedisTemplate.opsForValue()
+                    .setIfAbsent(lockKeyWithPrefix, identifier, expireTime, TimeUnit.MILLISECONDS);
+
+            if (Boolean.TRUE.equals(success)) {
+                // 获取锁成功
+                return identifier;
+            }
+
+            try {
+                // 短暂休眠,避免频繁尝试
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                return null;
+            }
+        }
+
+        // 获取锁超时
+        return null;
+    }
+
+    /**
+     * 释放分布式锁
+     * @param lockKey 锁的标识
+     * @param identifier 锁的value值,用于验证
+     * @return 是否释放成功
+     */
+    public boolean unlock(String lockKey, String identifier) {
+        if (identifier == null) {
+            return false;
+        }
+
+        String lockKeyWithPrefix = "inventory:lock:" + lockKey;
+
+        // 使用Lua脚本确保释放锁的原子性
+        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
+                "return redis.call('del', KEYS[1]) " +
+                "else " +
+                "return 0 " +
+                "end";
+
+        RedisScript<Long> script = new DefaultRedisScript<>(luaScript, Long.class);
+        Long result = stringRedisTemplate.execute(script,
+                Collections.singletonList(lockKeyWithPrefix),
+                identifier);
+
+        return result != null && result > 0;
+    }
+
+}

+ 69 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/DruidConfig.java

@@ -0,0 +1,69 @@
+package shop.alien.lawyer.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.druid.support.http.StatViewServlet;
+import com.alibaba.druid.support.http.WebStatFilter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.boot.web.servlet.ServletRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.sql.DataSource;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Druid
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4 9:13
+ */
+@Configuration
+public class DruidConfig {
+
+    @ConfigurationProperties(prefix = "spring.datasource.druid")
+    @Bean
+    public DataSource druid() {
+        return new DruidDataSource();
+    }
+
+    //配置Druid的监控
+    //1、配置一个管理后台的Servlet
+    @Bean
+    public ServletRegistrationBean statViewServlet() {
+        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
+        Map<String, String> initParams = new HashMap<>();
+        initParams.put("loginUsername", "admin");
+        initParams.put("loginPassword", "alien1234");
+        initParams.put("allow", "");//默认就是允许所有访问
+        //initParams.put("deny","127.0.0.1");
+        bean.setInitParameters(initParams);
+        return bean;
+    }
+
+    //2、配置一个web监控的filter
+    @Bean
+    public FilterRegistrationBean webStatFilter() {
+        FilterRegistrationBean bean = new FilterRegistrationBean();
+        bean.setFilter(new WebStatFilter());
+        Map<String, String> initParams = new HashMap<>();
+        initParams.put("exclusions", "*.js,*.css,/druid/*");
+        bean.setInitParameters(initParams);
+        bean.setUrlPatterns(Arrays.asList("/*"));
+        return bean;
+    }
+
+//    @Bean
+//    public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {
+//        ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
+//        registrationBean.addInitParameter("allow", "");// IP白名单 (没有配置或者为空,则允许所有访问)
+//        registrationBean.addInitParameter("deny", "");// IP黑名单 (存在共同时,deny优先于allow)
+//        registrationBean.addInitParameter("loginUsername", "root");
+//        registrationBean.addInitParameter("loginPassword", "alien1234");
+//        registrationBean.addInitParameter("resetEnable", "false");
+//        return registrationBean;
+//    }
+}

+ 61 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/MyBatisFieldHandler.java

@@ -0,0 +1,61 @@
+package shop.alien.lawyer.config;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+import shop.alien.util.common.JwtUtil;
+
+import java.util.Date;
+import java.util.Objects;
+
+/**
+ * Mybatis日期填充
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/6 10:19
+ */
+@Component
+@Slf4j
+public class MyBatisFieldHandler implements MetaObjectHandler {
+
+    /**
+     * insert操作时填充方法
+     *
+     * @param metaObject 元对象
+     */
+    @Override
+    public void insertFill(MetaObject metaObject) {
+        log.info("=================================insertFill=========================================");
+        System.out.println(metaObject.getOriginalObject());
+        //字段为实体类名, 不是表字段名
+        this.setFieldValByName("createdTime", new Date(), metaObject);
+        this.setFieldValByName("updatedTime", new Date(), metaObject);
+        if (JwtUtil.hasToken()) {
+            this.setFieldValByName("createdUserId", Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"), metaObject);
+            this.setFieldValByName("updatedUserId", Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"), metaObject);
+        } else {
+            this.setFieldValByName("createdUserId", 0, metaObject);
+            this.setFieldValByName("updatedUserId", 0, metaObject);
+        }
+    }
+
+    /**
+     * update操作时填充方法
+     *
+     * @param metaObject 元对象
+     */
+    @Override
+    public void updateFill(MetaObject metaObject) {
+        log.info("=================================updateFill=========================================");
+        //字段为实体类名, 不是表字段名
+        this.setFieldValByName("updatedTime", new Date(), metaObject);
+        if (JwtUtil.hasToken()) {
+            this.setFieldValByName("updatedUserId", 0, metaObject);
+//            this.setFieldValByName("updatedUserId", Objects.requireNonNull(JwtUtil.getCurrentUserInfo()).getInteger("userId"), metaObject);
+        } else {
+            this.setFieldValByName("updatedUserId", 0, metaObject);
+        }
+    }
+}

+ 13 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/MyBatisPlusPageConfig.java

@@ -0,0 +1,13 @@
+package shop.alien.lawyer.config;
+
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MyBatisPlusPageConfig {
+    @Bean
+    public PaginationInterceptor paginationInterceptor() {
+        return new PaginationInterceptor();
+    }
+}

+ 33 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/RedisKeyExpirationConfig.java

@@ -0,0 +1,33 @@
+package shop.alien.lawyer.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+
+/**
+ * Redis Key過期監聽配置
+ * 
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Configuration
+public class RedisKeyExpirationConfig {
+
+    /**
+     * 配置Redis消息監聽容器,用於監聽key過期事件
+     * 
+     * @param connectionFactory Redis連接工廠
+     * @return Redis消息監聽容器
+     */
+    @Bean
+    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory) {
+        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
+        container.setConnectionFactory(connectionFactory);
+        log.info("Redis Key過期監聽容器初始化完成");
+        return container;
+    }
+}
+

+ 57 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/SwaggerConfig.java

@@ -0,0 +1,57 @@
+package shop.alien.lawyer.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+/**
+ * swagger配置类
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/4 9:17
+ */
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+
+    /**
+     * controller扫描
+     *
+     * @return controller扫描
+     */
+    @Bean
+    public Docket docket() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(apiInfo())
+                .groupName("爱丽恩2.0Api服务")
+                .select()
+                .apis(RequestHandlerSelectors.any())
+                .paths(PathSelectors.any())
+                .build();
+    }
+
+    /**
+     * Api标题信息
+     *
+     * @return api标题Api标题信息
+     */
+    public ApiInfo apiInfo() {
+        Contact contact = new Contact("爱丽恩", "https://www.alien.shop", "");
+        return new ApiInfoBuilder()
+                .title("爱丽恩2.0Api服务")
+                .description("爱丽恩2.0Api服务")
+                .license("爱丽恩")
+                .contact(contact)
+                .termsOfServiceUrl("https://www.alien.shop")
+                .version("1.0.0")
+                .build();
+    }
+}

+ 49 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/TokenInfoArgumentResolver.java

@@ -0,0 +1,49 @@
+package shop.alien.lawyer.config;
+
+import com.alibaba.fastjson.JSONObject;
+import org.springframework.core.MethodParameter;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+import shop.alien.entity.store.UserLoginInfo;
+import shop.alien.util.common.JwtUtil;
+import shop.alien.util.common.TokenInfo;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class TokenInfoArgumentResolver implements HandlerMethodArgumentResolver {
+
+    @Override
+    public boolean supportsParameter(MethodParameter parameter) {
+        return parameter.hasParameterAnnotation(TokenInfo.class);
+    }
+
+    @Override
+    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
+                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
+        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
+        String token = request.getHeader("Authorization");
+        if (token != null) {
+            try {
+                JSONObject tokenInfo = JwtUtil.getTokenInfo(token);
+//                DecodedJWT tokenInfo = JWTUtils.getTokenInfo(token);
+                UserLoginInfo userLoginInfo = new UserLoginInfo();
+                String phone = tokenInfo.getString("phone");
+                String userName = tokenInfo.getString("userName");
+                String userId = tokenInfo.getString("userId");
+                String type = tokenInfo.getString("userType");
+                userLoginInfo.setUserPhone(phone);
+                userLoginInfo.setUserId(Integer.parseInt(userId));
+                userLoginInfo.setUserName(userName);
+                userLoginInfo.setToken(token);
+                userLoginInfo.setType(type);
+                // 这里返回用户信息,可根据需求调整续加
+                return userLoginInfo;
+            } catch (Exception e) {
+                return null;
+            }
+        }
+        return null;
+    }
+}

+ 16 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/WebConfig.java

@@ -0,0 +1,16 @@
+package shop.alien.lawyer.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.List;
+
+@Configuration
+public class WebConfig implements WebMvcConfigurer {
+
+    @Override
+    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
+        resolvers.add(new TokenInfoArgumentResolver());
+    }
+}

+ 23 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/WebSocketConfig.java

@@ -0,0 +1,23 @@
+package shop.alien.lawyer.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+
+/**
+ * WebSocketConfig
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/2/29 14:40
+ */
+@Configuration
+@EnableWebSocket
+public class WebSocketConfig {
+
+    @Bean
+    public ServerEndpointExporter serverEndpointExporter() {
+        return new ServerEndpointExporter();
+    }
+}

+ 241 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/config/WebSocketProcess.java

@@ -0,0 +1,241 @@
+package shop.alien.lawyer.config;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
+import shop.alien.entity.store.LifeBlacklist;
+import shop.alien.entity.store.LifeMessage;
+import shop.alien.entity.store.vo.WebSocketVo;
+import shop.alien.mapper.LifeBlacklistMapper;
+import shop.alien.mapper.LifeMessageMapper;
+import shop.alien.util.common.safe.ImageReviewServiceEnum;
+import shop.alien.util.common.safe.TextModerationResultVO;
+import shop.alien.util.common.safe.TextModerationUtil;
+import shop.alien.util.common.safe.TextReviewServiceEnum;
+
+import javax.websocket.*;
+import javax.websocket.server.PathParam;
+import javax.websocket.server.ServerEndpoint;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/**
+ * @author ssk
+ * @version 1.0
+ * @date 2024/2/29 14:42
+ */
+@Slf4j
+@Component
+@ServerEndpoint(value = "/socket/{sendId}")
+public class WebSocketProcess implements ApplicationContextAware {
+
+    private static ApplicationContext applicationContext;
+    private static LifeBlacklistMapper lifeBlacklistMapper;
+    private static LifeMessageMapper lifeMessageMapper;
+    private static BaseRedisService baseRedisService;
+    private static TextModerationUtil textModerationUtil;
+    /*
+     * 持有每个webSocket对象,以key-value存储到线程安全ConcurrentHashMap,
+     */
+    private static final ConcurrentHashMap<String, WebSocketProcess> concurrentHashMap = new ConcurrentHashMap<>(12);
+
+    @Override
+    public void setApplicationContext(ApplicationContext context) {
+        WebSocketProcess.applicationContext = context;
+        WebSocketProcess.lifeBlacklistMapper = context.getBean(LifeBlacklistMapper.class);
+        WebSocketProcess.lifeMessageMapper = context.getBean(LifeMessageMapper.class);
+        WebSocketProcess.baseRedisService = context.getBean(BaseRedisService.class);
+        WebSocketProcess.textModerationUtil = context.getBean(TextModerationUtil.class);
+    }
+
+    /**
+     * 会话对象
+     **/
+    private Session session;
+
+    /**
+     * 接收到客户端消息时触发
+     */
+    @OnMessage
+    public void onMessage(@PathParam("sendId") String id, String message) throws Exception {
+        try {
+            // 解析消息
+            WebSocketVo webSocketVo = JSONObject.parseObject(message, WebSocketVo.class);
+
+            // 过滤心跳
+            if (null == webSocketVo || "heartbeat".equals(webSocketVo.getCategory())) return;
+
+            // 记录已读消息id
+            if ("receipt".equals(webSocketVo.getCategory())) {
+                baseRedisService.setListRight("readMessageIdKey", String.valueOf(webSocketVo.getMessageId()));
+                return;
+            }
+
+            // 检查消息合规性
+            if (!checkCompliance(webSocketVo)) {
+                webSocketVo.setType("7");
+                webSocketVo.setText("发送内容存在违规行为");
+                sendMessage(webSocketVo.getSenderId(), JSONObject.from(webSocketVo).toJSONString());
+                return;
+            }
+
+            log.info("webSocketVo----------------{}", JSONObject.from(webSocketVo).toJSONString());
+            log.info("concurrentHashMap----------{}", concurrentHashMap.keySet());
+
+            // 保存消息记录
+            LifeMessage lifeMessage = new LifeMessage();
+            lifeMessage.setSenderId(webSocketVo.getSenderId());
+            lifeMessage.setReceiverId(webSocketVo.getReceiverId());
+            lifeMessage.setContent(webSocketVo.getText());
+            lifeMessage.setType(webSocketVo.getType());
+            lifeMessage.setBusinessId(webSocketVo.getBusinessId());
+            // 查询自己是否在对方的黑名单中
+            if (baseRedisService.hasKey("blackList_" + webSocketVo.getSenderId())) {
+                List<String> blackList = baseRedisService.getList("blackList_" + webSocketVo.getSenderId());
+                if (blackList.contains(webSocketVo.getReceiverId())) {
+                    lifeMessage.setDeletePhoneId(webSocketVo.getReceiverId());
+                    lifeMessageMapper.insert(lifeMessage);
+                    // 发送消息
+                    webSocketVo.setMessageId(lifeMessage.getId());
+                    webSocketVo.setCategory("message");
+                    webSocketVo.setCreatedTime(lifeMessage.getCreatedTime());
+                    sendMessage(webSocketVo.getSenderId(), JSONObject.from(webSocketVo).toJSONString());
+                    return;
+                }
+            }
+
+            lifeMessageMapper.insert(lifeMessage);
+            // 发送消息
+            webSocketVo.setMessageId(lifeMessage.getId());
+            webSocketVo.setCategory("message");
+            webSocketVo.setCreatedTime(lifeMessage.getCreatedTime());
+            sendMessage(webSocketVo.getSenderId(), JSONObject.from(webSocketVo).toJSONString());
+            sendMessage(webSocketVo.getReceiverId(), JSONObject.from(webSocketVo).toJSONString());
+        } catch (Exception e) {
+            log.error("WebSocketProcess.onMessage()----Exception----Message={}", e.getMessage());
+        }
+    }
+
+    /**
+     * 发送消息到指定客户端
+     */
+    public void sendMessage(String id, String message) throws Exception {
+        try {
+            log.info("WebSocketProcess.sendMessage()--readySend----id={},message={}", id, message);
+            // 根据id,从map中获取存储的webSocket对象
+            WebSocketProcess webSocketProcess = concurrentHashMap.get(id);
+            if (!ObjectUtils.isEmpty(webSocketProcess)) {
+                // 当客户端是Open状态时,才能发送消息
+                if (webSocketProcess.session.isOpen()) {
+                    webSocketProcess.session.getBasicRemote().sendText(message);
+                    log.info("WebSocketProcess.sendMessage()---sendSuccess---id={},message={}", id, message);
+                } else {
+                    log.error("WebSocketProcess.sendMessage()---sendError----websocket session:{} is closed ", id);
+                }
+            } else {
+                log.error("WebSocketProcess.sendMessage()---sendError----websocket session:{} is not exit ", id);
+            }
+        } catch (Exception e) {
+            log.error("WebSocketProcess.sendMessage()---Exception----Message={}", e.getMessage());
+        }
+    }
+
+    /**
+     * 客户端创建连接时触发
+     * */
+    @OnOpen
+    public void onOpen(@PathParam("sendId") String id, Session session) {
+        try {
+            //每新建立一个连接,就把当前客户id为key,this为value存储到map中
+            this.session = session;
+            concurrentHashMap.put(id, this);
+            log.info("WebSocketProcess.onOpen() Open a websocket. id={}", id);
+
+            // 获取拉黑自己的用户信息
+            LambdaQueryWrapper<LifeBlacklist> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(LifeBlacklist::getBlockedPhoneId, id);
+            List<String> blackList = lifeBlacklistMapper.selectList(wrapper).stream().map(LifeBlacklist::getBlockerPhoneId).collect(Collectors.toList());
+            if (CollectionUtil.isNotEmpty(blackList)) baseRedisService.setSaveOrOverwriteScriptList("blackList_" + id, blackList);
+        } catch (Exception e) {
+            log.error("WebSocketProcess.onOpen()----Exception----Message={}", e.getMessage());
+        }
+    }
+
+
+    /**
+     * 客户端连接关闭时触发
+     **/
+    @OnClose
+    public void onClose(Session session, @PathParam("sendId") String id) {
+        try {
+            //客户端连接关闭时,移除map中存储的键值对
+            concurrentHashMap.remove(id);
+            log.info("WebSocketProcess.onClose() close a websocket, concurrentHashMap remove sessionId= {}", id);
+            if (baseRedisService.hasKey("blackList_" + id)) baseRedisService.delete("blackList_" + id);
+        } catch (Exception e) {
+            log.error("WebSocketProcess.onClose()----Exception----Message={}", e.getMessage());
+        }
+    }
+
+    /**
+     * 连接发生异常时候触发
+     */
+    @OnError
+    public void onError(@PathParam("sendId") String id, Throwable error) {
+        try {
+            log.error("WebSocketProcess.onError() Error,id={}, Msg=", id, error);
+        } catch (Exception e) {
+            log.error("WebSocketProcess.onError()----Exception----Message={}", e.getMessage());
+        }
+    }
+
+    /**
+     * 验证消息合规性
+     */
+    private boolean checkCompliance(WebSocketVo websocketVo) throws Exception {
+        try {
+            List<String> servicesList = Lists.newArrayList();
+            TextModerationResultVO textModerationResultVO = null;
+            if ("1".equals(websocketVo.getType())) {
+                servicesList.add(TextReviewServiceEnum.AD_COMPLIANCE_DETECTION_PRO.getService());
+                servicesList.add(TextReviewServiceEnum.CHAT_DETECTION_PRO.getService());
+                textModerationResultVO = textModerationUtil.invokeFunction(websocketVo.getText(), servicesList);
+
+            } else if ("2".equals(websocketVo.getType())) {
+                servicesList.add(ImageReviewServiceEnum.TONALITY_IMPROVE.getService());
+                textModerationResultVO = textModerationUtil.invokeFunction(websocketVo.getText(), servicesList);
+            }
+            return !(null != textModerationResultVO && "high".equals(textModerationResultVO.getRiskLevel()));
+        } catch (Exception e) {
+            log.error("WebSocketProcess.checkCompliance()----Exception----Message={}", e.getMessage());
+            return true;
+        }
+    }
+
+    /**
+     * 发送消息到所有客户端
+     */
+    public void sendAllMessage(String msg) throws Exception {
+        Set<Map.Entry<String, WebSocketProcess>> entries = concurrentHashMap.entrySet();
+        for (Map.Entry<String, WebSocketProcess> entry : entries) {
+            String cid = entry.getKey();
+            WebSocketProcess webSocketProcess = entry.getValue();
+            boolean sessionOpen = webSocketProcess.session.isOpen();
+            if (sessionOpen) {
+                webSocketProcess.session.getBasicRemote().sendText(msg);
+            } else {
+                log.info("cid={} is closed,ignore send text", cid);
+            }
+        }
+    }
+
+}

+ 288 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/AiAutoReview.java

@@ -0,0 +1,288 @@
+package shop.alien.lawyer.controller;
+
+import com.alibaba.fastjson2.JSONObject;
+import io.swagger.annotations.Api;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+
+import shop.alien.entity.store.CommentAppeal;
+import shop.alien.entity.store.LawyerUserViolation;
+import shop.alien.lawyer.service.AiUserAuditTaskService;
+import shop.alien.lawyer.service.CommentAppealService;
+import shop.alien.lawyer.service.LawyerUserViolationService;
+import shop.alien.lawyer.util.ai.AiAuthTokenUtil;
+
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Api(tags = {"ai申诉"})
+@CrossOrigin
+@RestController
+@RequestMapping("/aiAutoReview")
+@RequiredArgsConstructor
+@RefreshScope
+public class AiAutoReview {
+
+    private final AiAuthTokenUtil aiAuthTokenUtil;
+
+    private final RestTemplate restTemplate;
+
+    private final AiUserAuditTaskService aiUserAuditTaskService;
+
+    private final LawyerUserViolationService lawyerUserViolationService;
+
+    private final CommentAppealService commentAppealService;
+
+    /**
+     * 调用 AI 服务,获取申诉结果
+     *
+     * @return 申诉结果
+     */
+    @RequestMapping("/aiReview")
+    public ResponseEntity<String> aiReview(@RequestBody Map<String, Object> requestBody){
+        // 参数校验
+        if (requestBody == null || requestBody.isEmpty()) {
+            log.warn("用户申诉任务请求参数为空");
+            JSONObject errorData = new JSONObject();
+            errorData.put("code", 400);
+            errorData.put("message", "请求参数不能为空");
+            return ResponseEntity.badRequest().body(errorData.toJSONString());
+        }
+
+
+        // 同步获取访问令牌(不在异步中执行)
+        String accessToken = aiAuthTokenUtil.getAccessToken();
+        if (!StringUtils.hasText(accessToken)) {
+            log.error("调用AI申诉接口失败,获取accessToken失败");
+            JSONObject errorData = new JSONObject();
+            errorData.put("code", 500);
+            errorData.put("message", "获取访问令牌失败");
+            return ResponseEntity.status(500).body(errorData.toJSONString());
+        }
+
+        // 初始化请求体Map
+        HttpHeaders aiHeaders = new HttpHeaders();
+        aiHeaders.setContentType(MediaType.APPLICATION_JSON);
+        aiHeaders.set("Authorization", "Bearer " + accessToken);
+
+        CommentAppeal commentAppeal = new CommentAppeal();
+        //申诉理由
+        commentAppeal.setAppealReason(requestBody.get("appeal_reason").toString());
+        //评价id
+        commentAppeal.setCommentId(Integer.parseInt(requestBody.get("comment_id").toString()));
+        //评价内容
+        commentAppeal.setCommentInfo(requestBody.get("user_review").toString());
+        //订单id
+        commentAppeal.setOrderId(Integer.parseInt(requestBody.get("order_id").toString()));
+        //申诉图片
+        commentAppeal.setImgUrl(requestBody.get("appeal_images").toString());
+        //评价图片
+//        commentAppeal.setReviewImages(requestBody.get("review_images").toString());
+        //律师id
+        commentAppeal.setLawyerUserId(requestBody.get("lawyer_user_id").toString());
+        commentAppealService.submitAppeal(commentAppeal);
+        HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, aiHeaders);
+        try {
+            aiUserAuditTaskService.asyncAiReview(requestBody, accessToken);
+            log.info("用户申诉任务已提交异步处理,请求参数:{}", requestBody);
+        } catch (Exception e) {
+            log.error("调用AI申诉接口 接口异常------", e);
+        }
+        // 立即返回成功响应
+        JSONObject responseData = new JSONObject();
+        responseData.put("code", 200);
+        responseData.put("message", "用户申诉任务已提交,正在后台处理");
+        responseData.put("data", new JSONObject());
+
+        return ResponseEntity.ok(responseData.toJSONString());
+    }
+
+    /**
+     * 用户申诉任务接口(异步处理)
+     * <p>
+     * 接口立即返回,AI服务调用在后台异步执行
+     * </p>
+     *
+     * @param requestBody 请求参数
+     * @return 立即返回成功响应
+     */
+    @RequestMapping("/userAuditTask")
+    public ResponseEntity<String> userAuditTask(@RequestBody Map<String, Object> requestBody) {
+        log.info("接收用户申诉任务请求,请求参数:{}", requestBody);
+
+        // 参数校验
+        if (requestBody == null || requestBody.isEmpty()) {
+            log.warn("用户申诉任务请求参数为空");
+            JSONObject errorData = new JSONObject();
+            errorData.put("code", 400);
+            errorData.put("message", "请求参数不能为空");
+            return ResponseEntity.badRequest().body(errorData.toJSONString());
+        }
+
+        // 构建 LawyerUserViolation 对象并调用 userReporting 服务
+        try {
+            LawyerUserViolation lawyerUserViolation = buildLawyerUserViolation(requestBody);
+            if (lawyerUserViolation == null) {
+                log.warn("构建 LawyerUserViolation 对象失败,请求参数:{}", requestBody);
+                JSONObject errorData = new JSONObject();
+                errorData.put("code", 400);
+                errorData.put("message", "订单号不能为空");
+                return ResponseEntity.badRequest().body(errorData.toJSONString());
+            }
+
+            // 调用 userReporting 服务(同步执行)
+            log.info("开始调用 userReporting 服务,订单号:{}", lawyerUserViolation.getOrderNumber());
+            int result = lawyerUserViolationService.userReporting(lawyerUserViolation);
+            if (result <= 0) {
+                log.warn("userReporting 服务调用失败,订单号:{}", lawyerUserViolation.getOrderNumber());
+                JSONObject errorData = new JSONObject();
+                errorData.put("code", 500);
+                errorData.put("message", "用户举报处理失败");
+                return ResponseEntity.status(500).body(errorData.toJSONString());
+            }
+            log.info("userReporting 服务调用成功,订单号:{},返回结果:{}", lawyerUserViolation.getOrderNumber(), result);
+        } catch (RuntimeException e) {
+            log.error("调用 userReporting 服务异常,请求参数:{},异常信息:{}", requestBody, e.getMessage(), e);
+            JSONObject errorData = new JSONObject();
+            errorData.put("code", 500);
+            errorData.put("message", "用户举报处理失败:" + e.getMessage());
+            return ResponseEntity.status(500).body(errorData.toJSONString());
+        } catch (Exception e) {
+            log.error("调用 userReporting 服务异常,请求参数:{},异常信息:{}", requestBody, e.getMessage(), e);
+            JSONObject errorData = new JSONObject();
+            errorData.put("code", 500);
+            errorData.put("message", "用户举报处理失败:" + e.getMessage());
+            return ResponseEntity.status(500).body(errorData.toJSONString());
+        }
+
+        // 同步获取访问令牌(不在异步中执行)
+        String accessToken = aiAuthTokenUtil.getAccessToken();
+        if (!StringUtils.hasText(accessToken)) {
+            log.error("调用AI用户申诉接口失败,获取accessToken失败");
+            JSONObject errorData = new JSONObject();
+            errorData.put("code", 500);
+            errorData.put("message", "获取访问令牌失败");
+            return ResponseEntity.status(500).body(errorData.toJSONString());
+        }
+
+        // 异步调用AI服务(传递已获取的访问令牌)
+        try {
+            aiUserAuditTaskService.asyncCallUserAuditTask(requestBody, accessToken);
+            log.info("用户申诉任务已提交异步处理,请求参数:{}", requestBody);
+        } catch (Exception e) {
+            log.error("提交用户申诉任务异步处理失败,请求参数:{},异常信息:{}", requestBody, e.getMessage(), e);
+        }
+
+        // 立即返回成功响应
+        JSONObject responseData = new JSONObject();
+        responseData.put("code", 200);
+        responseData.put("message", "用户申诉任务已提交,正在后台处理");
+        responseData.put("data", new JSONObject());
+
+        return ResponseEntity.ok(responseData.toJSONString());
+    }
+
+    /**
+     * 从请求参数构建 LawyerUserViolation 对象
+     *
+     * @param requestBody 请求参数
+     * @return LawyerUserViolation 对象,如果订单号为空则返回 null
+     */
+    private LawyerUserViolation buildLawyerUserViolation(Map<String, Object> requestBody) {
+        if (requestBody == null) {
+            return null;
+        }
+
+        // 获取订单号(必填)
+        Object orderNumberObj = requestBody.get("order_number");
+        if (orderNumberObj == null) {
+            log.warn("请求参数中缺少订单号");
+            return null;
+        }
+        String orderNumber = orderNumberObj.toString();
+
+        LawyerUserViolation lawyerUserViolation = new LawyerUserViolation();
+        lawyerUserViolation.setOrderNumber(orderNumber);
+
+        // 根据 user_demand 内容设置 violationReason
+        Object userDemandObj = requestBody.get("user_demand");
+        if (userDemandObj != null) {
+            String userDemand = userDemandObj.toString();
+            String violationReason = determineViolationReason(userDemand);
+            lawyerUserViolation.setViolationReason(violationReason);
+            if(violationReason.equals("4")){
+                lawyerUserViolation.setOtherReasonContent(userDemand);
+            }
+            log.info("根据 user_demand 设置 violationReason,user_demand:{},violationReason:{}", userDemand, violationReason);
+        }
+
+        // 设置举报凭证图片(report_images)
+        Object reportImagesObj = requestBody.get("report_images");
+        if (reportImagesObj != null) {
+            String reportEvidenceImg;
+            if (reportImagesObj instanceof List) {
+                // 如果是列表,转换为逗号分隔的字符串
+                @SuppressWarnings("unchecked")
+                List<String> imageList = (List<String>) reportImagesObj;
+                reportEvidenceImg = String.join(",", imageList);
+            } else {
+                reportEvidenceImg = reportImagesObj.toString();
+            }
+            lawyerUserViolation.setReportEvidenceImg(reportEvidenceImg);
+        }
+
+        // 设置举报内容分类(默认为用户举报律师)
+        lawyerUserViolation.setReportContextType("6");
+        lawyerUserViolation.setReportedUserType("3");
+        lawyerUserViolation.setReportingUserType("2");
+
+        log.info("构建 LawyerUserViolation 对象,订单号:{},举报原因:{}", orderNumber, lawyerUserViolation.getViolationReason());
+        return lawyerUserViolation;
+    }
+
+    /**
+     * 根据 user_demand 内容确定 violationReason
+     * <p>
+     * 规则:
+     * - 包含"服务态度差" -> violationReason = "1"
+     * - 包含"专业能力差" -> violationReason = "2"
+     * - 包含"响应时间超过24小时" -> violationReason = "3"
+     * - 其他情况 -> 返回原始 user_demand 内容
+     * </p>
+     *
+     * @param userDemand 用户需求内容
+     * @return violationReason 值
+     */
+    private String determineViolationReason(String userDemand) {
+        if (userDemand == null || userDemand.trim().isEmpty()) {
+            return null;
+        }
+
+        String demand = userDemand.trim();
+
+        // 检查是否包含"服务态度差"
+        if (demand.contains("服务态度差")) {
+            return "1";
+        } else if (demand.contains("专业能力差")) {
+            // 检查是否包含"专业能力差"
+            return "2";
+        } else if (demand.contains("响应时间超过24小时")) {
+            // 检查是否包含"响应时间超过24小时"
+            return "3";
+        } else {
+            return "4";
+        }
+    }
+}

+ 105 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/AliController.java

@@ -0,0 +1,105 @@
+package shop.alien.lawyer.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.lawyer.util.AliSms;
+import shop.alien.lawyer.util.ali.AliApi;
+
+/**
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/11 17:34
+ */
+@Slf4j
+@Api(tags = {"二期-阿里接口"})
+@ApiSort(9)
+@CrossOrigin
+@RestController
+@RequestMapping("/ali")
+@RequiredArgsConstructor
+public class AliController {
+
+    private final AliSms aliSmsConfig;
+
+    private final AliApi aliPayConfig;
+
+    private final AliApi aliApi;
+
+    @ApiOperation("发送短信")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({@ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true)})
+    @GetMapping("/sendSms")
+    public R sendSms(String phone) {
+        Integer code = aliSmsConfig.sendSms(phone);
+        log.info("AliController.sendSms?phone={}&code={}", phone, code);
+        if (code != null) {
+            return R.data("");
+        }
+        return R.fail("短信发送失败");
+    }
+
+
+
+
+    @ApiOperation("支付")
+    @ApiOperationSupport(order = 12)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "price", value = "金额", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "subject", value = "类目", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/zhifu")
+    public R<JSONObject> zhifu(@RequestParam(value = "price") String price, @RequestParam(value = "subject") String subject) {
+        return R.data(aliApi.promotionPackagePay(price, subject));
+    }
+
+    @ApiOperation("退款")
+    @ApiOperationSupport(order = 13)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "outTradeNo", value = "订单号", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "refundAmount", value = "退款金额", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "refundReason", value = "退款原因", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "partialRefundCode", value = "部分退款编码", dataType = "String", paramType = "query", required = false)
+    })
+    @GetMapping("/processRefund")
+    public String processRefund(@RequestParam(value = "outTradeNo") String outTradeNo,
+                                @RequestParam(value = "refundAmount") String refundAmount,
+                                @RequestParam(value = "refundReason") String refundReason,
+                                @RequestParam(value = "partialRefundCode") String partialRefundCode) {
+        return aliApi.processRefund(outTradeNo, refundAmount, refundReason, partialRefundCode);
+    }
+
+
+    @ApiOperation("律师端or用户退款")
+    @ApiOperationSupport(order = 14)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "outTradeNo", value = "订单号", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "refundAmount", value = "退款金额", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "refundReason", value = "退款原因", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "partialRefundCode", value = "部分退款编码", dataType = "String", paramType = "query", required = false)
+    })
+    @GetMapping("/processLawRefund")
+    public R<String> processLawRefund(@RequestParam(value = "outTradeNo") String outTradeNo,
+                                @RequestParam(value = "refundAmount") String refundAmount,
+                                @RequestParam(value = "refundReason") String refundReason,
+                                @RequestParam(value = "partialRefundCode") String partialRefundCode) {
+        return R.data(aliApi.processRefund(outTradeNo, refundAmount, refundReason, partialRefundCode));
+    }
+
+
+
+    @ApiOperation("根据订单号查询订单支付状态")
+    @ApiOperationSupport(order = 15)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "outTradeNo", value = "订单号", dataType = "String", paramType = "query", required = true),
+    })
+    @GetMapping("/queryPayment")
+    public R<String> queryPayment(String outTradeNo)  {
+
+        return aliApi.queryPayment(outTradeNo);
+    }
+
+}

+ 130 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/CommentAppealController.java

@@ -0,0 +1,130 @@
+package shop.alien.lawyer.controller;
+
+import com.alibaba.nacos.client.naming.utils.RandomUtils;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.CommentAppeal;
+import shop.alien.entity.store.dto.AuditAppealRequestDto;
+import shop.alien.entity.store.vo.CommentAppealVo;
+import shop.alien.lawyer.service.CommentAppealService;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 评论申诉表 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"评论申诉管理"})
+@ApiSort(20)
+@CrossOrigin
+@RestController
+@RequestMapping("/commentAppeal")
+@RequiredArgsConstructor
+public class CommentAppealController {
+
+    private final CommentAppealService commentAppealService;
+
+    @ApiOperation(value = "提交申诉", notes = "用户提交评论申诉")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "commentAppeal", value = "申诉信息", dataType = "CommentAppeal", paramType = "body", required = true)
+    })
+    @PostMapping("/submit")
+    public R<CommentAppeal> submitAppeal(@RequestBody CommentAppeal commentAppeal) {
+        log.info("CommentAppealController.submitAppeal?commentAppeal={}", commentAppeal);
+        //申诉时间
+//        commentAppeal.setAppealTime(new Date());
+//        commentAppeal.setAppealNumber(generateOrderNumber());
+        return commentAppealService.submitAppeal(commentAppeal);
+    }
+
+    @ApiOperation(value = "审核申诉", notes = "管理员审核申诉,状态:1-已通过,2-已驳回。审核通过时会删除评价及回复,并发送通知给相关用户")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "auditAppealRequestDto", value = "审核申诉请求", dataType = "AuditAppealRequestDto", paramType = "body", required = true)
+    })
+    @PostMapping("/audit")
+    public R<Boolean> auditAppeal(@RequestBody AuditAppealRequestDto auditAppealRequestDto) {
+        log.info("CommentAppealController.auditAppeal?auditAppealRequestDto={}", auditAppealRequestDto);
+        return commentAppealService.auditAppeal(auditAppealRequestDto.getId(), auditAppealRequestDto.getStatus(), auditAppealRequestDto.getReviewReasons());
+    }
+
+    @ApiOperation(value = "中台申诉列表", notes = "分页查询申诉列表,支持多条件筛选,包含订单编号、用户信息、律师信息等")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "申诉状态:0-待处理,1-已通过,2-已驳回", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "orderNumber", value = "评价单号(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "userName", value = "用户姓名(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "userPhone", value = "用户电话(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerName", value = "律师姓名(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerPhone", value = "律师电话(模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "startTime", value = "开始时间(格式:yyyy-MM-dd HH:mm:ss)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "endTime", value = "结束时间(格式:yyyy-MM-dd HH:mm:ss)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<CommentAppealVo>> getAppealPage(
+            @RequestParam int pageNum,
+            @RequestParam int pageSize,
+            @RequestParam(required = false) Integer status,
+            @RequestParam(required = false) String orderNumber,
+            @RequestParam(required = false) String userName,
+            @RequestParam(required = false) String userPhone,
+            @RequestParam(required = false) String lawyerName,
+            @RequestParam(required = false) String lawyerPhone,
+            @RequestParam(required = false) String startTime,
+            @RequestParam(required = false) String endTime) {
+        log.info("CommentAppealController.getAppealPage?pageNum={}, pageSize={}, status={}, orderNumber={}, userName={}, userPhone={}, lawyerName={}, lawyerPhone={}, startTime={}, endTime={}",
+                pageNum, pageSize, status, orderNumber, userName, userPhone, lawyerName, lawyerPhone, startTime, endTime);
+        return commentAppealService.getAppealPage(pageNum, pageSize, status, orderNumber, userName, userPhone, lawyerName, lawyerPhone, startTime, endTime);
+    }
+
+    @ApiOperation(value = "获取申诉详情", notes = "根据申诉ID获取申诉详情,包含评价和用户信息")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "申诉ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getDetail")
+    public R<CommentAppealVo> getAppealDetail(@RequestParam("id") Integer id) {
+        log.info("CommentAppealController.getAppealDetail?id={}", id);
+        return commentAppealService.getAppealDetail(id);
+    }
+
+    @ApiOperation(value = "申诉历史列表", notes = "分页查询申诉历史列表,支持按状态和评论ID筛选,包含评价和用户信息")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "申诉状态:0-待处理,1-已通过,2-已驳回", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getAppealHistory")
+    public R<List<CommentAppealVo>> getAppealHistory(
+            @RequestParam int pageNum,
+            @RequestParam int pageSize,
+            @RequestParam(required = false) Integer status,
+            @RequestParam(required = false) Integer lawyerUserId) {
+        log.info("CommentAppealController.getAppealHistory?pageNum={}, pageSize={}, status={}, lawyerUserId={}",
+                pageNum, pageSize, status, lawyerUserId);
+        List<CommentAppealVo> appealList = commentAppealService.getAppealHistory(pageNum, pageSize, status, lawyerUserId);
+        return R.data(appealList, "查询成功");
+    }
+
+//    private String generateOrderNumber() {
+//        String dateStr = new SimpleDateFormat("yyyyMMdd").format(new Date());
+//        String randomStr = String.format("%05d", RandomUtils.nextInt(100000));
+//        return "LAW" + dateStr + randomStr;
+//    }
+}
+
+

+ 66 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/FileUploadController.java

@@ -0,0 +1,66 @@
+package shop.alien.lawyer.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiOperationSupport;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.MultipartRequest;
+import shop.alien.entity.result.R;
+import shop.alien.lawyer.util.FileUploadUtil;
+
+import java.util.List;
+
+/**
+ * 二期-文件上传
+ *
+ * @author ssk
+ * @version 1.0
+ * @date 2024/12/6 15:30
+ */
+@Slf4j
+@Api(tags = {"二期-文件上传"})
+@ApiSort(6)
+@CrossOrigin
+@RestController
+@RequestMapping("/file")
+@RequiredArgsConstructor
+public class FileUploadController {
+
+    private final FileUploadUtil fileUpload;
+
+    @ApiOperation("单个文件上传(图片或视频,返回路径)")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/upload")
+    public R<String> upload(@RequestParam("file") MultipartFile file) {
+        log.info("StoreFileUploadController.upload fileName:{}", file.getOriginalFilename());
+        return R.data(fileUpload.uploadOneFile(file));
+    }
+
+    @ApiOperation("多个文件上传(图片或视频,视频会截取第一秒图片,返回路径)")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/uploadMore")
+    public R<List<String>> uploadMore(MultipartRequest multipartRequest) {
+        log.info("StoreFileUploadController.upload uploadMore:{}", multipartRequest);
+        return R.data(fileUpload.uploadMoreFile(multipartRequest));
+    }
+
+    @ApiOperation(value = "图片上传(其实也可以传视频,不会截图), 返回图片id", notes = "文件格式: file_0,file_1, list格式: [{\"storeId\":1,\"imgType\":3,\"imgDescription\":\"菜品1\",\"imgSort\":1},{\"storeId\":1,\"imgType\":3,\"imgDescription\":\"菜品2\",\"imgSort\":2}]")
+    @ApiOperationSupport(order = 3)
+    @PostMapping("/uploadImg")
+    public R<List<Integer>> uploadImg(MultipartRequest multipartRequest, @RequestParam("list") String list) {
+        return R.data(fileUpload.uploadImg(multipartRequest, list));
+    }
+
+    @ApiOperation("上传app(格式包含apk,ipa,wgt,返回路径)")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/uploadApp")
+    public R<String> uploadApp(@RequestParam("file") MultipartFile file) {
+        log.info("StoreFileUploadController.uploadApp fileName:{}", file.getOriginalFilename());
+        return R.data(fileUpload.uploadApp(file));
+    }
+
+}

+ 217 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawFirmController.java

@@ -0,0 +1,217 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawFirm;
+import shop.alien.entity.store.vo.LawFirmPaymentVO;
+import shop.alien.lawyer.service.LawFirmService;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * 律所表 前端控制器
+ * qxy
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-律所管理"})
+@ApiSort(20)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/firm")
+@RequiredArgsConstructor
+public class LawFirmController {
+
+    private final LawFirmService lawFirmService;
+
+    @ApiOperation(value = "新增律所", notes = "新增律所信息,支持同时添加收款账号列表。如果统一社会信用代码(creditCode)已存在,将自动执行更新操作。收款账号(payment_account)必须唯一,不能重复添加。")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawFirm", value = "律所信息对象,包含paymentList(收款账号列表)。如果creditCode已存在,将更新现有记录", dataType = "LawFirm", paramType = "body", required = true)
+    })
+    @PostMapping("/addLawFirm")
+    public R<LawFirm> addLawFirm(@RequestBody LawFirm lawFirm) {
+        log.info("LawFirmController.addLawFirm?lawFirm={}", lawFirm);
+        return lawFirmService.addLawFirm(lawFirm);
+    }
+
+    @ApiOperation(value = "编辑律所", notes = "编辑律所信息,支持同时新增、修改、删除收款账号。收款账号(payment_account)必须唯一,不能重复添加。")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawFirm", value = "律所信息对象,必须包含id。paymentList中:有id的为更新,无id的为新增,不在列表中的将被删除", dataType = "LawFirm", paramType = "body", required = true)
+    })
+    @PostMapping("/editLawFirm")
+    public R<LawFirm> editLawFirm(@RequestBody LawFirm lawFirm) {
+        log.info("LawFirmController.editLawFirm?lawFirm={}", lawFirm);
+        return lawFirmService.editLawFirm(lawFirm);
+    }
+
+    @ApiOperation(value = "删除律所", notes = "根据律所子表id删除收款账号。删除后如果该律所没有其他收款账号,将自动删除律所主表")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "律所子表ID(收款账号ID)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @DeleteMapping("/deleteLawFirm")
+    public R<Boolean> deleteLawFirm(@RequestParam(value = "id") Integer id) {
+        log.info("LawFirmController.deleteLawFirm?paymentId={}", id);
+        return lawFirmService.deleteLawFirm(id);
+    }
+
+//    @ApiOperation(value = "保存或更新律所", notes = "根据是否有id判断新增或更新。注意:此接口不支持收款账号的关联操作,建议使用addLawFirm或editLawFirm接口")
+//    @ApiOperationSupport(order = 4)
+//    @PostMapping("/saveOrUpdate")
+//    public R<LawFirm> saveOrUpdate(@RequestBody LawFirm lawFirm) {
+//        log.info("LawFirmController.saveOrUpdate?lawFirm={}", lawFirm);
+//        boolean result = lawFirmService.saveOrUpdate(lawFirm);
+//        if (result) {
+//            return R.data(lawFirm);
+//        }
+//        return R.fail("操作失败");
+//    }
+
+    @ApiOperation(value = "通用列表查询", notes = "查询律所列表,包含收款账号列表(paymentList)")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "firmName", value = "律所名称(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "creditCode", value = "统一社会信用代码", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "律所状态, 0:禁用, 1:启用", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "certificationStatus", value = "认证状态, 0:未认证, 1:认证中, 2:已认证, 3:认证失败", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "province", value = "所属省份", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "city", value = "所属城市", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<List<LawFirm>> getList(@ModelAttribute LawFirm lawFirm) {
+        log.info("LawFirmController.getList?lawFirm={}", lawFirm);
+        List<LawFirm> list = QueryBuilder.of(lawFirm)
+                .likeFields("firmName", "creditCode")  // 律所名称和统一社会信用代码支持模糊查询
+                .build()
+                .list(lawFirmService);
+        // 批量填充收款账号列表
+        lawFirmService.fillPaymentList(list);
+        return R.data(list);
+    }
+
+    @ApiOperation(value = "通用分页查询", notes = "分页查询律所列表,包含收款账号列表(paymentList)")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "firmName", value = "律所名称(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "creditCode", value = "统一社会信用代码", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "律所状态, 0:禁用, 1:启用", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<LawFirm>> getPage(@ModelAttribute LawFirm lawFirm,
+                                     @RequestParam(defaultValue = "1") int page,
+                                     @RequestParam(defaultValue = "10") int size) {
+        log.info("LawFirmController.getPage?lawFirm={},page={},size={}", lawFirm, page, size);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        IPage<LawFirm> pageResult = QueryBuilder.of(lawFirm)
+                .likeFields("firmName", "creditCode")  // 律所名称和统一社会信用代码支持模糊查询
+                .page(pageNum, pageSize)
+                .build()
+                .page(lawFirmService);
+        // 批量填充收款账号列表
+        if (pageResult != null && pageResult.getRecords() != null && !pageResult.getRecords().isEmpty()) {
+            lawFirmService.fillPaymentList(pageResult.getRecords());
+        }
+        return R.data(pageResult);
+    }
+
+    @ApiOperation(value = "获取律所详情", notes = "获取律所详细信息,包含收款账号列表(paymentList)")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "律所ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getDetail")
+    public R<LawFirm> getLawFirmDetail(@RequestParam(value = "id") Integer id) {
+        log.info("LawFirmController.getLawFirmDetail?id={}", id);
+        return lawFirmService.getLawFirmDetail(id);
+    }
+
+    @ApiOperation(value = "导出律所数据到Excel", notes = "导出律所数据到Excel文件,包含收款账号信息。支持按律所ID过滤和分页导出")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "firmId", value = "律所ID(可选,用于过滤特定律所)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "page", value = "页数(可选,默认1,如果pageSize为空则导出全部)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(可选,默认10,如果为空则导出全部数据)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/export")
+    public void exportLawFirm(HttpServletResponse response,
+                              @RequestParam(required = false) Integer firmId,
+                              @RequestParam(required = false) Integer page,
+                              @RequestParam(required = false) Integer size) throws IOException {
+        log.info("LawFirmController.exportLawFirm?firmId={},page={},size={}", firmId, page, size);
+        lawFirmService.exportLawFirm(response, firmId, page, size);
+    }
+
+    @ApiOperation(value = "从Excel导入律所数据", notes = "从Excel文件导入律所数据,支持批量导入。导入规则:1. 按统一社会信用代码(creditCode)分组,相同信用代码的数据会自动合并;2. 相同信用代码的多行数据会合并收款账号;3. 如果统一社会信用代码已存在,将更新现有记录并合并收款账号;4. 收款账号必须全局唯一,已存在的收款账号将跳过添加")
+    @ApiOperationSupport(order = 9)
+    @PostMapping("/import")
+    public R<String> importLawFirm(MultipartFile file) {
+        log.info("LawFirmController.importLawFirm fileName={}", file != null ? file.getOriginalFilename() : "null");
+        return lawFirmService.importLawFirm(file);
+    }
+
+    @ApiOperation(value = "下载律所Excel导入模板", notes = "下载律所数据导入的Excel模板文件")
+    @ApiOperationSupport(order = 10)
+    @GetMapping("/downloadTemplate")
+    public void downloadTemplate(HttpServletResponse response) throws IOException {
+        log.info("LawFirmController.downloadTemplate");
+        lawFirmService.downloadTemplate(response);
+    }
+
+    @ApiOperation(value = "分页查询律所子表关联律所表", notes = "以律所子表(law_firm_payment)为主表,关联律所表(law_firm)进行分页查询")
+    @ApiOperationSupport(order = 11)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "paymentAccount", value = "收款账号(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "firmName", value = "律所名称(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "creditCode", value = "统一社会信用代码", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "律所状态, 0:禁用, 1:启用", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "certificationStatus", value = "认证状态, 0:未认证, 1:认证中, 2:已认证, 3:认证失败", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "directorName", value = "负责人姓名(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTimeStart", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTimeEnd", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPaymentPageWithFirm")
+    public R<IPage<LawFirmPaymentVO>> getPaymentPageWithFirm(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int size,
+            @RequestParam(required = false) Integer firmId,
+            @RequestParam(required = false) String paymentAccount,
+            @RequestParam(required = false) String firmName,
+            @RequestParam(required = false) String creditCode,
+            @RequestParam(required = false) Integer status,
+            @RequestParam(required = false) Integer certificationStatus,
+            @RequestParam(required = false) String directorName,
+            @RequestParam(required = false) String createdTimeStart,
+            @RequestParam(required = false) String createdTimeEnd) {
+        log.info("LawFirmController.getPaymentPageWithFirm?page={},size={},firmId={},paymentAccount={},firmName={},creditCode={},status={},certificationStatus={},directorName={},createdTimeStart={},createdTimeEnd={}",
+                page, size, firmId, paymentAccount, firmName, creditCode, status, certificationStatus, directorName, createdTimeStart, createdTimeEnd);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        return lawFirmService.getPaymentPageWithFirm(pageNum, pageSize, firmId, paymentAccount, firmName, creditCode, status, certificationStatus, directorName, createdTimeStart, createdTimeEnd);
+    }
+}
+
+

+ 202 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawFirmReconciliationController.java

@@ -0,0 +1,202 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.vo.LawFirmListVO;
+import shop.alien.entity.store.vo.LawFirmReconciliationVO;
+import shop.alien.entity.store.vo.LawyerListVO;
+import shop.alien.lawyer.service.LawFirmReconciliationService;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.Date;
+
+/**
+ * 律所对账结算控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-律所对账结算"})
+@ApiSort(21)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/firm/reconciliation")
+@RequiredArgsConstructor
+public class LawFirmReconciliationController {
+
+    private final LawFirmReconciliationService lawFirmReconciliationService;
+
+    @ApiOperation("获取所有律所的对账")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "firmId", value = "律所ID(可选,不传则查询所有律所)", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "firmName", value = "律所名称(可选,模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "startDate", value = "开始日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "endDate", value = "结束日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getOverview")
+    public R<LawFirmReconciliationVO> getLawFirmReconciliation(
+            @RequestParam(value = "firmId", required = false) Integer firmId,
+            @RequestParam(value = "firmName", required = false) String firmName,
+            @RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
+            @RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate) {
+        log.info("LawFirmReconciliationController.getLawFirmReconciliation?firmId={},firmName={},startDate={},endDate={}",
+                firmId, firmName, startDate, endDate);
+        return lawFirmReconciliationService.getLawFirmReconciliation(firmId, firmName, startDate, endDate);
+    }
+
+    @ApiOperation("获取所有律所的对账统计列表(分页)")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "firmName", value = "律所名称(可选,模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "startDate", value = "开始日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "endDate", value = "结束日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "pageNum", value = "页码(默认1)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "页容(默认10)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getAllLawFirmList")
+    public R<IPage<LawFirmListVO>> getAllLawFirmReconciliationList(
+            @RequestParam(value = "firmName", required = false) String firmName,
+            @RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
+            @RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate,
+            @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
+            @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
+        log.info("LawFirmReconciliationController.getAllLawFirmReconciliationList?firmName={},startDate={},endDate={},pageNum={},pageSize={}",
+                firmName, startDate, endDate, pageNum, pageSize);
+        return lawFirmReconciliationService.getAllLawFirmReconciliationList(firmName, startDate, endDate, pageNum, pageSize);
+    }
+
+    @ApiOperation("获取律所下所有律师的对账统计列表(分页)")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "firmId", value = "律所ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "startDate", value = "开始日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "endDate", value = "结束日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "pageNum", value = "页码(默认1)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "页容(默认10)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getLawyerList")
+    public R<IPage<LawyerListVO>> getLawyerReconciliationList(
+            @RequestParam(value = "firmId") Integer firmId,
+            @RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
+            @RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate,
+            @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
+            @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
+        log.info("LawFirmReconciliationController.getLawyerReconciliationList?firmId={},startDate={},endDate={},pageNum={},pageSize={}",
+                firmId, startDate, endDate, pageNum, pageSize);
+        return lawFirmReconciliationService.getLawyerReconciliationList(firmId, startDate, endDate, pageNum, pageSize);
+    }
+
+    @ApiOperation("获取律所下所有律师的对账统计列表(分页,支持律师名称模糊查询)")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "firmId", value = "律所ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "lawyerName", value = "律师名称(可选,模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "startDate", value = "开始日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "endDate", value = "结束日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "pageNum", value = "页码(默认1)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "页容(默认10)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getLawyerListWithName")
+    public R<IPage<LawyerListVO>> getLawyerReconciliationListWithName(
+            @RequestParam(value = "firmId") Integer firmId,
+            @RequestParam(value = "lawyerName", required = false) String lawyerName,
+            @RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
+            @RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate,
+            @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
+            @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
+        log.info("LawFirmReconciliationController.getLawyerReconciliationListWithName?firmId={},lawyerName={},startDate={},endDate={},pageNum={},pageSize={}",
+                firmId, lawyerName, startDate, endDate, pageNum, pageSize);
+        return lawFirmReconciliationService.getLawyerReconciliationListWithName(firmId, lawyerName, startDate, endDate, pageNum, pageSize);
+    }
+
+    @ApiOperation("获取律师的已完成订单列表(分页)")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerId", value = "律师ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageNum", value = "页码(默认1)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "页容(默认10)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getLawyerOrderList")
+    public R<IPage<LawFirmReconciliationVO>> getLawyerOrderList(
+            @RequestParam(value = "lawyerId") Integer lawyerId,
+            @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
+            @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
+        log.info("LawFirmReconciliationController.getLawyerOrderList?lawyerId={},pageNum={},pageSize={}",
+                lawyerId, pageNum, pageSize);
+        return lawFirmReconciliationService.getLawyerOrderList(lawyerId, pageNum, pageSize);
+    }
+
+    @ApiOperation("导出所有律所的对账统计列表到Excel(基于/getAllLawFirmList接口数据)")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "firmName", value = "律所名称(可选,模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "startDate", value = "开始日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "endDate", value = "结束日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "pageNum", value = "页码(可选,不传或传0则导出全部,传值则导出本页)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "页容(可选,与pageNum配合使用)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping(value = "/exportAllLawFirmList", produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
+    public void exportAllLawFirmList(
+            HttpServletResponse response,
+            @RequestParam(value = "firmName", required = false) String firmName,
+            @RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
+            @RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate,
+            @RequestParam(value = "pageNum", required = false) Integer pageNum,
+            @RequestParam(value = "pageSize", required = false) Integer pageSize) throws Exception {
+        log.info("LawFirmReconciliationController.exportAllLawFirmList?firmName={},startDate={},endDate={},pageNum={},pageSize={}",
+                firmName, startDate, endDate, pageNum, pageSize);
+        try {
+            lawFirmReconciliationService.exportAllLawFirmList(response, firmName, startDate, endDate, pageNum, pageSize);
+        } catch (Exception e) {
+            log.error("导出律所对账列表异常", e);
+            if (!response.isCommitted()) {
+                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                response.setContentType("application/json;charset=UTF-8");
+                response.getWriter().write("{\"code\":500,\"msg\":\"导出失败:" + e.getMessage() + "\"}");
+            }
+            throw e;
+        }
+    }
+
+    @ApiOperation("导出律师对账统计列表到Excel(基于/getLawyerListWithName接口数据)")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "firmId", value = "律所ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "lawyerName", value = "律师名称(可选,模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "startDate", value = "开始日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "endDate", value = "结束日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "pageNum", value = "页码(可选,不传或传0则导出全部,传值则导出本页)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "页容(可选,与pageNum配合使用)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping(value = "/exportLawyerListWithName", produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
+    public void exportLawyerListWithName(
+            HttpServletResponse response,
+            @RequestParam(value = "firmId") Integer firmId,
+            @RequestParam(value = "lawyerName", required = false) String lawyerName,
+            @RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
+            @RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate,
+            @RequestParam(value = "pageNum", required = false) Integer pageNum,
+            @RequestParam(value = "pageSize", required = false) Integer pageSize) throws Exception {
+        log.info("LawFirmReconciliationController.exportLawyerListWithName?firmId={},lawyerName={},startDate={},endDate={},pageNum={},pageSize={}",
+                firmId, lawyerName, startDate, endDate, pageNum, pageSize);
+        try {
+            lawFirmReconciliationService.exportLawyerListWithName(response, firmId, lawyerName, startDate, endDate, pageNum, pageSize);
+        } catch (Exception e) {
+            log.error("导出律师对账列表异常", e);
+            if (!response.isCommitted()) {
+                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                response.setContentType("application/json;charset=UTF-8");
+                response.getWriter().write("{\"code\":500,\"msg\":\"导出失败:" + e.getMessage() + "\"}");
+            }
+            throw e;
+        }
+    }
+}
+

+ 183 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerAiInteractionLogController.java

@@ -0,0 +1,183 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerAiInteractionLog;
+import shop.alien.lawyer.service.LawyerAiInteractionLogService;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * AI交互日志 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-AI交互日志"})
+@ApiSort(21)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/aiInteractionLog")
+@RequiredArgsConstructor
+public class LawyerAiInteractionLogController {
+
+    private final LawyerAiInteractionLogService aiInteractionLogService;
+
+    @ApiOperation("新增AI交互日志")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/addAiInteractionLog")
+    public R<LawyerAiInteractionLog> addAiInteractionLog(@RequestBody LawyerAiInteractionLog aiInteractionLog) {
+        log.info("LawyerAiInteractionLogController.addAiInteractionLog?aiInteractionLog={}", aiInteractionLog);
+        return aiInteractionLogService.addAiInteractionLog(aiInteractionLog);
+    }
+
+    @ApiOperation("编辑AI交互日志")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/editAiInteractionLog")
+    public R<LawyerAiInteractionLog> editAiInteractionLog(@RequestBody LawyerAiInteractionLog aiInteractionLog) {
+        log.info("LawyerAiInteractionLogController.editAiInteractionLog?aiInteractionLog={}", aiInteractionLog);
+        return aiInteractionLogService.editAiInteractionLog(aiInteractionLog);
+    }
+
+    @ApiOperation("删除AI交互日志")
+    @ApiOperationSupport(order = 3)
+    @DeleteMapping("/deleteAiInteractionLog")
+    public R<Boolean> deleteAiInteractionLog(@RequestParam(value = "id") Integer id) {
+        log.info("LawyerAiInteractionLogController.deleteAiInteractionLog?id={}", id);
+        return aiInteractionLogService.deleteAiInteractionLog(id);
+    }
+
+    @ApiOperation("保存或更新AI交互日志")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/saveOrUpdate")
+    public R<LawyerAiInteractionLog> saveOrUpdate(@RequestBody LawyerAiInteractionLog aiInteractionLog) {
+        log.info("LawyerAiInteractionLogController.saveOrUpdate?aiInteractionLog={}", aiInteractionLog);
+        boolean result = aiInteractionLogService.saveOrUpdate(aiInteractionLog);
+        if (result) {
+            return R.data(aiInteractionLog);
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("通用列表查询")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "conversationId", value = "会话ID(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "problemScenarioId", value = "法律问题场景ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<List<LawyerAiInteractionLog>> getList(@ModelAttribute LawyerAiInteractionLog aiInteractionLog) {
+        log.info("LawyerAiInteractionLogController.getList?aiInteractionLog={}", aiInteractionLog);
+        List<LawyerAiInteractionLog> list = QueryBuilder.of(aiInteractionLog)
+                .likeFields("conversationId")  // 会话ID支持模糊查询
+                .build()
+                .list(aiInteractionLogService);
+        return R.data(list);
+    }
+
+    @ApiOperation("通用分页查询")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "conversationId", value = "会话ID(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "problemScenarioId", value = "法律问题场景ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<LawyerAiInteractionLog>> getPage(@ModelAttribute LawyerAiInteractionLog aiInteractionLog,
+                                                    @RequestParam(defaultValue = "1") int page,
+                                                    @RequestParam(defaultValue = "10") int size) {
+        log.info("LawyerAiInteractionLogController.getPage?aiInteractionLog={},page={},size={}", aiInteractionLog, page, size);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        IPage<LawyerAiInteractionLog> pageResult = QueryBuilder.of(aiInteractionLog)
+                .likeFields("conversationId")  // 会话ID支持模糊查询
+                .page(pageNum, pageSize)
+                .build()
+                .page(aiInteractionLogService);
+        return R.data(pageResult);
+    }
+
+    @ApiOperation("AI聊天接口")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "message", value = "用户发送的消息内容", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "id", value = "会话ID(AI)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID(可选)", dataType = "Integer", paramType = "query")
+    })
+    @PostMapping("/chat")
+    public R<Map<String, Object>> sendAIMessage(
+            @RequestParam String message,
+            @RequestParam(required = false) String sessionId,
+            @RequestParam(required = false) Integer clientUserId) {
+        log.info("LawyerAiInteractionLogController.sendAIMessage?message={},sessionId={},clientUserId={}", message, sessionId, clientUserId);
+        return aiInteractionLogService.sendAIMessage(message, sessionId, clientUserId);
+    }
+
+
+    @ApiOperation("AI聊天记录保存")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "queryText", value = "用户发送的消息内容", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "responseText", value = "AI回复内容", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/saveChatLog")
+    public R<Map<String, Object>> saveChatLog(@RequestBody LawyerAiInteractionLog aiInteractionLog) {
+        log.info("LawyerAiInteractionLogController.sendAIMessage?aiInteractionLog={}",aiInteractionLog);
+        return aiInteractionLogService.saveChatLog(aiInteractionLog);
+    }
+
+    @ApiOperation("删除聊天记录")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "AI交互日志表id", dataType = "Integer", paramType = "query", required = true),
+    })
+    @PostMapping("/updatedLog")
+    public R<Map<String, Object>> updatedLog(
+            @RequestParam Integer id) {
+        log.info("LawyerAiInteractionLogController.updatedLog?id={}", id);
+        return aiInteractionLogService.updatedLog(id);
+    }
+
+    @ApiOperation("分页查询对话历史")
+    @ApiOperationSupport(order = 10)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+//            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query", required = true)
+//            @ApiImplicitParam(name = "conversationId", value = "会话ID(支持模糊查询)", dataType = "String", paramType = "query"),
+//            @ApiImplicitParam(name = "problemScenarId", value = "法律问题场景ID", dataType = "Integer", paramType = "query"),
+//            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+//            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getLogList")
+    public R<IPage<LawyerAiInteractionLog>> getLogList(
+            @RequestParam Integer clientUserId,
+            @RequestParam(defaultValue = "0") int page,
+            @RequestParam(defaultValue = "10") int size
+    ) {
+        log.info("LawyerAiInteractionLogController.getLogList?,page={},size={},clientUserId{}", clientUserId, page, size);
+
+        return aiInteractionLogService.getLogList(clientUserId, page, size);
+    }
+
+
+}
+

+ 113 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerChatMessageController.java

@@ -0,0 +1,113 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerChatMessage;
+import shop.alien.lawyer.service.LawyerChatMessageService;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.util.List;
+
+/**
+ * 聊天消息 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-聊天消息"})
+@ApiSort(16)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/chatMessage")
+@RequiredArgsConstructor
+public class LawyerChatMessageController {
+
+    private final LawyerChatMessageService chatMessageService;
+
+    @ApiOperation("新增聊天消息")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/addChatMessage")
+    public R<LawyerChatMessage> addChatMessage(@RequestBody LawyerChatMessage chatMessage) {
+        log.info("LawyerChatMessageController.addChatMessage?chatMessage={}", chatMessage);
+        return chatMessageService.addChatMessage(chatMessage);
+    }
+
+    @ApiOperation("编辑聊天消息")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/editChatMessage")
+    public R<LawyerChatMessage> editChatMessage(@RequestBody LawyerChatMessage chatMessage) {
+        log.info("LawyerChatMessageController.editChatMessage?chatMessage={}", chatMessage);
+        return chatMessageService.editChatMessage(chatMessage);
+    }
+
+    @ApiOperation("删除聊天消息")
+    @ApiOperationSupport(order = 3)
+    @DeleteMapping("/deleteChatMessage")
+    public R<Boolean> deleteChatMessage(@RequestParam(value = "id") Integer id) {
+        log.info("LawyerChatMessageController.deleteChatMessage?id={}", id);
+        return chatMessageService.deleteChatMessage(id);
+    }
+
+    @ApiOperation("保存或更新聊天消息")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/saveOrUpdate")
+    public R<LawyerChatMessage> saveOrUpdate(@RequestBody LawyerChatMessage chatMessage) {
+        log.info("LawyerChatMessageController.saveOrUpdate?chatMessage={}", chatMessage);
+        boolean result = chatMessageService.saveOrUpdate(chatMessage);
+        if (result) {
+            return R.data(chatMessage);
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("通用列表查询")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "chatSessionId", value = "聊天会话ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "consultationOrderId", value = "咨询订单ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "senderType", value = "发送者类型", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<List<LawyerChatMessage>> getList(@ModelAttribute LawyerChatMessage chatMessage) {
+        log.info("LawyerChatMessageController.getList?chatMessage={}", chatMessage);
+        List<LawyerChatMessage> list = QueryBuilder.of(chatMessage)
+                .build()
+                .list(chatMessageService);
+        return R.data(list);
+    }
+
+    @ApiOperation("通用分页查询")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "chatSessionId", value = "聊天会话ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "consultationOrderId", value = "咨询订单ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "senderType", value = "发送者类型", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<LawyerChatMessage>> getPage(@ModelAttribute LawyerChatMessage chatMessage,
+                                               @RequestParam(defaultValue = "1") int page,
+                                               @RequestParam(defaultValue = "10") int size) {
+        log.info("LawyerChatMessageController.getPage?chatMessage={},page={},size={}", chatMessage, page, size);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        IPage<LawyerChatMessage> pageResult = QueryBuilder.of(chatMessage)
+                .page(pageNum, pageSize)
+                .build()
+                .page(chatMessageService);
+        return R.data(pageResult);
+    }
+}
+

+ 178 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerChatSessionController.java

@@ -0,0 +1,178 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerChatSession;
+import shop.alien.lawyer.service.LawyerChatSessionService;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 聊天会话 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-聊天会话"})
+@ApiSort(15)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/chatSession")
+@RequiredArgsConstructor
+public class LawyerChatSessionController {
+
+    private final LawyerChatSessionService chatSessionService;
+
+    @ApiOperation("新增聊天会话")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/addChatSession")
+    public R<LawyerChatSession> addChatSession(@RequestBody LawyerChatSession chatSession) {
+        log.info("LawyerChatSessionController.addChatSession?chatSession={}", chatSession);
+        return chatSessionService.addChatSession(chatSession);
+    }
+
+    @ApiOperation("编辑聊天会话")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/editChatSession")
+    public R<LawyerChatSession> editChatSession(@RequestBody LawyerChatSession chatSession) {
+        log.info("LawyerChatSessionController.editChatSession?chatSession={}", chatSession);
+        return chatSessionService.editChatSession(chatSession);
+    }
+
+    @ApiOperation("删除聊天会话")
+    @ApiOperationSupport(order = 3)
+    @DeleteMapping("/deleteChatSession")
+    public R<Boolean> deleteChatSession(@RequestParam(value = "id") Integer id) {
+        log.info("LawyerChatSessionController.deleteChatSession?id={}", id);
+        return chatSessionService.deleteChatSession(id);
+    }
+
+    @ApiOperation("保存或更新聊天会话")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/saveOrUpdate")
+    public R<LawyerChatSession> saveOrUpdate(@RequestBody LawyerChatSession chatSession) {
+        log.info("LawyerChatSessionController.saveOrUpdate?chatSession={}", chatSession);
+        boolean result = chatSessionService.saveOrUpdate(chatSession);
+        if (result) {
+            return R.data(chatSession);
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("通用列表查询")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "consultationOrderId", value = "咨询订单ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "会话状态", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<List<LawyerChatSession>> getList(@ModelAttribute LawyerChatSession chatSession) {
+        log.info("LawyerChatSessionController.getList?chatSession={}", chatSession);
+        List<LawyerChatSession> list = QueryBuilder.of(chatSession)
+                .build()
+                .list(chatSessionService);
+        return R.data(list);
+    }
+
+    @ApiOperation("通用分页查询")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "consultationOrderId", value = "咨询订单ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "会话状态", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<LawyerChatSession>> getPage(@ModelAttribute LawyerChatSession chatSession,
+                                               @RequestParam(defaultValue = "1") int page,
+                                               @RequestParam(defaultValue = "10") int size) {
+        log.info("LawyerChatSessionController.getPage?chatSession={},page={},size={}", chatSession, page, size);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        IPage<LawyerChatSession> pageResult = QueryBuilder.of(chatSession)
+                .page(pageNum, pageSize)
+                .build()
+                .page(chatSessionService);
+        return R.data(pageResult);
+    }
+
+    @ApiOperation("获取聊天历史记录")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "sessionId", value = "会话ID(可选,不传则获取所有会话列表)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "page", value = "页码(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "每页数量(默认20)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/history")
+    public R<Map<String, Object>> getChatHistory(
+            @RequestParam(required = false) String sessionId,
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "20") int pageSize) {
+        log.info("LawyerChatSessionController.getChatHistory?sessionId={},page={},pageSize={}", sessionId, page, pageSize);
+        return chatSessionService.getChatHistory(sessionId, page, pageSize);
+    }
+
+    @ApiOperation("保存聊天消息")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "sessionId", value = "会话ID(可选,不传则创建新会话)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "content", value = "消息内容", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "type", value = "消息类型:user或ai", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID(当type=user时必填)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "consultationOrderId", value = "咨询订单ID(可选)", dataType = "Integer", paramType = "query")
+    })
+    @PostMapping("/saveMessage")
+    public R<Map<String, Object>> saveChatMessage(
+            @RequestParam(required = false) String sessionId,
+            @RequestParam String content,
+            @RequestParam String type,
+            @RequestParam(required = false) Integer clientUserId,
+            @RequestParam(required = false) Integer consultationOrderId) {
+        log.info("LawyerChatSessionController.saveChatMessage?sessionId={},content={},type={},clientUserId={},consultationOrderId={}",
+                sessionId, content, type, clientUserId, consultationOrderId);
+        return chatSessionService.saveChatMessage(sessionId, content, type, clientUserId, consultationOrderId);
+    }
+
+    @ApiOperation("删除聊天消息")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "messageIds", value = "消息ID数组(逗号分隔)", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "sessionId", value = "会话ID(可选)", dataType = "String", paramType = "query")
+    })
+    @PostMapping("/deleteMessage")
+    public R<String> deleteChatMessages(
+            @RequestParam String messageIds,
+            @RequestParam(required = false) String sessionId) {
+        log.info("LawyerChatSessionController.deleteChatMessages?messageIds={},sessionId={}", messageIds, sessionId);
+        return chatSessionService.deleteChatMessages(messageIds, sessionId);
+    }
+
+    @ApiOperation("删除会话(通过会话ID)")
+    @ApiOperationSupport(order = 10)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "sessionId", value = "会话ID", dataType = "String", paramType = "query", required = true)
+    })
+    @PostMapping("/deleteSessionById")
+    public R<String> deleteChatSession(@RequestParam String sessionId) {
+        log.info("LawyerChatSessionController.deleteChatSession?sessionId={}", sessionId);
+        return chatSessionService.deleteChatSessionBySessionId(sessionId);
+    }
+}
+

+ 258 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerClientConsultationOrderController.java

@@ -0,0 +1,258 @@
+package shop.alien.lawyer.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerConsultationOrder;
+import shop.alien.entity.store.vo.LawyerConsultationOrderVO;
+import shop.alien.lawyer.service.LawyerClientConsultationOrderService;
+
+import java.util.Map;
+
+/**
+ * 咨询订单 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师端-咨询订单"})
+@ApiSort(14)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyerClient/consultationOrder")
+@RequiredArgsConstructor
+public class LawyerClientConsultationOrderController {
+
+    private final LawyerClientConsultationOrderService lawyerClientConsultationOrderService;
+
+    @ApiOperation("删除咨询订单(律师端)")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "订单ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @DeleteMapping("/deleteConsultationOrder")
+    public R<Boolean> deleteConsultationOrder(@RequestParam(value = "id", required = true) Integer id) {
+        log.info("删除咨询订单,订单ID={}", id);
+        if (id == null) {
+            log.warn("删除咨询订单失败:订单ID为空");
+            return R.fail("订单ID不能为空");
+        }
+        return lawyerClientConsultationOrderService.deleteConsultationOrder(id);
+    }
+
+    @ApiOperation("保存或更新咨询订单(律师端)")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/saveOrUpdate")
+    public R<LawyerConsultationOrder> saveOrUpdate(@RequestBody LawyerConsultationOrder consultationOrder) {
+        log.info("LawyerConsultationOrderController.saveOrUpdate?consultationOrder={}", consultationOrder);
+        boolean result = lawyerClientConsultationOrderService.saveOrUpdate(consultationOrder);
+        if (result) {
+            return R.data(consultationOrder);
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("查询订单详情(律师端)")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerOrderId", value = "法律订单ID", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getConsultationOrderDetail")
+    public R<LawyerConsultationOrderVO> getConsultationOrderDetail(@RequestParam(required = false) String lawyerOrderId) {
+        log.info("LawyerConsultationOrderController.getConsultationOrderDetail?lawyerOrderId={}",
+                lawyerOrderId);
+        return R.data(lawyerClientConsultationOrderService.getConsultationOrderDetail(lawyerOrderId));
+    }
+
+    @ApiOperation("查询咨询订单信息(律师端)")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "startDate", value = "开始时间", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "endDate", value = "结束时间", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserName", value = "用户姓名", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "orderStatus", value = "订单状态, 0:待支付,1.待接单 2:进行中, 3:已完成, 4:已取消,5.已退款", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerId", value = "律师ID", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/getLawyerConsultationOrderInfo")
+    public R<Map<String, Object>> getLawyerConsultationOrderInfo(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int size,
+            @RequestParam(required = false) String startDate,
+            @RequestParam(required = false) String endDate,
+            @RequestParam(required = false) String clientUserName,
+            @RequestParam(required = false) String orderStatus,
+            @RequestParam(required = true) String lawyerId) {
+        log.info("LawyerConsultationOrderController.getLawyerConsultationOrderInfo?page={},size={},startDate={},endDate={},clientUserName={},orderStatus={},lawyerId={}",
+                page, size, startDate, endDate, clientUserName, orderStatus, lawyerId);
+
+        // 参数校验
+        if (lawyerId == null || lawyerId.trim().isEmpty()) {
+            log.warn("查询咨询订单信息失败:律师ID为空");
+            return R.fail("律师ID不能为空");
+        }
+
+        // 分页参数校验和规范化
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+
+        return R.data(lawyerClientConsultationOrderService.getLawyerConsultationOrderInfo(pageNum, pageSize, startDate, endDate, clientUserName, orderStatus, lawyerId));
+    }
+
+    /**
+     * 律师确认订单(律师端)
+     * <p>
+     * 支持两种操作类型:
+     * 1. 接单:将订单状态从待接单更新为进行中
+     * 2. 取消:将订单状态更新为已取消
+     * </p>
+     *
+     * @param id        订单ID
+     * @param actionType 操作类型:1-接单,2-取消
+     * @return 操作结果
+     */
+    @ApiOperation("律师确认订单(律师端)")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "订单ID", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "actionType", value = "操作类型:1-接单,2-取消", dataType = "String", paramType = "query", required = true)
+    })
+    @PutMapping("/confirmOrder")
+    public R<Boolean> confirmOrder(@RequestParam(value = "id", required = true) String id,
+                                   @RequestParam(value = "actionType", required = true) String actionType) {
+        log.info("律师确认订单,订单ID={}, 操作类型={}", id, actionType);
+        
+        // 参数校验
+        if (id == null || id.trim().isEmpty()) {
+            log.warn("律师确认订单失败:订单ID为空");
+            return R.fail("订单ID不能为空");
+        }
+        
+        if (actionType == null || actionType.trim().isEmpty()) {
+            log.warn("律师确认订单失败:操作类型为空");
+            return R.fail("操作类型不能为空");
+        }
+        
+        // 操作类型格式校验
+        if (!"1".equals(actionType.trim()) && !"2".equals(actionType.trim())) {
+            log.warn("律师确认订单失败:操作类型无效,actionType={}", actionType);
+            return R.fail("操作类型无效,1-接单,2-取消");
+        }
+        
+        return lawyerClientConsultationOrderService.confirmOrder(id, actionType);
+    }
+
+    @ApiOperation("申请退款(律师端)")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "订单ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "refundReason", value = "退款原因", dataType = "String", paramType = "query", required = true)
+    })
+    @PostMapping("/requestRefund")
+    public R<Boolean> requestRefund(@RequestParam(value = "id", required = true) Integer id,
+                                     @RequestParam(value = "refundReason", required = true) String refundReason) {
+        log.info("申请退款,订单ID={}, 退款原因={}", id, refundReason);
+        if (id == null) {
+            log.warn("申请退款失败:订单ID为空");
+            return R.fail("订单ID不能为空");
+        }
+        if (refundReason == null || refundReason.trim().isEmpty()) {
+            log.warn("申请退款失败:退款原因为空");
+            return R.fail("退款原因不能为空");
+        }
+        return lawyerClientConsultationOrderService.requestRefund(id, refundReason);
+    }
+
+
+    @ApiOperation("退款申请处理(律师端)")
+    @ApiOperationSupport(order = 7)
+    @PostMapping("/refundApplyProcess")
+    public R<Boolean> refundApplyProcess(@RequestBody LawyerConsultationOrder lawyerConsultationOrder) {
+        return lawyerClientConsultationOrderService.refundApplyProcess(lawyerConsultationOrder);
+    }
+
+    /**
+     * 订单开始计时(律师端)
+     * <p>
+     * 订单开始计时:
+     * 1. 当订单为按时收费,订单状态为进行中时,订单开始计时
+     * </p>
+     *
+     * @param id        订单ID
+     * @return 操作结果
+     */
+    @ApiOperation("订单开始计时(律师端)")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "订单ID", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/orderStartTiming")
+    public R<Boolean> orderStartTiming(@RequestParam(value = "id", required = true) String id) {
+        log.info("订单开始计时,订单ID={}", id);
+
+        // 参数校验
+        if (id == null || id.trim().isEmpty()) {
+            log.warn("订单开始计时失败:订单ID为空");
+            return R.fail("订单ID不能为空");
+        }
+
+        return lawyerClientConsultationOrderService.orderStartTiming(id);
+    }
+
+    /**
+     * 检查订单接单后是否有消息记录(律师端)
+     * <p>
+     * 根据订单ID查询订单信息,获取律师接单时间、用户ID和律师ID,
+     * 然后查询 life_message 表,检查是否存在律师发送给用户的消息记录,
+     * 且消息创建时间大于接单时间。
+     * </p>
+     * <p>
+     * 处理流程:
+     * 1. 参数校验:订单ID不能为空
+     * 2. 查询订单信息:获取接单时间、用户ID、律师ID
+     * 3. 查询律师和用户信息:获取律师手机号和用户手机号
+     * 4. 构建消息查询条件:sender_id=律师phone,receiver_id=用户phone,created_time>接单时间
+     * 5. 查询消息记录:如果存在记录返回true,否则返回false
+     * </p>
+     *
+     * @param id 订单ID,不能为空
+     * @return 统一响应结果,true表示有消息记录,false表示无消息记录
+     * @author system
+     * @since 2025-01-XX
+     */
+    @ApiOperation("检查订单接单后是否有消息记录(律师端)")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "订单ID", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/checkMessageAfterAccept")
+    public R<Boolean> checkMessageAfterAccept(@RequestParam(value = "id", required = true) String id) {
+        log.info("检查订单接单后是否有消息记录请求开始,订单ID={}", id);
+
+        // 参数校验
+        if (id == null || id.trim().isEmpty()) {
+            log.warn("检查订单接单后是否有消息记录失败:订单ID为空");
+            return R.fail("订单ID不能为空");
+        }
+
+        try {
+            Boolean hasMessage = lawyerClientConsultationOrderService.checkMessageAfterAccept(id);
+            log.info("检查订单接单后是否有消息记录成功,订单ID={},是否有消息={}", id, hasMessage);
+            return R.data(hasMessage);
+        } catch (IllegalArgumentException e) {
+            log.warn("检查订单接单后是否有消息记录参数校验失败,订单ID={},错误信息:{}", id, e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (RuntimeException e) {
+            log.error("检查订单接单后是否有消息记录异常,订单ID={},异常信息:{}", id, e.getMessage(), e);
+            return R.fail("检查消息记录失败:" + e.getMessage());
+        } catch (Exception e) {
+            log.error("检查订单接单后是否有消息记录未知异常,订单ID={},异常信息:{}", id, e.getMessage(), e);
+            return R.fail("检查消息记录失败,请稍后重试");
+        }
+    }
+}
+

+ 129 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerCommonQuestionController.java

@@ -0,0 +1,129 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerCommonQuestion;
+import shop.alien.lawyer.service.LawyerCommonQuestionService;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.util.List;
+
+/**
+ * 常见问题 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-常见问题"})
+@ApiSort(18)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/commonQuestion")
+@RequiredArgsConstructor
+public class LawyerCommonQuestionController {
+
+    private final LawyerCommonQuestionService commonQuestionService;
+
+    @ApiOperation("新增常见问题")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/addCommonQuestion")
+    public R<LawyerCommonQuestion> addCommonQuestion(@RequestBody LawyerCommonQuestion commonQuestion) {
+        log.info("LawyerCommonQuestionController.addCommonQuestion?commonQuestion={}", commonQuestion);
+        return commonQuestionService.addCommonQuestion(commonQuestion);
+    }
+
+    @ApiOperation("编辑常见问题")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/editCommonQuestion")
+    public R<LawyerCommonQuestion> editCommonQuestion(@RequestBody LawyerCommonQuestion commonQuestion) {
+        log.info("LawyerCommonQuestionController.editCommonQuestion?commonQuestion={}", commonQuestion);
+        return commonQuestionService.editCommonQuestion(commonQuestion);
+    }
+
+    @ApiOperation("删除常见问题")
+    @ApiOperationSupport(order = 3)
+    @DeleteMapping("/deleteCommonQuestion")
+    public R<Boolean> deleteCommonQuestion(@RequestParam(value = "id") Integer id) {
+        log.info("LawyerCommonQuestionController.deleteCommonQuestion?id={}", id);
+        return commonQuestionService.deleteCommonQuestion(id);
+    }
+
+    @ApiOperation("保存或更新常见问题")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/saveOrUpdate")
+    public R<LawyerCommonQuestion> saveOrUpdate(@RequestBody LawyerCommonQuestion commonQuestion) {
+        log.info("LawyerCommonQuestionController.saveOrUpdate?commonQuestion={}", commonQuestion);
+        boolean result = commonQuestionService.saveOrUpdate(commonQuestion);
+        if (result) {
+            return R.data(commonQuestion);
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("通用列表查询")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "questionText", value = "问题文本(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "categoryType", value = "分类类型, 0:推荐, 1:常见问题", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "problemScenarioId", value = "法律问题场景ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "状态, 0:禁用, 1:启用", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<List<LawyerCommonQuestion>> getList(@ModelAttribute LawyerCommonQuestion commonQuestion) {
+        log.info("LawyerCommonQuestionController.getList?commonQuestion={}", commonQuestion);
+        List<LawyerCommonQuestion> list = QueryBuilder.of(commonQuestion)
+                .likeFields("questionText")  // 问题文本支持模糊查询
+                .build()
+                .list(commonQuestionService);
+        return R.data(list);
+    }
+
+    @ApiOperation("通用分页查询")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "questionText", value = "问题文本(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "categoryType", value = "分类类型, 0:推荐, 1:常见问题", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "problemScenarioId", value = "法律问题场景ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "状态, 0:禁用, 1:启用", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<LawyerCommonQuestion>> getPage(@ModelAttribute LawyerCommonQuestion commonQuestion,
+                                                  @RequestParam(defaultValue = "1") int page,
+                                                  @RequestParam(defaultValue = "10") int size) {
+        log.info("LawyerCommonQuestionController.getPage?commonQuestion={},page={},size={}", commonQuestion, page, size);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        IPage<LawyerCommonQuestion> pageResult = QueryBuilder.of(commonQuestion)
+                .likeFields("questionText")  // 问题文本支持模糊查询
+                .page(pageNum, pageSize)
+                .build()
+                .page(commonQuestionService);
+        return R.data(pageResult);
+    }
+
+    @ApiOperation("根据数量获取常见问题列表")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "num", value = "获取数量", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getListByNum")
+    public R<List<LawyerCommonQuestion>> getListByNum(@RequestParam Integer num) {
+        log.info("LawyerCommonQuestionController.getListByNum?num={}", num);
+        return commonQuestionService.getCommonQuestionListByNum(num);
+    }
+
+}
+

+ 454 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerConsultationOrderController.java

@@ -0,0 +1,454 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerConsultationOrder;
+import shop.alien.entity.store.dto.LawyerConsultationOrderDto;
+import shop.alien.entity.store.dto.PayStatusRequest;
+import shop.alien.entity.store.vo.LawyerConsultationOrderVO;
+import shop.alien.lawyer.service.LawyerConsultationOrderService;
+import shop.alien.lawyer.service.OrderExpirationService;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 咨询订单 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-咨询订单(用户端)"})
+@ApiSort(14)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/consultationOrder")
+@RequiredArgsConstructor
+public class LawyerConsultationOrderController {
+
+    @Value("${order.coefficient}")
+    private String coefficient;
+    @Value("${order.coefficients}")
+    private String coefficients;
+
+    private final LawyerConsultationOrderService consultationOrderService;
+    private final OrderExpirationService orderExpirationService;
+
+
+    @ApiOperation("分页查询咨询订单列表(支持按律师姓名关联查询,返回结果包含律师信息)")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "orderNumber", value = "订单编号", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerName", value = "律师姓名(支持模糊查询,关联查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "orderStatus", value = "订单状态, 0:待支付, 1:已支付, 2:进行中, 3:已完成, 4:已取消", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/getConsultationOrderList")
+    public R<IPage<LawyerConsultationOrderVO>> getConsultationOrderList(@RequestParam(defaultValue = "1") int page,
+                                                                        @RequestParam(defaultValue = "10") int size,
+                                                                        @RequestParam(required = false) String orderNumber,
+                                                                        @RequestParam(required = false) Integer clientUserId,
+                                                                        @RequestParam(required = false) Integer lawyerUserId,
+                                                                        @RequestParam(required = false) String lawyerName,
+                                                                        @RequestParam(required = false) Integer orderStatus) {
+        log.info("LawyerConsultationOrderController.getConsultationOrderList?page={},size={},orderNumber={},clientUserId={},lawyerUserId={},lawyerName={},orderStatus={}",
+                page, size, orderNumber, clientUserId, lawyerUserId, lawyerName, orderStatus);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        return consultationOrderService.getConsultationOrderList(pageNum, pageSize, orderNumber, clientUserId, lawyerUserId, lawyerName, orderStatus);
+    }
+
+    @ApiOperation("新增咨询订单")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/addConsultationOrder")
+    public R<LawyerConsultationOrder> addConsultationOrder(@RequestBody LawyerConsultationOrder consultationOrder) {
+        log.info("LawyerConsultationOrderController.addConsultationOrder?consultationOrder={}", consultationOrder);
+        return consultationOrderService.addConsultationOrder(consultationOrder);
+    }
+
+    @ApiOperation("编辑咨询订单")
+    @ApiOperationSupport(order = 3)
+    @PostMapping("/editConsultationOrder")
+    public R<LawyerConsultationOrder> editConsultationOrder(@RequestBody LawyerConsultationOrder consultationOrder) {
+        log.info("开始编辑咨询订单,订单ID={}, 订单编号={}",
+                consultationOrder != null ? consultationOrder.getId() : null,
+                consultationOrder != null ? consultationOrder.getOrderNumber() : null);
+
+        // 参数校验:订单ID不能为空
+        if (consultationOrder == null || consultationOrder.getId() == null) {
+            log.warn("编辑咨询订单失败:订单ID为空");
+            return R.fail("订单ID不能为空");
+        }
+
+        // 记录修改前的关键信息
+        Integer orderId = consultationOrder.getId();
+        Integer newOrderStatus = consultationOrder.getOrderStatus();
+        Integer newPaymentStatus = consultationOrder.getPaymentStatus();
+        log.info("编辑咨询订单,订单ID={}, 新订单状态={}, 新支付状态={}",
+                orderId, newOrderStatus, newPaymentStatus);
+
+        R<LawyerConsultationOrder> result = consultationOrderService.editConsultationOrder(consultationOrder);
+
+        if (result.isSuccess()) {
+            log.info("编辑咨询订单成功,订单ID={}, 订单编号={}",
+                    orderId, consultationOrder.getOrderNumber());
+        } else {
+            log.warn("编辑咨询订单失败,订单ID={}, 失败原因={}",
+                    orderId, result.getMsg());
+        }
+
+        return result;
+    }
+
+    @ApiOperation("删除咨询订单(用户端)")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "订单ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @DeleteMapping("/deleteConsultationOrder")
+    public R<Boolean> deleteConsultationOrder(@RequestParam(value = "id", required = true) Integer id) {
+        log.info("删除咨询订单,订单ID={}", id);
+        if (id == null) {
+            log.warn("删除咨询订单失败:订单ID为空");
+            return R.fail("订单ID不能为空");
+        }
+        return consultationOrderService.deleteConsultationOrder(id);
+    }
+
+    @ApiOperation("保存或更新咨询订单")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/saveOrUpdate")
+    public R<LawyerConsultationOrder> saveOrUpdate(@RequestBody LawyerConsultationOrder consultationOrder) {
+        log.info("LawyerConsultationOrderController.saveOrUpdate?consultationOrder={}", consultationOrder);
+        boolean result = consultationOrderService.saveOrUpdate(consultationOrder);
+        if (result) {
+            return R.data(consultationOrder);
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("通用列表查询")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "orderNumber", value = "订单编号(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "problemScenarioId", value = "法律问题场景ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "orderStatus", value = "订单状态, 0:待支付, 1:已支付, 2:进行中, 3:已完成, 4:已取消", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "paymentStatus", value = "支付状态, 0:未支付, 1:已支付", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<List<LawyerConsultationOrder>> getList(@ModelAttribute LawyerConsultationOrder consultationOrder) {
+        log.info("LawyerConsultationOrderController.getList?consultationOrder={}", consultationOrder);
+        List<LawyerConsultationOrder> list = QueryBuilder.of(consultationOrder)
+                .likeFields("orderNumber")  // 订单编号支持模糊查询
+                .build()
+                .list(consultationOrderService);
+        return R.data(list);
+    }
+
+    @ApiOperation("通用分页查询")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "orderNumber", value = "订单编号(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "problemScenarioId", value = "法律问题场景ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "orderStatus", value = "订单状态, 0:待支付, 1:已支付, 2:进行中, 3:已完成, 4:已取消", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "paymentStatus", value = "支付状态, 0:未支付, 1:已支付", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<LawyerConsultationOrder>> getPage(@ModelAttribute LawyerConsultationOrder consultationOrder,
+                                                     @RequestParam(defaultValue = "1") int page,
+                                                     @RequestParam(defaultValue = "10") int size) {
+        log.info("LawyerConsultationOrderController.getPage?consultationOrder={},page={},size={}", consultationOrder, page, size);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        IPage<LawyerConsultationOrder> pageResult = QueryBuilder.of(consultationOrder)
+                .likeFields("orderNumber")  // 订单编号支持模糊查询
+                .page(pageNum, pageSize)
+                .build()
+                .page(consultationOrderService);
+        return R.data(pageResult);
+    }
+
+    /*@ApiOperation("开始咨询律师")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerId", value = "律师ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "question", value = "咨询问题(可选)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "sessionId", value = "关联的AI会话ID(可选)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID(可选)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "problemScenarioId", value = "法律问题场景ID(可选)", dataType = "Integer", paramType = "query")
+    })
+    @PostMapping("/start")
+    public R<Map<String, Object>> startConsultation(
+            @RequestParam Integer lawyerId,
+            @RequestParam(required = false) String question,
+            @RequestParam(required = false) String sessionId,
+            @RequestParam(required = false) Integer clientUserId,
+            @RequestParam(required = false) Integer problemScenarioId) {
+        log.info("LawyerConsultationOrderController.startConsultation?lawyerId={},question={},sessionId={},clientUserId={},problemScenarioId={}",
+                lawyerId, question, sessionId, clientUserId, problemScenarioId);
+        return consultationOrderService.startConsultation(lawyerId, question, sessionId, clientUserId, problemScenarioId);
+    }*/
+
+
+    @ApiOperation("立即咨询(用户端)")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "problemScenarioId", value = "法律问题场景ID", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "problemDescription", value = "问题描述", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "alipayNo", value = "支付宝订单编号", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "orderStr", value = "支付宝订单字符串", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "payType", value = "支付方式 1支付宝 2微信", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "chargeMinute", value = "分钟收费-钱数", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "minuteNum", value = "分钟收费-分钟数", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "chargeTime", value = "次数收费-钱数", dataType = "Integer", paramType = "query"),
+//            @ApiImplicitParam(name = "lawyerName", value = "律师姓名(支持模糊查询,关联查询)", dataType = "String", paramType = "query"),
+//            @ApiImplicitParam(name = "consultationFee", value = "咨询费用,单位分", dataType = "Integer", paramType = "query"),
+//            @ApiImplicitParam(name = "startTime", value = "咨询开始时间", dataType = "Date", paramType = "query"),
+//            @ApiImplicitParam(name = "endTime", value = "咨询结束时间", dataType = "Date", paramType = "query"),
+//            @ApiImplicitParam(name = "validityPeriod", value = "订单有效期", dataType = "Date", paramType = "query"),
+//            @ApiImplicitParam(name = "orderStatus", value = "订单状态, 0:待支付, 1:已支付, 2:进行中, 3:已完成, 4:已取消", dataType = "Integer", paramType = "query"),
+//            @ApiImplicitParam(name = "paymentStatus", value = "支付状态, 0:未支付, 1:已支付", dataType = "Integer", paramType = "query"),
+    })
+    @PostMapping("/consultNow")
+    public R<LawyerConsultationOrderDto> startConsultation(@RequestBody LawyerConsultationOrder lawyerConsultationOrder) {
+        log.info("LawyerConsultationOrderController.consultNow?lawyerConsultationOrder={}", lawyerConsultationOrder);
+        //创建订单时,存储redis中,设置过期时间
+        R<LawyerConsultationOrderDto> layerConsultationOrderDto = consultationOrderService.consultNow(lawyerConsultationOrder);
+        orderExpirationService.setOrderPaymentTimeout(layerConsultationOrderDto.getData().getOrderNumber(), 60 * Long.parseLong(coefficient));
+        return layerConsultationOrderDto;
+    }
+
+
+    @ApiOperation("是否支付成功更改订单表状态")
+    @ApiOperationSupport(order = 10)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "orderNumber", value = "订单编号", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "orderStatus", value = "订单状态, 0:待支付, 1:待接单, 2:进行中, 3:已完成, 4:已取消", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "paymentStatus", value = "支付状态, 0:未支付, 1:已支付", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "payType", value = "支付方式 1支付宝 2微信", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "orderStr", value = "支付宝或VX的str", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "alipayNo", value = "支付宝/VX订单编号", dataType = "String", paramType = "query"),
+    })
+    @PostMapping("/payStatus")
+    public R<LawyerConsultationOrderDto> payStatus(@RequestBody PayStatusRequest request) {
+        log.info("LawyerConsultationOrderController.payStatus?orderNumber={},paymentStatus={},orderStatus={}",
+                request.getOrderNumber(), request.getPaymentStatus(), request.getOrderStatus());
+        //如果订单已支付,关闭redis中的记时
+        if (request.getPaymentStatus() != null && request.getPaymentStatus() == 1) {
+            orderExpirationService.cancelOrderPaymentTimeout(request.getOrderNumber());
+            orderExpirationService.setOrderAcceptTimeout(request.getOrderNumber(), Long.parseLong(coefficients)*60);
+        }
+        return consultationOrderService.payStatus(request);
+    }
+
+    @ApiOperation("根据用户ID查询律师咨询订单(用户端)")
+    @ApiOperationSupport(order = 11)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "userId", value = "客户端用户ID", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "orderStatus", value = "订单状态, 0:待支付,1.待接单 2:进行中, 3:已完成, 4:已取消,5.已退款", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerName", value = "律师姓名(支持模糊查询,关联查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getConsultationOrderListById")
+    public R<Map<String, Object>> getConsultationOrderListById(@RequestParam(defaultValue = "1") int page,
+                                                                            @RequestParam(defaultValue = "10") int size,
+                                                                            @RequestParam(required = true) String userId,
+                                                                            @RequestParam(required = false) String orderStatus,
+                                                                            @RequestParam(required = false) String lawyerName) {
+        log.info("LawyerConsultationOrderController.getConsultationOrderListById?pageNum={},pageSize={},userId={},orderStatus={},lawyerName={}",
+                page, size, userId, orderStatus, lawyerName);
+        return R.data(consultationOrderService.getConsultationOrderListById(page, size, userId, orderStatus, lawyerName));
+    }
+
+    @ApiOperation("查询订单详情")
+    @ApiOperationSupport(order = 12)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerOrderId", value = "法律订单ID", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getConsultationOrderDetail")
+    public R<LawyerConsultationOrderVO> getConsultationOrderDetail(@RequestParam(required = false) String lawyerOrderId) {
+        log.info("LawyerConsultationOrderController.getConsultationOrderDetail?lawyerOrderId={}",
+                lawyerOrderId);
+        return R.data(consultationOrderService.getConsultationOrderDetail(lawyerOrderId));
+    }
+
+    /**
+     * 取消律师咨询订单(用户端)
+     * <p>
+     * 支持取消待支付和待接单状态的订单:
+     * - 待支付订单:取消后订单状态变更为已取消,同时取消订单支付超时计时器
+     * - 待接单订单:取消后订单状态变更为已退款
+     * 已取消或已完成的订单不允许再次取消
+     * </p>
+     *
+     * @param id 订单ID
+     * @return 取消结果,true表示成功,false表示失败
+     */
+    @ApiOperation("取消律师咨询订单(用户端)")
+    @ApiOperationSupport(order = 13)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "订单ID", dataType = "String", paramType = "query", required = true)
+    })
+    @PostMapping("/cancelOrder")
+    public R<Boolean> cancelOrder(@RequestParam(value = "id", required = true) String id) {
+        log.info("LawyerConsultationOrderController.cancelOrder?id={}", id);
+
+        // 参数校验
+        if (id == null || id.trim().isEmpty()) {
+            log.warn("取消订单失败:订单ID为空");
+            return R.fail("订单ID不能为空");
+        }
+
+        try {
+            boolean success = consultationOrderService.cancelOrder(id);
+            if (success) {
+                log.info("取消订单成功,订单ID={}", id);
+                return R.data(true);
+            } else {
+                log.warn("取消订单失败,订单ID={}", id);
+                return R.fail("取消订单失败,请检查订单状态");
+            }
+        } catch (Exception e) {
+            log.error("取消订单异常,订单ID={},异常信息:{}", id, e.getMessage(), e);
+            return R.fail("取消订单失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("查询咨询订单信息(律师端)")
+    @ApiOperationSupport(order = 14)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "startDate", value = "开始时间", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "endDate", value = "结束时间", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserName", value = "用户姓名", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "orderStatus", value = "订单状态(2 进行中,3 已完成)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerId", value = "律师ID", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/getLawyerConsultationOrderInfo")
+    public R<Map<String, Object>> getLawyerConsultationOrderInfo(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int size,
+            @RequestParam(required = false) String startDate,
+            @RequestParam(required = false) String endDate,
+            @RequestParam(required = false) String clientUserName,
+            @RequestParam(required = false) String orderStatus,
+            @RequestParam(required = true) String lawyerId) {
+        log.info("LawyerConsultationOrderController.getLawyerConsultationOrderInfo?page={},size={},startDate={},endDate={},clientUserName={},orderStatus={},lawyerId={}",
+                page, size, startDate, endDate, clientUserName, orderStatus, lawyerId);
+
+        // 参数校验
+        if (lawyerId == null || lawyerId.trim().isEmpty()) {
+            log.warn("查询咨询订单信息失败:律师ID为空");
+            return R.fail("律师ID不能为空");
+        }
+
+        // 分页参数校验和规范化
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+
+        return R.data(consultationOrderService.getLawyerConsultationOrderInfo(pageNum, pageSize, startDate, endDate, clientUserName, orderStatus, lawyerId));
+    }
+
+
+    @ApiOperation("创建订单前的校验")
+    @ApiOperationSupport(order = 15)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query", required = true),
+    })
+    @PostMapping("/checkOrder")
+    public R<Map<String, String>> checkOrder(@RequestParam Integer clientUserId, @RequestParam Integer lawyerUserId) {
+        log.info("LawyerConsultationOrderController.checkOrder?clientUserId={},lawyerUserId={}", clientUserId, lawyerUserId);
+        
+        // 参数校验
+        if (clientUserId == null) {
+            log.warn("创建订单前校验失败:客户端用户ID为空");
+            return R.fail("客户端用户ID不能为空");
+        }
+        
+        if (lawyerUserId == null) {
+            log.warn("创建订单前校验失败:律师用户ID为空");
+            return R.fail("律师用户ID不能为空");
+        }
+        
+        return consultationOrderService.checkOrder(clientUserId, lawyerUserId);
+    }
+
+    @ApiOperation("订单收益详情接口")
+    @ApiOperationSupport(order = 16)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "startTime", value = "开始时间", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "endTime", value = "结束时间", dataType = "String", paramType = "query")
+    })
+    @PostMapping("/getOrderIncome")
+    public R<Map<String, Object>> getOrderIncome(@RequestBody LawyerConsultationOrderVO lawyerConsultationOrderVO) {
+        log.info("LawyerConsultationOrderController.getOrderIncome?lawyerConsultationOrderVO{}", lawyerConsultationOrderVO);
+        return consultationOrderService.getOrderIncome(lawyerConsultationOrderVO);
+    }
+
+    @ApiOperation("用户申请退款")
+    @ApiOperationSupport(order = 17)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "orderId", value = "订单ID", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "applyRefundReason", value = "申请退款原因", dataType = "String", paramType = "query", required = true)
+    })
+    @PostMapping("/applyRefund")
+    public R<Boolean> applyRefund(@RequestParam String clientUserId, @RequestParam String orderId, @RequestParam String applyRefundReason) {
+        log.info("LawyerConsultationOrderController.applyRefund?clientUserId={},orderId{}", clientUserId, orderId);
+        return consultationOrderService.applyRefund(clientUserId, orderId, applyRefundReason);
+    }
+
+    @ApiOperation("用户完成订单")
+    @ApiOperationSupport(order = 18)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "orderId", value = "订单ID", dataType = "String", paramType = "query", required = true)
+    })
+    @PostMapping("/completeOrder")
+    public R<Boolean> completeOrder(@RequestParam String clientUserId, @RequestParam String orderId) {
+        log.info("LawyerConsultationOrderController.completeOrder?clientUserId={},orderId{}", clientUserId, orderId);
+        return consultationOrderService.completeOrder(clientUserId, orderId);
+    }
+
+
+    @ApiOperation("查看申请退款详情")
+    @ApiOperationSupport(order = 19)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "orderId", value = "法律订单ID", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getApplyRefundDetail")
+    public R<LawyerConsultationOrder> getApplyRefundDetail(@RequestParam(required = false) String orderId) {
+        log.info("LawyerConsultationOrderController.getApplyRefundDetail?orderId={}",
+                orderId);
+        return R.data(consultationOrderService.getApplyRefundDetail(orderId));
+    }
+}
+

+ 115 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerConsultationReviewController.java

@@ -0,0 +1,115 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerConsultationReview;
+import shop.alien.lawyer.service.LawyerConsultationReviewService;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.util.List;
+
+/**
+ * 咨询评价 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-咨询评价"})
+@ApiSort(19)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/consultationReview")
+@RequiredArgsConstructor
+public class LawyerConsultationReviewController {
+
+    private final LawyerConsultationReviewService consultationReviewService;
+
+    @ApiOperation("新增咨询评价")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/addConsultationReview")
+    public R<LawyerConsultationReview> addConsultationReview(@RequestBody LawyerConsultationReview consultationReview) {
+        log.info("LawyerConsultationReviewController.addConsultationReview?consultationReview={}", consultationReview);
+        return consultationReviewService.addConsultationReview(consultationReview);
+    }
+
+    @ApiOperation("编辑咨询评价")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/editConsultationReview")
+    public R<LawyerConsultationReview> editConsultationReview(@RequestBody LawyerConsultationReview consultationReview) {
+        log.info("LawyerConsultationReviewController.editConsultationReview?consultationReview={}", consultationReview);
+        return consultationReviewService.editConsultationReview(consultationReview);
+    }
+
+    @ApiOperation("删除咨询评价")
+    @ApiOperationSupport(order = 3)
+    @DeleteMapping("/deleteConsultationReview")
+    public R<Boolean> deleteConsultationReview(@RequestParam(value = "id") Integer id) {
+        log.info("LawyerConsultationReviewController.deleteConsultationReview?id={}", id);
+        return consultationReviewService.deleteConsultationReview(id);
+    }
+
+    @ApiOperation("保存或更新咨询评价")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/saveOrUpdate")
+    public R<LawyerConsultationReview> saveOrUpdate(@RequestBody LawyerConsultationReview consultationReview) {
+        log.info("LawyerConsultationReviewController.saveOrUpdate?consultationReview={}", consultationReview);
+        boolean result = consultationReviewService.saveOrUpdate(consultationReview);
+        if (result) {
+            return R.data(consultationReview);
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("通用列表查询")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "consultationOrderId", value = "咨询订单ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "rating", value = "评分, 1-5星", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<List<LawyerConsultationReview>> getList(@ModelAttribute LawyerConsultationReview consultationReview) {
+        log.info("LawyerConsultationReviewController.getList?consultationReview={}", consultationReview);
+        List<LawyerConsultationReview> list = QueryBuilder.of(consultationReview)
+                .build()
+                .list(consultationReviewService);
+        return R.data(list);
+    }
+
+    @ApiOperation("通用分页查询")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "consultationOrderId", value = "咨询订单ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "rating", value = "评分, 1-5星", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<LawyerConsultationReview>> getPage(@ModelAttribute LawyerConsultationReview consultationReview,
+                                                      @RequestParam(defaultValue = "1") int page,
+                                                      @RequestParam(defaultValue = "10") int size) {
+        log.info("LawyerConsultationReviewController.getPage?consultationReview={},page={},size={}", consultationReview, page, size);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        IPage<LawyerConsultationReview> pageResult = QueryBuilder.of(consultationReview)
+                .page(pageNum, pageSize)
+                .build()
+                .page(consultationReviewService);
+        return R.data(pageResult);
+    }
+}
+

+ 145 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerExpertiseAreaController.java

@@ -0,0 +1,145 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerExpertiseArea;
+import shop.alien.lawyer.service.LawyerExpertiseAreaService;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.util.List;
+
+/**
+ * 领域信息 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-领域信息"})
+@ApiSort(15)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/expertiseArea")
+@RequiredArgsConstructor
+public class LawyerExpertiseAreaController {
+
+    private final LawyerExpertiseAreaService expertiseAreaService;
+
+    @ApiOperation("分页查询领域信息列表")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "expertiseAreaInfo", value = "领域信息(支持模糊查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getExpertiseAreaList")
+    public R<IPage<LawyerExpertiseArea>> getExpertiseAreaList(@RequestParam(defaultValue = "1") int page,
+                                                              @RequestParam(defaultValue = "10") int size,
+                                                              @RequestParam(required = false) String expertiseAreaInfo) {
+        log.info("LawyerExpertiseAreaController.getExpertiseAreaList?page={},size={},expertiseAreaInfo={}",
+                page, size, expertiseAreaInfo);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        return expertiseAreaService.getExpertiseAreaList(pageNum, pageSize, expertiseAreaInfo);
+    }
+
+    @ApiOperation("新增领域信息")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/addExpertiseArea")
+    public R<LawyerExpertiseArea> addExpertiseArea(@RequestBody LawyerExpertiseArea expertiseArea) {
+        log.info("LawyerExpertiseAreaController.addExpertiseArea?expertiseArea={}", expertiseArea);
+        return expertiseAreaService.addExpertiseArea(expertiseArea);
+    }
+
+    @ApiOperation("编辑领域信息")
+    @ApiOperationSupport(order = 3)
+    @PostMapping("/editExpertiseArea")
+    public R<LawyerExpertiseArea> editExpertiseArea(@RequestBody LawyerExpertiseArea expertiseArea) {
+        log.info("LawyerExpertiseAreaController.editExpertiseArea?expertiseArea={}", expertiseArea);
+
+        // 参数校验:ID不能为空
+        if (expertiseArea == null || expertiseArea.getId() == null) {
+            log.warn("编辑领域信息失败:ID为空");
+            return R.fail("ID不能为空");
+        }
+
+        return expertiseAreaService.editExpertiseArea(expertiseArea);
+    }
+
+    @ApiOperation("删除领域信息")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @DeleteMapping("/deleteExpertiseArea")
+    public R<Boolean> deleteExpertiseArea(@RequestParam(value = "id", required = true) Integer id) {
+        log.info("LawyerExpertiseAreaController.deleteExpertiseArea?id={}", id);
+        if (id == null) {
+            log.warn("删除领域信息失败:ID为空");
+            return R.fail("ID不能为空");
+        }
+        return expertiseAreaService.deleteExpertiseArea(id);
+    }
+
+    @ApiOperation("保存或更新领域信息")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/saveOrUpdate")
+    public R<LawyerExpertiseArea> saveOrUpdate(@RequestBody LawyerExpertiseArea expertiseArea) {
+        log.info("LawyerExpertiseAreaController.saveOrUpdate?expertiseArea={}", expertiseArea);
+        boolean result = expertiseAreaService.saveOrUpdate(expertiseArea);
+        if (result) {
+            return R.data(expertiseArea);
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("通用列表查询")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "expertiseAreaInfo", value = "领域信息(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "deleteFlag", value = "删除标记, 0:未删除, 1:已删除", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<List<LawyerExpertiseArea>> getList(@ModelAttribute LawyerExpertiseArea expertiseArea) {
+        log.info("LawyerExpertiseAreaController.getList?expertiseArea={}", expertiseArea);
+        List<LawyerExpertiseArea> list = QueryBuilder.of(expertiseArea)
+                .likeFields("expertiseAreaInfo")  // 领域信息支持模糊查询
+                .build()
+                .list(expertiseAreaService);
+        return R.data(list);
+    }
+
+    @ApiOperation("通用分页查询")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "expertiseAreaInfo", value = "领域信息(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "deleteFlag", value = "删除标记, 0:未删除, 1:已删除", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<LawyerExpertiseArea>> getPage(@ModelAttribute LawyerExpertiseArea expertiseArea,
+                                                 @RequestParam(defaultValue = "1") int page,
+                                                 @RequestParam(defaultValue = "10") int size) {
+        log.info("LawyerExpertiseAreaController.getPage?expertiseArea={},page={},size={}", expertiseArea, page, size);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        IPage<LawyerExpertiseArea> pageResult = QueryBuilder.of(expertiseArea)
+                .likeFields("expertiseAreaInfo")  // 领域信息支持模糊查询
+                .page(pageNum, pageSize)
+                .build()
+                .page(expertiseAreaService);
+        return R.data(pageResult);
+    }
+}
+

+ 107 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerImgController.java

@@ -0,0 +1,107 @@
+package shop.alien.lawyer.controller;
+
+import com.alibaba.nacos.common.utils.CollectionUtils;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerImg;
+import shop.alien.entity.store.vo.LawyerImgTypeVo;
+import shop.alien.lawyer.service.LawyerImgService;
+
+import java.util.List;
+
+/**
+ * 律师图片Controller
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-律师图片"})
+@ApiSort(12)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/img")
+@RequiredArgsConstructor
+public class LawyerImgController {
+
+    private final LawyerImgService lawyerImgService;
+
+    @ApiOperation("获取图片")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerId", value = "律师ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "imgType", value = "图片类型, 0:其他, 1:头像, 2:执业证照片, 3:身份证正面, 4:身份证反面, 5:案例图片, 6:相册图片, 7:资质证书, 8:荣誉证书, 9:工作照片", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getByLawyerId")
+    public R<List<LawyerImg>> getByLawyerId(Integer lawyerId, Integer imgType) {
+        log.info("LawyerImgController.getByLawyerId?lawyerId={}&imgType={}", lawyerId, imgType);
+        return R.data(lawyerImgService.getLawyerImg(lawyerId, imgType));
+    }
+
+    @ApiOperation("获取所有图片类型和数量")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({@ApiImplicitParam(name = "lawyerId", value = "律师ID", dataType = "Integer", paramType = "query", required = true)})
+    @GetMapping("/getLawyerImgTypeCount")
+    public R<List<LawyerImgTypeVo>> getLawyerImgTypeCount(Integer lawyerId) {
+        log.info("LawyerImgController.getLawyerImgTypeCount?lawyerId={}", lawyerId);
+        return R.data(lawyerImgService.getLawyerImgTypeCount(lawyerId));
+    }
+
+    @ApiOperation("新增或修改图片")
+    @ApiOperationSupport(order = 3)
+    @PostMapping("/saveOrUpdate")
+    public R<String> saveOrUpdate(@RequestBody List<LawyerImg> lawyerImgList) {
+        log.info("LawyerImgController.saveOrUpdate?lawyerImgList={}", lawyerImgList);
+        if (CollectionUtils.isEmpty(lawyerImgList)) {
+            return R.fail("图片列表不能为空");
+        }
+        Integer id = lawyerImgList.get(0).getId();
+        if (lawyerImgService.saveOrUpdateBatch(lawyerImgList)) {
+            if (null != id) {
+                return R.success("修改成功");
+            }
+            return R.success("新增成功");
+        }
+        return R.fail("失败");
+    }
+
+    @ApiOperation(value = "删除图片")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/delete")
+    public R<String> delete(@RequestBody List<Integer> ids) {
+        log.info("LawyerImgController.delete?ids={}", ids);
+        if (lawyerImgService.removeByIds(ids)) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @ApiOperation("获取图片URL列表")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "律师ID", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "imgType", value = "图片类型", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getUrl")
+    public R<List<String>> getUrl(String id, Integer imgType) {
+        log.info("LawyerImgController.getUrl?id={}&imgType={}", id, imgType);
+        return R.data(lawyerImgService.getUrl(id, imgType));
+    }
+
+    @ApiOperation("通过businessId获取图片")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerId", value = "律师ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "imgType", value = "图片类型, 0:其他, 1:头像, 2:执业证照片, 3:身份证正面, 4:身份证反面, 5:案例图片, 6:相册图片, 7:资质证书, 8:荣誉证书, 9:工作照片", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "businessId", value = "业务ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getByBusinessId")
+    public R<List<LawyerImg>> getByBusinessId(Integer lawyerId, Integer imgType, Integer businessId) {
+        log.info("LawyerImgController.getByBusinessId?lawyerId={}&imgType={}&businessId={}", lawyerId, imgType, businessId);
+        return R.data(lawyerImgService.getByBusinessId(lawyerId, imgType, businessId));
+    }
+}
+

+ 150 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerLegalProblemScenarioController.java

@@ -0,0 +1,150 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerLegalProblemScenario;
+import shop.alien.entity.store.dto.LawyerLegalProblemScenarioStatusDto;
+import shop.alien.lawyer.service.LawyerLegalProblemScenarioService;
+import shop.alien.mapper.LawyerImgMapper;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.util.List;
+
+/**
+ * 法律问题场景 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-法律问题场景"})
+@ApiSort(11)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/legalProblemScenar")
+@RequiredArgsConstructor
+public class LawyerLegalProblemScenarioController {
+
+    private final LawyerLegalProblemScenarioService lawyerLegalProblemScenarioService;
+
+    private final LawyerImgMapper lawyerImgMapper;
+
+    @ApiOperation("新增法律问题场景")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/addLawyerLegalProblemScenar")
+    public R<LawyerLegalProblemScenario> addLawyerLegalProblemScenar(@RequestBody LawyerLegalProblemScenario lawyerLegalProblemScenario) {
+        log.info("LawyerLegalProblemScenarioController.addLawyerLegalProblemScenar?lawyerLegalProblemScenario={}", lawyerLegalProblemScenario);
+        return lawyerLegalProblemScenarioService.addLawyerLegalProblemScenario(lawyerLegalProblemScenario);
+    }
+
+    @ApiOperation("编辑法律问题场景")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/editLawyerLegalProblemScenar")
+    public R<LawyerLegalProblemScenario> editLawyerLegalProblemScenar(@RequestBody LawyerLegalProblemScenario lawyerLegalProblemScenario) {
+        log.info("LawyerLegalProblemScenarioController.editLawyerLegalProblemScenar?lawyerLegalProblemScenario={}", lawyerLegalProblemScenario);
+        return lawyerLegalProblemScenarioService.editLawyerLegalProblemScenario(lawyerLegalProblemScenario);
+    }
+
+    @ApiOperation("删除法律问题场景")
+    @ApiOperationSupport(order = 3)
+    @DeleteMapping("/deleteLawyerLegalProblemScenar")
+    public R<Boolean> deleteLawyerLegalProblemScenar(@RequestParam(value = "id") Integer id) {
+        log.info("LawyerLegalProblemScenarioController.deleteLawyerLegalProblemScenar?id={}", id);
+        return lawyerLegalProblemScenarioService.deleteLawyerLegalProblemScenario(id);
+    }
+
+    @ApiOperation("保存或更新法律问题场景")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/saveOrUpdate")
+    public R<LawyerLegalProblemScenario> saveOrUpdate(@RequestBody LawyerLegalProblemScenario lawyerLegalProblemScenario) {
+        log.info("LawyerLegalProblemScenarioController.saveOrUpdate?lawyerLegalProblemScenario={}", lawyerLegalProblemScenario);
+        boolean result = lawyerLegalProblemScenarioService.saveOrUpdate(lawyerLegalProblemScenario);
+        if (result) {
+            return R.data(lawyerLegalProblemScenario);
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("通用列表查询")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "code", value = "编号", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "name", value = "名称(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "level", value = "问题场景层级", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "parentId", value = "父类主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "parentCode", value = "父类编号", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "状态, 0:禁用, 1:启用", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<List<LawyerLegalProblemScenario>> getList(@ModelAttribute LawyerLegalProblemScenario lawyerLegalProblemScenario) {
+        log.info("LawyerLegalProblemScenarioController.getList?lawyerLegalProblemScenario={}", lawyerLegalProblemScenario);
+        // 使用 QueryBuilder 工具类进行列表查询
+        List<LawyerLegalProblemScenario> list = QueryBuilder.of(lawyerLegalProblemScenario)
+                .likeFields("name")  // 指定 name 字段使用模糊查询,其他字段使用等值查询
+                .build()
+                .list(lawyerLegalProblemScenarioService);
+
+        return R.data(list);
+    }
+
+    @ApiOperation("通用分页查询")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "code", value = "编号", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "name", value = "名称(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "level", value = "问题场景层级", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "parentId", value = "父类主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "parentCode", value = "父类编号", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "状态, 0:禁用, 1:启用", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<LawyerLegalProblemScenario>> getPage(@ModelAttribute LawyerLegalProblemScenario lawyerLegalProblemScenario,
+                                                        @RequestParam(defaultValue = "1") int page,
+                                                        @RequestParam(defaultValue = "10") int size) {
+        log.info("LawyerLegalProblemScenarioController.getPage?lawyerLegalProblemScenario={},page={},size={}",
+                lawyerLegalProblemScenario, page, size);
+        // 参数校验(确保大于0)
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        // 使用 QueryBuilder 工具类进行分页查询
+        IPage<LawyerLegalProblemScenario> pageResult = QueryBuilder.of(lawyerLegalProblemScenario)
+                .likeFields("name")  // 指定 name 字段使用模糊查询,其他字段使用等值查询
+                .page(pageNum, pageSize)
+                .build()
+                .page(lawyerLegalProblemScenarioService);
+        return R.data(pageResult);
+    }
+
+    @ApiOperation("根据一级场景主键获取分类树(包含一级场景及其下所有二级和三级分类)")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "一级场景主键", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getCategoryTree")
+    public R<LawyerLegalProblemScenario> getCategoryTree(@RequestParam Integer id) {
+        log.info("LawyerLegalProblemScenarioController.getCategoryTree?id={}", id);
+        return lawyerLegalProblemScenarioService.getCategoryTreeByFirstLevelId(id);
+    }
+
+    @ApiOperation("设置法律场景启用/禁用状态")
+    @ApiOperationSupport(order = 8)
+    @PostMapping("/setStatus")
+    public R<Boolean> setStatus(@RequestBody LawyerLegalProblemScenarioStatusDto request) {
+        log.info("LawyerLegalProblemScenarioController.setStatus?request={}", request);
+        return lawyerLegalProblemScenarioService.setStatus(request.getId(), request.getStatus());
+    }
+
+}
+

+ 150 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerNoticeController.java

@@ -0,0 +1,150 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.vo.LifeNoticeVo;
+import shop.alien.lawyer.service.LawyerNoticeService;
+
+/**
+ * 律师通知控制器
+ *
+ * @author system
+ */
+@Api(tags = {"律师模块-通知"})
+@Slf4j
+@RestController
+@CrossOrigin
+@RequestMapping("/lawyerNotice")
+@RequiredArgsConstructor
+public class LawyerNoticeController {
+
+    private final LawyerNoticeService lawyerNoticeService;
+
+    /**
+     * 获取律师通知列表
+     *
+     * @param pageNum    页码,从1开始
+     * @param pageSize   每页大小
+     * @param receiverId 接收人ID
+     * @param noticeType 通知类型:0-系统通知和订单提醒之外的类型,1-系统通知,2-订单提醒
+     * @return 分页通知列表
+     */
+    @GetMapping("/list")
+    @ApiOperation("律师通知列表")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页码", dataType = "Integer", paramType = "query", required = true, example = "1"),
+            @ApiImplicitParam(name = "pageSize", value = "每页大小", dataType = "Integer", paramType = "query", required = true, example = "10"),
+            @ApiImplicitParam(name = "receiverId", value = "接收人ID", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "noticeType", value = "通知类型:0-系统通知和订单提醒之外的类型,1-系统通知,2-订单提醒", dataType = "Integer", paramType = "query", example = "0")})
+    public R<IPage<LifeNoticeVo>> getLawyerNoticeList(
+            @RequestParam Integer pageNum,
+            @RequestParam Integer pageSize,
+            @RequestParam String receiverId,
+            @RequestParam(defaultValue = "0") Integer noticeType) {
+        log.info("LawyerNoticeController.getLawyerNoticeList, pageNum={}, pageSize={}, receiverId={}, noticeType={}", 
+                pageNum, pageSize, receiverId, noticeType);
+        
+        // 参数校验
+        if (pageNum == null || pageNum < 1) {
+            log.warn("分页参数无效,pageNum={}", pageNum);
+            return R.fail("页码必须大于0");
+        }
+        if (pageSize == null || pageSize < 1) {
+            log.warn("分页参数无效,pageSize={}", pageSize);
+            return R.fail("每页大小必须大于0");
+        }
+        if (StringUtils.isBlank(receiverId)) {
+            log.warn("接收人ID为空");
+            return R.fail("接收人ID不能为空");
+        }
+        if (noticeType == null || noticeType < 0) {
+            log.warn("通知类型参数无效,noticeType={}", noticeType);
+            return R.fail("通知类型参数无效");
+        }
+        
+        return lawyerNoticeService.getLawyerNoticeList(pageNum, pageSize, receiverId, noticeType);
+    }
+
+    /**
+     * 查询接收人是否有未读通知
+     *
+     * @param receiverId 接收人ID,不能为空
+     * @return 是否有未读通知,true-有未读,false-无未读
+     */
+    @GetMapping("/hasUnread")
+    @ApiOperation("查询是否有未读通知")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParam(name = "receiverId", value = "接收人ID", dataType = "String", paramType = "query", required = true)
+    public R<Boolean> hasUnreadNotice(@RequestParam String receiverId) {
+        log.info("LawyerNoticeController.hasUnreadNotice, receiverId={}", receiverId);
+        
+        // 参数校验
+        if (StringUtils.isBlank(receiverId)) {
+            log.warn("接收人ID为空");
+            return R.fail("接收人ID不能为空");
+        }
+        
+        return lawyerNoticeService.hasUnreadNotice(receiverId);
+    }
+    /**
+     * 标记通知为已读
+     * <p>
+     * 根据通知ID将指定通知标记为已读状态
+     * </p>
+     * <p>
+     * 处理流程:
+     * 1. 参数校验:通知ID必须大于0且不能为null
+     * 2. 调用Service层方法执行更新操作
+     * 3. 根据更新结果返回相应的响应
+     * </p>
+     *
+     * @param id 通知ID,必须大于0且不能为null
+     * @return 统一响应结果,true表示已读成功,false表示已读失败
+     * @author system
+     * @since 2025-01-XX
+     */
+    @GetMapping("/readNoticeById")
+    @ApiOperation("通知已读")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "通知ID(主键id)", dataType = "Integer", paramType = "query", required = true)
+    })
+    public R<Boolean> readNoticeById(@RequestParam(value = "id", required = true) Integer id) {
+        log.info("标记通知为已读请求开始,id={}", id);
+
+        // 参数校验
+        if (id == null || id <= 0) {
+            log.warn("标记通知为已读请求失败:通知ID为空或无效,id={}", id);
+            return R.fail("通知ID不能为空且必须大于0");
+        }
+
+        try {
+            // 调用Service层方法执行更新
+            int affectedRows = lawyerNoticeService.readNoticeById(id);
+
+            // 根据更新结果返回响应
+            if (affectedRows > 0) {
+                log.info("标记通知为已读请求成功,id={},受影响记录数={}", id, affectedRows);
+                return R.success("已读成功");
+            } else {
+                log.warn("标记通知为已读请求失败,id={},受影响记录数={},可能是通知不存在或已被删除", id, affectedRows);
+                return R.fail("已读失败,通知不存在或已被删除");
+            }
+        } catch (IllegalArgumentException e) {
+            log.warn("标记通知为已读请求参数校验失败,id={},错误信息:{}", id, e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (RuntimeException e) {
+            log.error("标记通知为已读请求异常,id={},异常信息:{}", id, e.getMessage(), e);
+            return R.fail("标记通知为已读失败:" + e.getMessage());
+        } catch (Exception e) {
+            log.error("标记通知为已读请求未知异常,id={},异常信息:{}", id, e.getMessage(), e);
+            return R.fail("标记通知为已读失败,请稍后重试");
+        }
+    }
+}

+ 113 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerPaymentTransactionController.java

@@ -0,0 +1,113 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerPaymentTransaction;
+import shop.alien.lawyer.service.LawyerPaymentTransactionService;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.util.List;
+
+/**
+ * 支付交易 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-支付交易"})
+@ApiSort(20)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/paymentTransaction")
+@RequiredArgsConstructor
+public class LawyerPaymentTransactionController {
+
+    private final LawyerPaymentTransactionService paymentTransactionService;
+
+    @ApiOperation("新增支付交易")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/addPaymentTransaction")
+    public R<LawyerPaymentTransaction> addPaymentTransaction(@RequestBody LawyerPaymentTransaction paymentTransaction) {
+        log.info("LawyerPaymentTransactionController.addPaymentTransaction?paymentTransaction={}", paymentTransaction);
+        return paymentTransactionService.addPaymentTransaction(paymentTransaction);
+    }
+
+    @ApiOperation("编辑支付交易")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/editPaymentTransaction")
+    public R<LawyerPaymentTransaction> editPaymentTransaction(@RequestBody LawyerPaymentTransaction paymentTransaction) {
+        log.info("LawyerPaymentTransactionController.editPaymentTransaction?paymentTransaction={}", paymentTransaction);
+        return paymentTransactionService.editPaymentTransaction(paymentTransaction);
+    }
+
+    @ApiOperation("删除支付交易")
+    @ApiOperationSupport(order = 3)
+    @DeleteMapping("/deletePaymentTransaction")
+    public R<Boolean> deletePaymentTransaction(@RequestParam(value = "id") Integer id) {
+        log.info("LawyerPaymentTransactionController.deletePaymentTransaction?id={}", id);
+        return paymentTransactionService.deletePaymentTransaction(id);
+    }
+
+    @ApiOperation("保存或更新支付交易")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/saveOrUpdate")
+    public R<LawyerPaymentTransaction> saveOrUpdate(@RequestBody LawyerPaymentTransaction paymentTransaction) {
+        log.info("LawyerPaymentTransactionController.saveOrUpdate?paymentTransaction={}", paymentTransaction);
+        boolean result = paymentTransactionService.saveOrUpdate(paymentTransaction);
+        if (result) {
+            return R.data(paymentTransaction);
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("通用列表查询")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "consultationOrderId", value = "咨询订单ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "transactionStatus", value = "交易状态", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<List<LawyerPaymentTransaction>> getList(@ModelAttribute LawyerPaymentTransaction paymentTransaction) {
+        log.info("LawyerPaymentTransactionController.getList?paymentTransaction={}", paymentTransaction);
+        List<LawyerPaymentTransaction> list = QueryBuilder.of(paymentTransaction)
+                .build()
+                .list(paymentTransactionService);
+        return R.data(list);
+    }
+
+    @ApiOperation("通用分页查询")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "consultationOrderId", value = "咨询订单ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "transactionStatus", value = "交易状态", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<LawyerPaymentTransaction>> getPage(@ModelAttribute LawyerPaymentTransaction paymentTransaction,
+                                                      @RequestParam(defaultValue = "1") int page,
+                                                      @RequestParam(defaultValue = "10") int size) {
+        log.info("LawyerPaymentTransactionController.getPage?paymentTransaction={},page={},size={}", paymentTransaction, page, size);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        IPage<LawyerPaymentTransaction> pageResult = QueryBuilder.of(paymentTransaction)
+                .page(pageNum, pageSize)
+                .build()
+                .page(paymentTransactionService);
+        return R.data(pageResult);
+    }
+}
+

+ 114 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerServiceAreaController.java

@@ -0,0 +1,114 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerServiceArea;
+
+import shop.alien.lawyer.service.LawyerServiceAreaService;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.util.List;
+
+/**
+ * 律师服务领域关联 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-律师服务领域关联"})
+@ApiSort(17)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/serviceArea")
+@RequiredArgsConstructor
+public class LawyerServiceAreaController {
+
+    private final LawyerServiceAreaService lawyerServiceAreaService;
+
+    @ApiOperation("新增律师服务领域")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/addLawyerServiceArea")
+    public R<LawyerServiceArea> addLawyerServiceArea(@RequestBody LawyerServiceArea lawyerServiceArea) {
+        log.info("LawyerServiceAreaController.addLawyerServiceArea?lawyerServiceArea={}", lawyerServiceArea);
+        return lawyerServiceAreaService.addLawyerServiceArea(lawyerServiceArea);
+    }
+
+    @ApiOperation("编辑律师服务领域")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/editLawyerServiceArea")
+    public R<LawyerServiceArea> editLawyerServiceArea(@RequestBody LawyerServiceArea lawyerServiceArea) {
+        log.info("LawyerServiceAreaController.editLawyerServiceArea?lawyerServiceArea={}", lawyerServiceArea);
+        return lawyerServiceAreaService.editLawyerServiceArea(lawyerServiceArea);
+    }
+
+    @ApiOperation("删除律师服务领域")
+    @ApiOperationSupport(order = 3)
+    @DeleteMapping("/deleteLawyerServiceArea")
+    public R<Boolean> deleteLawyerServiceArea(@RequestParam(value = "id") Integer id) {
+        log.info("LawyerServiceAreaController.deleteLawyerServiceArea?id={}", id);
+        return lawyerServiceAreaService.deleteLawyerServiceArea(id);
+    }
+
+    @ApiOperation("保存或更新律师服务领域")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/saveOrUpdate")
+    public R<LawyerServiceArea> saveOrUpdate(@RequestBody LawyerServiceArea lawyerServiceArea) {
+        log.info("LawyerServiceAreaController.saveOrUpdate?lawyerServiceArea={}", lawyerServiceArea);
+        boolean result = lawyerServiceAreaService.saveOrUpdate(lawyerServiceArea);
+        if (result) {
+            return R.data(lawyerServiceArea);
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("通用列表查询")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "problemScenarioId", value = "法律问题场景ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "状态, 0:禁用, 1:启用", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<List<LawyerServiceArea>> getList(@ModelAttribute LawyerServiceArea lawyerServiceArea) {
+        log.info("LawyerServiceAreaController.getList?lawyerServiceArea={}", lawyerServiceArea);
+        List<LawyerServiceArea> list = QueryBuilder.of(lawyerServiceArea)
+                .build()
+                .list(lawyerServiceAreaService);
+        return R.data(list);
+    }
+
+    @ApiOperation("通用分页查询")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "problemScenarioId", value = "法律问题场景ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "状态, 0:禁用, 1:启用", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<LawyerServiceArea>> getPage(@ModelAttribute LawyerServiceArea lawyerServiceArea,
+                                               @RequestParam(defaultValue = "1") int page,
+                                               @RequestParam(defaultValue = "10") int size) {
+        log.info("LawyerServiceAreaController.getPage?lawyerServiceArea={},page={},size={}", lawyerServiceArea, page, size);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        IPage<LawyerServiceArea> pageResult = QueryBuilder.of(lawyerServiceArea)
+                .page(pageNum, pageSize)
+                .build()
+                .page(lawyerServiceAreaService);
+        return R.data(pageResult);
+    }
+}
+

+ 96 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerStatisticsController.java

@@ -0,0 +1,96 @@
+package shop.alien.lawyer.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.vo.LawyerDashboardVO;
+import shop.alien.entity.store.vo.LawyerOrderStatisticsVO;
+import shop.alien.lawyer.service.LawyerStatisticsService;
+
+/**
+ * 律师统计 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-统计管理"})
+@ApiSort(15)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/statistics")
+@RequiredArgsConstructor
+public class LawyerStatisticsController {
+
+    private final LawyerStatisticsService lawyerStatisticsService;
+
+    /**
+     * 获取律师订单统计数据
+     * <p>
+     * 获取律师的订单统计数据,包括本月订单量、本月收益、总订单量、总收益,
+     * 以及进行中、待接单、已退款等各状态订单的统计信息
+     * </p>
+     *
+     * @param lawyerUserId 律师用户ID,必须大于0
+     * @return 统一响应结果,包含订单统计数据
+     * @author system
+     * @since 2025-01-XX
+     */
+    @ApiOperation("获取律师订单统计数据")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getOrderStatistics")
+    public R<LawyerOrderStatisticsVO> getOrderStatistics(
+            @RequestParam(value = "lawyerUserId", required = true) Integer lawyerUserId) {
+        log.info("获取律师订单统计数据请求开始,lawyerUserId={}", lawyerUserId);
+
+        // 参数校验
+        if (!validateLawyerUserId(lawyerUserId)) {
+            log.warn("获取律师订单统计数据失败:律师用户ID为空或无效,lawyerUserId={}", lawyerUserId);
+            return R.fail("律师用户ID不能为空");
+        }
+
+        try {
+            R<LawyerOrderStatisticsVO> result = lawyerStatisticsService.getOrderStatistics(lawyerUserId);
+            log.info("获取律师订单统计数据请求完成,lawyerUserId={}", lawyerUserId);
+            return result;
+        } catch (Exception e) {
+            log.error("获取律师订单统计数据请求异常,lawyerUserId={},异常信息:{}",
+                    lawyerUserId, e.getMessage(), e);
+            return R.fail("获取统计数据失败,请稍后重试");
+        }
+    }
+
+    /**
+     * 校验律师用户ID
+     * <p>
+     * 检查律师用户ID是否为空或无效
+     * </p>
+     *
+     * @param lawyerUserId 律师用户ID
+     * @return true表示有效,false表示无效
+     */
+    private boolean validateLawyerUserId(Integer lawyerUserId) {
+        return lawyerUserId != null && lawyerUserId > 0;
+    }
+
+    @ApiOperation("获取律师仪表板数据(包含个人信息、订单统计和收益统计)")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getLawyerDashboard")
+    public R<LawyerDashboardVO> getLawyerDashboard(@RequestParam(value = "lawyerUserId", required = true) Integer lawyerUserId) {
+        log.info("LawyerStatisticsController.getLawyerDashboard?lawyerUserId={}", lawyerUserId);
+        if (lawyerUserId == null || lawyerUserId <= 0) {
+            log.warn("获取律师仪表板数据失败:律师用户ID为空或无效");
+            return R.fail("律师用户ID不能为空");
+        }
+        return lawyerStatisticsService.getLawyerDashboard(lawyerUserId);
+    }
+}
+

+ 424 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerStoreCommentController.java

@@ -0,0 +1,424 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartRequest;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreComment;
+import shop.alien.entity.store.dto.OrderReviewDto;
+import shop.alien.entity.store.dto.ReviewCommentRequestDto;
+import shop.alien.entity.store.dto.ReviewReplyDto;
+import shop.alien.entity.store.vo.LifeUserOrderCommentVo;
+import shop.alien.entity.store.vo.OrderReviewDetailVo;
+import shop.alien.entity.store.vo.OrderReviewVo;
+import shop.alien.entity.store.vo.ReviewCommentVo;
+import shop.alien.entity.store.vo.StoreCommentCountVo;
+import shop.alien.entity.store.vo.StoreCommentVo;
+import shop.alien.entity.store.vo.StoreCommitPercentVo;
+import shop.alien.lawyer.service.StoreCommentService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 评论表 前端控制器
+ *
+ * @author ssk
+ * @since 2025-01-02
+ */
+@Slf4j
+@Api(tags = {"二期-评论/评价"})
+@ApiSort(11)
+@CrossOrigin
+@RestController
+@RequestMapping("/storeComment")
+@RequiredArgsConstructor
+public class LawyerStoreCommentController {
+
+    private final StoreCommentService storeCommentService;
+
+    @ApiOperation("评论/评价列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "businessId", value = "业务id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "businessType", value = "业务类型(1:订单评论, 2:动态社区评论, 3:活动评论, 4:店铺打卡评论, 5:订单评价, 6:订单评论的评论)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "replyStatus", value = "回复状态(0:全部, 1:已回复, 2:未回复)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "commentLevel", value = "评论等级(0:全部, 1:好评, 2:中评, 3:差评)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "days", value = "查询时间, 多少天前", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "phoneId", value = "消息标识", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "userType", value = "评论的用户类型(0:商家, 其他:用户)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "tagId", value = "标签id)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "hasImage", value = "是否有图片(0否/1是)", dataType = "Boolean", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<IPage<StoreCommentVo>> getList(Integer pageNum, Integer pageSize, Integer businessId, Integer businessType, Integer storeId, Integer replyStatus, Integer commentLevel, Integer days, String phoneId, Integer userType, Integer tagId,@RequestParam(required = false, defaultValue = "false") Boolean hasImage) {
+        log.info("StoreCommentController.getList?pageNum={}&pageSize={}&businessId={}&businessType={}&storeId={}&replyStatus={}&commentLevel={}&days={}&phoneId={}&userType={}&tagId={}&hasImage={}", pageNum, pageSize, businessId, businessType, storeId, replyStatus, commentLevel, days, phoneId, userType, tagId, hasImage);
+        return R.data(storeCommentService.getList(pageNum, pageSize, businessId, businessType, storeId, replyStatus, commentLevel, days, phoneId, userType, tagId, hasImage));
+    }
+
+    @ApiOperation("获取最新一条评论/评价")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "businessId", value = "业务id", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "businessType", value = "业务类型(1:订单评论, 2:动态社区评论, 3:活动评论, 4:店铺打卡评论, 5:订单评价)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "userType", value = "用户类型(0:商家, 其他:用户)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getOne")
+    public R<StoreCommentVo> getOne(Integer businessId, Integer businessType, Integer storeId) {
+        log.info("StoreCommentController.getOne?businessId={}&businessType={}&storeId={}", businessId, businessType, storeId);
+        return R.data(storeCommentService.getOne(businessId, businessType, storeId));
+    }
+
+    @ApiOperation("评论/评价数量和评分")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "businessId", value = "业务id", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "businessType", value = "业务类型(1:订单评论, 2:动态社区评论, 3:活动评论, 4:店铺打卡评论, 5:订单评价)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getCommitCountAndScore")
+    public R<Map<String, Object>> getCommitCountAndScore(Integer businessId, Integer businessType, Integer storeId, String phoneId, Integer days) {
+        log.info("StoreCommentController.getCommitCountAndScore?businessId={}&businessType={}&storeId={}&phoneId={}&days={}", businessId, businessType, storeId, phoneId, days);
+        return R.data(storeCommentService.getCommitCountAndScore(businessId, businessType, storeId, phoneId, days));
+    }
+
+    @ApiOperation(value = "新增评论(旧)", notes = "0:成功, 1:失败, 2:文本内容异常, 3:图片内容异常")
+    @ApiImplicitParams({@ApiImplicitParam(name = "multipartRequest", value = "文件", dataType = "File", paramType = "query"),
+            @ApiImplicitParam(name = "businessId", value = "业务id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "businessType", value = "业务类型(1:订单评论, 2:动态社区评论, 3:活动评论)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "replyId", value = "回复id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "commentContent", value = "评论内容", dataType = "String", paramType = "query")
+    })
+    @PostMapping("/userComment")
+    public R<Integer> userComment(MultipartRequest multipartRequest,
+                                  Integer businessId,
+                                  Integer businessType,
+                                  Integer storeId,
+                                  Integer userId,
+                                  Integer replyId,
+                                  String commentContent) {
+        log.info("StoreCommentController.userComment?businessId={}&businessType={}&storeId={}&userId={}&replyId={}&commentContent={}", businessId, businessType, storeId, userId, replyId, commentContent);
+        Set<String> fileNameSet = multipartRequest.getMultiFileMap().keySet();
+        log.info(String.valueOf(fileNameSet.size()));
+        return R.data(storeCommentService.userComment(multipartRequest, businessId, businessType, storeId, userId, replyId, commentContent));
+    }
+
+    @ApiOperation(value = "新增或修改评论/评价", notes = "0:成功, 1:失败, 2:文本内容异常, 3:图片内容异常")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "multipartRequest", value = "文件", dataType = "File", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "businessId", value = "业务id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "businessType", value = "业务类型(1:订单评论, 2:动态社区评论, 3:活动评论, 4:店铺打卡评论, 5:订单评价)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "replyId", value = "回复id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "commentContent", value = "评论内容", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "score", value = "评分", dataType = "Double", paramType = "query"),
+            @ApiImplicitParam(name = "otherScore", value = "其他评分", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "isAnonymous", value = "是否匿名(0:否(默认), 1:是)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "evaluationTags", value = "评价标签", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "phoneId", value = "用户id", dataType = "String", paramType = "query")
+    })
+    @PostMapping("/saveComment")
+    public R<Integer> saveComment(MultipartRequest multipartRequest, Integer id, Integer businessId, Integer businessType, Integer storeId, Integer userId, Integer replyId, String commentContent, Double score, String otherScore, Integer isAnonymous, String evaluationTags, String phoneId) {
+        log.info("StoreCommentController.saveComment?id={}&businessId={}&businessType={}&storeId={}&userId={}&replyId={}&commentContent={}&score={}&otherScore={}&isAnonymous={}&evaluationTags={}&phoneId={}", id, businessId, businessType, storeId, userId, replyId, commentContent, score, otherScore, isAnonymous, evaluationTags, phoneId);
+        Set<String> fileNameSet = multipartRequest.getMultiFileMap().keySet();
+        log.info(String.valueOf(fileNameSet.size()));
+        return R.data(storeCommentService.addComment(multipartRequest, id, businessId, businessType, storeId, userId, replyId, commentContent, score, otherScore, isAnonymous, evaluationTags, phoneId));
+    }
+
+    @ApiOperation(value = "新增或修改评论/评价(new)", notes = "0:成功, 1:失败, 2:文本内容异常, 3:图片内容异常")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "multipartRequest", value = "文件", dataType = "File", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "businessId", value = "业务id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "businessType", value = "业务类型(1:订单评论, 2:动态社区评论, 3:活动评论, 4:店铺打卡评论, 5:订单评价)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "orderId", value = "订单id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerId", value = "律师id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "replyId", value = "回复id", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "commentContent", value = "评论内容", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "score", value = "评分", dataType = "Double", paramType = "query"),
+            @ApiImplicitParam(name = "otherScore", value = "其他评分", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "isAnonymous", value = "是否匿名(0:否(默认), 1:是)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "evaluationTags", value = "评价标签", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "phoneId", value = "用户id", dataType = "String", paramType = "query")
+    })
+    @PostMapping("/saveCommentNew")
+    public R<Integer> saveCommentNew(MultipartRequest multipartRequest, Integer id, Integer businessId, Integer businessType, Integer storeId, Integer orderId, Integer lawyerId, Integer userId, Integer replyId, String commentContent, Double score, String otherScore, Integer isAnonymous, String evaluationTags, String phoneId) {
+        log.info("StoreCommentController.saveCommentNew?id={}&businessId={}&businessType={}&storeId={}&orderId={}&lawyerId={}&userId={}&replyId={}&commentContent={}&score={}&otherScore={}&isAnonymous={}&evaluationTags={}&phoneId={}", id, businessId, businessType, storeId, orderId, lawyerId, userId, replyId, commentContent, score, otherScore, isAnonymous, evaluationTags, phoneId);
+        Set<String> fileNameSet = multipartRequest.getMultiFileMap().keySet();
+        log.info(String.valueOf(fileNameSet.size()));
+        return R.data(storeCommentService.addCommentNew(multipartRequest, id, businessId, businessType, storeId, orderId, lawyerId, userId, replyId, commentContent, score, otherScore, isAnonymous, evaluationTags, phoneId));
+    }
+
+    @ApiOperation(value = "回复率, 评价比例")
+    @ApiImplicitParams({@ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query", required = true)})
+    @GetMapping("/getCommitPercent")
+    public R<StoreCommitPercentVo> getCommitPercent(@RequestParam("storeId") Integer storeId) {
+        return R.data(storeCommentService.getCommitPercent(storeId));
+    }
+
+    @ApiOperation(value = "删除评论/评价")
+    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "评论主键", dataType = "Integer", paramType = "query", required = true)})
+    @GetMapping("/delete")
+    public R<Boolean> delete(Integer id) {
+        if (storeCommentService.removeById(id)) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @ApiOperation("未评价/已评价列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "type", value = "1:未评价, 2:已评价", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户id", dataType = "String", paramType = "query", required = true),
+    })
+    @GetMapping("/getCommentOrderPage")
+    public R<IPage<LifeUserOrderCommentVo>> getCommentOrderPage(Integer pageNum, Integer pageSize, Integer type, String userId) {
+        log.info("StoreCommentController.getCommentOrderPage?pageNum={}&pageSize={}&type={}&userId={}", pageNum, pageSize, type, userId);
+        return R.data(storeCommentService.getCommentOrderPage(pageNum, pageSize, type, userId));
+    }
+
+    @ApiOperation("当前用户全部评价(店铺+律师)")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "pageSize", value = "页容", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "type", value = "预留字段,传1或2均可", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", paramType = "query", required = true),
+    })
+    @GetMapping("/getUserAllCommentsPage")
+    public R<IPage<LifeUserOrderCommentVo>> getUserAllCommentsPage(Integer pageNum, Integer pageSize, Integer type, Integer userId) {
+        log.info("StoreCommentController.getUserAllCommentsPage?pageNum={}&pageSize={}&type={}&userId={}", pageNum, pageSize, type, userId);
+        return R.data(storeCommentService.getUserAllCommentsPage(pageNum, pageSize, userId));
+    }
+
+    @ApiOperation("获取店铺评价计数统计")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getAppraiseCount")
+    public R<StoreCommentCountVo> getAppraiseCount(@RequestParam("storeId") Integer storeId) {
+        log.info("StoreCommentController.getAppraiseCount?storeId={}", storeId);
+        return R.data(storeCommentService.getAppraiseCount(storeId));
+    }
+
+    // ==================== 订单评价相关接口(迁移自 OrderReviewController)====================
+
+    @ApiOperation("创建订单评价(只有订单用户才能评价)")
+    @ApiOperationSupport(order = 20)
+    @PostMapping("/orderReview/create")
+    public R<StoreComment> createOrderReview(@RequestBody OrderReviewDto reviewDto) {
+        log.info("StoreCommentController.createOrderReview?reviewDto={}", reviewDto);
+        if (reviewDto.getUserId() == null) {
+            return R.fail("用户未登录");
+        }
+        return storeCommentService.createOrderReview(reviewDto);
+    }
+
+    @ApiOperation("获取评价详情(包含评论和回复)")
+    @ApiOperationSupport(order = 21)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "orderId", value = "评价ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentUserId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/orderReview/detail")
+    public R<OrderReviewDetailVo> getOrderReviewDetail(
+            @RequestParam(required = false) Integer orderId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("StoreCommentController.getOrderReviewDetail?reviewId={}, currentUserId={}", orderId, currentUserId);
+        if (orderId == null) {
+            return R.fail("订单id不为空");
+        }
+        return storeCommentService.getOrderReviewDetail(orderId, currentUserId);
+    }
+
+    @ApiOperation("点赞评价")
+    @ApiOperationSupport(order = 22)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/orderReview/like")
+    public R<Boolean> likeOrderReview(@RequestBody StoreComment storeComment) {
+        Integer reviewId = storeComment.getId();
+        Integer userId = storeComment.getUserId();
+        log.info("StoreCommentController.likeOrderReview?reviewId={}, userId={}", reviewId, userId);
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        if (reviewId == null) {
+            return R.fail("评价ID不能为空");
+        }
+        return storeCommentService.likeOrderReview(reviewId, userId);
+    }
+
+    @ApiOperation("取消点赞评价")
+    @ApiOperationSupport(order = 23)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/orderReview/cancelLike")
+    public R<Boolean> cancelLikeOrderReview(@RequestBody StoreComment storeComment) {
+        Integer reviewId = storeComment.getId();
+        Integer userId = storeComment.getUserId();
+        log.info("StoreCommentController.cancelLikeOrderReview?reviewId={}, userId={}", reviewId, userId);
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        return storeCommentService.cancelLikeOrderReview(reviewId, userId);
+    }
+
+    @ApiOperation("分页查询评价列表")
+    @ApiOperationSupport(order = 24)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "orderId", value = "订单ID", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "userId", value = "评价用户ID", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/orderReview/list")
+    public R<IPage<OrderReviewVo>> getOrderReviewList(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int size,
+            @RequestParam(required = false) Integer orderId,
+            @RequestParam(required = false) Integer lawyerUserId,
+            @RequestParam(required = false) Integer userId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("StoreCommentController.getOrderReviewList?page={}, size={}, orderId={}, lawyerUserId={}, userId={}, currentUserId={}",
+                page, size, orderId, lawyerUserId, userId, currentUserId);
+        return storeCommentService.getOrderReviewList(page, size, orderId, lawyerUserId, userId, currentUserId);
+    }
+
+    @ApiOperation("根据订单ID查询评价")
+    @ApiOperationSupport(order = 25)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "orderId", value = "订单ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/orderReview/getByOrderId")
+    public R<OrderReviewVo> getOrderReviewByOrderId(
+            @RequestParam Integer orderId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("StoreCommentController.getOrderReviewByOrderId?orderId={}, currentUserId={}", orderId, currentUserId);
+        return storeCommentService.getOrderReviewByOrderId(orderId, currentUserId);
+    }
+
+    // ==================== 评价评论相关接口(迁移自 ReviewCommentController)====================
+
+    @ApiOperation("创建评论(其他用户对评价的评论)")
+    @ApiOperationSupport(order = 30)
+    @PostMapping("/reviewComment/create")
+    public R<StoreComment> createReviewComment(@RequestBody StoreComment comment) {
+        log.info("StoreCommentController.createReviewComment?comment={}", comment);
+        if (comment.getUserId() == null) {
+            return R.fail("用户未登录");
+        }
+        return storeCommentService.createReviewComment(comment);
+    }
+
+    @ApiOperation("根据评价ID查询评论列表")
+    @ApiOperationSupport(order = 31)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/reviewComment/list")
+    public R<List<ReviewCommentVo>> getReviewCommentListByReviewId(
+            @RequestParam Integer reviewId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("StoreCommentController.getReviewCommentListByReviewId?reviewId={}, currentUserId={}", reviewId, currentUserId);
+        return storeCommentService.getReviewCommentListByReviewId(reviewId, currentUserId);
+    }
+
+    @ApiOperation("点赞评论")
+    @ApiOperationSupport(order = 32)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "commentId", value = "评论ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/reviewComment/like")
+    public R<Boolean> likeReviewComment(@RequestBody ReviewCommentRequestDto requestDto) {
+        log.info("StoreCommentController.likeReviewComment?commentId={}, userId={}", requestDto.getCommentId(), requestDto.getUserId());
+        if (requestDto.getUserId() == null) {
+            return R.fail("用户未登录");
+        }
+        if (requestDto.getCommentId() == null) {
+            return R.fail("评论ID不能为空");
+        }
+        return storeCommentService.likeReviewComment(requestDto);
+    }
+
+    @ApiOperation("取消点赞评论")
+    @ApiOperationSupport(order = 33)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "commentId", value = "评论ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/reviewComment/cancelLike")
+    public R<Boolean> cancelLikeReviewComment(@RequestBody ReviewCommentRequestDto requestDto) {
+        log.info("StoreCommentController.cancelLikeReviewComment?commentId={}, userId={}", requestDto.getCommentId(), requestDto.getUserId());
+        if (requestDto.getUserId() == null) {
+            return R.fail("用户未登录");
+        }
+        if (requestDto.getCommentId() == null) {
+            return R.fail("评论ID不能为空");
+        }
+        return storeCommentService.cancelLikeReviewComment(requestDto);
+    }
+
+    @ApiOperation("创建回复(用户对评论的回复)")
+    @ApiOperationSupport(order = 34)
+    @PostMapping("/reviewComment/reply/create")
+    public R<StoreComment> createReviewReply(@RequestBody ReviewReplyDto replyDto) {
+        log.info("StoreCommentController.createReviewReply?replyDto={}", replyDto);
+        if (replyDto.getUserId() == null) {
+            return R.fail("用户未登录");
+        }
+        return storeCommentService.createReviewReply(replyDto);
+    }
+
+    @ApiOperation("根据首评ID查询回复列表")
+    @ApiOperationSupport(order = 35)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "headId", value = "首评ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/reviewComment/reply/list")
+    public R<List<ReviewCommentVo>> getReviewReplyListByHeadId(
+            @RequestParam Integer headId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("StoreCommentController.getReviewReplyListByHeadId?headId={}, currentUserId={}", headId, currentUserId);
+        return storeCommentService.getReviewReplyListByHeadId(headId, currentUserId);
+    }
+
+    @ApiOperation("删除回复")
+    @ApiOperationSupport(order = 36)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "replyId", value = "回复ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/reviewComment/reply/delete")
+    public R<Boolean> deleteReviewReply(
+            @RequestParam Integer replyId,
+            @RequestParam Integer userId) {
+        log.info("StoreCommentController.deleteReviewReply?replyId={}, userId={}", replyId, userId);
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        return storeCommentService.deleteReviewReply(replyId, userId);
+    }
+}

+ 347 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserController.java

@@ -0,0 +1,347 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerUser;
+import shop.alien.entity.store.dto.LawyerRecommendedDto;
+import shop.alien.entity.store.dto.LawyerUserDto;
+import shop.alien.entity.store.vo.LawyerUserVo;
+import shop.alien.lawyer.service.LawyerUserService;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 律师用户 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-律师用户"})
+@ApiSort(10)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/user")
+@RequiredArgsConstructor
+public class LawyerUserController {
+
+    private final LawyerUserService lawyerUserService;
+
+    @ApiOperation("新增律师用户")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/addLawyerUser")
+    public R<LawyerUser> addLawyerUser(@RequestBody LawyerUser lawyerUser) {
+
+        log.info("LawyerUserController.addLawyerUser?lawyerUser={}", lawyerUser);
+        return lawyerUserService.addLawyerUser(lawyerUser);
+    }
+
+    @ApiOperation("编辑律师用户")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/editLawyerUser")
+    public R<LawyerUser> editLawyerUser(@RequestBody LawyerUser lawyerUser) {
+        log.info("LawyerUserController.editLawyerUser?lawyerUser={}", lawyerUser);
+        return lawyerUserService.editLawyerUser(lawyerUser);
+    }
+
+    @ApiOperation("删除律师用户")
+    @ApiOperationSupport(order = 3)
+    @DeleteMapping("/deleteLawyerUser")
+    public R<Boolean> deleteLawyerUser(@RequestParam(value = "id") Integer id) {
+        log.info("LawyerUserController.deleteLawyerUser?id={}", id);
+        return lawyerUserService.deleteLawyerUser(id);
+    }
+
+    @ApiOperation("保存或更新律师用户")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/saveOrUpdate")
+    public R<LawyerUser> saveOrUpdate(@RequestBody LawyerUser lawyerUser) {
+        log.info("LawyerUserController.saveOrUpdate?lawyerUser={}", lawyerUser);
+        boolean result = lawyerUserService.saveOrUpdate(lawyerUser);
+        if (result) {
+            return R.data(lawyerUser);
+        }
+        return R.fail("操作失败");
+    }
+
+    @ApiOperation("通用列表查询")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "name", value = "姓名(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "phone", value = "手机号(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "用户状态, 0:禁用, 1:启用", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "certificationStatus", value = "资质认证状态, 0:未认证, 1:认证中, 2:已认证, 3:认证失败", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<List<LawyerUser>> getList(@ModelAttribute LawyerUser lawyerUser) {
+        log.info("LawyerUserController.getList?lawyerUser={}", lawyerUser);
+        List<LawyerUser> list = QueryBuilder.of(lawyerUser)
+                .likeFields("name", "phone")  // 姓名和手机号支持模糊查询
+                .build()
+                .list(lawyerUserService);
+        return R.data(list);
+    }
+
+    @ApiOperation("通用分页查询")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "name", value = "姓名(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "phone", value = "手机号(支持模糊查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "status", value = "用户状态, 0:禁用, 1:启用", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "certificationStatus", value = "资质认证状态, 0:未认证, 1:认证中, 2:已认证, 3:认证失败", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<LawyerUser>> getPage(@ModelAttribute LawyerUser lawyerUser,
+                                        @RequestParam(defaultValue = "1") int page,
+                                        @RequestParam(defaultValue = "10") int size) {
+        log.info("LawyerUserController.getPage?lawyerUser={},page={},size={}", lawyerUser, page, size);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        IPage<LawyerUser> pageResult = QueryBuilder.of(lawyerUser)
+                .likeFields("name", "phone")  // 姓名和手机号支持模糊查询
+                .page(pageNum, pageSize)
+                .build()
+                .page(lawyerUserService);
+        return R.data(pageResult);
+    }
+
+    @ApiOperation("获取律师详情")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerId", value = "律师ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/detail")
+    public R<Map<String, Object>> getLawyerDetail(@RequestParam Integer lawyerId) {
+        log.info("LawyerUserController.getLawyerDetail?lawyerId={}", lawyerId);
+        return lawyerUserService.getLawyerDetail(lawyerId);
+    }
+
+    @ApiOperation("获取律师在线状态")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerIds", value = "律师ID数组(逗号分隔)", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/onlineStatus")
+    public R<Map<Integer, Boolean>> getLawyerOnlineStatus(@RequestParam String lawyerIds) {
+        log.info("LawyerUserController.getLawyerOnlineStatus?lawyerIds={}", lawyerIds);
+        return lawyerUserService.getLawyerOnlineStatus(lawyerIds);
+    }
+
+    @ApiOperation("获取推荐律师列表")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页码(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "每页数量(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "categoryId", value = "分类ID(可选)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerId", value = "律师ID(可选)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/recommend/list")
+    public R<IPage<LawyerUser>> getRecommendedLawyerList(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) Integer categoryId,
+            @RequestParam(required = false) Integer lawyerId) {
+        log.info("LawyerUserController.getRecommendedLawyerList?page={},pageSize={},categoryId={},lawyerId={}",
+                page, pageSize, categoryId, lawyerId);
+        return lawyerUserService.getRecommendedLawyerList(page, pageSize, categoryId, lawyerId);
+    }
+
+    @ApiOperation("根据会话获取推荐律师列表")
+    @ApiOperationSupport(order = 10)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "sessionId", value = "会话ID", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "messageId", value = "消息ID(可选)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/recommend/bySession")
+    public R<IPage<LawyerUser>> getRecommendedLawyersBySession(
+            @RequestParam String sessionId,
+            @RequestParam(required = false) Integer messageId) {
+        log.info("LawyerUserController.getRecommendedLawyersBySession?sessionId={},messageId={}", sessionId, messageId);
+        return lawyerUserService.getRecommendedLawyersBySession(sessionId, messageId);
+    }
+
+    @ApiOperation("通过昵称模糊查询律师(并保存搜索历史)")
+    @ApiOperationSupport(order = 11)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "nickName", value = "律师昵称(支持模糊查询,为空时返回所有律师)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "page", value = "页码(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "每页数量(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID(可选,用于保存搜索历史)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping("/searchByName")
+    public R<IPage<LawyerUser>> searchLawyerByName(
+            @RequestParam(required = false) String nickName,
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) Integer clientUserId) {
+        log.info("LawyerUserController.searchLawyerByName?nickName={},page={},pageSize={},clientUserId={}", nickName, page, pageSize, clientUserId);
+        return lawyerUserService.searchLawyerByName(nickName, page, pageSize, clientUserId);
+    }
+
+
+    @ApiOperation("根据AI返回场景获取推荐律师列表")
+    @ApiOperationSupport(order = 12)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页码(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "每页数量(默认5)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "categoryId", value = "场景ID(必传)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/aiRecommendList")
+    public R<IPage<LawyerUser>> getAiRecommendList(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "5") int size,
+            @RequestParam(required = true) Integer categoryId) {
+        log.info("LawyerUserController.aiRecommendList?page={},pageSize={},categoryId={}",
+                page, size, categoryId);
+        return lawyerUserService.getAiRecommendList(page, size, categoryId);
+    }
+
+
+    @ApiOperation("注销律师用户")
+    @ApiOperationSupport(order = 13)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "律师ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/logOutLawyerUser")
+    public R<Map<String, Object>> logOutLawyerUser(@RequestParam(value = "id") Integer id) {
+        log.info("LawyerUserController.logOutLawyerUser?id={}", id);
+        return lawyerUserService.logOutLawyerUser(id);
+    }
+
+
+    @ApiOperation("律师信息详情")
+    @ApiOperationSupport(order = 14)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerId", value = "律师ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getLawyerInfo")
+    public R<LawyerUserVo> getLawyerInfo(@RequestParam Integer lawyerId) {
+        log.info("LawyerUserController.getLawyerInfo?lawyerId={}", lawyerId);
+        return lawyerUserService.getLawyerInfo(lawyerId);
+    }
+
+
+    @ApiOperation("中台律师列表")
+    @ApiOperationSupport(order = 17)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "name", value = "姓名(支持模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "phone", value = "手机号(支持模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "firmId", value = "律所ID", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "startTime", value = "开始日期(用于查询执业开始日期practice_start_date,格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "endTime", value = "结束日期(用于查询执业开始日期practice_start_date,格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "page", value = "页码(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "每页数量(默认10)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/getLawyerList")
+    public R<IPage<LawyerUserVo>> getLawyerList(@RequestParam(required = false) String name,
+                                                @RequestParam(required = false) String phone,
+                                                @RequestParam(required = false) Integer firmId,
+                                                @RequestParam(required = false) String startTime,
+                                                @RequestParam(required = false) String endTime,
+                                                @RequestParam int page,
+                                                @RequestParam int size) {
+        log.info("LawyerUserController.getLawyerList?name={},phone={},firmId={},startTime={},endTime={},page={},size={}",
+                name, phone, firmId, startTime, endTime, page, size);
+        return lawyerUserService.getLawyerList(name, phone, firmId, startTime, endTime, page, size);
+    }
+
+    @ApiOperation("导出中台律师列表")
+    @ApiOperationSupport(order = 19)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "name", value = "姓名(支持模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "phone", value = "手机号(支持模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "firmId", value = "律所ID", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "startTime", value = "开始日期(用于查询执业开始日期practice_start_date,格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "endTime", value = "结束日期(用于查询执业开始日期practice_start_date,格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "pageNum", value = "页码(可选,不传或传0则导出全部,传值则导出本页)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "页容(可选,与pageNum配合使用)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping(value = "/exportLawyerList", produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
+    public void exportLawyerList(
+            HttpServletResponse response,
+            @RequestParam(required = false) String name,
+            @RequestParam(required = false) String phone,
+            @RequestParam(required = false) Integer firmId,
+            @RequestParam(required = false) String startTime,
+            @RequestParam(required = false) String endTime,
+            @RequestParam(required = false) Integer pageNum,
+            @RequestParam(required = false) Integer pageSize) throws Exception {
+        log.info("LawyerUserController.exportLawyerList?name={},phone={},firmId={},startTime={},endTime={},pageNum={},pageSize={}",
+                name, phone, firmId, startTime, endTime, pageNum, pageSize);
+        try {
+            lawyerUserService.exportLawyerList(response, name, phone, firmId, startTime, endTime, pageNum, pageSize);
+        } catch (Exception e) {
+            log.error("导出律师列表异常", e);
+            if (!response.isCommitted()) {
+                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                response.setContentType("application/json;charset=UTF-8");
+                response.getWriter().write("{\"code\":500,\"msg\":\"导出失败:" + e.getMessage() + "\"}");
+            }
+            throw e;
+        }
+    }
+
+
+
+
+    @ApiOperation("编辑新律师用户")
+    @ApiOperationSupport(order = 15)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerId", value = "律师ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "name", value = "律师姓名", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "phone", value = "律师手机号", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "address", value = "联系地址", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "introduction", value = "律师简介", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "headImg", value = "律师头像", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "certificationTime", value = "认证时间(从业时间??待确认)", dataType = "Data", paramType = "query"),
+    })
+    @PostMapping("/updateLawyerUser")
+    public R<LawyerUserVo> updateLawyerUser(@RequestBody LawyerUserVo lawyerUserVo) {
+        log.info("LawyerUserController.updateLawyerUser?lawyerUserVo={}", lawyerUserVo);
+        return lawyerUserService.updateLawyerUser(lawyerUserVo);
+    }
+
+    @ApiOperation("通过手机号查询律师信息")
+    @ApiOperationSupport(order = 16)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "phone", value = "手机号", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/getLawyerInfoByPhone")
+    public R<LawyerUser> getLawyerInfoByPhone(@RequestParam String phone) {
+        log.info("LawyerUserController.getLawyerInfoByPhone?phone={}", phone);
+        return lawyerUserService.getLawyerInfoByPhone(phone);
+    }
+
+    @ApiOperation("设置律师推荐状态")
+    @ApiOperationSupport(order = 18)
+    @PostMapping("/isRecommended")
+    public R<Boolean> isRecommended(@RequestBody LawyerRecommendedDto request) {
+        log.info("LawyerUserController.isRecommended?request={}", request);
+        return lawyerUserService.isRecommended(request.getLawyerId(), request.getIsRecommended());
+    }
+
+    @ApiOperation("编辑律师收费")
+    @ApiOperationSupport(order = 19)
+    @PostMapping("/updateLawyerUserCharge")
+    public R<LawyerUserVo> updateLawyerUserCharge(@RequestBody LawyerUserDto lawyerUserDto) {
+        log.info("LawyerUserController.updateLawyerUserCharge?lawyerUserDto={}", lawyerUserDto);
+        return lawyerUserService.updateLawyerUserCharge(lawyerUserDto);
+    }
+
+}
+

+ 129 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserLogInController.java

@@ -0,0 +1,129 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiOperationSupport;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerUser;
+import shop.alien.entity.store.dto.LawyerUserDto;
+import shop.alien.entity.store.vo.LawyerUserVo;
+import shop.alien.lawyer.config.BaseRedisService;
+import shop.alien.lawyer.service.LawyerUserLogInService;
+import shop.alien.mapper.LawyerUserMapper;
+
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * 门店用户 前端控制器
+ *
+ * @author ssk
+ * @since 2024-12-11
+ */
+@Slf4j
+@Api(tags = {"律师用户"})
+@ApiSort(8)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/user")
+@RequiredArgsConstructor
+public class LawyerUserLogInController {
+
+    private final LawyerUserLogInService lawyerUserService;
+    private final LawyerUserMapper lawyerUserMapper;
+    private final BaseRedisService baseRedisService;
+
+    @ApiOperation("律师用户注册")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/register")
+    public R<LawyerUserVo> register(@RequestBody LawyerUserDto lawyerUserDto) {
+        log.info("LawyerUserLogInController.login?lawyerUserDto={}", lawyerUserDto);
+        return lawyerUserService.register(lawyerUserDto);
+    }
+
+    @ApiOperation("律师用户验证码校验")
+    @ApiOperationSupport(order = 2)
+    @PostMapping("/checkMsgCode")
+    public R checkMsgCode(@RequestBody LawyerUserDto lawyerUserDto) {
+        log.info("LawyerUserLogInController.checkMsgCode?lawyerUserDto={}", lawyerUserDto);
+        return lawyerUserService.checkMsgCode(lawyerUserDto);
+    }
+
+    @ApiOperation("律师用户登录")
+    @ApiOperationSupport(order = 3)
+    @PostMapping("/login")
+    public R<LawyerUserVo> login(@RequestBody LawyerUserDto lawyerUserDto) {
+        log.info("LawyerUserLogInController.login?lawyerUserDto={}", lawyerUserDto);
+        LawyerUser lawyerUser = lawyerUserMapper.selectOne(new LambdaQueryWrapper<LawyerUser>()
+                .eq(LawyerUser::getPhone, lawyerUserDto.getPhone()));
+        if (null == lawyerUser) {
+            return R.fail("当前账号不存在,请先去注册账号!");
+        }
+        if (lawyerUser.getStatus() == 0) {
+            return R.fail("账号被禁用");
+        }
+        LawyerUser lawyerUser1 = new LawyerUser();
+        lawyerUser1.setId(lawyerUser.getId());
+        lawyerUser1.setIsOnline(1);
+        lawyerUserMapper.updateById(lawyerUser1);
+        return Optional.ofNullable(lawyerUser).
+                map(user -> lawyerUserDto.getIsPassword() ? checkPassword(user, lawyerUserDto.getPassword()) : lawyerUserService.createToKen(user)).
+                orElseGet(() -> R.fail("手机号不存在"));
+    }
+
+    @ApiOperation("律师用户登出")
+    @ApiOperationSupport(order = 4)
+    @PostMapping("/logout")
+    public R logout(@RequestBody LawyerUserDto lawyerUserDto) {
+        log.info("LawyerUserLogInController.logout?lawyerUserDto={}", lawyerUserDto);
+        return lawyerUserService.logout(lawyerUserDto);
+    }
+
+
+    @ApiOperation("律师用户修改密码")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/updatePassWord")
+    public R updatePassWord(@RequestBody LawyerUserDto lawyerUserDto) {
+        log.info("LawyerUserLogInController.updatePassWord?lawyerUserDto={}", lawyerUserDto);
+        try {
+            LawyerUser lawyerUser = lawyerUserMapper.selectOne(new LambdaQueryWrapper<LawyerUser>()
+                    .eq(LawyerUser::getPhone, lawyerUserDto.getPhone()));
+            if (null == lawyerUser) {
+                return R.fail("当前账号不存在,请先去注册账号!");
+            }
+            if (lawyerUser.getStatus() == 0) {
+                return R.fail("账号被禁用");
+            }
+            return lawyerUserService.updatePassWord(lawyerUserDto);
+        }catch (Exception e){
+            return R.fail("修改密码失败");
+        }
+    }
+
+    @ApiOperation("判断用户是否已经注册")
+    @ApiOperationSupport(order = 6)
+    @PostMapping("/checkUserRegister")
+    public R checkUserRegister(@RequestBody LawyerUserDto lawyerUserDto) {
+        log.info("LawyerUserLogInController.updatePassWord?lawyerUserDto={}", lawyerUserDto);
+        try {
+            Boolean res = lawyerUserService.checkRegister(lawyerUserDto);
+            return R.data(res);
+        }catch (Exception e){
+            return R.fail("判断失败");
+        }
+    }
+
+    private R<LawyerUserVo> checkPassword(LawyerUser lawyerUser, String password) {
+        return Objects.equals(password, lawyerUser.getPassword())
+                ? lawyerUserService.createToKen(lawyerUser)
+                : R.fail("密码错误");
+    }
+}

+ 94 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserSearchHistoryController.java

@@ -0,0 +1,94 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerUserSearchHistory;
+import shop.alien.lawyer.service.LawyerUserSearchHistoryService;
+
+
+import java.util.List;
+
+/**
+ * 用户搜索历史 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-用户搜索历史"})
+@ApiSort(22)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/userSearchHistory")
+@RequiredArgsConstructor
+public class LawyerUserSearchHistoryController {
+
+    private final LawyerUserSearchHistoryService userSearchHistoryService;
+
+    @ApiOperation("新增用户搜索历史(自动限制每个用户最多10条记录,超过则删除最老的记录)")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/addUserSearchHistory")
+    public R<LawyerUserSearchHistory> addUserSearchHistory(@RequestBody LawyerUserSearchHistory userSearchHistory) {
+        log.info("LawyerUserSearchHistoryController.addUserSearchHistory?userSearchHistory={}", userSearchHistory);
+        return userSearchHistoryService.addUserSearchHistory(userSearchHistory);
+    }
+
+    @ApiOperation("删除用户搜索历史")
+    @ApiOperationSupport(order = 2)
+    @DeleteMapping("/deleteUserSearchHistory")
+    public R<Boolean> deleteUserSearchHistory(@RequestParam(value = "id") Integer id) {
+        log.info("LawyerUserSearchHistoryController.deleteUserSearchHistory?id={}", id);
+        return userSearchHistoryService.deleteUserSearchHistory(id);
+    }
+
+    @ApiOperation("清空用户搜索历史")
+    @ApiOperationSupport(order = 3)
+    @DeleteMapping("/clearUserSearchHistory")
+    public R<Boolean> clearUserSearchHistory(@RequestParam(value = "clientUserId") Integer clientUserId) {
+        log.info("LawyerUserSearchHistoryController.clearUserSearchHistory?clientUserId={}", clientUserId);
+        return userSearchHistoryService.clearUserSearchHistory(clientUserId);
+    }
+
+    @ApiOperation("通用列表查询(按创建时间降序,新的在前)")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "searchType", value = "搜索类型", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getList")
+    public R<List<LawyerUserSearchHistory>> getList(@ModelAttribute LawyerUserSearchHistory userSearchHistory) {
+        log.info("LawyerUserSearchHistoryController.getList?userSearchHistory={}", userSearchHistory);
+        List<LawyerUserSearchHistory> list = userSearchHistoryService.getListWithOrder(userSearchHistory);
+        return R.data(list);
+    }
+
+    @ApiOperation("通用分页查询(按创建时间降序,新的在前)")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "id", value = "主键", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "clientUserId", value = "客户端用户ID", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "searchType", value = "搜索类型", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_Start", value = "创建时间开始(范围查询)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "createdTime_End", value = "创建时间结束(范围查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getPage")
+    public R<IPage<LawyerUserSearchHistory>> getPage(@ModelAttribute LawyerUserSearchHistory userSearchHistory,
+                                                     @RequestParam(defaultValue = "1") int page,
+                                                     @RequestParam(defaultValue = "10") int size) {
+        log.info("LawyerUserSearchHistoryController.getPage?userSearchHistory={},page={},size={}", userSearchHistory, page, size);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        IPage<LawyerUserSearchHistory> pageResult = userSearchHistoryService.getPageWithOrder(userSearchHistory, pageNum, pageSize);
+        return R.data(pageResult);
+    }
+}
+

+ 298 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerUserViolationController.java

@@ -0,0 +1,298 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerUserViolation;
+import shop.alien.entity.store.StoreDictionary;
+import shop.alien.entity.store.dto.LawyerUserViolationDto;
+import shop.alien.entity.store.vo.LawyerUserViolationVo;
+import shop.alien.entity.store.vo.LawyerViolationDetailVO;
+import shop.alien.lawyer.service.LawyerUserViolationService;
+
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * <p>
+ * 律师用户举报 前端控制器
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Api(tags = {"律师订单用户举报"})
+@Slf4j
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/user-violation")
+@RequiredArgsConstructor
+public class LawyerUserViolationController {
+
+    private final LawyerUserViolationService lawyerUserViolationService;
+
+    /**
+     * 用户举报接口
+     * <p>
+     * 用于律师用户提交举报信息,包括被举报用户信息、举报类型、举报内容等
+     * </p>
+     *
+     * @param lawyerUserViolation 举报信息对象,包含以下必填字段:
+     *                            <ul>
+     *                            <li>reportedUserId: 被举报用户ID</li>
+     *                            <li>reportingUserId: 举报用户ID</li>
+     *                            <li>violationType: 违规类型(1-12)</li>
+     *                            <li>reportContextType: 举报内容分类</li>
+     *                            </ul>
+     * @return 统一响应结果,成功返回"举报成功",失败返回"举报失败"
+     * @author system
+     * @since 2025-01-XX
+     */
+    @ApiOperation(value = "用户举报", notes = "提交用户举报信息,系统将自动发送通知消息")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reportedUserId", value = "被举报用户ID", required = true, dataType = "String", paramType = "body"),
+            @ApiImplicitParam(name = "reportingUserId", value = "举报用户ID", required = true, dataType = "String", paramType = "body"),
+            @ApiImplicitParam(name = "violationType", value = "违规类型1-服务态度差,2-专业能力差,3-响应时间超过24小时,4-其他原因", required = true, dataType = "String", paramType = "body"),
+            @ApiImplicitParam(name = "reportContextType", value = "举报内容分类", required = true, dataType = "String", paramType = "body"),
+            @ApiImplicitParam(name = "otherReasonContent", value = "其他原因具体内容(当violationType为12时必填)", required = false, dataType = "String", paramType = "body"),
+            @ApiImplicitParam(name = "reportEvidenceImg", value = "举报凭证图片", required = false, dataType = "String", paramType = "body")
+    })
+    @PostMapping("/userReporting")
+    public R<String> userReporting(@RequestBody LawyerUserViolation lawyerUserViolation) {
+        log.info("用户举报请求,被举报用户ID:{},举报用户ID:{}",
+                lawyerUserViolation.getReportedUserId(),
+                lawyerUserViolation.getReportingUserId());
+
+        try {
+            int result = lawyerUserViolationService.userReporting(lawyerUserViolation);
+            if (result > 0) {
+                log.info("用户举报成功,被举报用户ID:{},举报用户ID:{}",
+                        lawyerUserViolation.getReportedUserId(),
+                        lawyerUserViolation.getReportingUserId());
+                return R.success("举报成功");
+            } else {
+                log.warn("用户举报失败,被举报用户ID:{},举报用户ID:{}",
+                        lawyerUserViolation.getReportedUserId(),
+                        lawyerUserViolation.getReportingUserId());
+                return R.fail("举报失败");
+            }
+        } catch (RuntimeException e) {
+            // 业务异常,返回具体的错误信息
+            String errorMessage = e.getMessage();
+            log.warn("用户举报业务异常,被举报用户ID:{},举报用户ID:{},异常信息:{}",
+                    lawyerUserViolation.getReportedUserId(),
+                    lawyerUserViolation.getReportingUserId(),
+                    errorMessage);
+            return R.fail(StringUtils.isNotEmpty(errorMessage) ? errorMessage : "举报失败");
+        } catch (Exception e) {
+            log.error("用户举报异常,被举报用户ID:{},举报用户ID:{},异常信息:{}",
+                    lawyerUserViolation.getReportedUserId(),
+                    lawyerUserViolation.getReportingUserId(),
+                    e.getMessage(), e);
+            return R.fail("举报处理异常,请稍后重试");
+        }
+    }
+
+    @ApiOperation("举报结果")
+    @ApiOperationSupport(order = 2)
+    @GetMapping("/reportListByUserId")
+    public R<Map<String, Object>> reportListByUserId(@RequestParam(value = "userId") String userId) {
+        return R.data(lawyerUserViolationService.reportListByUserId(userId));
+    }
+
+    /**
+     * 根据ID查询举报详情
+     * <p>
+     * 根据举报ID查询举报详细信息,包括被举报人信息、举报原因、举报凭证等
+     * </p>
+     *
+     * @param id 举报记录ID
+     * @return 统一响应结果,包含举报详情VO对象
+     * @author system
+     * @since 2025-01-XX
+     */
+    @ApiOperation(value = "举报详细信息", notes = "根据举报ID查询举报详细信息")
+    @ApiOperationSupport(order = 3)
+    @GetMapping("/reportListById")
+    public R<LawyerUserViolationVo> reportListById(@RequestParam(value = "id") String id) {
+        log.info("查询举报详情请求,ID:{}", id);
+        try {
+            LawyerUserViolationVo violationVo = lawyerUserViolationService.reportListById(id);
+            if (violationVo == null) {
+                log.warn("举报记录不存在,ID:{}", id);
+                return R.fail("举报记录不存在");
+            }
+            return R.data(violationVo);
+        } catch (RuntimeException e) {
+            log.warn("查询举报详情业务异常,ID:{},异常信息:{}", id, e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("查询举报详情异常,ID:{},异常信息:{}", id, e.getMessage(), e);
+            return R.fail("查询举报详情失败,请稍后重试");
+        }
+    }
+
+    @ApiOperation("举报分页")
+    @ApiOperationSupport(order = 4)
+    @GetMapping("/page")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "pageNum", value = "页数", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "大小", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "orderNumber", value = "订单号", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "phone", value = "用户电话", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "reportedUserName", value = "律师名称", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "processingStatus", value = "状态(0:待处理,1:已通过,2:已驳回)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "reportedPhone", value = "律师电话", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "violationReason", value = "举报原因", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "startTime", value = "开始时间", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "endTime", value = "结束时间", dataType = "String", paramType = "query")
+    })
+    public R<IPage<LawyerUserViolationDto>> getViolationPage(
+            @RequestParam(defaultValue = "1") int pageNum,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) String orderNumber,
+            @RequestParam(required = false) String phone,
+            @RequestParam(required = false) String reportedUserName,
+            @RequestParam(required = false) String processingStatus,
+            @RequestParam(required = false) String reportedPhone,
+            @RequestParam(required = false) String violationReason,
+            @RequestParam(required = false) String startTime,
+            @RequestParam(required = false) String endTime
+
+    ) {
+        log.info("LawyerUserViolationController.getViolationPage?pageNum={},pageSize={},orderId={},processingStatus={},reportedUserName={},violationReason={}", pageNum, pageSize, orderNumber, processingStatus, reportedUserName, violationReason);
+        return R.data(lawyerUserViolationService.getViolationPage(pageNum, pageSize, orderNumber, phone, processingStatus, reportedUserName, reportedPhone, violationReason, startTime, endTime));
+    }
+
+    @ApiOperation(value = "举报审核")
+    @ApiOperationSupport(order = 5)
+    @GetMapping("/approve")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "id", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "processingStatus", value = "审批状态(1:审批成功,2:审批失败)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "reportResult", value = "处理结果", dataType = "String", paramType = "query")
+    })
+    @ResponseBody
+    public R<String> approveStoreInfo(@RequestParam("id") int id, @RequestParam("processingStatus") String processingStatus, @RequestParam("reportResult") String reportResult) {
+        log.info("LawyerUserViolationController.approveStoreInfo?id={}&processingStatus={}", id, processingStatus);
+        lawyerUserViolationService.approve(id, processingStatus, reportResult);
+        return R.success("审批完成");
+    }
+
+    @ApiOperation("查看详情")
+    @ApiOperationSupport(order = 6)
+    @GetMapping("/byId")
+    public LawyerUserViolationDto byId(@RequestParam(defaultValue = "10") int id) {
+        return lawyerUserViolationService.byId(id);
+    }
+
+    @ApiOperation("查看通知详情")
+    @ApiOperationSupport(order = 7)
+    @GetMapping("/byIdNotice")
+    public LawyerUserViolationDto byIdNotice(@RequestParam(defaultValue = "10") int id) {
+        return lawyerUserViolationService.byIdNotice(id);
+    }
+
+    /**
+     * 获取举报原因列表
+     * <p>
+     * 用于获取所有可用的律师违规举报原因选项,供前端下拉选择使用
+     * </p>
+     *
+     * @return 统一响应结果,包含举报原因字典列表
+     * @author system
+     * @since 2025-01-XX
+     */
+    @ApiOperation(value = "获取举报原因", notes = "获取所有可用的律师违规举报原因选项列表")
+    @ApiOperationSupport(order = 8)
+    @GetMapping("/getViolationReason")
+    public R<List<StoreDictionary>> getViolationReason() {
+        log.info("获取举报原因列表请求");
+        try {
+            List<StoreDictionary> violationReasonList = lawyerUserViolationService.getViolationReason();
+            log.info("获取举报原因列表成功,共{}条记录", violationReasonList.size());
+            return R.data(violationReasonList);
+        } catch (Exception e) {
+            log.error("获取举报原因列表异常,异常信息:{}", e.getMessage(), e);
+            return R.fail("获取举报原因列表失败,请稍后重试");
+        }
+    }
+
+    /**
+     * 根据订单ID查询举报详情
+     * <p>
+     * 查询举报详情信息,包括举报的详情信息、订单的详情信息以及订单律师名字相关信息
+     * </p>
+     * <p>
+     * 功能说明:
+     * 1. 通过订单ID查询订单、举报、律师关联信息
+     * 2. 校验订单是否存在以及是否存在举报记录
+     * 3. 填充举报人姓名和订单价格字符串等附加信息
+     * 4. 返回完整的举报详情信息
+     * </p>
+     *
+     * @param orderId 订单ID,必须大于0
+     * @return 统一响应结果,包含举报详情VO对象(包含举报信息、订单信息、律师信息)
+     * @author system
+     * @since 2025-01-XX
+     */
+    @ApiOperation(value = "根据订单ID查询举报详情", notes = "查询举报详情信息,包括举报的详情信息、订单的详情信息以及订单律师名字相关信息")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "orderId", value = "订单ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getViolationDetailByOrderId")
+    public R<LawyerViolationDetailVO> getViolationDetailByOrderId(
+            @RequestParam(value = "orderId", required = true) Integer orderId) {
+        log.info("根据订单ID查询举报详情请求开始,orderId={}", orderId);
+
+        // 参数校验
+        if (!validateOrderId(orderId)) {
+            log.warn("根据订单ID查询举报详情失败:订单ID为空或无效,orderId={}", orderId);
+            return R.fail("订单ID不能为空");
+        }
+
+        try {
+            LawyerViolationDetailVO detailVO = lawyerUserViolationService.getViolationDetailByOrderId(orderId);
+            if (detailVO == null) {
+                log.warn("根据订单ID查询举报详情:订单不存在或未举报,orderId={}", orderId);
+                return R.fail("订单不存在或未举报");
+            }
+
+            log.info("根据订单ID查询举报详情成功,orderId={}, violationId={}, orderNumber={}",
+                    orderId, detailVO.getViolationId(), detailVO.getOrderNumber());
+            return R.data(detailVO);
+
+        } catch (IllegalArgumentException e) {
+            log.warn("根据订单ID查询举报详情参数校验失败,orderId={},错误信息:{}", orderId, e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (RuntimeException e) {
+            log.warn("根据订单ID查询举报详情业务异常,orderId={},异常信息:{}", orderId, e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("根据订单ID查询举报详情异常,orderId={},异常信息:{}", orderId, e.getMessage(), e);
+            return R.fail("查询举报详情失败,请稍后重试");
+        }
+    }
+
+    /**
+     * 校验订单ID
+     * <p>
+     * 检查订单ID是否为空或无效
+     * </p>
+     *
+     * @param orderId 订单ID
+     * @return true表示有效,false表示无效
+     */
+    private boolean validateOrderId(Integer orderId) {
+        return orderId != null && orderId > 0;
+    }
+
+}
+

+ 302 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/OrderReviewController.java

@@ -0,0 +1,302 @@
+package shop.alien.lawyer.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.OrderReview;
+import shop.alien.entity.store.dto.OrderReviewDto;
+import shop.alien.entity.store.vo.LawyerReviewStatisticsVo;
+import shop.alien.entity.store.vo.OrderReviewDetailVo;
+import shop.alien.entity.store.vo.OrderReviewVo;
+import shop.alien.entity.store.vo.PendingReviewVo;
+import shop.alien.lawyer.service.OrderReviewService;
+
+/**
+ * 订单评价 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-订单评价"})
+@ApiSort(15)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/orderReview")
+@RequiredArgsConstructor
+public class OrderReviewController {
+
+    private final OrderReviewService orderReviewService;
+
+    @ApiOperation("创建订单评价(只有订单用户才能评价)")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/create")
+    public R<OrderReview> createReview(@RequestBody OrderReviewDto reviewDto) {
+        log.info("OrderReviewController.createReview?reviewDto={}, ", reviewDto);
+        if (reviewDto.getUserId() == null) {
+            return R.fail("用户未登录");
+        }
+        return orderReviewService.createReview(reviewDto);
+    }
+
+    @ApiOperation("获取评价详情(包含评论和回复)")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/detail/reviewId")
+    public R<OrderReviewDetailVo> getReviewDetail(
+            @RequestParam Integer reviewId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("OrderReviewController.getReviewDetail?reviewId={}, currentUserId={}", reviewId, currentUserId);
+        if (reviewId == null) {
+            return R.fail("评价ID不能为空");
+        }
+        return orderReviewService.getReviewDetail(reviewId, currentUserId);
+    }
+
+    @ApiOperation("获取评价")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "orderId", value = "订单ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentUserId", value = "用户ID只能删除自己的评价)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/select/reviewIdByOrderId")
+    public R<OrderReviewDetailVo> reviewIdByOrderId(
+            @RequestParam Integer orderId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("OrderReviewController.getReviewDetail?reviewId={}, currentUserId={}", orderId, currentUserId);
+        QueryWrapper<OrderReview> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("order_id", orderId);
+        queryWrapper.eq("delete_flag", 0);
+        OrderReview review = orderReviewService.getOne(queryWrapper);
+        if (review == null) {
+            return R.fail("评价ID不能为空");
+        }
+        return orderReviewService.getReviewDetail(review.getId(), currentUserId);
+    }
+
+    @ApiOperation("点赞评价")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/like")
+    public R<Boolean> likeReview(@RequestBody OrderReview orderReview) {
+        Integer reviewId = orderReview.getId();
+        Integer userId = orderReview.getUserId();
+        log.info("OrderReviewController.likeReview?reviewId={}, userId={}", reviewId, userId);
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        if (reviewId == null) {
+            return R.fail("评价ID不能为空");
+        }
+        return orderReviewService.likeReview(reviewId, userId);
+    }
+
+    @ApiOperation("取消点赞评价")
+    @ApiOperationSupport(order = 9)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/cancelLike")
+    public R<Boolean> cancelLikeReview(@RequestBody OrderReview orderReview) {
+        Integer reviewId = orderReview.getId();
+        Integer userId = orderReview.getUserId();
+        log.info("OrderReviewController.cancelLikeReview?reviewId={}, userId={}", reviewId, userId);
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        return orderReviewService.cancelLikeReview(reviewId, userId);
+    }
+
+    @ApiOperation("分页查询评价列表")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "orderId", value = "订单ID", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "userId", value = "评价用户ID", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/list")
+    public R<IPage<OrderReviewVo>> getReviewList(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int size,
+            @RequestParam(required = false) Integer orderId,
+            @RequestParam(required = false) Integer lawyerUserId,
+            @RequestParam(required = false) Integer userId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("OrderReviewController.getReviewList?page={}, size={}, orderId={}, lawyerUserId={}, userId={}, currentUserId={}",
+                page, size, orderId, lawyerUserId, userId, currentUserId);
+        return orderReviewService.getReviewList(page, size, orderId, lawyerUserId, userId, currentUserId);
+    }
+
+    @ApiOperation("用户删除评价(只能删除自己的评价,删除评价时,会级联删除该评价下的所有评论和回复)")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID(必填,只能删除自己的评价)", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/delete/reviewId")
+    public R<Boolean> deleteReview(@RequestParam Integer reviewId,
+                                   @RequestParam Integer userId) {
+        log.info("OrderReviewController.deleteReview?reviewId={}, userId={}", reviewId, userId);
+        if (userId == null) {
+            return R.fail("用户ID不能为空");
+        }
+        return orderReviewService.deleteReview(reviewId, userId);
+    }
+
+    @ApiOperation("管理员删除评价(可以删除任何评价,删除评价时,会级联删除该评价下的所有评论和回复)")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/admin/delete/reviewId")
+    public R<Boolean> deleteReviewByAdmin(@RequestParam Integer reviewId) {
+        log.info("OrderReviewController.deleteReviewByAdmin?reviewId={}", reviewId);
+        return orderReviewService.deleteReviewByAdmin(reviewId);
+    }
+
+    @ApiOperation("根据订单ID查询评价")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "orderId", value = "订单ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/order/orderId")
+    public R<OrderReviewVo> getReviewByOrderId(
+            @RequestParam Integer orderId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("OrderReviewController.getReviewByOrderId?orderId={}, currentUserId={}", orderId, currentUserId);
+        return orderReviewService.getReviewByOrderId(orderId, currentUserId);
+    }
+
+
+    @ApiOperation("分页查询待评价的订单列表(返回未评论的订单信息以及律师信息)")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/pending/list")
+    public R<IPage<PendingReviewVo>> getPendingReviewList(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int size,
+            @RequestParam Integer userId) {
+        log.info("OrderReviewController.getPendingReviewList?page={}, size={}, userId={}", page, size, userId);
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        return orderReviewService.getPendingReviewList(page, size, userId);
+    }
+
+    @ApiOperation("分页查询我的评价列表(查询当前用户的所有评价)")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/my/list")
+    public R<IPage<OrderReviewVo>> getMyReviewList(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int size,
+            @RequestParam Integer userId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("OrderReviewController.getMyReviewList?page={}, size={}, userId={}, currentUserId={}", page, size, userId, currentUserId);
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        return orderReviewService.getMyReviewList(page, size, userId, currentUserId);
+    }
+
+    @ApiOperation("获取律师评价统计数据(全部数量、好评数量、中评数量、差评数量、有图数量)")
+    @ApiOperationSupport(order = 10)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/statistics")
+    public R<LawyerReviewStatisticsVo> getLawyerReviewStatistics(@RequestParam Integer lawyerUserId) {
+        log.info("OrderReviewController.getLawyerReviewStatistics?lawyerUserId={}", lawyerUserId);
+        if (lawyerUserId == null) {
+            return R.fail("律师ID不能为空");
+        }
+        return orderReviewService.getLawyerReviewStatistics(lawyerUserId);
+    }
+
+    @ApiOperation("根据律师ID分页查询评价列表(查询指定律师的所有评价)")
+    @ApiOperationSupport(order = 11)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/list/byLawyer")
+    public R<IPage<OrderReviewVo>> getReviewListByLawyer(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int size,
+            @RequestParam Integer lawyerUserId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("OrderReviewController.getReviewListByLawyer?page={}, size={}, lawyerUserId={}, currentUserId={}",
+                page, size, lawyerUserId, currentUserId);
+        if (lawyerUserId == null) {
+            return R.fail("律师ID不能为空");
+        }
+        return orderReviewService.getReviewList(page, size, null, lawyerUserId, null, currentUserId);
+    }
+
+    @ApiOperation("根据律师ID和类型分页查询评价列表(不包含评论)")
+    @ApiOperationSupport(order = 12)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "page", value = "页数(默认1)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "size", value = "页容(默认10)", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "type", value = "查询分类(1:好评,2:中评,3:差评,4:有图,为空时返回全部)", dataType = "int", paramType = "query", required = false),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/list/byLawyerAndType")
+    public R<IPage<OrderReviewVo>> getReviewListByLawyerAndType(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int size,
+            @RequestParam Integer lawyerUserId,
+            @RequestParam(required = false) Integer type,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("OrderReviewController.getReviewListByLawyerAndType?page={}, size={}, lawyerUserId={}, type={}, currentUserId={}",
+                page, size, lawyerUserId, type, currentUserId);
+        if (lawyerUserId == null) {
+            return R.fail("律师ID不能为空");
+        }
+        return orderReviewService.getReviewListByLawyerAndType(page, size, lawyerUserId, type, currentUserId);
+    }
+
+    @ApiOperation("根据订单ID查询订单关联的评价")
+    @ApiOperationSupport(order = 13)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "orderId", value = "订单ID", dataType = "int", paramType = "query", required = true)
+    })
+    @GetMapping("/getByOrderId")
+    public R<OrderReviewVo> queryReviewByOrderId(
+            @RequestParam Integer orderId) {
+        log.info("OrderReviewController.queryReviewByOrderId?orderId={}", orderId);
+        if (orderId == null) {
+            return R.fail("订单ID不能为空");
+        }
+        return orderReviewService.getOrderEvaluation(orderId);
+    }
+
+}
+

+ 92 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/PaymentController.java

@@ -0,0 +1,92 @@
+package shop.alien.lawyer.controller;
+
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import shop.alien.entity.result.R;
+import shop.alien.lawyer.payment.PaymentStrategyFactory;
+
+
+import java.util.Map;
+
+/**
+ * @author lyx
+ * @version 1.0
+ * @date 2025/11/20 17:34
+ */
+@Slf4j
+@Api(tags = {"2.5-支付接口"})
+@CrossOrigin
+@RestController
+@RequestMapping("/payment")
+@RequiredArgsConstructor
+public class PaymentController {
+
+    private final PaymentStrategyFactory paymentStrategyFactory;
+
+
+    @ApiOperation("创建预支付订单")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "price", value = "订单金额", required = true, paramType = "query", dataType = "String"),
+            @ApiImplicitParam(name = "subject", value = "订单标题", required = true, paramType = "query", dataType = "String"),
+            @ApiImplicitParam(name = "payType", value = "支付类型(alipay:支付宝, wechatPay:微信支付)", required = true, paramType = "query", dataType = "String")
+    })
+    @RequestMapping("/prePay")
+    public R prePay(String price, String subject, String payType) {
+        log.info("PaymentController:prePay, price: {}, subject: {}, payType: {}", price, subject, payType);
+        try {
+            return paymentStrategyFactory.getStrategy(payType).createPrePayOrder(price, subject);
+        } catch (Exception e) {
+            return R.fail(e.getMessage());
+        }
+    }
+
+    /**
+     * 通知接口 之后可能会用
+     * @param notifyData
+     * @return
+     */
+    @RequestMapping("/notify")
+    public R notify(String notifyData) {
+        return  null;
+    }
+
+    /**
+     * 查询订单状态
+     * @param transactionId 交易订单号(微信支付订单号/商户订单号)
+     * @param payType 支付类型
+     * @param id 订单id
+     * @param id 订单str
+     * @return 订单状态信息
+     */
+    @RequestMapping("/searchOrderByOutTradeNoPath")
+    public R searchOrderByOutTradeNoPath(String transactionId, String payType ,Integer id ,String orderStr) {
+        try {
+            return paymentStrategyFactory.getStrategy(payType).searchOrderByOutTradeNoPath(transactionId,id,orderStr);
+        } catch (Exception e) {
+            return R.fail(e.getMessage());
+        }
+    }
+
+    /**
+     * 退款接口
+     * @param params 退款参数(包含订单号、退款金额等)
+     * @return 退款结果
+     */
+    @RequestMapping("/refunds")
+    public R refunds(@RequestBody Map<String, String> params) {
+        try {
+            return R.data(paymentStrategyFactory.getStrategy(params.get("payType")).handleRefund(params));
+        } catch (Exception e) {
+            return R.fail(e.getMessage());
+        }
+    }
+}

+ 215 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/ReviewCommentController.java

@@ -0,0 +1,215 @@
+package shop.alien.lawyer.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.ReviewComment;
+import shop.alien.entity.store.dto.LawyerReplyDto;
+import shop.alien.entity.store.dto.ReviewCommentRequestDto;
+import shop.alien.entity.store.dto.ReviewReplyDto;
+import shop.alien.entity.store.vo.ReviewCommentVo;
+import shop.alien.lawyer.service.ReviewCommentService;
+
+import java.util.List;
+
+/**
+ * 评价评论 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-评价评论"})
+@ApiSort(16)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/reviewComment")
+@RequiredArgsConstructor
+public class ReviewCommentController {
+
+    private final ReviewCommentService reviewCommentService;
+
+    @ApiOperation("创建评论(用户对评价的评论,支持普通用户和律师)")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "body", required = true),
+            @ApiImplicitParam(name = "sendUserId", value = "发送用户ID(评论用户ID,如果传入userId则自动映射到sendUserId)", dataType = "int", paramType = "body", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID(登录用户ID,会自动映射到sendUserId,如果sendUserId已存在则优先使用sendUserId)", dataType = "int", paramType = "body", required = false),
+            @ApiImplicitParam(name = "receiveUserId", value = "接收用户ID(可选,不填时默认为评价的创建者)", dataType = "int", paramType = "body", required = false),
+            @ApiImplicitParam(name = "sendUserType", value = "发送用户类型:1-普通用户,2-律师(必填)", dataType = "int", paramType = "body", required = true),
+            @ApiImplicitParam(name = "receiveUserType", value = "接收用户类型:1-普通用户,2-律师(必填)", dataType = "int", paramType = "body", required = true),
+            @ApiImplicitParam(name = "commentContent", value = "评论内容", dataType = "String", paramType = "body", required = true)
+    })
+    @PostMapping("/create")
+    public R<ReviewComment> createComment(@RequestBody ReviewComment comment) {
+        log.info("ReviewCommentController.createComment?comment={}", comment);
+        
+        // 参数校验:优先使用sendUserId,如果不存在则使用userId
+        if (comment.getSendUserId() == null) {
+            if (comment.getUserId() == null) {
+                return R.fail("用户ID不能为空");
+            }
+            // 将userId映射到sendUserId
+            comment.setSendUserId(comment.getUserId());
+        }
+        
+        // 校验用户类型字段:必须传入
+        if (comment.getSendUserType() == null) {
+            return R.fail("发送用户类型不能为空");
+        }
+        if (comment.getReceiveUserType() == null) {
+            return R.fail("接收用户类型不能为空");
+        }
+        
+        return reviewCommentService.createComment(comment);
+    }
+
+    @ApiOperation("根据评价ID查询评论列表")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/list/reviewId")
+    public R<List<ReviewCommentVo>> getCommentListByReviewId(
+            @RequestParam Integer reviewId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("ReviewCommentController.getCommentListByReviewId?reviewId={}, currentUserId={}", reviewId, currentUserId);
+        return reviewCommentService.getCommentListByReviewId(reviewId, currentUserId);
+    }
+
+    @ApiOperation("点赞评论")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "commentId", value = "评论ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/like")
+    public R<Boolean> likeComment(@RequestBody ReviewComment reviewComment) {
+        Integer commentId = reviewComment.getId();
+        Integer userId = reviewComment.getUserId();
+        log.info("ReviewCommentController.likeComment?commentId={}, userId={}", commentId, userId);
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        if (commentId == null) {
+            return R.fail("评论ID不能为空");
+        }
+        ReviewCommentRequestDto requestDto = new ReviewCommentRequestDto();
+        requestDto.setCommentId(commentId);
+        requestDto.setUserId(userId);
+        return reviewCommentService.likeComment(requestDto);
+    }
+
+    @ApiOperation("取消点赞评论")
+    @ApiOperationSupport(order = 5)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "commentId", value = "评论ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/cancelLike")
+    public R<Boolean> cancelLikeComment(@RequestBody ReviewComment reviewComment) {
+        Integer commentId = reviewComment.getId();
+        Integer userId = reviewComment.getUserId();
+        log.info("ReviewCommentController.cancelLikeComment?commentId={}, userId={}", commentId, userId);
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        if (commentId == null) {
+            return R.fail("评论ID不能为空");
+        }
+        ReviewCommentRequestDto requestDto = new ReviewCommentRequestDto();
+        requestDto.setCommentId(commentId);
+        requestDto.setUserId(userId);
+        return reviewCommentService.cancelLikeComment(requestDto);
+    }
+
+    @ApiOperation("创建回复(用户对评论的回复)")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "commentId", value = "首评ID", dataType = "int", paramType = "body", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID(发送用户ID)", dataType = "int", paramType = "body", required = true),
+            @ApiImplicitParam(name = "replyToUserId", value = "被回复用户ID(可选,不填时默认为首评的发送用户)", dataType = "int", paramType = "body", required = false),
+            @ApiImplicitParam(name = "sendUserType", value = "发送用户类型:1-普通用户,2-律师(必填)", dataType = "int", paramType = "body", required = true),
+            @ApiImplicitParam(name = "receiveUserType", value = "接收用户类型:1-普通用户,2-律师(必填)", dataType = "int", paramType = "body", required = true),
+            @ApiImplicitParam(name = "replyContent", value = "回复内容", dataType = "String", paramType = "body", required = true)
+    })
+    @PostMapping("/reply/create")
+    public R<ReviewComment> createReply(@RequestBody ReviewReplyDto replyDto) {
+        log.info("ReviewCommentController.createReply?replyDto={}", replyDto);
+        
+        if (replyDto.getUserId() == null) {
+            return R.fail("用户ID不能为空");
+        }
+        
+        // 校验用户类型字段:必须传入
+        if (replyDto.getSendUserType() == null) {
+            return R.fail("发送用户类型不能为空");
+        }
+        if (replyDto.getReceiveUserType() == null) {
+            return R.fail("接收用户类型不能为空");
+        }
+        
+        return reviewCommentService.createReply(replyDto);
+    }
+
+    @ApiOperation("根据首评ID查询回复列表")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "headId", value = "首评ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/reply/list/headId")
+    public R<List<ReviewCommentVo>> getReplyListByHeadId(
+            @RequestParam Integer headId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("ReviewCommentController.getReplyListByHeadId?headId={}, currentUserId={}", headId, currentUserId);
+        return reviewCommentService.getReplyListByHeadId(headId, currentUserId);
+    }
+
+    @ApiOperation("删除回复")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "replyId", value = "回复ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/reply/delete")
+    public R<Boolean> deleteReply(
+            @RequestParam Integer replyId,
+            @RequestParam Integer userId) {
+        log.info("ReviewCommentController.deleteReply?replyId={}, userId={}", replyId, userId);
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        return reviewCommentService.deleteReply(replyId, userId);
+    }
+
+    @ApiOperation(value = "删除评论(根据ID)", notes = "根据评论ID删除评论,userId有值时只能删除自己发布的评论,userId为空时允许删除任何评论(管理员删除)")
+    @ApiOperationSupport(order = 11)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "评论ID", dataType = "int", paramType = "body", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID(可选,有值时只能删除自己的评论,为空时允许删除任何评论)", dataType = "int", paramType = "body", required = false)
+    })
+    @PostMapping("/deleteReviewComment")
+    public R<Boolean> deleteReviewComment(@RequestBody ReviewComment reviewComment) {
+        log.info("ReviewCommentController.deleteReviewComment?reviewComment={}", reviewComment);
+        return reviewCommentService.deleteReviewComment(reviewComment);
+    }
+
+    @Deprecated
+    @ApiOperation(value = "律师回复(律师对用户的评价或评论的回复)", notes = "【已废弃】请使用 createComment(回复评价)或 createReply(回复评论)接口,通过 sendUserType=2 标识律师身份,系统会自动进行权限校验")
+    @ApiOperationSupport(order = 12)
+    @PostMapping("/lawyer/reply")
+    public R<ReviewComment> lawyerReply(@RequestBody LawyerReplyDto replyDto) {
+        log.warn("【废弃接口】ReviewCommentController.lawyerReply 已废弃,请使用 createComment 或 createReply 接口");
+        log.info("ReviewCommentController.lawyerReply?replyDto={}", replyDto);
+        if (replyDto.getLawyerUserId() == null) {
+            return R.fail("律师用户ID不能为空");
+        }
+        return reviewCommentService.lawyerReply(replyDto);
+    }
+
+}
+

+ 43 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/controller/TestController.java

@@ -0,0 +1,43 @@
+package shop.alien.lawyer.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.lawyer.service.TestService;
+
+/**
+ * @author ssk
+ * @version 1.0
+ * @date 2025/11/10 14:09
+ */
+@Slf4j
+@Api(tags = {"测试接口"})
+@ApiSort(1)
+@CrossOrigin
+@RestController
+@RequestMapping("/test")
+@RequiredArgsConstructor
+public class TestController {
+
+    private final TestService testService;
+
+    @ApiOperation("测试")
+    @ApiOperationSupport(order = 1)
+    @ApiImplicitParams({
+            @ApiImplicitParam(
+                    name = "name",
+                    value = "姓名",
+                    dataType = "String",
+                    paramType = "query",
+                    required = true,
+                    defaultValue = "张三")
+    })
+    @GetMapping("/test")
+    public R getIdInfo(@RequestParam("name") String name) {
+        log.info("AliController.getIdInfo?name={}", name);
+        return R.data(testService.test(name));
+    }
+
+}

+ 12 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/feign/AlienSecondFeign.java

@@ -0,0 +1,12 @@
+package shop.alien.lawyer.feign;
+
+import org.springframework.cloud.openfeign.FeignClient;
+
+/**
+ * @author ssk
+ * @version 1.0
+ * @date 2025/11/10 14:08
+ */
+@FeignClient(name = "alienSecondFeign", url = "${feign.alienSecond.url}")
+public interface AlienSecondFeign {
+}

+ 39 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/feign/AlienStoreFeign.java

@@ -0,0 +1,39 @@
+package shop.alien.lawyer.feign;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import shop.alien.entity.result.R;
+
+import java.util.Map;
+
+/**
+ * @author ssk
+ * @version 1.0
+ * @date 2025/11/10 14:08
+ */
+@FeignClient(name = "alienStoreFeign", url = "${feign.alienStore.url}")
+public interface AlienStoreFeign {
+
+    /**
+     * 处理退款
+     * @param outTradeNo 订单编号
+     * @param refundAmount 退款金额
+     * @param refundReason 退款原因
+     * @param partialRefundCode 部分退款编码
+     * @return 退款结果
+     */
+    @GetMapping("ali/processRefund")
+    String processRefund(@RequestParam(value = "outTradeNo") String outTradeNo,
+                         @RequestParam(value = "refundAmount") String refundAmount,
+                         @RequestParam(value = "refundReason") String refundReason,
+                         @RequestParam(value = "partialRefundCode") String partialRefundCode);
+
+    /**
+     * 微信/支付宝退款
+     */
+    @PostMapping("payment/refunds")
+    R paymentRefunds(@RequestBody Map<String, String> params);
+}

+ 90 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/listener/RedisKeyExpirationHandler.java

@@ -0,0 +1,90 @@
+package shop.alien.lawyer.listener;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+
+/**
+ * Redis Key過期事件處理器
+ * 支持註冊不同key前綴的處理邏輯
+ * 
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Component
+public class RedisKeyExpirationHandler {
+
+    /**
+     * 存儲不同key前綴對應的處理器
+     * key: key前綴(如 "order:pay:")
+     * value: 處理邏輯
+     */
+    private final Map<String, Consumer<String>> handlers = new ConcurrentHashMap<>();
+
+    /**
+     * 處理過期的key
+     * 
+     * @param expiredKey 過期的key
+     */
+    public void handleExpiration(String expiredKey) {
+        log.info("開始處理過期Key: {}", expiredKey);
+        
+        // 遍歷所有註冊的處理器,找到匹配的前綴
+        for (Map.Entry<String, Consumer<String>> entry : handlers.entrySet()) {
+            String prefix = entry.getKey();
+            if (expiredKey.startsWith(prefix)) {
+                try {
+                    log.info("找到匹配的處理器,前綴: {}, key: {}", prefix, expiredKey);
+                    entry.getValue().accept(expiredKey);
+                } catch (Exception e) {
+                    log.error("執行過期處理邏輯失敗,前綴: {}, key: {}", prefix, expiredKey, e);
+                }
+                return;
+            }
+        }
+        
+        log.warn("未找到匹配的處理器,key: {}", expiredKey);
+    }
+
+    /**
+     * 註冊key過期處理器
+     * 
+     * @param keyPrefix key前綴(如 "order:pay:")
+     * @param handler 處理邏輯
+     */
+    public void registerHandler(String keyPrefix, Consumer<String> handler) {
+        if (keyPrefix == null || keyPrefix.isEmpty()) {
+            throw new IllegalArgumentException("key前綴不能為空");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("處理器不能為null");
+        }
+        
+        handlers.put(keyPrefix, handler);
+        log.info("註冊Redis Key過期處理器,前綴: {}", keyPrefix);
+    }
+
+    /**
+     * 移除key過期處理器
+     * 
+     * @param keyPrefix key前綴
+     */
+    public void unregisterHandler(String keyPrefix) {
+        handlers.remove(keyPrefix);
+        log.info("移除Redis Key過期處理器,前綴: {}", keyPrefix);
+    }
+
+    /**
+     * 獲取所有已註冊的前綴
+     * 
+     * @return 前綴集合
+     */
+    public java.util.Set<String> getRegisteredPrefixes() {
+        return handlers.keySet();
+    }
+}
+

+ 46 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/listener/RedisKeyExpirationListener.java

@@ -0,0 +1,46 @@
+package shop.alien.lawyer.listener;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+import org.springframework.stereotype.Component;
+
+/**
+ * Redis Key過期事件監聽器
+ * 
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Component
+public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
+
+    private final RedisKeyExpirationHandler expirationHandler;
+
+    public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer,
+                                      RedisKeyExpirationHandler expirationHandler) {
+        super(listenerContainer);
+        this.expirationHandler = expirationHandler;
+    }
+
+    /**
+     * 當Redis key過期時觸發此方法
+     * 
+     * @param message 過期消息,包含過期的key
+     * @param pattern 匹配的模式
+     */
+    @Override
+    public void onMessage(Message message, byte[] pattern) {
+        String expiredKey = message.toString();
+        log.info("檢測到Redis Key過期: {}", expiredKey);
+        
+        try {
+            // 處理過期事件
+            expirationHandler.handleExpiration(expiredKey);
+        } catch (Exception e) {
+            log.error("處理Redis Key過期事件失敗, key: {}", expiredKey, e);
+        }
+    }
+}
+

+ 61 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/payment/PaymentStrategy.java

@@ -0,0 +1,61 @@
+package shop.alien.lawyer.payment;
+
+
+import shop.alien.entity.result.R;
+
+import java.util.Map;
+
+/**
+ * 支付策略接口
+ *
+ * @author lyx
+ * @date 2025/11/20
+ */
+public interface PaymentStrategy {
+
+    /** 生成预支付订单
+     *
+     * @param price 订单金额
+     * @param subject 订单标题
+     * @return 预支付订单信息
+     * @throws Exception 生成异常
+     */
+    R createPrePayOrder(String price, String subject) throws Exception;
+
+
+    /**
+     * 处理支付通知
+     *
+     * @param notifyData 支付通知数据
+     * @return 处理结果
+     * @throws Exception 处理异常
+     */
+    R handleNotify(String notifyData) throws Exception;
+
+     /**
+     * 查询订单状态
+     *
+     * @param transactionId 交易订单号
+     * @return 订单状态信息
+     * @throws Exception 查询异常
+     */
+    R searchOrderByOutTradeNoPath(String transactionId,Integer id,String orderStr) throws Exception;
+
+     /**
+     * 处理退款请求
+     *
+     * @param params 退款请求参数
+     * @return 处理结果
+     * @throws Exception 处理异常
+     */
+     String handleRefund(Map<String,String> params) throws Exception;
+
+
+
+    /**
+     * 获取策略类型字符串
+     *
+     * @return 策略类型字符串
+     */
+    String getType();
+}

+ 64 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/payment/PaymentStrategyFactory.java

@@ -0,0 +1,64 @@
+package shop.alien.lawyer.payment;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 支付策略工厂
+ *
+ * @author lyx
+ * @date 2025/11/20
+ */
+@Slf4j
+@Component
+public class PaymentStrategyFactory {
+
+    @Autowired
+    private List<PaymentStrategy> paymentStrategies;
+
+    private final Map<String, PaymentStrategy> strategyMap = new HashMap<>();
+
+        /**
+         * 初始化策略映射
+         */
+        @PostConstruct
+        public void init() {
+            if (paymentStrategies != null && !paymentStrategies.isEmpty()) {
+                for (PaymentStrategy strategy : paymentStrategies) {
+                    strategyMap.put(strategy.getType(), strategy);
+                    log.info("注册支付策略: {} -> {}", strategy.getType(), strategy.getClass().getSimpleName());
+                }
+            }
+        }
+
+    /**
+     * 根据类型获取OCR策略
+     *
+     * @param type OCR类型
+     * @return OCR策略实例
+     * @throws IllegalArgumentException 如果类型不存在
+     */
+    public PaymentStrategy getStrategy(String type) {
+        PaymentStrategy strategy = strategyMap.get(type);
+        if (strategy == null) {
+            throw new IllegalArgumentException("不支持的支付类型: " + type);
+        }
+        return strategy;
+    }
+
+    /**
+     * 检查是否支持指定的OCR类型
+     *
+     * @param type OCR类型
+     * @return 是否支持
+     */
+    public boolean supports(String type) {
+        return strategyMap.containsKey(type);
+    }
+}

+ 602 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/payment/impl/AlipayPaymentStrategyImpl.java

@@ -0,0 +1,602 @@
+package shop.alien.lawyer.payment.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.AlipayClient;
+import com.alipay.api.AlipayConfig;
+import com.alipay.api.DefaultAlipayClient;
+import com.alipay.api.domain.AlipayTradeAppPayModel;
+import com.alipay.api.domain.AlipayTradeRefundModel;
+import com.alipay.api.request.AlipayTradeAppPayRequest;
+import com.alipay.api.request.AlipayTradeQueryRequest;
+import com.alipay.api.request.AlipayTradeRefundRequest;
+import com.alipay.api.response.AlipayTradeAppPayResponse;
+import com.alipay.api.response.AlipayTradeQueryResponse;
+import com.alipay.api.response.AlipayTradeRefundResponse;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.ibatis.jdbc.Null;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerConsultationOrder;
+import shop.alien.entity.store.RefundRecord;
+import shop.alien.entity.store.StoreAliPayRefundLog;
+import shop.alien.mapper.LawyerConsultationOrderMapper;
+import shop.alien.lawyer.payment.PaymentStrategy;
+import shop.alien.lawyer.service.RefundRecordService;
+import shop.alien.lawyer.service.StoreAliPayRefundLogService;
+import shop.alien.util.common.UniqueRandomNumGenerator;
+import shop.alien.util.common.UrlEncode;
+import shop.alien.util.common.constant.PaymentEnum;
+import shop.alien.util.system.OSUtil;
+
+import java.math.BigDecimal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * 支付宝支付策略
+ *
+ * @author lyx
+ * @date 2025/11/20
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AlipayPaymentStrategyImpl implements PaymentStrategy {
+
+    /**
+     * 商家端appId
+     */
+    @Value("${payment.aliPay.business.appId}")
+    private String businessAppId;
+
+    /**
+     * 商家端app私钥
+     */
+    @Value("${payment.aliPay.business.appPrivateKey}")
+    private String businessAppPrivateKey;
+
+    /**
+     * 商家端app公钥
+     */
+    @Value("${payment.aliPay.business.appPublicKey}")
+    private String businessAppPublicKey;
+
+    /**
+     * windows应用公钥证书文件路径
+     */
+    @Value("${payment.aliPay.business.win.appCertPath}")
+    private String businessWinAppCertPath;
+
+    /**
+     * windows支付宝公钥证书文件路径
+     */
+    @Value("${payment.aliPay.business.win.alipayPublicCertPath}")
+    private String businessWinAlipayPublicCertPath;
+
+    /**
+     * windows支付宝根证书文件路径
+     */
+    @Value("${payment.aliPay.business.win.alipayRootCertPath}")
+    private String businessWinAlipayRootCertPath;
+
+    /**
+     * linux应用公钥证书文件路径
+     */
+    @Value("${payment.aliPay.business.linux.appCertPath}")
+    private String businessLinuxAppCertPath;
+
+    /**
+     * linux支付宝公钥证书文件路径
+     */
+    @Value("${payment.aliPay.business.linux.alipayPublicCertPath}")
+    private String businessLinuxAlipayPublicCertPath;
+
+    /**
+     * linux支付宝根证书文件路径
+     */
+    @Value("${payment.aliPay.business.linux.alipayRootCertPath}")
+    private String businessLinuxAlipayRootCertPath;
+
+    /**
+     * 支对称加密算法密钥
+     */
+    @Value("${ali.aes.encryptKey}")
+    private String encryptKey;
+    /**
+     * 支付宝网关地址
+     */
+    @Value("${payment.aliPay.host}")
+    private String aliPayApiHost;
+
+    private final StoreAliPayRefundLogService storeAliPayRefundLogService;
+
+    private final RefundRecordService refundRecordService;
+
+    private final LawyerConsultationOrderMapper lawyerConsultationOrderMapper;
+
+
+    /**
+     * 填写阿里配置
+     *
+     * @return AlipayConfig
+     */
+    private AlipayConfig setAlipayConfig(String type) {
+        AlipayConfig alipayConfig = new AlipayConfig();
+        alipayConfig.setServerUrl(aliPayApiHost);
+        alipayConfig.setAppId(businessAppId);
+        alipayConfig.setPrivateKey(businessAppPrivateKey);
+        alipayConfig.setFormat("json");
+        alipayConfig.setCharset("UTF-8");
+        alipayConfig.setSignType("RSA2");
+        if ("windows".equals(OSUtil.getOsName())) {
+            alipayConfig.setAppCertPath(businessWinAppCertPath);
+            alipayConfig.setAlipayPublicCertPath(businessWinAlipayPublicCertPath);
+            alipayConfig.setRootCertPath(businessWinAlipayRootCertPath);
+        } else {
+            alipayConfig.setAppCertPath(businessLinuxAppCertPath);
+            alipayConfig.setAlipayPublicCertPath(businessLinuxAlipayPublicCertPath);
+            alipayConfig.setRootCertPath(businessLinuxAlipayRootCertPath);
+        }
+        if ("aes".equals(type)) {
+            alipayConfig.setEncryptType("AES");
+            alipayConfig.setEncryptKey(encryptKey);
+        }
+        return alipayConfig;
+    }
+
+    @Override
+    public R createPrePayOrder(String price, String subject) throws Exception {
+        log.info("创建支付宝预支付订单,价格:{},描述:{}", price, subject);
+        // 参数验证
+        if (price == null || price.trim().isEmpty()) {
+            return R.fail("价格不能为空");
+        }
+        if (subject == null || subject.trim().isEmpty()) {
+            return R.fail("订单描述不能为空");
+        }
+        try {
+            BigDecimal amount = new BigDecimal(price);
+            if (amount.compareTo(BigDecimal.ZERO) <= 0) {
+                return R.fail("价格必须大于0");
+            }
+        } catch (NumberFormatException e) {
+            return R.fail("价格格式不正确");
+        }
+        // 生成订单号
+        String orderNo = UniqueRandomNumGenerator.generateUniqueCode(19);
+        // 构建预支付请求
+        AlipayTradeAppPayRequest request = buildPrePayRequest(price, subject,orderNo);
+        
+        try {
+            // 调用支付宝接口创建预支付订单
+            JSONObject result = prePayOrderRun(request);
+            result.put("orderNo", orderNo);
+            log.info("支付宝预支付订单创建成功,商家订单号:{}", orderNo);
+            return R.data(result);
+        } catch (AlipayApiException e) {
+            log.error("支付宝预支付失败,错误码:{},错误信息:{}", e.getErrCode(), e.getErrMsg());
+            return R.fail("支付宝预支付失败:" + e.getErrMsg());
+        }
+    }
+
+    @Override
+    public R<Object> handleNotify(String notifyData) throws Exception {
+        log.info("处理支付宝支付通知,通知数据:{}", notifyData);
+        // TODO: 实现支付通知处理逻辑
+        // 1. 验证签名
+        // 2. 解析通知数据
+        // 3. 更新订单状态
+        // 4. 返回处理结果
+        return R.fail("功能尚未实现");
+    }
+
+    @Override
+    public R<Object> searchOrderByOutTradeNoPath(String transactionId,Integer id ,String orderStr) throws Exception {
+        try {
+
+            //根据id把transactionId和orderStr更新到lawyer_consultation_order表中
+
+            LambdaUpdateWrapper<LawyerConsultationOrder> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.eq(LawyerConsultationOrder::getId, id);
+            updateWrapper.set(LawyerConsultationOrder::getAlipayNo, transactionId);
+            updateWrapper.set(LawyerConsultationOrder::getOrderStr, orderStr);
+
+            int a=lawyerConsultationOrderMapper.update(null, updateWrapper);
+
+            if (a>0){
+                log.info("更新订单成功");
+            }
+
+            AlipayClient alipayClient = new DefaultAlipayClient(setAlipayConfig(null));
+            // 2. 构造查询请求
+            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
+
+            // 设置biz_content(JSON格式)
+            JSONObject bizContent = new JSONObject();
+            if (StringUtils.isNotBlank(transactionId)) {
+                bizContent.put("out_trade_no", transactionId);
+            }
+//            if (StringUtils.isNotBlank(tradeNo)) {
+//                bizContent.put("trade_no", tradeNo);
+//            }
+            request.setBizContent(bizContent.toJSONString());
+
+            // 3. 调用接口并获取响应
+            AlipayTradeQueryResponse response = alipayClient.certificateExecute(request);
+            if (response.isSuccess()) {
+                // 4. 解析支付状态
+                String tradeStatus = response.getTradeStatus();
+                log.info("订单查询成功,支付状态:" + tradeStatus);
+
+                // 支付状态说明:
+                // WAIT_BUYER_PAY:等待买家付款
+                // TRADE_CLOSED:交易关闭(超时未支付/已取消)
+                // TRADE_SUCCESS:支付成功(即时到账/普通转账)
+                // TRADE_FINISHED:交易完成(不可退款的场景,如即时到账)
+                if ("TRADE_SUCCESS".equals(tradeStatus)) {
+                    return R.success("支付成功");
+                } else if ("TRADE_CLOSED".equals(tradeStatus)) {
+                    return R.fail("交易已关闭");
+                } else if ("WAIT_BUYER_PAY".equals(tradeStatus)) {
+                    return R.fail("等待买家付款");
+                } else if ("TRADE_FINISHED".equals(tradeStatus)) {
+                    return R.success("交易完成");
+                }
+                return R.success("订单状态:" + tradeStatus);
+            } else {
+                return R.fail("订单查询失败:" + response.getMsg() + "(" + response.getSubMsg() + ")");
+            }
+        } catch (AlipayApiException e) {
+            log.error("支付宝订单查询异常", e);
+            return R.fail("查询异常:" + e.getMessage());
+        }
+
+    }
+
+    @Override
+    public String handleRefund(Map<String, String> params) throws Exception {
+        log.info("处理支付宝退款请求,参数:{}", params);
+        try {
+            AlipayTradeRefundRequest request = buildRefundRequest(params);
+            AlipayTradeRefundResponse response = refundRun(request);
+            String refundReslut = "";
+            JSONObject responseBody = JSONObject.parseObject(response.getBody());
+            JSONObject refundResponse = responseBody.getJSONObject("alipay_trade_refund_response");
+            
+            if (response.isSuccess()) {
+                refundReslut = "调用成功";
+                // 保存退款信息进入到支付宝退款记录表(保留原有逻辑)
+                StoreAliPayRefundLog refundLog = new StoreAliPayRefundLog();
+                // 响应基本信息
+                refundLog.setResponseCode(refundResponse.getString("code"));
+                refundLog.setResponseMsg(refundResponse.getString("msg"));
+                // 买家信息
+                refundLog.setBuyerLogonId(refundResponse.getString("buyer_logon_id"));
+                refundLog.setBuyerOpenId(refundResponse.getString("buyer_open_id"));
+                // 资金变动信息
+                refundLog.setFundChange(refundResponse.getString("fund_change"));
+                // 订单信息
+                refundLog.setOutTradeNo(refundResponse.getString("out_trade_no"));
+                refundLog.setTradeNo(refundResponse.getString("trade_no"));
+                // 退款金额信息
+                refundLog.setRefundFee(refundResponse.getString("refund_fee"));
+                refundLog.setSendBackFee(refundResponse.getString("send_back_fee"));
+                // 退款渠道明细(转换为JSON字符串)
+                if (refundResponse.containsKey("refund_detail_item_list")) {
+                    JSONArray refundDetailList = refundResponse.getJSONArray("refund_detail_item_list");
+                    if (refundDetailList != null) {
+                        refundLog.setRefundDetailItemList(JSON.toJSONString(refundDetailList));
+                    }
+                }
+                // 退款时间(字符串转Date)
+                String gmtRefundPayStr = refundResponse.getString("gmt_refund_pay");
+                if (StringUtils.isNotBlank(gmtRefundPayStr)) {
+                    try {
+                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                        refundLog.setGmtRefundPay(sdf.parse(gmtRefundPayStr));
+                    } catch (ParseException e) {
+                        log.warn("解析退款时间失败: {}", gmtRefundPayStr, e);
+                    }
+                }
+                // 退款原因和部分退款编号(从请求参数中获取)
+                refundLog.setRefundReason(params.get("refundReason"));
+                refundLog.setOutRequestNo(params.get("partialRefundCode"));
+                // 证书和签名信息
+                refundLog.setAlipayCertSn(responseBody.getString("alipay_cert_sn"));
+                refundLog.setSign(responseBody.getString("sign"));
+                // 标准字段
+                refundLog.setDeleteFlag(0);
+                refundLog.setCreatedTime(new Date());
+                storeAliPayRefundLogService.save(refundLog);
+                
+                // 保存到通用退款记录表
+                try {
+                    RefundRecord refundRecord = buildRefundRecordFromAlipayResponse(refundResponse, responseBody, params);
+                    if (refundRecord != null) {
+                        // 检查是否已存在,避免重复插入
+                        long count = refundRecordService.lambdaQuery()
+                                .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
+                                .count();
+                        if (count == 0) {
+                            refundRecordService.save(refundRecord);
+                            log.info("支付宝退款记录已保存到RefundRecord表,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        } else {
+                            log.info("支付宝退款记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("保存支付宝退款记录到RefundRecord表失败", e);
+                    // 不抛出异常,避免影响原有逻辑
+                }
+            } else {
+                log.warn("AliPayConfig.processRefund ERROR Msg={}", response.getBody());
+                refundReslut = refundResponse.getString("sub_msg");
+                
+                // 保存失败记录到通用退款记录表
+                try {
+                    RefundRecord refundRecord = buildRefundRecordFromAlipayError(refundResponse, responseBody, params);
+                    if (refundRecord != null) {
+                        // 检查是否已存在,避免重复插入
+                        long count = refundRecordService.lambdaQuery()
+                                .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
+                                .count();
+                        if (count == 0) {
+                            refundRecordService.save(refundRecord);
+                            log.info("支付宝退款失败记录已保存到RefundRecord表,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        } else {
+                            log.info("支付宝退款失败记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("保存支付宝退款失败记录到RefundRecord表失败", e);
+                }
+            }
+            return refundReslut;
+        } catch (AlipayApiException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getType() {
+        return PaymentEnum.ALIPAY.getType();
+    }
+
+
+    /**
+     * 构建预支付请求
+     */
+    private AlipayTradeAppPayRequest buildPrePayRequest(String price, String subject,String outTradeNo) {
+            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+            AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
+            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
+            model.setOutTradeNo(outTradeNo);
+            model.setTotalAmount(price);
+            model.setSubject(subject);
+            model.setPassbackParams(UrlEncode.getUrlEncode(dateFormat.toString()));
+            request.setBizModel(model);
+            return request;
+    }
+
+    /**
+     * 构建退款请求
+     */
+    private AlipayTradeRefundRequest buildRefundRequest(Map<String, String> params) {
+        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
+        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
+        model.setOutTradeNo(params.get("outTradeNo"));
+        model.setRefundAmount(params.get("refundAmount"));
+        model.setRefundReason(params.get("refundReason"));
+        if (StringUtils.isNotBlank(params.get("partialRefundCode"))) {
+            model.setOutRequestNo(params.get("partialRefundCode"));
+        }
+        request.setBizModel(model);
+        return request;
+    }
+
+    /**
+     * 执行预支付订单请求
+     */
+    private JSONObject prePayOrderRun(AlipayTradeAppPayRequest request) throws AlipayApiException {
+        JSONObject jsonObject = new JSONObject();
+        AlipayClient alipayClient = new DefaultAlipayClient(setAlipayConfig(null));
+        AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
+        String orderStr = "";
+        if (response.isSuccess()) {
+            orderStr = response.getBody();
+        } else {
+            System.out.println("调用失败 response:" + response);
+            orderStr = "调用失败";
+        }
+        jsonObject.put("orderStr", orderStr);
+        return jsonObject;
+    }
+
+
+
+    /**
+     * 执行退款请求
+     */
+    private AlipayTradeRefundResponse refundRun(AlipayTradeRefundRequest request) throws AlipayApiException {
+        AlipayClient alipayClient = new DefaultAlipayClient(setAlipayConfig(null));
+        AlipayTradeRefundResponse response = alipayClient.certificateExecute(request);
+        return response;
+    }
+
+    /**
+     * 从支付宝退款响应构建RefundRecord对象(成功情况)
+     */
+    private RefundRecord buildRefundRecordFromAlipayResponse(JSONObject refundResponse, JSONObject responseBody, Map<String, String> params) {
+        try {
+            RefundRecord record = new RefundRecord();
+            
+            // 基本信息
+            record.setPayType(PaymentEnum.ALIPAY.getType());
+            record.setOutTradeNo(refundResponse.getString("out_trade_no"));
+            record.setTransactionId(refundResponse.getString("trade_no"));
+            // 部分退款编号作为商户退款单号,如果没有则生成一个
+            String outRefundNo = params.get("partialRefundCode");
+            if (StringUtils.isBlank(outRefundNo)) {
+                outRefundNo = UniqueRandomNumGenerator.generateUniqueCode(19);
+            }
+            record.setOutRefundNo(outRefundNo);
+            record.setRefundId(null); // 支付宝没有单独的退款单号
+            record.setRefundStatus("SUCCESS");
+            
+            // 金额信息(支付宝返回的是元,需要转换为分)
+            String refundFeeStr = refundResponse.getString("refund_fee");
+            String sendBackFeeStr = refundResponse.getString("send_back_fee");
+            if (StringUtils.isNotBlank(refundFeeStr)) {
+                record.setRefundAmount(new BigDecimal(refundFeeStr).multiply(new BigDecimal(100)).longValue());
+            }
+            if (StringUtils.isNotBlank(sendBackFeeStr)) {
+                record.setActualRefundAmount(new BigDecimal(sendBackFeeStr).multiply(new BigDecimal(100)).longValue());
+            }
+            // 订单总金额从params获取,如果没有则从退款金额推算
+            String totalAmountStr = params.get("totalAmount");
+            if (StringUtils.isNotBlank(totalAmountStr)) {
+                record.setTotalAmount(new BigDecimal(totalAmountStr).multiply(new BigDecimal(100)).longValue());
+            }
+            record.setCurrency("CNY");
+            
+            // 退款原因
+            record.setRefundReason(params.get("refundReason"));
+            
+            // 退款详情
+            if (refundResponse.containsKey("refund_detail_item_list")) {
+                JSONArray refundDetailList = refundResponse.getJSONArray("refund_detail_item_list");
+                if (refundDetailList != null) {
+                    record.setRefundDetail(JSON.toJSONString(refundDetailList));
+                }
+            }
+            
+            // 用户收到退款账户
+            record.setUserReceivedAccount(refundResponse.getString("buyer_logon_id"));
+            
+            // 退款时间
+            String gmtRefundPayStr = refundResponse.getString("gmt_refund_pay");
+            if (StringUtils.isNotBlank(gmtRefundPayStr)) {
+                try {
+                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                    Date refundTime = sdf.parse(gmtRefundPayStr);
+                    record.setRefundSuccessTime(refundTime);
+                    record.setRefundCreateTime(refundTime); // 支付宝没有单独的创建时间
+                } catch (ParseException e) {
+                    log.warn("解析退款时间失败: {}", gmtRefundPayStr, e);
+                }
+            }
+            
+            // 业务信息(从params获取,如果有的话)
+            if (params.containsKey("userId")) {
+                try {
+                    record.setUserId(Integer.parseInt(params.get("userId")));
+                } catch (NumberFormatException e) {
+                    log.warn("解析userId失败: {}", params.get("userId"));
+                }
+            }
+            if (params.containsKey("orderId")) {
+                record.setOrderId(params.get("orderId"));
+            }
+            if (params.containsKey("storeId")) {
+                try {
+                    record.setStoreId(Integer.parseInt(params.get("storeId")));
+                } catch (NumberFormatException e) {
+                    log.warn("解析storeId失败: {}", params.get("storeId"));
+                }
+            }
+            
+            // 响应数据
+            record.setResponseData(responseBody.toJSONString());
+            
+            // 标准字段
+            record.setDeleteFlag(0);
+            record.setCreatedTime(new Date());
+            
+            return record;
+        } catch (Exception e) {
+            log.error("构建RefundRecord对象失败", e);
+            return null;
+        }
+    }
+
+    /**
+     * 从支付宝退款错误响应构建RefundRecord对象(失败情况)
+     */
+    private RefundRecord buildRefundRecordFromAlipayError(JSONObject refundResponse, JSONObject responseBody, Map<String, String> params) {
+        try {
+            RefundRecord record = new RefundRecord();
+            
+            // 基本信息
+            record.setPayType(PaymentEnum.ALIPAY.getType());
+            record.setOutTradeNo(params.get("outTradeNo"));
+            // 部分退款编号作为商户退款单号,如果没有则生成一个
+            String outRefundNo = params.get("partialRefundCode");
+            if (StringUtils.isBlank(outRefundNo)) {
+                outRefundNo = UniqueRandomNumGenerator.generateUniqueCode(19);
+            }
+            record.setOutRefundNo(outRefundNo);
+            record.setRefundStatus("ABNORMAL");
+            
+            // 金额信息
+            String refundAmountStr = params.get("refundAmount");
+            if (StringUtils.isNotBlank(refundAmountStr)) {
+                record.setRefundAmount(new BigDecimal(refundAmountStr).multiply(new BigDecimal(100)).longValue());
+            }
+            String totalAmountStr = params.get("totalAmount");
+            if (StringUtils.isNotBlank(totalAmountStr)) {
+                record.setTotalAmount(new BigDecimal(totalAmountStr).multiply(new BigDecimal(100)).longValue());
+            }
+            record.setCurrency("CNY");
+            
+            // 退款原因
+            record.setRefundReason(params.get("refundReason"));
+            
+            // 错误信息
+            record.setErrorCode(refundResponse.getString("code"));
+            record.setErrorMsg(refundResponse.getString("sub_msg"));
+            
+            // 业务信息
+            if (params.containsKey("userId")) {
+                try {
+                    record.setUserId(Integer.parseInt(params.get("userId")));
+                } catch (NumberFormatException e) {
+                    log.warn("解析userId失败: {}", params.get("userId"));
+                }
+            }
+            if (params.containsKey("orderId")) {
+                record.setOrderId(params.get("orderId"));
+            }
+            if (params.containsKey("storeId")) {
+                try {
+                    record.setStoreId(Integer.parseInt(params.get("storeId")));
+                } catch (NumberFormatException e) {
+                    log.warn("解析storeId失败: {}", params.get("storeId"));
+                }
+            }
+            
+            // 响应数据
+            record.setResponseData(responseBody.toJSONString());
+            
+            // 标准字段
+            record.setDeleteFlag(0);
+            record.setCreatedTime(new Date());
+            record.setRefundCreateTime(new Date());
+            
+            return record;
+        } catch (Exception e) {
+            log.error("构建RefundRecord对象失败", e);
+            return null;
+        }
+    }
+
+}
+

+ 1265 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/payment/impl/WeChatPaymentStrategyImpl.java

@@ -0,0 +1,1265 @@
+package shop.alien.lawyer.payment.impl;
+
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.*;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerConsultationOrder;
+import shop.alien.entity.store.RefundRecord;
+
+import shop.alien.lawyer.payment.PaymentStrategy;
+import shop.alien.lawyer.service.RefundRecordService;
+import shop.alien.lawyer.util.WXPayUtility;
+import shop.alien.mapper.LawyerConsultationOrderMapper;
+import shop.alien.util.common.UniqueRandomNumGenerator;
+import shop.alien.util.common.constant.PaymentEnum;
+import shop.alien.util.system.OSUtil;
+
+import javax.annotation.PostConstruct;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+/**
+ * 微信支付策略
+ *
+ * @author lyx
+ * @date 2025/11/20
+ */
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class WeChatPaymentStrategyImpl implements PaymentStrategy {
+
+    private final RefundRecordService refundRecordService;
+
+    @Value("${payment.wechatPay.host}")
+    private String wechatPayApiHost;
+
+    @Value("${payment.wechatPay.prePayPath}")
+    private String prePayPath;
+
+    @Value("${payment.wechatPay.searchOrderByTransactionIdPath}")
+    private String searchOrderByTransactionIdPath;
+
+    @Value("${payment.wechatPay.searchOrderByOutTradeNoPath}")
+    private String searchOrderByOutTradeNoPath;
+
+    @Value("${payment.wechatPay.refundPath}")
+    private String refundPath;
+
+    /**
+     * 微信支付应用id
+     */
+    @Value("${payment.wechatPay.business.appId}")
+    private String appId;
+
+    /**
+     * 微信支付商户id
+     */
+    @Value("${payment.wechatPay.business.mchId}")
+    private String mchId;
+
+    /**
+     * 微信支付商户私钥路径
+     */
+    @Value("${payment.wechatPay.business.win.privateKeyPath}")
+    private String privateWinKeyPath;
+
+    /**
+     * 微信支付商户私钥路径(Linux环境)
+     */
+    @Value("${payment.wechatPay.business.linux.privateKeyPath}")
+    private String privateLinuxKeyPath;
+
+    /**
+     * 微信支付公钥路径
+     */
+    @Value("${payment.wechatPay.business.win.wechatPayPublicKeyFilePath}")
+    private String wechatWinPayPublicKeyFilePath;
+
+    /**
+     * 微信支付公钥路径(Linux环境)
+     */
+    @Value("${payment.wechatPay.business.linux.wechatPayPublicKeyFilePath}")
+    private String wechatLinuxPayPublicKeyFilePath;
+
+    /**
+     * 微信支付商户证书序列号
+     */
+    @Value("${payment.wechatPay.business.merchantSerialNumber}")
+    private String merchantSerialNumber;
+
+    /**
+     * 微信支付API V3密钥
+     */
+    @Value("${payment.wechatPay.business.apiV3key}")
+    private String apiV3key;
+
+    /**
+     * 微信支付公钥id
+     */
+    @Value("${payment.wechatPay.business.wechatPayPublicKeyId}")
+    private String wechatPayPublicKeyId;
+
+    @Value("${payment.wechatPay.business.prePayNotifyUrl}")
+    private String prePayNotifyUrl;
+
+    @Value("${payment.wechatPay.business.refundNotifyUrl}")
+    private String refundNotifyUrl;
+
+    private PrivateKey privateKey;
+    private PublicKey wechatPayPublicKey;
+
+    private static String POSTMETHOD = "POST";
+    private static String GETMETHOD = "GET";
+
+
+    private final LawyerConsultationOrderMapper lawyerConsultationOrderMapper;
+
+    @PostConstruct
+    public void setWeChatPaymentConfig() {
+        String privateKeyPath;
+        String wechatPayPublicKeyFilePath;
+        if ("windows".equals(OSUtil.getOsName())) {
+            privateKeyPath = privateWinKeyPath;
+            wechatPayPublicKeyFilePath = wechatWinPayPublicKeyFilePath;
+        } else {
+            privateKeyPath = privateLinuxKeyPath;
+            wechatPayPublicKeyFilePath = wechatLinuxPayPublicKeyFilePath;
+        }
+        this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyPath);
+        this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
+    }
+
+    @Override
+    public R createPrePayOrder(String price, String subject) throws Exception {
+        log.info("创建微信预支付订单,价格:{},描述:{}", price, subject);
+        // 参数验证
+        if (price == null || price.trim().isEmpty()) {
+            return R.fail("价格不能为空");
+        }
+        if (subject == null || subject.trim().isEmpty()) {
+            return R.fail("订单描述不能为空");
+        }
+        try {
+            BigDecimal amount = new BigDecimal(price);
+            if (amount.compareTo(BigDecimal.ZERO) <= 0) {
+                return R.fail("价格必须大于0");
+            }
+        } catch (NumberFormatException e) {
+            return R.fail("价格格式不正确");
+        }
+
+        CommonPrepayRequest request = new CommonPrepayRequest();
+        request.appid = appId;
+        request.mchid = mchId;
+        request.description = subject;
+        request.outTradeNo = UniqueRandomNumGenerator.generateUniqueCode(19);
+
+//        request.timeExpire = "2018-06-08T10:34:56+08:00"; 超时支付不传默认7天
+//        request.attach = "自定义数据说明";
+        // 目前没用,但是必填
+        request.notifyUrl = prePayNotifyUrl;
+//        request.goodsTag = "WXG";
+//        request.supportFapiao = false;
+        request.amount = new CommonAmountInfo();
+        request.amount.total = new BigDecimal(price).multiply(new BigDecimal(100)).longValue();
+        request.amount.currency = "CNY";
+        /* 【优惠功能】 优惠功能
+        request.detail = new CouponInfo();
+        request.detail.costPrice = 608800L;
+        request.detail.invoiceId = "微信123";
+        request.detail.goodsDetail = new ArrayList<>();
+        {
+            GoodsDetail goodsDetailItem = new GoodsDetail();
+            goodsDetailItem.merchantGoodsId = "1246464644";
+            goodsDetailItem.wechatpayGoodsId = "1001";
+            goodsDetailItem.goodsName = "iPhoneX 256G";
+            goodsDetailItem.quantity = 1L;
+            goodsDetailItem.unitPrice = 528800L;
+            request.detail.goodsDetail.add(goodsDetailItem);
+        };*/
+        /* 【场景信息】 场景信息
+        request.sceneInfo = new CommonSceneInfo();
+        request.sceneInfo.payerClientIp = "14.23.150.211";
+        request.sceneInfo.deviceId = "013467007045764";
+        request.sceneInfo.storeInfo = new StoreInfo();
+        request.sceneInfo.storeInfo.id = "0001";
+        request.sceneInfo.storeInfo.name = "腾讯大厦分店";
+        request.sceneInfo.storeInfo.areaCode = "440305";
+        request.sceneInfo.storeInfo.address = "广东省深圳市南山区科技中一道10000号";*/
+        /* 【结算信息】 结算信息
+        request.settleInfo = new SettleInfo();
+        request.settleInfo.profitSharing = false;*/
+        try {
+            DirectAPIv3AppPrepayResponse response = this.prePayOrderRun(request);
+            log.info("微信预支付订单创建成功,预支付ID:{}", response.prepayId);
+            Map<String,String> result = new HashMap<>();
+            result.put("prepayId", response.prepayId);
+            result.put("appId", appId);
+            result.put("mchId", mchId);
+            result.put("orderNo", request.outTradeNo);
+            // 生成sign
+//            appId
+
+//            随机字符串
+//            prepay_id
+            long timestamp = System.currentTimeMillis() / 1000; // 时间戳
+            String nonce = WXPayUtility.createNonce(32); // 随机字符串
+            String prepayId = response.prepayId;
+            String message = String.format("%s\n%s\n%s\n%s\n", appId, timestamp, nonce, prepayId);
+//            String sign = WXPayUtility.sign(message, sha256withRSA, privateKey);
+            Signature sign = Signature.getInstance("SHA256withRSA");
+            sign.initSign(privateKey);
+            sign.update(message.getBytes(StandardCharsets.UTF_8));
+            result.put("sign", Base64.getEncoder().encodeToString(sign.sign()));
+            result.put("timestamp", String.valueOf(timestamp));
+            result.put("nonce", nonce);
+            return R.data(result);
+        } catch (WXPayUtility.ApiException e) {
+            log.error("微信支付预支付失败,状态码:{},错误信息:{}", e.getErrorCode(), e.getMessage());
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @Override
+    public R handleNotify(String notifyData) throws Exception {
+        /**
+         * 目前没用,先写着
+         */
+        return null;
+    }
+
+    @Override
+    public R searchOrderByOutTradeNoPath(String transactionId ,Integer id,String orderStr) throws Exception {
+        log.info("查询微信支付订单状态,交易订单号:{}", transactionId);
+
+        LambdaUpdateWrapper<LawyerConsultationOrder> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(LawyerConsultationOrder::getId, id);
+        updateWrapper.set(LawyerConsultationOrder::getAlipayNo, transactionId);
+        updateWrapper.set(LawyerConsultationOrder::getOrderStr, orderStr);
+
+        int a=lawyerConsultationOrderMapper.update(null, updateWrapper);
+
+        if (a>0){
+            log.info("更新订单成功");
+        }
+
+
+
+        QueryByWxTradeNoRequest request = new QueryByWxTradeNoRequest();
+        request.transactionId = transactionId;
+        request.mchid = mchId;
+        try {
+            DirectAPIv3QueryResponse response = searchOrderRun(request);
+
+            if (response.tradeState.equals("NOTPAY")) {
+                log.info("微信支付订单已支付,订单号:{}", response.outTradeNo);
+                return R.fail("NOTPAY");
+            }
+
+
+            return R.data(response);
+        } catch (WXPayUtility.ApiException e) {
+            log.error("查询微信支付订单状态失败,状态码:{},错误信息:{}", e.getErrorCode(), e.getMessage());
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @Override
+    public String handleRefund(Map<String,String> params) throws Exception {
+
+        CreateRequest request = new CreateRequest();
+        // 微信支付订单号和商户订单号必须二选一,不能同时为空,查询的时候使用的是商户订单号
+        //request.transactionId = "1217752501201407033233368018";
+        request.outTradeNo = params.get("outTradeNo");
+        // 退款订单号
+        request.outRefundNo = UniqueRandomNumGenerator.generateUniqueCode(19);
+        // 退款原因
+        request.reason = params.get("reason");
+        // 退款回调通知地址
+        request.notifyUrl = refundNotifyUrl;
+        // 退款资金来源选填
+        //request.fundsAccount = ReqFundsAccount.AVAILABLE;
+        // 金额信息
+        request.amount = new AmountReq();
+        request.amount.refund = new BigDecimal(params.get("refundAmount")).longValue();
+        // 退款出资账户及金额 目前不需要,需要的时候再看
+        /*
+        request.amount.from = new ArrayList<>();
+        {
+            FundsFromItem fromItem = new FundsFromItem();
+            fromItem.account = Account.AVAILABLE;
+            fromItem.amount = 444L;
+            request.amount.from.add(fromItem);
+        };*/
+        // 订单总金额
+        request.amount.total = new BigDecimal(params.get("totalAmount")).longValue();
+        // 退款币种 目前默认人民币 CNY
+        request.amount.currency = "CNY";
+        // 退款商品信息 目前不需要,需要的时候再看
+        /*
+        request.goodsDetail = new ArrayList<>();
+        {
+            GoodsDetail goodsDetailItem = new GoodsDetail();
+            goodsDetailItem.merchantGoodsId = "1217752501201407033233368018";
+            goodsDetailItem.wechatpayGoodsId = "1001";
+            goodsDetailItem.goodsName = "iPhone6s 16G";
+            goodsDetailItem.unitPrice = 528800L;
+            goodsDetailItem.refundAmount = 528800L;
+            goodsDetailItem.refundQuantity = 1L;
+            request.goodsDetail.add(goodsDetailItem);
+        };*/
+        // 记录退款请求信息
+        log.info("开始处理微信支付退款,商户订单号:{},商户退款单号:{},退款金额:{}分,订单总金额:{}分,退款原因:{}",
+                request.outTradeNo, request.outRefundNo, request.amount.refund, request.amount.total, request.reason);
+        String refundResult = "";
+        try {
+            Refund response = refundRun(request);
+            // 退款状态
+            String status = response.status != null ? response.status.name() : "UNKNOWN";
+            if ("SUCCESS".equals(status) || "PROCESSING".equals(status)) {
+                // refund_id 申请退款受理成功时,该笔退款单在微信支付侧生成的唯一标识。
+                String refundId = response.refundId;
+                // 商户申请退款时传的商户系统内部退款单号。
+                String outRefundNo = response.outRefundNo != null ? response.outRefundNo : request.outRefundNo;
+                // 微信支付订单号
+                String transactionId = response.transactionId;
+
+                // 退款金额信息
+                String refundAmount = response.amount != null && response.amount.refund != null
+                        ? String.valueOf(response.amount.refund) : String.valueOf(request.amount.refund);
+                String totalAmount = response.amount != null && response.amount.total != null
+                        ? String.valueOf(response.amount.total) : String.valueOf(request.amount.total);
+                // 退款成功时间
+                String successTime = response.successTime;
+                // 退款创建时间
+                String createTime = response.createTime;
+                // 退款渠道
+                String channel = response.channel != null ? response.channel.name() : "UNKNOWN";
+
+                // 记录退款成功详细信息
+                log.info("微信支付退款成功 - 商户订单号:{},微信支付订单号:{},商户退款单号:{},微信退款单号:{}," +
+                                "退款状态:{},退款金额:{}分,订单总金额:{}分,退款渠道:{},创建时间:{},成功时间:{}",
+                        request.outTradeNo, transactionId, outRefundNo, refundId, status, refundAmount,
+                        totalAmount, channel, createTime, successTime != null ? successTime : "未完成");
+
+                // 保存到通用退款记录表
+                try {
+                    RefundRecord refundRecord = buildRefundRecordFromWeChatResponse(response, request, params);
+                    if (refundRecord != null) {
+                        // 检查是否已存在,避免重复插入
+                        long count = refundRecordService.lambdaQuery()
+                                .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
+                                .count();
+                        if (count == 0) {
+                            refundRecordService.save(refundRecord);
+                            log.info("微信支付退款记录已保存到RefundRecord表,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        } else {
+                            log.info("微信支付退款记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("保存微信支付退款记录到RefundRecord表失败", e);
+                    // 不抛出异常,避免影响原有逻辑
+                }
+
+                refundResult = "调用成功";
+                return refundResult;
+            } else {
+                log.error("微信支付退款失败 - 商户订单号:{},商户退款单号:{},退款金额:{}分,订单总金额:{}分," +
+                                "退款状态:{}",
+                        request.outTradeNo, request.outRefundNo, request.amount.refund,
+                        request.amount.total, status);
+                
+                // 保存失败记录到通用退款记录表
+                try {
+                    RefundRecord refundRecord = buildRefundRecordFromWeChatError(response, request, params, status);
+                    if (refundRecord != null) {
+                        // 检查是否已存在,避免重复插入
+                        long count = refundRecordService.lambdaQuery()
+                                .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
+                                .count();
+                        if (count == 0) {
+                            refundRecordService.save(refundRecord);
+                            log.info("微信支付退款失败记录已保存到RefundRecord表,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        } else {
+                            log.info("微信支付退款失败记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("保存微信支付退款失败记录到RefundRecord表失败", e);
+                }
+                
+                return "退款失败";
+            }
+        }
+        catch (Exception e) {
+            // 记录其他异常
+            log.error("微信支付退款异常 - 商户订单号:{},商户退款单号:{},退款金额:{}分,订单总金额:{}分," +
+                    "退款原因:{},异常信息:{}",
+                    request.outTradeNo, request.outRefundNo, request.amount.refund, 
+                    request.amount.total, request.reason, e.getMessage(), e);
+            
+            // 保存异常记录到通用退款记录表
+            try {
+                RefundRecord refundRecord = buildRefundRecordFromWeChatException(request, params, e);
+                if (refundRecord != null) {
+                    // 检查是否已存在,避免重复插入
+                    long count = refundRecordService.lambdaQuery()
+                            .eq(RefundRecord::getOutRefundNo, refundRecord.getOutRefundNo())
+                            .count();
+                    if (count == 0) {
+                        refundRecordService.save(refundRecord);
+                        log.info("微信支付退款异常记录已保存到RefundRecord表,商户退款单号:{}", refundRecord.getOutRefundNo());
+                    } else {
+                        log.info("微信支付退款异常记录已存在,跳过插入,商户退款单号:{}", refundRecord.getOutRefundNo());
+                    }
+                }
+            } catch (Exception ex) {
+                log.error("保存微信支付退款异常记录到RefundRecord表失败", ex);
+            }
+            
+            refundResult = "退款处理异常:" + e.getMessage();
+            return refundResult;
+        }
+    }
+
+
+    @Override
+    public String getType() {
+        return PaymentEnum.WECHAT_PAY.getType();
+    }
+
+
+    public DirectAPIv3AppPrepayResponse prePayOrderRun(CommonPrepayRequest request) {
+        String uri = prePayPath;
+        String reqBody = WXPayUtility.toJson(request);
+
+        Request.Builder reqBuilder = new Request.Builder().url(wechatPayApiHost + uri);
+        reqBuilder.addHeader("Accept", "application/json");
+        reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
+        reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchId, merchantSerialNumber, privateKey, POSTMETHOD, uri, reqBody));
+        reqBuilder.addHeader("Content-Type", "application/json");
+        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
+        reqBuilder.method(POSTMETHOD, requestBody);
+        Request httpRequest = reqBuilder.build();
+
+        // 发送HTTP请求
+        OkHttpClient client = new OkHttpClient.Builder().build();
+        try (Response httpResponse = client.newCall(httpRequest).execute()) {
+            String respBody = WXPayUtility.extractBody(httpResponse);
+            if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
+                // 2XX 成功,验证应答签名
+                WXPayUtility.validateResponse(wechatPayPublicKeyId, wechatPayPublicKey,
+                        httpResponse.headers(), respBody);
+
+                // 从HTTP应答报文构建返回数据
+                return WXPayUtility.fromJson(respBody, DirectAPIv3AppPrepayResponse.class);
+            } else {
+                throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
+        }
+    }
+
+    public DirectAPIv3QueryResponse searchOrderRun(QueryByWxTradeNoRequest request) {
+        String uri = searchOrderByOutTradeNoPath;
+        uri = uri.replace("{out_trade_no}", WXPayUtility.urlEncode(request.transactionId));
+        Map<String, Object> args = new HashMap<>();
+        args.put("mchid", mchId);
+        String queryString = WXPayUtility.urlEncode(args);
+        if (!queryString.isEmpty()) {
+            uri = uri + "?" + queryString;
+        }
+
+        Request.Builder reqBuilder = new Request.Builder().url(wechatPayApiHost + uri);
+        reqBuilder.addHeader("Accept", "application/json");
+        reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
+        reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchId, merchantSerialNumber, privateKey, GETMETHOD, uri, null));
+        reqBuilder.method(GETMETHOD, null);
+        Request httpRequest = reqBuilder.build();
+
+        // 发送HTTP请求
+        OkHttpClient client = new OkHttpClient.Builder().build();
+        try (Response httpResponse = client.newCall(httpRequest).execute()) {
+            String respBody = WXPayUtility.extractBody(httpResponse);
+            if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
+                // 2XX 成功,验证应答签名
+                WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
+                        httpResponse.headers(), respBody);
+
+                // 从HTTP应答报文构建返回数据
+                return WXPayUtility.fromJson(respBody, DirectAPIv3QueryResponse.class);
+            } else {
+                throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
+        }
+    }
+
+    public Refund refundRun(CreateRequest request) {
+        String uri = refundPath;
+        String reqBody = WXPayUtility.toJson(request);
+
+        Request.Builder reqBuilder = new Request.Builder().url(wechatPayApiHost + uri);
+        reqBuilder.addHeader("Accept", "application/json");
+        reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
+        reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchId, merchantSerialNumber, privateKey, POSTMETHOD, uri, reqBody));
+        reqBuilder.addHeader("Content-Type", "application/json");
+        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
+        reqBuilder.method(POSTMETHOD, requestBody);
+        Request httpRequest = reqBuilder.build();
+
+        // 发送HTTP请求
+        OkHttpClient client = new OkHttpClient.Builder().build();
+        try (Response httpResponse = client.newCall(httpRequest).execute()) {
+            String respBody = WXPayUtility.extractBody(httpResponse);
+            if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
+                // 2XX 成功,验证应答签名
+                WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
+                        httpResponse.headers(), respBody);
+
+                // 从HTTP应答报文构建返回数据
+                return WXPayUtility.fromJson(respBody, Refund.class);
+            } else {
+                throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
+        }
+    }
+
+    /*****************************************下面为支付使用参数*****************************************/
+    public static class CommonPrepayRequest {
+        @SerializedName("appid")
+        public String appid;
+
+        @SerializedName("mchid")
+        public String mchid;
+
+        @SerializedName("description")
+        public String description;
+
+        @SerializedName("out_trade_no")
+        public String outTradeNo;
+
+        @SerializedName("time_expire")
+        public String timeExpire;
+
+        @SerializedName("attach")
+        public String attach;
+
+        @SerializedName("notify_url")
+        public String notifyUrl;
+
+        @SerializedName("goods_tag")
+        public String goodsTag;
+
+        @SerializedName("support_fapiao")
+        public Boolean supportFapiao;
+
+        @SerializedName("amount")
+        public CommonAmountInfo amount;
+
+        @SerializedName("detail")
+        public CouponInfo detail;
+
+        @SerializedName("scene_info")
+        public CommonSceneInfo sceneInfo;
+
+        @SerializedName("settle_info")
+        public SettleInfo settleInfo;
+    }
+
+    public static class DirectAPIv3AppPrepayResponse {
+        @SerializedName("prepay_id")
+        public String prepayId;
+    }
+
+    public static class CommonAmountInfo {
+        @SerializedName("total")
+        public Long total;
+
+        @SerializedName("currency")
+        public String currency;
+    }
+
+    public static class CouponInfo {
+        @SerializedName("cost_price")
+        public Long costPrice;
+
+        @SerializedName("invoice_id")
+        public String invoiceId;
+
+        @SerializedName("goods_detail")
+        public List<GoodsDetail> goodsDetail;
+    }
+
+    public static class CommonSceneInfo {
+        @SerializedName("payer_client_ip")
+        public String payerClientIp;
+
+        @SerializedName("device_id")
+        public String deviceId;
+
+        @SerializedName("store_info")
+        public StoreInfo storeInfo;
+    }
+
+    public static class SettleInfo {
+        @SerializedName("profit_sharing")
+        public Boolean profitSharing;
+    }
+
+    public static class StoreInfo {
+        @SerializedName("id")
+        public String id;
+
+        @SerializedName("name")
+        public String name;
+
+        @SerializedName("area_code")
+        public String areaCode;
+
+        @SerializedName("address")
+        public String address;
+    }
+
+    public static class QueryByWxTradeNoRequest {
+        @SerializedName("mchid")
+        @Expose(serialize = false)
+        public String mchid;
+
+        @SerializedName("transaction_id")
+        @Expose(serialize = false)
+        public String transactionId;
+    }
+
+    public static class DirectAPIv3QueryResponse {
+        @SerializedName("appid")
+        public String appid;
+
+        @SerializedName("mchid")
+        public String mchid;
+
+        @SerializedName("out_trade_no")
+        public String outTradeNo;
+
+        @SerializedName("transaction_id")
+        public String transactionId;
+
+        @SerializedName("trade_type")
+        public String tradeType;
+
+        @SerializedName("trade_state")
+        public String tradeState;
+
+        @SerializedName("trade_state_desc")
+        public String tradeStateDesc;
+
+        @SerializedName("bank_type")
+        public String bankType;
+
+        @SerializedName("attach")
+        public String attach;
+
+        @SerializedName("success_time")
+        public String successTime;
+
+        @SerializedName("payer")
+        public CommRespPayerInfo payer;
+
+        @SerializedName("amount")
+        public CommRespAmountInfo amount;
+
+        @SerializedName("scene_info")
+        public CommRespSceneInfo sceneInfo;
+
+        @SerializedName("promotion_detail")
+        public List<PromotionDetail> promotionDetail;
+    }
+
+    public static class CommRespPayerInfo {
+        @SerializedName("openid")
+        public String openid;
+    }
+
+    public static class CommRespAmountInfo {
+        @SerializedName("total")
+        public Long total;
+
+        @SerializedName("payer_total")
+        public Long payerTotal;
+
+        @SerializedName("currency")
+        public String currency;
+
+        @SerializedName("payer_currency")
+        public String payerCurrency;
+    }
+
+    public static class CommRespSceneInfo {
+        @SerializedName("device_id")
+        public String deviceId;
+    }
+
+    public static class PromotionDetail {
+        @SerializedName("coupon_id")
+        public String couponId;
+
+        @SerializedName("name")
+        public String name;
+
+        @SerializedName("scope")
+        public String scope;
+
+        @SerializedName("type")
+        public String type;
+
+        @SerializedName("amount")
+        public Long amount;
+
+        @SerializedName("stock_id")
+        public String stockId;
+
+        @SerializedName("wechatpay_contribute")
+        public Long wechatpayContribute;
+
+        @SerializedName("merchant_contribute")
+        public Long merchantContribute;
+
+        @SerializedName("other_contribute")
+        public Long otherContribute;
+
+        @SerializedName("currency")
+        public String currency;
+
+        @SerializedName("goods_detail")
+        public List<GoodsDetailInPromotion> goodsDetail;
+    }
+
+    public static class GoodsDetailInPromotion {
+        @SerializedName("goods_id")
+        public String goodsId;
+
+        @SerializedName("quantity")
+        public Long quantity;
+
+        @SerializedName("unit_price")
+        public Long unitPrice;
+
+        @SerializedName("discount_amount")
+        public Long discountAmount;
+
+        @SerializedName("goods_remark")
+        public String goodsRemark;
+    }
+
+    public static class CreateRequest {
+        @SerializedName("transaction_id")
+        public String transactionId;
+
+        @SerializedName("out_trade_no")
+        public String outTradeNo;
+
+        @SerializedName("out_refund_no")
+        public String outRefundNo;
+
+        @SerializedName("reason")
+        public String reason;
+
+        @SerializedName("notify_url")
+        public String notifyUrl;
+
+        @SerializedName("funds_account")
+        public ReqFundsAccount fundsAccount;
+
+        @SerializedName("amount")
+        public AmountReq amount;
+
+        @SerializedName("goods_detail")
+        public List<GoodsDetail> goodsDetail;
+
+    }
+
+    public static class Refund {
+        @SerializedName("refund_id")
+        public String refundId;
+
+        @SerializedName("out_refund_no")
+        public String outRefundNo;
+
+        @SerializedName("transaction_id")
+        public String transactionId;
+
+        @SerializedName("out_trade_no")
+        public String outTradeNo;
+
+        @SerializedName("channel")
+        public Channel channel;
+
+        @SerializedName("user_received_account")
+        public String userReceivedAccount;
+
+        @SerializedName("success_time")
+        public String successTime;
+
+        @SerializedName("create_time")
+        public String createTime;
+
+        @SerializedName("status")
+        public Status status;
+
+        @SerializedName("funds_account")
+        public FundsAccount fundsAccount;
+
+        @SerializedName("amount")
+        public Amount amount;
+
+        @SerializedName("promotion_detail")
+        public List<Promotion> promotionDetail;
+
+    }
+
+    public enum ReqFundsAccount {
+        @SerializedName("AVAILABLE")
+        AVAILABLE,
+        @SerializedName("UNSETTLED")
+        UNSETTLED
+    }
+
+    public static class AmountReq {
+        @SerializedName("refund")
+        public Long refund;
+
+        @SerializedName("from")
+        public List<FundsFromItem> from;
+
+        @SerializedName("total")
+        public Long total;
+
+        @SerializedName("currency")
+        public String currency;
+    }
+
+    public static class GoodsDetail {
+        @SerializedName("merchant_goods_id")
+        public String merchantGoodsId;
+
+        @SerializedName("wechatpay_goods_id")
+        public String wechatpayGoodsId;
+
+        @SerializedName("goods_name")
+        public String goodsName;
+
+        @SerializedName("quantity")
+        public Long quantity;
+
+        @SerializedName("unit_price")
+        public Long unitPrice;
+
+        @SerializedName("refund_amount")
+        public Long refundAmount;
+
+        @SerializedName("refund_quantity")
+        public Long refundQuantity;
+    }
+
+    public enum Channel {
+        @SerializedName("ORIGINAL")
+        ORIGINAL,
+        @SerializedName("BALANCE")
+        BALANCE,
+        @SerializedName("OTHER_BALANCE")
+        OTHER_BALANCE,
+        @SerializedName("OTHER_BANKCARD")
+        OTHER_BANKCARD
+    }
+
+    public enum Status {
+        @SerializedName("SUCCESS")
+        SUCCESS,
+        @SerializedName("CLOSED")
+        CLOSED,
+        @SerializedName("PROCESSING")
+        PROCESSING,
+        @SerializedName("ABNORMAL")
+        ABNORMAL
+    }
+
+    public enum FundsAccount {
+        @SerializedName("UNSETTLED")
+        UNSETTLED,
+        @SerializedName("AVAILABLE")
+        AVAILABLE,
+        @SerializedName("UNAVAILABLE")
+        UNAVAILABLE,
+        @SerializedName("OPERATION")
+        OPERATION,
+        @SerializedName("BASIC")
+        BASIC,
+        @SerializedName("ECNY_BASIC")
+        ECNY_BASIC
+    }
+
+    public static class Amount {
+        @SerializedName("total")
+        public Long total;
+
+        @SerializedName("refund")
+        public Long refund;
+
+        @SerializedName("from")
+        public List<FundsFromItem> from;
+
+        @SerializedName("payer_total")
+        public Long payerTotal;
+
+        @SerializedName("payer_refund")
+        public Long payerRefund;
+
+        @SerializedName("settlement_refund")
+        public Long settlementRefund;
+
+        @SerializedName("settlement_total")
+        public Long settlementTotal;
+
+        @SerializedName("discount_refund")
+        public Long discountRefund;
+
+        @SerializedName("currency")
+        public String currency;
+
+        @SerializedName("refund_fee")
+        public Long refundFee;
+    }
+
+    public static class Promotion {
+        @SerializedName("promotion_id")
+        public String promotionId;
+
+        @SerializedName("scope")
+        public PromotionScope scope;
+
+        @SerializedName("type")
+        public PromotionType type;
+
+        @SerializedName("amount")
+        public Long amount;
+
+        @SerializedName("refund_amount")
+        public Long refundAmount;
+
+        @SerializedName("goods_detail")
+        public List<GoodsDetail> goodsDetail;
+    }
+
+    public static class FundsFromItem {
+        @SerializedName("account")
+        public Account account;
+
+        @SerializedName("amount")
+        public Long amount;
+    }
+
+    public enum PromotionScope {
+        @SerializedName("GLOBAL")
+        GLOBAL,
+        @SerializedName("SINGLE")
+        SINGLE
+    }
+
+    public enum PromotionType {
+        @SerializedName("CASH")
+        CASH,
+        @SerializedName("NOCASH")
+        NOCASH
+    }
+
+    public enum Account {
+        @SerializedName("AVAILABLE")
+        AVAILABLE,
+        @SerializedName("UNAVAILABLE")
+        UNAVAILABLE
+    }
+
+    /**
+     * 从微信支付退款响应构建RefundRecord对象(成功情况)
+     */
+    private RefundRecord buildRefundRecordFromWeChatResponse(Refund response, CreateRequest request, Map<String, String> params) {
+        try {
+            RefundRecord record = new RefundRecord();
+            
+            // 基本信息
+            record.setPayType(PaymentEnum.WECHAT_PAY.getType());
+            record.setOutTradeNo(response.outTradeNo != null ? response.outTradeNo : request.outTradeNo);
+            record.setTransactionId(response.transactionId);
+            record.setOutRefundNo(response.outRefundNo != null ? response.outRefundNo : request.outRefundNo);
+            record.setRefundId(response.refundId);
+            record.setRefundStatus(response.status != null ? response.status.name() : "UNKNOWN");
+            
+            // 金额信息(微信返回的是分)
+            if (response.amount != null) {
+                record.setTotalAmount(response.amount.total);
+                record.setRefundAmount(response.amount.refund);
+                record.setActualRefundAmount(response.amount.refund); // 微信退款金额就是实际退款金额
+                if (response.amount.currency != null) {
+                    record.setCurrency(response.amount.currency);
+                } else {
+                    record.setCurrency("CNY");
+                }
+            } else {
+                record.setTotalAmount(request.amount.total);
+                record.setRefundAmount(request.amount.refund);
+                record.setActualRefundAmount(request.amount.refund);
+                record.setCurrency(request.amount.currency != null ? request.amount.currency : "CNY");
+            }
+            
+            // 退款原因
+            record.setRefundReason(request.reason);
+            
+            // 退款渠道
+            if (response.channel != null) {
+                record.setRefundChannel(response.channel.name());
+            }
+            
+            // 退款资金来源
+            if (response.fundsAccount != null) {
+                record.setFundsAccount(response.fundsAccount.name());
+            }
+            
+            // 用户收到退款账户
+            record.setUserReceivedAccount(response.userReceivedAccount);
+            
+            // 退款详情(如果有优惠信息)
+            if (response.promotionDetail != null && !response.promotionDetail.isEmpty()) {
+                record.setRefundDetail(WXPayUtility.toJson(response.promotionDetail));
+            }
+            
+            // 退款时间(微信返回的是ISO 8601格式字符串,需要转换为Date)
+            if (response.createTime != null) {
+                Date createTime = parseWeChatTime(response.createTime);
+                if (createTime != null) {
+                    record.setRefundCreateTime(createTime);
+                }
+            }
+            if (response.successTime != null) {
+                Date successTime = parseWeChatTime(response.successTime);
+                if (successTime != null) {
+                    record.setRefundSuccessTime(successTime);
+                }
+            }
+            
+            // 业务信息(从params获取,如果有的话)
+            if (params != null) {
+                if (params.containsKey("userId")) {
+                    try {
+                        record.setUserId(Integer.parseInt(params.get("userId")));
+                    } catch (NumberFormatException e) {
+                        log.warn("解析userId失败: {}", params.get("userId"));
+                    }
+                }
+                if (params.containsKey("orderId")) {
+                    record.setOrderId(params.get("orderId"));
+                }
+                if (params.containsKey("storeId")) {
+                    try {
+                        record.setStoreId(Integer.parseInt(params.get("storeId")));
+                    } catch (NumberFormatException e) {
+                        log.warn("解析storeId失败: {}", params.get("storeId"));
+                    }
+                }
+            }
+            
+            // 响应数据
+            record.setResponseData(WXPayUtility.toJson(response));
+            
+            // 标准字段
+            record.setDeleteFlag(0);
+            record.setCreatedTime(new Date());
+            
+            return record;
+        } catch (Exception e) {
+            log.error("构建RefundRecord对象失败", e);
+            return null;
+        }
+    }
+
+    /**
+     * 从微信支付退款错误响应构建RefundRecord对象(失败情况)
+     */
+    private RefundRecord buildRefundRecordFromWeChatError(Refund response, CreateRequest request, Map<String, String> params, String status) {
+        try {
+            RefundRecord record = new RefundRecord();
+            
+            // 基本信息
+            record.setPayType(PaymentEnum.WECHAT_PAY.getType());
+            record.setOutTradeNo(response.outTradeNo != null ? response.outTradeNo : request.outTradeNo);
+            record.setTransactionId(response.transactionId);
+            record.setOutRefundNo(response.outRefundNo != null ? response.outRefundNo : request.outRefundNo);
+            record.setRefundId(response.refundId);
+            record.setRefundStatus(status);
+            
+            // 金额信息
+            if (response.amount != null) {
+                record.setTotalAmount(response.amount.total);
+                record.setRefundAmount(response.amount.refund);
+                record.setCurrency(response.amount.currency != null ? response.amount.currency : "CNY");
+            } else {
+                record.setTotalAmount(request.amount.total);
+                record.setRefundAmount(request.amount.refund);
+                record.setCurrency(request.amount.currency != null ? request.amount.currency : "CNY");
+            }
+            
+            // 退款原因
+            record.setRefundReason(request.reason);
+            
+            // 退款时间
+            if (response.createTime != null) {
+                Date createTime = parseWeChatTime(response.createTime);
+                if (createTime != null) {
+                    record.setRefundCreateTime(createTime);
+                }
+            }
+            
+            // 业务信息
+            if (params != null) {
+                if (params.containsKey("userId")) {
+                    try {
+                        record.setUserId(Integer.parseInt(params.get("userId")));
+                    } catch (NumberFormatException e) {
+                        log.warn("解析userId失败: {}", params.get("userId"));
+                    }
+                }
+                if (params.containsKey("orderId")) {
+                    record.setOrderId(params.get("orderId"));
+                }
+                if (params.containsKey("storeId")) {
+                    try {
+                        record.setStoreId(Integer.parseInt(params.get("storeId")));
+                    } catch (NumberFormatException e) {
+                        log.warn("解析storeId失败: {}", params.get("storeId"));
+                    }
+                }
+            }
+            
+            // 响应数据
+            record.setResponseData(WXPayUtility.toJson(response));
+            
+            // 标准字段
+            record.setDeleteFlag(0);
+            record.setCreatedTime(new Date());
+            
+            return record;
+        } catch (Exception e) {
+            log.error("构建RefundRecord对象失败", e);
+            return null;
+        }
+    }
+
+    /**
+     * 从微信支付退款异常构建RefundRecord对象(异常情况)
+     */
+    private RefundRecord buildRefundRecordFromWeChatException(CreateRequest request, Map<String, String> params, Exception e) {
+        try {
+            RefundRecord record = new RefundRecord();
+            
+            // 基本信息
+            record.setPayType(PaymentEnum.WECHAT_PAY.getType());
+            record.setOutTradeNo(request.outTradeNo);
+            record.setOutRefundNo(request.outRefundNo);
+            record.setRefundStatus("ABNORMAL");
+            
+            // 金额信息
+            record.setTotalAmount(request.amount.total);
+            record.setRefundAmount(request.amount.refund);
+            record.setCurrency(request.amount.currency != null ? request.amount.currency : "CNY");
+            
+            // 退款原因
+            record.setRefundReason(request.reason);
+            
+            // 错误信息
+            if (e instanceof WXPayUtility.ApiException) {
+                WXPayUtility.ApiException apiException = (WXPayUtility.ApiException) e;
+                record.setErrorCode(String.valueOf(apiException.getErrorCode()));
+                record.setErrorMsg(apiException.getMessage());
+            } else {
+                record.setErrorMsg(e.getMessage());
+            }
+            
+            // 业务信息
+            if (params != null) {
+                if (params.containsKey("userId")) {
+                    try {
+                        record.setUserId(Integer.parseInt(params.get("userId")));
+                    } catch (NumberFormatException ex) {
+                        log.warn("解析userId失败: {}", params.get("userId"));
+                    }
+                }
+                if (params.containsKey("orderId")) {
+                    record.setOrderId(params.get("orderId"));
+                }
+                if (params.containsKey("storeId")) {
+                    try {
+                        record.setStoreId(Integer.parseInt(params.get("storeId")));
+                    } catch (NumberFormatException ex) {
+                        log.warn("解析storeId失败: {}", params.get("storeId"));
+                    }
+                }
+            }
+            
+            // 标准字段
+            record.setDeleteFlag(0);
+            record.setCreatedTime(new Date());
+            record.setRefundCreateTime(new Date());
+            
+            return record;
+        } catch (Exception ex) {
+            log.error("构建RefundRecord对象失败", ex);
+            return null;
+        }
+    }
+
+    /**
+     * 解析微信支付返回的时间字符串(ISO 8601格式)
+     * 例如:2018-06-08T10:34:56+08:00
+     */
+    private Date parseWeChatTime(String timeStr) {
+        if (timeStr == null || timeStr.trim().isEmpty()) {
+            return null;
+        }
+        try {
+            // 尝试解析ISO 8601格式
+            OffsetDateTime dateTime = OffsetDateTime.parse(timeStr, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+            return Date.from(dateTime.toInstant());
+        } catch (Exception e) {
+            // 如果ISO 8601解析失败,尝试其他格式
+            try {
+                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                return sdf.parse(timeStr);
+            } catch (ParseException ex) {
+                log.warn("解析微信支付时间失败: {}", timeStr, ex);
+                return null;
+            }
+        }
+    }
+}

+ 24 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/AiUserAuditTaskService.java

@@ -0,0 +1,24 @@
+package shop.alien.lawyer.service;
+
+import java.util.Map;
+
+/**
+ * AI用户申诉任务服务接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface AiUserAuditTaskService {
+
+    /**
+     * 异步调用AI用户申诉接口
+     *
+     * @param requestBody 请求参数
+     * @param accessToken 访问令牌(已同步获取)
+     */
+    void asyncCallUserAuditTask(Map<String, Object> requestBody, String accessToken);
+
+
+    void asyncAiReview(Map<String, Object> requestBody, String accessToken);
+}
+

+ 76 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/CommentAppealService.java

@@ -0,0 +1,76 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.CommentAppeal;
+import shop.alien.entity.store.vo.CommentAppealVo;
+
+import java.util.List;
+
+/**
+ * 评论申诉表 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface CommentAppealService extends IService<CommentAppeal> {
+
+    /**
+     * 提交申诉
+     *
+     * @param commentAppeal 申诉信息
+     * @return 操作结果
+     */
+    R<CommentAppeal> submitAppeal(CommentAppeal commentAppeal);
+
+    /**
+     * 审核申诉
+     *
+     * @param id            申诉ID
+     * @param status        审核状态 1:已通过, 2:已驳回
+     * @param reviewReasons 审核原因(可选)
+     * @return 操作结果
+     */
+    R<Boolean> auditAppeal(Integer id, Integer status, String reviewReasons);
+
+    /**
+     * 分页查询申诉列表(中台使用,支持多条件筛选)
+     *
+     * @param pageNum     页数
+     * @param pageSize    页容
+     * @param status      申诉状态 0:待处理, 1:已通过, 2:已驳回
+     * @param orderNumber 评价单号(可选,模糊查询)
+     * @param userName    用户姓名(可选,模糊查询)
+     * @param userPhone   用户电话(可选)
+     * @param lawyerName  律师姓名(可选)
+     * @param lawyerPhone 律师电话(可选)
+     * @param startTime   开始时间(可选,格式:yyyy-MM-dd HH:mm:ss)
+     * @param endTime     结束时间(可选,格式:yyyy-MM-dd HH:mm:ss)
+     * @return 分页结果
+     */
+    R<IPage<CommentAppealVo>> getAppealPage(Integer pageNum, Integer pageSize, Integer status,
+                                             String orderNumber, String userName, String userPhone, String lawyerName, String lawyerPhone,
+                                             String startTime, String endTime);
+
+    /**
+     * 获取申诉详情
+     *
+     * @param id 申诉ID
+     * @return 申诉详情
+     */
+    R<CommentAppealVo> getAppealDetail(Integer id);
+
+    /**
+     * 分页申诉历史列表
+     *
+     * @param pageNum  页数
+     * @param pageSize 页容
+     * @param status   申诉状态 0:待处理, 1:已通过, 2:已驳回
+     * @param lawyerUserId 评论ID(可选)
+     * @return 分页结果
+     */
+    List<CommentAppealVo> getAppealHistory(Integer pageNum, Integer pageSize, Integer status, Integer lawyerUserId);
+}
+
+

+ 16 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawFirmPaymentService.java

@@ -0,0 +1,16 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.LawFirmPayment;
+
+/**
+ * 律所子表 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawFirmPaymentService extends IService<LawFirmPayment> {
+
+}
+
+

+ 149 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawFirmReconciliationService.java

@@ -0,0 +1,149 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.vo.LawFirmListVO;
+import shop.alien.entity.store.vo.LawFirmReconciliationVO;
+import shop.alien.entity.store.vo.LawyerListVO;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.Date;
+
+/**
+ * 律所对账结算服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawFirmReconciliationService {
+
+
+    /**
+     * 获取律所对账总览
+     *
+     * @param firmId   律所ID(可选,不传则查询所有律所)
+     * @param firmName 律所名称(可选,模糊查询)
+     * @param startDate 开始日期(可选)
+     * @param endDate   结束日期(可选)
+     * @return 律所对账总览(包含总订单数量、总订单金额、总平台信息服务费)
+     */
+    R<LawFirmReconciliationVO> getLawFirmReconciliation(
+            Integer firmId,
+            String firmName,
+            Date startDate,
+            Date endDate
+    );
+
+    /**
+     * 获取所有律所的对账统计列表(分页)
+     *
+     * @param firmName 律所名称(可选,模糊查询)
+     * @param startDate 开始日期(可选)
+     * @param endDate   结束日期(可选)
+     * @param pageNum   页码
+     * @param pageSize  页容
+     * @return 律所对账统计列表(分页)
+     */
+    R<IPage<LawFirmListVO>> getAllLawFirmReconciliationList(
+            String firmName,
+            Date startDate,
+            Date endDate,
+            Integer pageNum,
+            Integer pageSize
+    );
+
+    /**
+     * 获取律所下所有律师的对账统计列表(分页)
+     *
+     * @param firmId   律所ID
+     * @param startDate 开始日期(可选)
+     * @param endDate   结束日期(可选)
+     * @param pageNum   页码
+     * @param pageSize  页容
+     * @return 律师对账统计列表(分页)
+     */
+    R<IPage<LawyerListVO>> getLawyerReconciliationList(
+            Integer firmId,
+            Date startDate,
+            Date endDate,
+            Integer pageNum,
+            Integer pageSize
+    );
+
+    /**
+     * 获取律所下所有律师的对账统计列表(分页,支持律师名称模糊查询)
+     *
+     * @param firmId    律所ID
+     * @param lawyerName 律师名称(可选,模糊查询)
+     * @param startDate 开始日期(可选)
+     * @param endDate   结束日期(可选)
+     * @param pageNum   页码
+     * @param pageSize  页容
+     * @return 律师对账统计列表(分页)
+     */
+    R<IPage<LawyerListVO>> getLawyerReconciliationListWithName(
+            Integer firmId,
+            String lawyerName,
+            Date startDate,
+            Date endDate,
+            Integer pageNum,
+            Integer pageSize
+    );
+
+    /**
+     * 获取律师的已完成订单列表(分页)
+     *
+     * @param lawyerId 律师ID
+     * @param pageNum   页码
+     * @param pageSize  页容
+     * @return 律师订单列表(分页)
+     */
+    R<IPage<LawFirmReconciliationVO>> getLawyerOrderList(
+            Integer lawyerId,
+            Integer pageNum,
+            Integer pageSize
+    );
+
+    /**
+     * 导出所有律所的对账统计列表到Excel
+     *
+     * @param response  HTTP响应对象
+     * @param firmName  律所名称(可选,模糊查询)
+     * @param startDate 开始日期(可选)
+     * @param endDate   结束日期(可选)
+     * @param pageNum   页码(可选,不传或传0则导出全部,传值则导出本页)
+     * @param pageSize  页容(可选,与pageNum配合使用)
+     * @throws Exception 导出过程中的异常
+     */
+    void exportAllLawFirmList(
+            HttpServletResponse response,
+            String firmName,
+            Date startDate,
+            Date endDate,
+            Integer pageNum,
+            Integer pageSize
+    ) throws Exception;
+
+    /**
+     * 导出律师对账统计列表到Excel
+     *
+     * @param response   HTTP响应对象
+     * @param firmId     律所ID
+     * @param lawyerName 律师名称(可选,模糊查询)
+     * @param startDate  开始日期(可选)
+     * @param endDate    结束日期(可选)
+     * @param pageNum    页码(可选,不传或传0则导出全部,传值则导出本页)
+     * @param pageSize   页容(可选,与pageNum配合使用)
+     * @throws Exception 导出过程中的异常
+     */
+    void exportLawyerListWithName(
+            HttpServletResponse response,
+            Integer firmId,
+            String lawyerName,
+            Date startDate,
+            Date endDate,
+            Integer pageNum,
+            Integer pageSize
+    ) throws Exception;
+}
+

+ 131 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawFirmService.java

@@ -0,0 +1,131 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawFirm;
+import shop.alien.entity.store.vo.LawFirmPaymentVO;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * 律所表 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawFirmService extends IService<LawFirm> {
+
+    /**
+     * 分页查询律所列表
+     *
+     * @param pageNum  页码
+     * @param pageSize 页容
+     * @param firmName 律所名称
+     * @param status   状态
+     * @return IPage<LawFirm>
+     */
+    R<IPage<LawFirm>> getLawFirmList(int pageNum, int pageSize, String firmName, Integer status);
+
+    /**
+     * 新增律所
+     *
+     * @param lawFirm 律所
+     * @return R<LawFirm>
+     */
+    R<LawFirm> addLawFirm(LawFirm lawFirm);
+
+    /**
+     * 编辑律所
+     *
+     * @param lawFirm 律所
+     * @return R<LawFirm>
+     */
+    R<LawFirm> editLawFirm(LawFirm lawFirm);
+
+    /**
+     * 删除律所
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteLawFirm(Integer id);
+
+    /**
+     * 律所注册
+     *
+     * @param lawFirm 律所信息
+     * @return R<LawFirm>
+     */
+    R<LawFirm> register(LawFirm lawFirm);
+
+    /**
+     * 导出律所数据到Excel
+     *
+     * @param response HTTP响应
+     * @param firmId 律所ID(可选)
+     * @param pageNum 页码(可选,默认1)
+     * @param pageSize 页容(可选,默认10,如果为null则导出全部)
+     * @throws IOException IO异常
+     */
+    void exportLawFirm(HttpServletResponse response, Integer firmId, Integer pageNum, Integer pageSize) throws IOException;
+
+    /**
+     * 导入律所数据从Excel
+     *
+     * @param file Excel文件
+     * @return R<String> 返回导入结果信息
+     */
+    R<String> importLawFirm(MultipartFile file);
+
+    /**
+     * 获取律所详情
+     *
+     * @param id 律所ID
+     * @return R<LawFirm>
+     */
+    R<LawFirm> getLawFirmDetail(Integer id);
+
+    /**
+     * 下载律所Excel导入模板
+     *
+     * @param response HTTP响应
+     * @throws IOException IO异常
+     */
+    void downloadTemplate(HttpServletResponse response) throws IOException;
+
+    /**
+     * 批量填充律所的收款账号列表
+     *
+     * @param lawFirmList 律所列表
+     */
+    void fillPaymentList(List<LawFirm> lawFirmList);
+
+    /**
+     * 分页查询律所子表关联律所表
+     *
+     * @param pageNum 页码
+     * @param pageSize 页容
+     * @param firmId 律所ID
+     * @param paymentAccount 收款账号(模糊查询)
+     * @param firmName 律所名称(模糊查询)
+     * @param creditCode 统一社会信用代码
+     * @param status 律所状态
+     * @param certificationStatus 认证状态
+     * @param directorName 负责人姓名(模糊查询)
+     * @param createdTimeStart 创建时间开始
+     * @param createdTimeEnd 创建时间结束
+     * @return IPage<LawFirmPaymentVO>
+     */
+    R<IPage<LawFirmPaymentVO>> getPaymentPageWithFirm(
+            int pageNum, int pageSize,
+            Integer firmId, String paymentAccount, String firmName, String creditCode,
+            Integer status, Integer certificationStatus, String directorName,
+            String createdTimeStart, String createdTimeEnd
+    );
+}
+
+

+ 88 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerAiInteractionLogService.java

@@ -0,0 +1,88 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerAiInteractionLog;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * AI交互日志 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerAiInteractionLogService extends IService<LawyerAiInteractionLog> {
+
+    /**
+     * 分页查询AI交互日志列表
+     *
+     * @param pageNum         页码
+     * @param pageSize        页容
+     * @param clientUserId    客户端用户ID
+     * @param conversationId  会话ID
+     * @param problemScenarioId 法律问题场景ID
+     * @return IPage<LawyerAiInteractionLog>
+     */
+    R<IPage<LawyerAiInteractionLog>> getAiInteractionLogList(int pageNum, int pageSize, Integer clientUserId,
+                                                        String conversationId, Integer problemScenarioId);
+
+    /**
+     * 根据会话ID查询交互日志列表
+     *
+     * @param conversationId 会话ID
+     * @return List<LawyerAiInteractionLog>
+     */
+    List<LawyerAiInteractionLog> getListByConversationId(String conversationId);
+
+    /**
+     * 新增AI交互日志
+     *
+     * @param aiInteractionLog AI交互日志
+     * @return R<LawyerAiInteractionLog>
+     */
+    R<LawyerAiInteractionLog> addAiInteractionLog(LawyerAiInteractionLog aiInteractionLog);
+
+    /**
+     * 编辑AI交互日志
+     *
+     * @param aiInteractionLog AI交互日志
+     * @return R<LawyerAiInteractionLog>
+     */
+    R<LawyerAiInteractionLog> editAiInteractionLog(LawyerAiInteractionLog aiInteractionLog);
+
+    /**
+     * 删除AI交互日志
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteAiInteractionLog(Integer id);
+
+    /**
+     * AI聊天接口
+     *
+     * @param message     用户发送的消息内容
+     * @param sessionId   会话ID(可选)
+     * @param clientUserId 客户端用户ID(可选)
+     * @return R<Map<String, Object>>
+     */
+    R<Map<String, Object>> sendAIMessage(String message, String sessionId, Integer clientUserId);
+
+    /**
+     * AI聊天接口
+     *
+     * @param queryText     用户发送的消息内容
+     * @param responseText   AI回复内容
+     * @param clientUserId 客户端用户ID
+     * @return R<Map<String, Object>>
+     */
+    R<Map<String, Object>> saveChatLog(LawyerAiInteractionLog aiInteractionLog);
+
+    R<Map<String, Object>> updatedLog(Integer id);
+
+    R<IPage<LawyerAiInteractionLog>> getLogList(Integer clientUserId,Integer page, Integer size );
+}
+

+ 63 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerChatMessageService.java

@@ -0,0 +1,63 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerChatMessage;
+
+import java.util.List;
+
+/**
+ * 聊天消息 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerChatMessageService extends IService<LawyerChatMessage> {
+
+    /**
+     * 分页查询聊天消息列表
+     *
+     * @param pageNum             页码
+     * @param pageSize            页容
+     * @param chatSessionId       聊天会话ID
+     * @param consultationOrderId 咨询订单ID
+     * @param senderType          发送者类型
+     * @return IPage<LawyerChatMessage>
+     */
+    R<IPage<LawyerChatMessage>> getChatMessageList(int pageNum, int pageSize, Integer chatSessionId,
+                                             Integer consultationOrderId, Integer senderType);
+
+    /**
+     * 根据会话ID查询消息列表
+     *
+     * @param chatSessionId 聊天会话ID
+     * @return List<LawyerChatMessage>
+     */
+    List<LawyerChatMessage> getListBySessionId(Integer chatSessionId);
+
+    /**
+     * 新增聊天消息
+     *
+     * @param chatMessage 聊天消息
+     * @return R<LawyerChatMessage>
+     */
+    R<LawyerChatMessage> addChatMessage(LawyerChatMessage chatMessage);
+
+    /**
+     * 编辑聊天消息
+     *
+     * @param chatMessage 聊天消息
+     * @return R<LawyerChatMessage>
+     */
+    R<LawyerChatMessage> editChatMessage(LawyerChatMessage chatMessage);
+
+    /**
+     * 删除聊天消息
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteChatMessage(Integer id);
+}
+

+ 103 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerChatSessionService.java

@@ -0,0 +1,103 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerChatSession;
+
+import java.util.Map;
+
+/**
+ * 聊天会话 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerChatSessionService extends IService<LawyerChatSession> {
+
+    /**
+     * 分页查询聊天会话列表
+     *
+     * @param pageNum             页码
+     * @param pageSize            页容
+     * @param consultationOrderId 咨询订单ID
+     * @param clientUserId        客户端用户ID
+     * @param lawyerUserId        律师用户ID
+     * @param status              会话状态
+     * @return IPage<LawyerChatSession>
+     */
+    R<IPage<LawyerChatSession>> getChatSessionList(int pageNum, int pageSize, Integer consultationOrderId,
+                                             Integer clientUserId, Integer lawyerUserId, Integer status);
+
+    /**
+     * 根据订单ID查询会话
+     *
+     * @param consultationOrderId 咨询订单ID
+     * @return LawyerChatSession
+     */
+    LawyerChatSession getByOrderId(Integer consultationOrderId);
+
+    /**
+     * 新增聊天会话
+     *
+     * @param chatSession 聊天会话
+     * @return R<LawyerChatSession>
+     */
+    R<LawyerChatSession> addChatSession(LawyerChatSession chatSession);
+
+    /**
+     * 编辑聊天会话
+     *
+     * @param chatSession 聊天会话
+     * @return R<LawyerChatSession>
+     */
+    R<LawyerChatSession> editChatSession(LawyerChatSession chatSession);
+
+    /**
+     * 删除聊天会话
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteChatSession(Integer id);
+
+    /**
+     * 获取聊天历史记录
+     *
+     * @param sessionId 会话ID(可选,不传则获取所有会话列表)
+     * @param page      页码
+     * @param pageSize  每页数量
+     * @return R<Map<String, Object>>
+     */
+    R<Map<String, Object>> getChatHistory(String sessionId, int page, int pageSize);
+
+    /**
+     * 保存聊天消息
+     *
+     * @param sessionId         会话ID(可选,不传则创建新会话)
+     * @param content           消息内容
+     * @param type              消息类型:user或ai
+     * @param clientUserId      客户端用户ID(当type=user时必填)
+     * @param consultationOrderId 咨询订单ID(可选)
+     * @return R<Map<String, Object>>
+     */
+    R<Map<String, Object>> saveChatMessage(String sessionId, String content, String type, Integer clientUserId, Integer consultationOrderId);
+
+    /**
+     * 删除聊天消息
+     *
+     * @param messageIds 消息ID数组(逗号分隔)
+     * @param sessionId 会话ID(可选)
+     * @return R<String>
+     */
+    R<String> deleteChatMessages(String messageIds, String sessionId);
+
+    /**
+     * 删除会话(通过会话ID字符串)
+     *
+     * @param sessionId 会话ID
+     * @return R<String>
+     */
+    R<String> deleteChatSessionBySessionId(String sessionId);
+}
+

+ 109 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerClientConsultationOrderService.java

@@ -0,0 +1,109 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerConsultationOrder;
+import shop.alien.entity.store.vo.LawyerConsultationOrderVO;
+
+import java.util.Map;
+
+/**
+ * 咨询订单 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerClientConsultationOrderService extends IService<LawyerConsultationOrder> {
+
+    /**
+     * 删除咨询订单
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteConsultationOrder(Integer id);
+
+
+    /**
+     * 获取咨询订单详情
+     *
+     * @param lawyerOrderId 订单ID
+     * @return 咨询订单详情VO
+     */
+    LawyerConsultationOrderVO getConsultationOrderDetail(String lawyerOrderId);
+
+    /**
+     * 查询咨询订单(律师端)
+     *
+     * @param pageNum      页码
+     * @param pageSize     页容
+     * @param startDate  开始时间
+     * @param endDate 结束时间
+     * @param lawyerId 律师ID
+     * @return IPage<LawyerConsultationOrderVO>
+     */
+    Map<String, Object> getLawyerConsultationOrderInfo(int pageNum, int pageSize, String startDate, String endDate, String clientUserName, String orderStatus, String lawyerId);
+
+    /**
+     * 律师确认订单
+     * <p>
+     * 支持两种操作类型:
+     * 1. 接单:将订单状态从待接单更新为进行中
+     * 2. 取消:将订单状态更新为已取消
+     * </p>
+     *
+     * @param id        订单ID
+     * @param actionType 操作类型:1-接单,2-取消
+     * @return R<Boolean> 是否成功
+     */
+    R<Boolean> confirmOrder(String id, String actionType);
+
+
+    /**
+     * 订单开始计时(律师端)
+     * <p>
+     * 订单开始计时:
+     * 1. 当订单为按时收费,订单状态为进行中时,订单开始计时
+     * </p>
+     *
+     * @param id        订单ID
+     * @return R<Boolean> 是否成功
+     */
+    R<Boolean> orderStartTiming(String id);
+
+    /**
+     * 申请退款
+     *
+     * @param id 订单ID
+     * @param refundReason 退款原因
+     * @return R<Boolean> 是否成功
+     */
+    R<Boolean> requestRefund(Integer id, String refundReason);
+
+
+    /**
+     * 退款申请处理
+     *
+     * @param lawyerConsultationOrder 订单对象
+     * @return R<Boolean> 是否成功
+     */
+    R<Boolean> refundApplyProcess(LawyerConsultationOrder lawyerConsultationOrder);
+
+    /**
+     * 检查订单接单后是否有消息记录
+     * <p>
+     * 根据订单ID查询订单信息,获取律师接单时间、用户ID和律师ID,
+     * 然后查询 life_message 表,检查是否存在律师发送给用户的消息记录,
+     * 且消息创建时间大于接单时间。
+     * </p>
+     *
+     * @param id 订单ID,不能为空
+     * @return true表示有消息记录,false表示无消息记录
+     * @throws IllegalArgumentException 当订单ID无效时抛出
+     * @throws RuntimeException        当查询异常时抛出
+     * @author system
+     * @since 2025-01-XX
+     */
+    Boolean checkMessageAfterAccept(String id);
+}
+

+ 73 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerCommonQuestionService.java

@@ -0,0 +1,73 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerCommonQuestion;
+
+import java.util.List;
+
+/**
+ * 常见问题 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerCommonQuestionService extends IService<LawyerCommonQuestion> {
+
+    /**
+     * 分页查询常见问题列表
+     *
+     * @param pageNum         页码
+     * @param pageSize        页容
+     * @param questionText    问题文本
+     * @param categoryType    分类类型
+     * @param problemScenarioId 法律问题场景ID
+     * @param status          状态
+     * @return IPage<LawyerCommonQuestion>
+     */
+    R<IPage<LawyerCommonQuestion>> getCommonQuestionList(int pageNum, int pageSize, String questionText,
+                                                    Integer categoryType, Integer problemScenarioId, Integer status);
+
+    /**
+     * 根据分类类型查询常见问题列表
+     *
+     * @param categoryType 分类类型
+     * @return List<LawyerCommonQuestion>
+     */
+    List<LawyerCommonQuestion> getListByCategoryType(Integer categoryType);
+
+    /**
+     * 新增常见问题
+     *
+     * @param commonQuestion 常见问题
+     * @return R<LawyerCommonQuestion>
+     */
+    R<LawyerCommonQuestion> addCommonQuestion(LawyerCommonQuestion commonQuestion);
+
+    /**
+     * 编辑常见问题
+     *
+     * @param commonQuestion 常见问题
+     * @return R<LawyerCommonQuestion>
+     */
+    R<LawyerCommonQuestion> editCommonQuestion(LawyerCommonQuestion commonQuestion);
+
+    /**
+     * 删除常见问题
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteCommonQuestion(Integer id);
+
+    /**
+     * 根据数量获取常见问题列表
+     *
+     * @param num 获取数量
+     * @return R<List<LawyerCommonQuestion>>
+     */
+    R<List<LawyerCommonQuestion>> getCommonQuestionListByNum(Integer num);
+
+}
+

+ 159 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerConsultationOrderService.java

@@ -0,0 +1,159 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.web.bind.annotation.RequestParam;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerConsultationOrder;
+import shop.alien.entity.store.dto.LawyerConsultationOrderDto;
+import shop.alien.entity.store.dto.PayStatusRequest;
+import shop.alien.entity.store.vo.LawyerConsultationOrderVO;
+
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * 咨询订单 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerConsultationOrderService extends IService<LawyerConsultationOrder> {
+
+    /**
+     * 分页查询咨询订单列表(包含律师信息)
+     *
+     * @param pageNum      页码
+     * @param pageSize     页容
+     * @param orderNumber  订单编号
+     * @param clientUserId 客户端用户ID
+     * @param lawyerUserId 律师用户ID
+     * @param lawyerName   律师姓名
+     * @param orderStatus  订单状态
+     * @return IPage<LawyerConsultationOrderVO>
+     */
+    R<IPage<LawyerConsultationOrderVO>> getConsultationOrderList(int pageNum, int pageSize, String orderNumber,
+                                                          Integer clientUserId, Integer lawyerUserId, String lawyerName, Integer orderStatus);
+
+    /**
+     * 新增咨询订单
+     *
+     * @param consultationOrder 咨询订单
+     * @return R<LawyerConsultationOrder>
+     */
+    R<LawyerConsultationOrder> addConsultationOrder(LawyerConsultationOrder consultationOrder);
+
+    /**
+     * 编辑咨询订单
+     *
+     * @param consultationOrder 咨询订单
+     * @return R<LawyerConsultationOrder>
+     */
+    R<LawyerConsultationOrder> editConsultationOrder(LawyerConsultationOrder consultationOrder);
+
+    /**
+     * 删除咨询订单
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteConsultationOrder(Integer id);
+
+    /**
+     * 开始咨询律师
+     *
+     * @param lawyerConsultationOrder        律师ID
+     * @param lawyerConsultationOrder        咨询问题(可选)
+     * @param lawyerConsultationOrder       关联的AI会话ID(可选)
+     * @param lawyerConsultationOrder    客户端用户ID(可选)
+     * @param lawyerConsultationOrder 法律问题场景ID(可选)
+     * @return R<Map<String, Object>>
+     */
+    //R<Map<String, Object>> startConsultation(Integer lawyerId, String question, String sessionId, Integer clientUserId, Integer problemScenarioId);
+
+
+    R<LawyerConsultationOrderDto> consultNow(LawyerConsultationOrder lawyerConsultationOrder);
+
+    /**
+     * 支付订单
+     *
+     * @param  request 请求对象
+     * @return R<Map<String, Object>>
+     */
+    R<LawyerConsultationOrderDto> payStatus(PayStatusRequest request);
+
+
+    /**
+     * 根据用户Id查询订单列表(包含律师信息)
+     *
+     * @param pageNum      页码
+     * @param pageSize     页容
+     * @param userId  订单编号
+     * @param orderStatus 订单状态
+     * @return IPage<LawyerConsultationOrderVO>
+     */
+    Map<String, Object> getConsultationOrderListById(int pageNum, int pageSize, String userId, String orderStatus, String lawyerName);
+
+    /**
+     * 获取咨询订单详情
+     *
+     * @param lawyerOrderId 订单ID
+     * @return 咨询订单详情VO
+     */
+    LawyerConsultationOrderVO getConsultationOrderDetail(String lawyerOrderId);
+
+    boolean cancelOrder(String id);
+
+    /**
+     * 查询咨询订单(律师端)
+     *
+     * @param pageNum      页码
+     * @param pageSize     页容
+     * @param startDate  开始时间
+     * @param endDate 结束时间
+     * @param lawyerId 律师ID
+     * @return IPage<LawyerConsultationOrderVO>
+     */
+    Map<String, Object> getLawyerConsultationOrderInfo(int pageNum, int pageSize, String startDate, String endDate, String clientUserName, String orderStatus, String lawyerId);
+
+    /**
+     * 订单检查
+     *
+     * @param clientUserId
+     * @param lawyerUserId
+     * @return
+     */
+   R<Map<String, String>> checkOrder(Integer clientUserId,Integer lawyerUserId);
+
+
+   /**
+   * 获取律师收入
+   *
+   * @param lawyerConsultationOrderVO
+   * @return
+   */
+   R<Map<String, Object>> getOrderIncome(LawyerConsultationOrderVO lawyerConsultationOrderVO);
+
+
+    /**
+     * 用户端申请退款
+     *
+     * @param clientUserId
+     * @param orderId
+     * @return
+     */
+    R<Boolean> applyRefund(String clientUserId, String orderId, String applyRefundReason);
+
+
+    /**
+     * 用户端完成订单
+     *
+     * @param clientUserId
+     * @param orderId
+     * @return
+     */
+    R<Boolean> completeOrder(String clientUserId, String orderId);
+
+    LawyerConsultationOrder getApplyRefundDetail(String orderId);
+}
+

+ 63 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerConsultationReviewService.java

@@ -0,0 +1,63 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerConsultationReview;
+
+import java.util.List;
+
+/**
+ * 咨询评价 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerConsultationReviewService extends IService<LawyerConsultationReview> {
+
+    /**
+     * 分页查询咨询评价列表
+     *
+     * @param pageNum             页码
+     * @param pageSize            页容
+     * @param consultationOrderId 咨询订单ID
+     * @param lawyerUserId        律师用户ID
+     * @param clientUserId        客户端用户ID
+     * @return IPage<LawyerConsultationReview>
+     */
+    R<IPage<LawyerConsultationReview>> getConsultationReviewList(int pageNum, int pageSize, Integer consultationOrderId,
+                                                            Integer lawyerUserId, Integer clientUserId);
+
+    /**
+     * 根据律师ID查询评价列表
+     *
+     * @param lawyerUserId 律师用户ID
+     * @return List<LawyerConsultationReview>
+     */
+    List<LawyerConsultationReview> getListByLawyerId(Integer lawyerUserId);
+
+    /**
+     * 新增咨询评价
+     *
+     * @param consultationReview 咨询评价
+     * @return R<LawyerConsultationReview>
+     */
+    R<LawyerConsultationReview> addConsultationReview(LawyerConsultationReview consultationReview);
+
+    /**
+     * 编辑咨询评价
+     *
+     * @param consultationReview 咨询评价
+     * @return R<LawyerConsultationReview>
+     */
+    R<LawyerConsultationReview> editConsultationReview(LawyerConsultationReview consultationReview);
+
+    /**
+     * 删除咨询评价
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteConsultationReview(Integer id);
+}
+

+ 70 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerExpertiseAreaService.java

@@ -0,0 +1,70 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerExpertiseArea;
+
+import java.util.List;
+
+/**
+ * 领域信息 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerExpertiseAreaService extends IService<LawyerExpertiseArea> {
+
+    /**
+     * 分页查询领域信息列表
+     *
+     * @param pageNum           页码
+     * @param pageSize          页容
+     * @param expertiseAreaInfo 领域信息(支持模糊查询)
+     * @return IPage<LawyerExpertiseArea>
+     */
+    R<IPage<LawyerExpertiseArea>> getExpertiseAreaList(int pageNum, int pageSize, String expertiseAreaInfo);
+
+    /**
+     * 新增领域信息
+     *
+     * @param expertiseArea 领域信息
+     * @return R<LawyerExpertiseArea>
+     */
+    R<LawyerExpertiseArea> addExpertiseArea(LawyerExpertiseArea expertiseArea);
+
+    /**
+     * 编辑领域信息
+     *
+     * @param expertiseArea 领域信息
+     * @return R<LawyerExpertiseArea>
+     */
+    R<LawyerExpertiseArea> editExpertiseArea(LawyerExpertiseArea expertiseArea);
+
+    /**
+     * 删除领域信息
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteExpertiseArea(Integer id);
+
+    /**
+     * 通用列表查询
+     *
+     * @param expertiseArea 领域信息查询条件
+     * @return List<LawyerExpertiseArea>
+     */
+    R<List<LawyerExpertiseArea>> getList(LawyerExpertiseArea expertiseArea);
+
+    /**
+     * 通用分页查询
+     *
+     * @param expertiseArea 领域信息查询条件
+     * @param page          页码
+     * @param size          页容
+     * @return IPage<LawyerExpertiseArea>
+     */
+    R<IPage<LawyerExpertiseArea>> getPage(LawyerExpertiseArea expertiseArea, int page, int size);
+}
+

+ 61 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerImgService.java

@@ -0,0 +1,61 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.LawyerImg;
+import shop.alien.entity.store.vo.LawyerImgTypeVo;
+
+import java.util.List;
+
+/**
+ * 律师图片 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerImgService extends IService<LawyerImg> {
+
+    /**
+     * 获取律师图片
+     *
+     * @param lawyerId 律师ID
+     * @param imgType  图片类型, 0:其他, 1:头像, 2:执业证照片, 3:身份证正面, 4:身份证反面, 5:案例图片, 6:相册图片, 7:资质证书, 8:荣誉证书, 9:工作照片
+     * @return list
+     */
+    List<LawyerImg> getLawyerImg(Integer lawyerId, Integer imgType);
+
+    /**
+     * 获取所有图片类型和数量
+     *
+     * @param lawyerId 律师ID
+     * @return LawyerImgTypeVo
+     */
+    List<LawyerImgTypeVo> getLawyerImgTypeCount(Integer lawyerId);
+
+    /**
+     * 获取图片URL列表
+     *
+     * @param id      律师ID
+     * @param imgType 图片类型
+     * @return List<String>
+     */
+    List<String> getUrl(String id, Integer imgType);
+
+    /**
+     * 通过businessId获取图片
+     *
+     * @param lawyerId  律师ID
+     * @param imgType   图片类型
+     * @param businessId 业务ID
+     * @return list
+     */
+    List<LawyerImg> getByBusinessId(Integer lawyerId, Integer imgType, Integer businessId);
+
+    /**
+     * 删除图片
+     *
+     * @param lawyerId 律师ID
+     * @param imgType  图片类型
+     */
+    int saveOrUpdateImg(Integer lawyerId, Integer imgType);
+}
+

+ 56 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerLegalProblemScenarioService.java

@@ -0,0 +1,56 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerLegalProblemScenario;
+
+/**
+ * 法律问题场景 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerLegalProblemScenarioService extends IService<LawyerLegalProblemScenario> {
+
+    /**
+     * 新增法律问题场景
+     *
+     * @param lawyerLegalProblemScenario 法律问题场景
+     * @return R<LawyerLegalProblemScenar>
+     */
+    R<LawyerLegalProblemScenario> addLawyerLegalProblemScenario(LawyerLegalProblemScenario lawyerLegalProblemScenario);
+
+    /**
+     * 编辑法律问题场景
+     *
+     * @param lawyerLegalProblemScenario 法律问题场景
+     * @return R<LawyerLegalProblemScenar>
+     */
+    R<LawyerLegalProblemScenario> editLawyerLegalProblemScenario(LawyerLegalProblemScenario lawyerLegalProblemScenario);
+
+    /**
+     * 删除法律问题场景
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteLawyerLegalProblemScenario(Integer id);
+
+    /**
+     * 根据一级场景主键获取其下所有分类(包括二级和三级分类的树形结构)
+     *
+     * @param id 一级场景主键
+     * @return R<LawyerLegalProblemScenario> 返回一级场景及其完整的树形结构(一级场景的children包含所有二级分类,每个二级分类的children包含其下的三级分类)
+     */
+    R<LawyerLegalProblemScenario> getCategoryTreeByFirstLevelId(Integer id);
+
+    /**
+     * 设置法律场景启用/禁用状态
+     *
+     * @param id 场景ID
+     * @param status 状态,0:禁用,1:启用
+     * @return R<Boolean> 操作结果
+     */
+    R<Boolean> setStatus(Integer id, Integer status);
+}
+

+ 50 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerNoticeService.java

@@ -0,0 +1,50 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeNotice;
+import shop.alien.entity.store.vo.LifeNoticeVo;
+
+/**
+ * 律师通知服务接口
+ *
+ * @author system
+ */
+public interface LawyerNoticeService extends IService<LifeNotice> {
+
+    /**
+     * 获取律师通知列表
+     *
+     * @param pageNum    页码,从1开始
+     * @param pageSize   每页大小
+     * @param receiverId 接收人ID
+     * @param noticeType 通知类型:0-系统通知和订单提醒之外的类型,1-系统通知,2-订单提醒
+     * @return 分页通知列表
+     */
+    R<IPage<LifeNoticeVo>> getLawyerNoticeList(Integer pageNum, Integer pageSize, String receiverId, Integer noticeType);
+
+    /**
+     * 查询接收人是否有未读通知
+     *
+     * @param receiverId 接收人ID,不能为空
+     * @return 是否有未读通知,true-有未读,false-无未读
+     */
+    R<Boolean> hasUnreadNotice(String receiverId);
+
+    /**
+     * 根据通知ID标记通知为已读
+     * <p>
+     * 将指定ID的通知标记为已读状态(isRead = 1)
+     * </p>
+     *
+     * @param id 通知ID,必须大于0且不能为null
+     * @return 受影响的记录数,>0表示更新成功,0表示未找到匹配的记录或更新失败
+     * @throws IllegalArgumentException 当通知ID无效时抛出
+     * @throws RuntimeException        当更新操作异常时抛出
+     * @author system
+     * @since 2025-01-XX
+     */
+    int readNoticeById(Integer id);
+
+}

+ 53 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerPaymentTransactionService.java

@@ -0,0 +1,53 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerPaymentTransaction;
+
+/**
+ * 支付交易 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerPaymentTransactionService extends IService<LawyerPaymentTransaction> {
+
+    /**
+     * 分页查询支付交易列表
+     *
+     * @param pageNum             页码
+     * @param pageSize            页容
+     * @param consultationOrderId 咨询订单ID
+     * @param clientUserId        客户端用户ID
+     * @param transactionStatus   交易状态
+     * @return IPage<LawyerPaymentTransaction>
+     */
+    R<IPage<LawyerPaymentTransaction>> getPaymentTransactionList(int pageNum, int pageSize, Integer consultationOrderId,
+                                                           Integer clientUserId, Integer transactionStatus);
+
+    /**
+     * 新增支付交易
+     *
+     * @param paymentTransaction 支付交易
+     * @return R<LawyerPaymentTransaction>
+     */
+    R<LawyerPaymentTransaction> addPaymentTransaction(LawyerPaymentTransaction paymentTransaction);
+
+    /**
+     * 编辑支付交易
+     *
+     * @param paymentTransaction 支付交易
+     * @return R<LawyerPaymentTransaction>
+     */
+    R<LawyerPaymentTransaction> editPaymentTransaction(LawyerPaymentTransaction paymentTransaction);
+
+    /**
+     * 删除支付交易
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deletePaymentTransaction(Integer id);
+}
+

+ 39 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerServiceAreaService.java

@@ -0,0 +1,39 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerServiceArea;
+
+/**
+ * 律师服务领域关联 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerServiceAreaService extends IService<LawyerServiceArea> {
+
+    /**
+     * 新增律师服务领域
+     *
+     * @param lawyerServiceArea 律师服务领域关联
+     * @return R<LawyerServiceArea>
+     */
+    R<LawyerServiceArea> addLawyerServiceArea(LawyerServiceArea lawyerServiceArea);
+
+    /**
+     * 编辑律师服务领域
+     *
+     * @param lawyerServiceArea 律师服务领域关联
+     * @return R<LawyerServiceArea>
+     */
+    R<LawyerServiceArea> editLawyerServiceArea(LawyerServiceArea lawyerServiceArea);
+
+    /**
+     * 删除律师服务领域
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteLawyerServiceArea(Integer id);
+}
+

+ 31 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerStatisticsService.java

@@ -0,0 +1,31 @@
+package shop.alien.lawyer.service;
+
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.vo.LawyerDashboardVO;
+import shop.alien.entity.store.vo.LawyerOrderStatisticsVO;
+
+/**
+ * 律师统计 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerStatisticsService {
+
+    /**
+     * 获取律师订单统计数据
+     *
+     * @param lawyerUserId 律师用户ID
+     * @return 订单统计数据
+     */
+    R<LawyerOrderStatisticsVO> getOrderStatistics(Integer lawyerUserId);
+
+    /**
+     * 获取律师仪表板数据(包含个人信息、订单统计和收益统计)
+     *
+     * @param lawyerUserId 律师用户ID
+     * @return 仪表板数据
+     */
+    R<LawyerDashboardVO> getLawyerDashboard(Integer lawyerUserId);
+}
+

+ 29 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerUserLogInService.java

@@ -0,0 +1,29 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerUser;
+import shop.alien.entity.store.dto.LawyerUserDto;
+import shop.alien.entity.store.vo.LawyerUserVo;
+
+/**
+ * 二期-门店用户 服务类
+ *
+ * @author ssk
+ * @since 2024-12-11
+ */
+public interface LawyerUserLogInService extends IService<LawyerUser> {
+
+    /**
+     * 创建token
+     * @return
+     */
+    R<LawyerUserVo> createToKen(LawyerUser lawyerUser);
+    R<LawyerUserVo> register(LawyerUserDto lawyerUserDto);
+    R checkMsgCode(LawyerUserDto lawyerUserDto);
+
+    R logout(LawyerUserDto lawyerUserDto);
+    R updatePassWord(LawyerUserDto lawyerUserDto);
+
+    Boolean checkRegister(LawyerUserDto lawyerUserDto);
+}

+ 79 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerUserSearchHistoryService.java

@@ -0,0 +1,79 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerUserSearchHistory;
+
+import java.util.List;
+
+/**
+ * 用户搜索历史 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerUserSearchHistoryService extends IService<LawyerUserSearchHistory> {
+
+    /**
+     * 分页查询用户搜索历史列表
+     *
+     * @param pageNum     页码
+     * @param pageSize    页容
+     * @param clientUserId 客户端用户ID
+     * @param searchType  搜索类型
+     * @return IPage<LawyerUserSearchHistory>
+     */
+    R<IPage<LawyerUserSearchHistory>> getUserSearchHistoryList(int pageNum, int pageSize, Integer clientUserId, Integer searchType);
+
+    /**
+     * 根据用户ID查询搜索历史列表
+     *
+     * @param clientUserId 客户端用户ID
+     * @return List<LawyerUserSearchHistory>
+     */
+    List<LawyerUserSearchHistory> getListByUserId(Integer clientUserId);
+
+    /**
+     * 新增用户搜索历史
+     *
+     * @param userSearchHistory 用户搜索历史
+     * @return R<LawyerUserSearchHistory>
+     */
+    R<LawyerUserSearchHistory> addUserSearchHistory(LawyerUserSearchHistory userSearchHistory);
+
+    /**
+     * 删除用户搜索历史
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteUserSearchHistory(Integer id);
+
+    /**
+     * 清空用户搜索历史
+     *
+     * @param clientUserId 客户端用户ID
+     * @return R<Boolean>
+     */
+    R<Boolean> clearUserSearchHistory(Integer clientUserId);
+
+    /**
+     * 通用列表查询(按创建时间降序)
+     *
+     * @param userSearchHistory 查询条件
+     * @return List<LawyerUserSearchHistory>
+     */
+    List<LawyerUserSearchHistory> getListWithOrder(LawyerUserSearchHistory userSearchHistory);
+
+    /**
+     * 通用分页查询(按创建时间降序)
+     *
+     * @param userSearchHistory 查询条件
+     * @param pageNum 页码
+     * @param pageSize 每页数量
+     * @return IPage<LawyerUserSearchHistory>
+     */
+    IPage<LawyerUserSearchHistory> getPageWithOrder(LawyerUserSearchHistory userSearchHistory, int pageNum, int pageSize);
+}
+

+ 199 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerUserService.java

@@ -0,0 +1,199 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerUser;
+import shop.alien.entity.store.dto.LawyerUserDto;
+import shop.alien.entity.store.vo.LawyerUserVo;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.Map;
+
+/**
+ * 律师用户 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerUserService extends IService<LawyerUser> {
+
+    /**
+     * 分页查询律师用户列表
+     *
+     * @param pageNum  页码
+     * @param pageSize 页容
+     * @param name     姓名
+     * @param phone    手机号
+     * @param status   状态
+     * @return IPage<LawyerUser>
+     */
+    R<IPage<LawyerUser>> getLawyerUserList(int pageNum, int pageSize, String name, String phone, Integer status);
+
+    /**
+     * 新增律师用户
+     *
+     * @param lawyerUser 律师用户
+     * @return R<LawyerUser>
+     */
+    R<LawyerUser> addLawyerUser(LawyerUser lawyerUser);
+
+    /**
+     * 编辑律师用户
+     *
+     * @param lawyerUser 律师用户
+     * @return R<LawyerUser>
+     */
+    R<LawyerUser> editLawyerUser(LawyerUser lawyerUser);
+
+    /**
+     * 删除律师用户
+     *
+     * @param id 主键
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteLawyerUser(Integer id);
+
+    /**
+     * 获取律师详情
+     *
+     * @param lawyerId 律师ID
+     * @return R<Map < String, Object>>
+     */
+    R<Map<String, Object>> getLawyerDetail(Integer lawyerId);
+
+    /**
+     * 获取律师在线状态
+     *
+     * @param lawyerIds 律师ID数组(逗号分隔)
+     * @return R<Map < Integer, Boolean>>
+     */
+    R<Map<Integer, Boolean>> getLawyerOnlineStatus(String lawyerIds);
+
+    /**
+     * 获取推荐律师列表
+     *
+     * @param page       页码
+     * @param pageSize   每页数量
+     * @param categoryId 分类ID(可选)
+     * @param lawyerId   律师ID(可选)
+     * @return R<IPage < LawyerUser>>
+     */
+    R<IPage<LawyerUser>> getRecommendedLawyerList(int page, int pageSize, Integer categoryId, Integer lawyerId);
+
+    /**
+     * 根据会话获取推荐律师列表
+     *
+     * @param sessionId 会话ID
+     * @param messageId 消息ID(可选)
+     * @return R<IPage < LawyerUser>>
+     */
+    R<IPage<LawyerUser>> getRecommendedLawyersBySession(String sessionId, Integer messageId);
+
+    /**
+     * 通过姓名模糊查询律师(并保存搜索历史)
+     *
+     * @param nickName     律师姓名(支持模糊查询)
+     * @param page         页码
+     * @param pageSize     每页数量
+     * @param clientUserId 客户端用户ID(可选,用于保存搜索历史)
+     * @return R<IPage < LawyerUser>>
+     */
+    R<IPage<LawyerUser>> searchLawyerByName(String nickName, int page, int pageSize, Integer clientUserId);
+
+    /**
+     * 获取推荐律师列表
+     *
+     * @param page       页码
+     * @param size       每页数量
+     * @param categoryId 分类ID(可选)
+     * @return R<IPage < LawyerUser>>
+     */
+    R<IPage<LawyerUser>> getAiRecommendList(int page, int size, Integer categoryId);
+
+    /**
+     * 注销律师用户
+     * <p>
+     * 注销前会进行以下校验:
+     * 1. 参数校验:律师ID不能为空
+     * 2. 律师存在性校验:律师必须存在
+     * 3. 注销状态校验:已注销的律师不允许重复注销
+     * 4. 设置注销相关字段:注销标记、注销时间、状态等
+     * </p>
+     *
+     * @param id 律师ID
+     * @return R<Map < String, Object>> 注销结果,包含律师ID、姓名、手机号、注销标记、注销时间等信息
+     */
+    R<Map<String, Object>> logOutLawyerUser(Integer id);
+
+    /**
+     * 获取律师信息
+     *
+     * @param lawyerId 律师ID
+     * @return R<LawyerUser> 律师信息
+     */
+    R<LawyerUserVo> getLawyerInfo(Integer lawyerId);
+
+    /**
+     * 更新律师用户信息
+     *
+     * @param lawyerUserVo 律师用户信息VO
+     * @return R<LawyerUser> 更新后的律师信息
+     */
+    R<LawyerUserVo> updateLawyerUser(LawyerUserVo lawyerUserVo);
+
+    /**
+     * 通过手机号查询律师信息
+     *
+     * @param phone 手机号
+     * @return R<LawyerUser> 律师信息
+     */
+    R<LawyerUser> getLawyerInfoByPhone(String phone);
+
+    /**
+     * 获取中台律师列表
+     *
+     * @return R<LawyerUser> 律师信息
+     */
+    R<IPage<LawyerUserVo>> getLawyerList(String name,
+                                         String phone,
+                                         Integer firmId,
+                                         String startTime,
+                                         String endTime,
+                                         int page,
+                                         int pageSize);
+
+    /**
+     * 设置律师推荐状态
+     *
+     * @param lawyerId 律师ID
+     * @param isRecommended 推荐状态,0:取消推荐,1:推荐
+     * @return R<Boolean> 操作结果
+     */
+    R<Boolean> isRecommended(Integer lawyerId, Integer isRecommended);
+
+    /**
+     * 导出中台律师列表
+     *
+     * @param response HttpServletResponse对象
+     * @param name 姓名(支持模糊查询)
+     * @param phone 手机号(支持模糊查询)
+     * @param firmId 律所ID
+     * @param startTime 开始日期
+     * @param endTime 结束日期
+     * @param pageNum 页码(可选,不传或传0则导出全部)
+     * @param pageSize 页容(可选,与pageNum配合使用)
+     * @throws Exception 导出异常
+     */
+    void exportLawyerList(HttpServletResponse response,
+                          String name,
+                          String phone,
+                          Integer firmId,
+                          String startTime,
+                          String endTime,
+                          Integer pageNum,
+                          Integer pageSize) throws Exception;
+
+    R<LawyerUserVo> updateLawyerUserCharge(LawyerUserDto lawyerUserDto);
+}
+

+ 87 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/LawyerUserViolationService.java

@@ -0,0 +1,87 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.LawyerUserViolation;
+import shop.alien.entity.store.StoreDictionary;
+import shop.alien.entity.store.UserLoginInfo;
+import shop.alien.entity.store.dto.LawyerUserViolationDto;
+import shop.alien.entity.store.vo.LawyerUserViolationVo;
+import shop.alien.entity.store.vo.LawyerViolationDetailVO;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>
+ * 律师用户举报 服务类
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface LawyerUserViolationService extends IService<LawyerUserViolation> {
+
+    /**
+     * 用户举报处理
+     *
+     * @param lawyerUserViolation 举报信息对象
+     * @return 插入成功的记录数,失败返回0
+     */
+    int userReporting(LawyerUserViolation lawyerUserViolation);
+
+    Map<String, Object> reportListByUserId(String userId);
+
+    /**
+     * 根据ID查询举报详情
+     * <p>
+     * 根据举报ID查询举报详细信息,包括被举报人信息、举报原因、举报凭证等
+     * </p>
+     *
+     * @param id 举报记录ID,不能为空
+     * @return 举报详情VO对象,如果记录不存在返回null
+     * @throws RuntimeException 当参数无效或查询异常时抛出
+     * @author system
+     * @since 2025-01-XX
+     */
+    LawyerUserViolationVo reportListById(String id);
+
+    IPage<LawyerUserViolationDto> getViolationPage(int pageNum, int pageSize, String orderNumber, String phone, String processingStatus, String reportedUserName, String reportedPhone, String violationReason, String startTime, String endTime);
+
+    void approve(int id, String processingStatus, String reportResult);
+
+    LawyerUserViolationDto byId(Integer id);
+
+    LawyerUserViolationDto byIdNotice(Integer id);
+
+    String exportExcel(String nickName, String phone, String processingStatus) throws IOException;
+
+    String level(UserLoginInfo userLoginInfo);
+
+    /**
+     * 获取举报原因列表
+     * <p>
+     * 查询所有未删除的律师违规举报原因字典数据
+     * </p>
+     *
+     * @return 举报原因字典列表,如果查询失败或没有数据则返回空列表
+     * @author system
+     * @since 2025-01-XX
+     */
+    List<StoreDictionary> getViolationReason();
+
+    /**
+     * 根据订单ID查询举报详情
+     * <p>
+     * 查询举报详情信息,包括举报的详情信息、订单的详情信息以及订单律师名字相关信息
+     * </p>
+     *
+     * @param orderId 订单ID,必须大于0
+     * @return 举报详情VO对象,包含举报信息、订单信息、律师信息,如果订单不存在或未举报则返回null
+     * @author system
+     * @since 2025-01-XX
+     */
+    LawyerViolationDetailVO getViolationDetailByOrderId(Integer orderId);
+}
+

+ 84 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/OrderExpirationService.java

@@ -0,0 +1,84 @@
+package shop.alien.lawyer.service;
+
+/**
+ * 訂單過期處理服務接口
+ * 
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface OrderExpirationService {
+
+    /**
+     * 處理訂單支付超時
+     * 
+     * @param orderNum 訂單no
+     */
+    void handleOrderPaymentTimeout(String orderNum);
+    /**
+     * 處理訂單待接單超時
+     *
+     * @param expiredKey 訂單no
+     */
+    void handleRefundOrderKey(String expiredKey);
+
+    /**
+     * 處理訂單支付超時
+     *
+     * @param orderNum 訂單no
+     */
+    void handleTimeOutCompleteOrder(String orderNum);
+
+    /**
+     * 設置訂單支付超時監聽
+     * 
+     * @param orderNumber 訂單NO
+     * @param timeoutSeconds 超時時間(秒),默認30分鐘
+     */
+    void setOrderPaymentTimeout(String orderNumber, long timeoutSeconds);
+
+    /**
+     * 設置订单待接单超时監聽
+     *
+     * @param orderNumber 訂單NO
+     * @param timeoutSeconds 超時時間(秒),默認30分鐘
+     */
+    void setOrderAcceptTimeout(String orderNumber, long timeoutSeconds);
+
+    /**
+     * 設置訂單退款超時監聽
+     *
+     * @param orderNumber 訂單NO
+     * @param timeoutSeconds 超時時間(秒),默認30分鐘
+     */
+    void setOrderRefundTimeout(String orderNumber, long timeoutSeconds);
+
+    /**
+     * 设置订单超时完成监听
+     *
+     * @param orderNumber 订单NO
+     * @param timeoutSeconds 超时时间(秒),默認30分鐘
+     */
+    void setOrderCompleteTimeout(String orderNumber, long timeoutSeconds);
+
+    /**
+     * 取消訂單支付超時監聽(當訂單已支付時調用)
+     * 
+     * @param orderNumber 訂單NO
+     */
+    void cancelOrderPaymentTimeout(String orderNumber);
+
+    /**
+     * 取消訂單支付超時監聽(當訂單已支付時調用)
+     *
+     * @param orderNumber 訂單NO
+     */
+    void cancelOrderAcceptTimeout(String orderNumber);
+
+    /**
+     * 取消訂單支付超時監聽(當訂單已支付時調用)
+     *
+     * @param orderNumber 訂單NO
+     */
+    void cancelOrderRefundTimeout(String orderNumber);
+}
+

+ 146 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/OrderReviewService.java

@@ -0,0 +1,146 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.OrderReview;
+import shop.alien.entity.store.dto.OrderReviewDto;
+import shop.alien.entity.store.vo.OrderReviewDetailVo;
+import shop.alien.entity.store.vo.OrderReviewVo;
+import shop.alien.entity.store.vo.PendingReviewVo;
+
+/**
+ * 订单评价 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface OrderReviewService extends IService<OrderReview> {
+
+    /**
+     * 创建订单评价(只有订单用户才能评价)
+     *
+     * @param reviewDto 评价DTO
+     *
+     * @return R<OrderReview>
+     */
+    R<OrderReview> createReview(OrderReviewDto reviewDto);
+
+    /**
+     * 获取评价详情(包含评论和回复)
+     *
+     * @param reviewId 评价ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<OrderReviewDetailVo>
+     */
+    R<OrderReviewDetailVo> getReviewDetail(Integer reviewId, Integer currentUserId);
+
+    /**
+     * 点赞评价
+     *
+     * @param reviewId 评价ID
+     * @param userId 用户ID
+     * @return R<Boolean>
+     */
+    R<Boolean> likeReview(Integer reviewId, Integer userId);
+
+    /**
+     * 取消点赞评价
+     *
+     * @param reviewId 评价ID
+     * @param userId 用户ID
+     * @return R<Boolean>
+     */
+    R<Boolean> cancelLikeReview(Integer reviewId, Integer userId);
+
+    /**
+     * 分页查询评价列表
+     *
+     * @param pageNum 页码
+     * @param pageSize 页大小
+     * @param orderId 订单ID(可选)
+     * @param lawyerUserId 律师用户ID(可选)
+     * @param userId 评价用户ID(可选)
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<IPage<OrderReviewVo>>
+     */
+    R<IPage<OrderReviewVo>> getReviewList(int pageNum, int pageSize, Integer orderId, Integer lawyerUserId, Integer userId, Integer currentUserId);
+
+
+    /**
+     * 用户删除评价(只能删除自己的评价,删除评价时,会级联删除该评价下的所有评论和回复)
+     *
+     * @param reviewId 评价ID
+     * @param userId 用户ID(必填,只能删除自己的评价)
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteReview(Integer reviewId, Integer userId);
+
+    /**
+     * 管理员删除评价(可以删除任何评价,删除评价时,会级联删除该评价下的所有评论和回复)
+     *
+     * @param reviewId 评价ID
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteReviewByAdmin(Integer reviewId);
+
+    /**
+     * 根据订单ID查询评价
+     *
+     * @param orderId 订单ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<OrderReviewVo>
+     */
+    R<OrderReviewVo> getReviewByOrderId(Integer orderId, Integer currentUserId);
+
+    /**
+     * 分页查询待评价列表(查询用户已完成但未评价的订单对应的律师信息)
+     *
+     * @param pageNum 页码
+     * @param pageSize 页大小
+     * @param userId 用户ID
+     * @return R<IPage<PendingReviewVo>>
+     */
+    R<IPage<PendingReviewVo>> getPendingReviewList(int pageNum, int pageSize, Integer userId);
+
+    /**
+     * 分页查询我的评价列表(查询当前用户的所有评价)
+     *
+     * @param pageNum 页码
+     * @param pageSize 页大小
+     * @param userId 用户ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<IPage<OrderReviewVo>>
+     */
+    R<IPage<OrderReviewVo>> getMyReviewList(int pageNum, int pageSize, Integer userId, Integer currentUserId);
+
+    /**
+     * 根据律师ID和类型分页查询评价列表(不包含评论)
+     *
+     * @param pageNum 页码
+     * @param pageSize 页大小
+     * @param lawyerUserId 律师用户ID
+     * @param type 查询分类(1:好评,2:中评,3:差评,4:有图,为空时返回全部)
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<IPage<OrderReviewVo>>
+     */
+    R<IPage<OrderReviewVo>> getReviewListByLawyerAndType(int pageNum, int pageSize, Integer lawyerUserId, Integer type, Integer currentUserId);
+
+    /**
+     * 获取律师评价统计数据
+     *
+     * @param lawyerUserId 律师用户ID
+     * @return R<LawyerReviewStatisticsVo>
+     */
+    R<shop.alien.entity.store.vo.LawyerReviewStatisticsVo> getLawyerReviewStatistics(Integer lawyerUserId);
+
+    /**
+     * 根据订单ID查询关联评价
+     *
+     * @param orderId 订单ID
+     * @return R<OrderReviewVo>
+     */
+    R<OrderReviewVo> getOrderEvaluation(Integer orderId);
+
+}
+

+ 17 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/RefundRecordService.java

@@ -0,0 +1,17 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.RefundRecord;
+
+/**
+ * <p>
+ * 退款记录表 服务类
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface RefundRecordService extends IService<RefundRecord> {
+
+}
+

+ 100 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/ReviewCommentService.java

@@ -0,0 +1,100 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.ReviewComment;
+import shop.alien.entity.store.dto.LawyerReplyDto;
+import shop.alien.entity.store.dto.ReviewCommentRequestDto;
+import shop.alien.entity.store.dto.ReviewReplyDto;
+import shop.alien.entity.store.vo.ReviewCommentVo;
+
+import java.util.List;
+
+/**
+ * 评价评论 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface ReviewCommentService extends IService<ReviewComment> {
+
+    /**
+     * 创建评论(其他用户对评价的评论)
+     *
+     * @param comment 评论实体
+     * @return R<ReviewComment>
+     */
+    R<ReviewComment> createComment(ReviewComment comment);
+
+    /**
+     * 根据评价ID查询评论列表
+     *
+     * @param reviewId 评价ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<List<ReviewCommentVo>>
+     */
+    R<List<ReviewCommentVo>> getCommentListByReviewId(Integer reviewId, Integer currentUserId);
+
+    /**
+     * 点赞评论
+     *
+     * @param requestDto 请求DTO
+     * @return R<Boolean>
+     */
+    R<Boolean> likeComment(ReviewCommentRequestDto requestDto);
+
+    /**
+     * 取消点赞评论
+     *
+     * @param requestDto 请求DTO
+     * @return R<Boolean>
+     */
+    R<Boolean> cancelLikeComment(ReviewCommentRequestDto requestDto);
+
+    /**
+     * 创建回复(用户对评论的回复)
+     *
+     * @param replyDto 回复DTO
+     * @return R<ReviewComment>
+     */
+    R<ReviewComment> createReply(ReviewReplyDto replyDto);
+
+    /**
+     * 根据首评ID查询回复列表
+     *
+     * @param headId 首评ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<List<ReviewCommentVo>>
+     */
+    R<List<ReviewCommentVo>> getReplyListByHeadId(Integer headId, Integer currentUserId);
+
+    /**
+     * 删除回复
+     *
+     * @param replyId 回复ID
+     * @param userId 用户ID
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteReply(Integer replyId, Integer userId);
+
+    /**
+     * 删除评论(根据ID,物理删除)
+     * userId有值时只能删除自己发布的评论,userId为空时允许删除任何评论(管理员删除)
+     *
+     * @param reviewComment 评论对象(包含id和userId,userId可选)
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteReviewComment(ReviewComment reviewComment);
+
+    /**
+     * 律师回复(律师对用户的评价或评论的回复)
+     * 
+     * @deprecated 已废弃,请使用 createComment(回复评价)或 createReply(回复评论)接口
+     *             通过 sendUserType=2 标识律师身份,系统会自动进行权限校验
+     * @param replyDto 律师回复DTO
+     * @return R<ReviewComment>
+     */
+    @Deprecated
+    R<ReviewComment> lawyerReply(LawyerReplyDto replyDto);
+}
+

+ 17 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/StoreAliPayErrorLogService.java

@@ -0,0 +1,17 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.StoreAliPayErrorLog;
+
+/**
+ * <p>
+ * 支付宝转账错误记录表 服务类
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StoreAliPayErrorLogService extends IService<StoreAliPayErrorLog> {
+
+}
+

+ 17 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/StoreAliPayRefundLogService.java

@@ -0,0 +1,17 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.StoreAliPayRefundLog;
+
+/**
+ * <p>
+ * 支付宝退款记录表 服务类
+ * </p>
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StoreAliPayRefundLogService extends IService<StoreAliPayRefundLog> {
+
+}
+

+ 269 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/StoreCommentService.java

@@ -0,0 +1,269 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.web.multipart.MultipartRequest;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreComment;
+import shop.alien.entity.store.dto.OrderReviewDto;
+import shop.alien.entity.store.dto.ReviewCommentRequestDto;
+import shop.alien.entity.store.dto.ReviewReplyDto;
+import shop.alien.entity.store.vo.LifeUserOrderCommentVo;
+import shop.alien.entity.store.vo.OrderReviewDetailVo;
+import shop.alien.entity.store.vo.OrderReviewVo;
+import shop.alien.entity.store.vo.ReviewCommentVo;
+import shop.alien.entity.store.vo.StoreCommentCountVo;
+import shop.alien.entity.store.vo.StoreCommentVo;
+import shop.alien.entity.store.vo.StoreCommitPercentVo;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 评论表 服务类
+ *
+ * @author ssk
+ * @since 2025-01-02
+ */
+public interface StoreCommentService extends IService<StoreComment> {
+
+    /**
+     * 评论列表
+     *
+     * @param pageNum      页数
+     * @param pageSize     页容
+     * @param businessId   业务id
+     * @param businessType 业务类型(1:订单评论, 2:动态社区评论, 3:活动评论, 4:店铺打卡评论, 5:订单评价, 6:订单评论的评论)
+     * @param storeId      门店id
+     * @param replyStatus  回复状态(0:全部, 1:已回复, 2:未回复)
+     * @param commentLevel 评论等级(0:全部, 1:好评, 2:中评, 3:差评)
+     * @param days         查询时间, 多少天前
+     * @param phoneId      消息标识
+     * @param userType     评论的用户类型(0:商家, 其他:用户)
+     * @param tagId        标签id
+     * @param hasImage     是否有图片(0否/1是)
+     * @return IPage<StoreComment>
+     */
+    IPage<StoreCommentVo> getList(Integer pageNum, Integer pageSize, Integer businessId, Integer businessType, Integer storeId, Integer replyStatus, Integer commentLevel, Integer days, String phoneId, Integer userType, Integer tagId, Boolean hasImage);
+
+    /**
+     * 获取最新一条评论/评价
+     *
+     * @param businessId   业务id
+     * @param businessType 业务类型(1:订单评论, 2:动态社区评论, 3:活动评论,4:店铺打卡评论)
+     * @param storeId      门店id
+     * @return StoreCommentVo
+     */
+    StoreCommentVo getOne(Integer businessId, Integer businessType, Integer storeId);
+
+    /**
+     * 评论/评价数量和评分
+     *
+     * @param businessId   业务id
+     * @param businessType 业务类型(1:订单评论, 2:动态社区评论, 3:活动评论,4:店铺打卡评论)
+     * @param storeId      门店id
+     * @return Integer
+     */
+    Map<String, Object> getCommitCountAndScore(Integer businessId, Integer businessType, Integer storeId, String phoneId, Integer days);
+
+    /**
+     * 评论
+     *
+     * @param multipartRequest 文件
+     * @param businessId       业务id
+     * @param businessType     业务类型(1:订单评论, 2:动态社区评论, 3:活动评论,4:店铺打卡评论)
+     * @param storeId          门店id
+     * @param userId           用户id
+     * @param replyId          回复id
+     * @param commentContent   评价内容
+     * @return 0:成功, 1:失败, 2:文本内容异常, 3:图片内容异常
+     */
+    Integer userComment(MultipartRequest multipartRequest, Integer businessId, Integer businessType, Integer storeId, Integer userId, Integer replyId, String commentContent);
+
+    /**
+     * 新增或修改评论/评价
+     *
+     * @param multipartRequest 文件
+     * @param id               主键
+     * @param businessId       业务id
+     * @param businessType     业务类型(1:订单评论, 2:动态社区评论, 3:活动评论,4:店铺打卡评论)
+     * @param storeId          门店id
+     * @param userId           用户id
+     * @param replyId          回复id
+     * @param commentContent   评价内容
+     * @param score            评分
+     * @param otherScore       其他评分
+     * @param isAnonymous      是否匿名(0:否(默认), 1:是)
+     * @param evaluationTags   评价标签
+     * @param phoneId          用户id
+     * @return 0:成功, 1:失败, 2:文本内容异常, 3:图片内容异常
+     */
+    Integer addComment(MultipartRequest multipartRequest, Integer id, Integer businessId, Integer businessType, Integer storeId, Integer userId, Integer replyId, String commentContent, Double score, String otherScore, Integer isAnonymous, String evaluationTags, String phoneId);
+
+    /**
+     * 新增或修改评论/评价
+     *
+     * @param multipartRequest 文件
+     * @param id               主键
+     * @param businessId       业务id
+     * @param businessType     业务类型(1:订单评论, 2:动态社区评论, 3:活动评论,4:店铺打卡评论)
+     * @param storeId          门店id
+     * @param userId           用户id
+     * @param replyId          回复id
+     * @param commentContent   评价内容
+     * @param score            评分
+     * @param otherScore       其他评分
+     * @param isAnonymous      是否匿名(0:否(默认), 1:是)
+     * @param evaluationTags   评价标签
+     * @param phoneId          用户id
+     * @return 0:成功, 1:失败, 2:文本内容异常, 3:图片内容异常
+     */
+    Integer addCommentNew(MultipartRequest multipartRequest, Integer id, Integer businessId, Integer businessType, Integer storeId, Integer orderId, Integer lawyerId, Integer userId, Integer replyId, String commentContent, Double score, String otherScore, Integer isAnonymous, String evaluationTags, String phoneId);
+
+    /**
+     * 回复率, 评价比例
+     *
+     * @param storeId 门店id
+     * @return StoreCommitPercentVo
+     */
+    StoreCommitPercentVo getCommitPercent(Integer storeId);
+
+    /**
+     * @param type 1:未评价, 2:已评价
+     */
+    IPage<LifeUserOrderCommentVo> getCommentOrderPage(Integer pageNum, Integer pageSize, Integer type, String userId);
+
+    /**
+     * 查询当前用户全部评价(店铺+律师),格式同 LifeUserOrderCommentVo
+     */
+    IPage<LifeUserOrderCommentVo> getUserAllCommentsPage(Integer pageNum, Integer pageSize, Integer userId);
+
+    /**
+     * 获取店铺评价计数统计
+     *
+     * @param storeId 门店id
+     * @return StoreCommentCountVo
+     */
+    StoreCommentCountVo getAppraiseCount(Integer storeId);
+
+    // ==================== 订单评价相关方法(迁移自 OrderReviewService)====================
+
+    /**
+     * 创建订单评价(只有订单用户才能评价)
+     *
+     * @param reviewDto 评价DTO
+     * @return R<StoreComment>
+     */
+    R<StoreComment> createOrderReview(OrderReviewDto reviewDto);
+
+    /**
+     * 获取评价详情(包含评论和回复)
+     *
+     * @param orderId 评价ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<OrderReviewDetailVo>
+     */
+    R<OrderReviewDetailVo> getOrderReviewDetail(Integer orderId, Integer currentUserId);
+
+    /**
+     * 点赞评价
+     *
+     * @param reviewId 评价ID
+     * @param userId 用户ID
+     * @return R<Boolean>
+     */
+    R<Boolean> likeOrderReview(Integer reviewId, Integer userId);
+
+    /**
+     * 取消点赞评价
+     *
+     * @param reviewId 评价ID
+     * @param userId 用户ID
+     * @return R<Boolean>
+     */
+    R<Boolean> cancelLikeOrderReview(Integer reviewId, Integer userId);
+
+    /**
+     * 分页查询评价列表
+     *
+     * @param page 页码
+     * @param size 页大小
+     * @param orderId 订单ID(可选)
+     * @param lawyerUserId 律师用户ID(可选)
+     * @param userId 评价用户ID(可选)
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<IPage<OrderReviewVo>>
+     */
+    R<IPage<OrderReviewVo>> getOrderReviewList(int page, int size, Integer orderId, Integer lawyerUserId, Integer userId, Integer currentUserId);
+
+    /**
+     * 根据订单ID查询评价
+     *
+     * @param orderId 订单ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<OrderReviewVo>
+     */
+    R<OrderReviewVo> getOrderReviewByOrderId(Integer orderId, Integer currentUserId);
+
+    // ==================== 评价评论相关方法(迁移自 ReviewCommentService)====================
+
+    /**
+     * 创建评论(其他用户对评价的评论)
+     *
+     * @param comment 评论实体
+     * @return R<StoreComment>
+     */
+    R<StoreComment> createReviewComment(StoreComment comment);
+
+    /**
+     * 根据评价ID查询评论列表
+     *
+     * @param reviewId 评价ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<List<ReviewCommentVo>>
+     */
+    R<List<ReviewCommentVo>> getReviewCommentListByReviewId(Integer reviewId, Integer currentUserId);
+
+    /**
+     * 点赞评论
+     *
+     * @param requestDto 请求DTO
+     * @return R<Boolean>
+     */
+    R<Boolean> likeReviewComment(ReviewCommentRequestDto requestDto);
+
+    /**
+     * 取消点赞评论
+     *
+     * @param requestDto 请求DTO
+     * @return R<Boolean>
+     */
+    R<Boolean> cancelLikeReviewComment(ReviewCommentRequestDto requestDto);
+
+    /**
+     * 创建回复(用户对评论的回复)
+     *
+     * @param replyDto 回复DTO
+     * @return R<StoreComment>
+     */
+    R<StoreComment> createReviewReply(ReviewReplyDto replyDto);
+
+    /**
+     * 根据首评ID查询回复列表
+     *
+     * @param headId 首评ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<List<ReviewCommentVo>>
+     */
+    R<List<ReviewCommentVo>> getReviewReplyListByHeadId(Integer headId, Integer currentUserId);
+
+    /**
+     * 删除回复
+     *
+     * @param replyId 回复ID
+     * @param userId 用户ID
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteReviewReply(Integer replyId, Integer userId);
+
+}

+ 73 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/StoreImgService.java

@@ -0,0 +1,73 @@
+package shop.alien.lawyer.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreImg;
+import shop.alien.entity.store.vo.StoreImgTypeVo;
+
+import java.util.List;
+
+/**
+ * 二期-门店图片 服务类
+ *
+ * @author ssk
+ * @since 2024-12-05
+ */
+public interface StoreImgService extends IService<StoreImg> {
+
+    /**
+     * 获取门店图片
+     *
+     * @param storeId 门店id
+     * @param imgType 图片类型, 0:其他, 1:入口图, 2:相册, 3:菜品, 4:环境, 5:价目表, 6:推荐菜, 7:菜单, 8:用户评论, 9:商家申诉, 10:商家头像, 11:店铺轮播图
+     * @return list
+     */
+    List<StoreImg> getStoreImg(Integer storeId, Integer imgType);
+
+    /**
+     * 获取所有图片类型和数量
+     *
+     * @param storeId 门店id
+     * @return StoreImgTypeVo
+     */
+    List<StoreImgTypeVo> getStoreImgTypeCount(Integer storeId);
+
+    /**
+     * 获取轮播图
+     *
+     * @return List<StoreImg>
+     */
+    List<StoreImg> getCarouselImage();
+
+    /**
+     * @param id
+     * @param imgType
+     * @return
+     */
+    List<String> getUrl(String id, Integer imgType);
+
+    /**
+     * 获取用户打卡广场小人图片
+     *
+     * @param userId 用户ID,用于标识和获取特定用户的信息
+     * @return
+     */
+    R<String> getUserClockImg(Integer userId);
+
+    /**
+     * 通过businessId获取图片
+     *
+     * @param storeId 门店id
+     * @param imgType 图片类型, 0:其他, 1:入口图, 2:相册, 3:菜品, 4:环境, 5:价目表, 6:推荐菜, 7:菜单, 8:用户评论, 9:商家申诉, 10:商家头像, 11:店铺轮播图
+     * @return list
+     */
+    List<StoreImg> getByBusinessId(Integer storeId, Integer imgType, Integer businessId);
+
+    /**
+     * 删除图片
+     *
+     * @param storeId 门店id
+     * @param imgType 图片类型, 0:其他, 1:入口图, 2:相册, 3:菜品, 4:环境, 5:价目表, 6:推荐菜, 7:菜单, 8:用户评论, 9:商家申诉, 10:商家头像, 11:店铺轮播图
+     */
+    int saveOrUpdateImg(Integer storeId, Integer imgType);
+}

+ 11 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/TestService.java

@@ -0,0 +1,11 @@
+package shop.alien.lawyer.service;
+
+/**
+ * @author ssk
+ * @version 1.0
+ * @date 2025/11/10 14:31
+ */
+public interface TestService {
+
+    String test( String name);
+}

+ 620 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/AiUserAuditTaskServiceImpl.java

@@ -0,0 +1,620 @@
+package shop.alien.lawyer.service.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.HttpServerErrorException;
+import org.springframework.web.client.RestTemplate;
+import shop.alien.entity.store.LawyerConsultationOrder;
+import shop.alien.entity.store.LawyerUserViolation;
+import shop.alien.entity.store.LifeNotice;
+import shop.alien.entity.store.LifeUser;
+import shop.alien.entity.store.LawyerUser;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.entity.store.vo.WebSocketVo;
+import shop.alien.lawyer.config.WebSocketProcess;
+import shop.alien.lawyer.service.AiUserAuditTaskService;
+import shop.alien.lawyer.service.LawyerUserViolationService;
+import shop.alien.lawyer.util.ali.AliApi;
+import shop.alien.mapper.LawyerConsultationOrderMapper;
+import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.mapper.LifeUserMapper;
+import shop.alien.mapper.LawyerUserMapper;
+import shop.alien.mapper.StoreUserMapper;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Map;
+
+/**
+ * AI用户申诉任务服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@RefreshScope
+public class AiUserAuditTaskServiceImpl implements AiUserAuditTaskService {
+
+    private final RestTemplate restTemplate;
+
+    private final LawyerUserViolationService lawyerUserViolationService;
+
+    @Value("${third-party-ai-auto-review.user-complaint-url:http://192.168.2.250:9100/ai/auto-review/api/v1/lawyer_user_complaint_audit_task/submit}")
+    private String aiUserAuditTaskUrl;
+
+    @Value("${third-party-ai-auto-review.lawyer-complaint-url:http://192.168.2.250:9100/ai/auto-review/api/v1/lawyer_complaint_audit_task/submit}")
+    private String aiAutoReviewUrl;
+
+    private final LawyerConsultationOrderMapper consultationOrderMapper;
+
+    private final AliApi aliApi;
+
+    private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final WebSocketProcess webSocketProcess;
+
+    private final LifeUserMapper lifeUserMapper;
+
+    private final LawyerUserMapper lawyerUserMapper;
+
+    private final StoreUserMapper storeUserMapper;
+
+    private static final String SYSTEM_SENDER_ID = "system";
+
+    @Override
+    @Async("lawyerTaskExecutor")
+    public void asyncCallUserAuditTask(Map<String, Object> requestBody, String accessToken) {
+        log.info("开始异步调用AI用户申诉接口,请求URL:{},请求参数:{}", aiUserAuditTaskUrl, requestBody);
+
+        try {
+            // 初始化请求头(使用已获取的访问令牌)
+            HttpHeaders aiHeaders = new HttpHeaders();
+            aiHeaders.setContentType(MediaType.APPLICATION_JSON);
+            aiHeaders.set("Authorization", "Bearer " + accessToken);
+
+            HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, aiHeaders);
+
+            log.info("异步调用AI用户申诉接口,URL:{},请求头:{}", aiUserAuditTaskUrl, aiHeaders);
+
+            // 调用AI服务
+            ResponseEntity<String> responseEntity = restTemplate.postForEntity(
+                    aiUserAuditTaskUrl, request, String.class);
+
+            log.info("异步调用AI用户申诉接口成功,响应状态:{},响应体:{}",
+                    responseEntity.getStatusCode(), responseEntity.getBody());
+
+            // 解析响应并更新举报记录
+            parseAndUpdateViolation(responseEntity.getBody());
+
+        } catch (HttpServerErrorException e) {
+            String responseBody = e.getResponseBodyAsString();
+            log.error("异步调用AI用户申诉接口返回服务器错误,状态码:{},响应体:{},URL:{}",
+                    e.getStatusCode(), responseBody, aiUserAuditTaskUrl, e);
+            // 即使返回错误,也尝试解析响应体(可能包含审核结果)
+            if (StringUtils.hasText(responseBody)) {
+                parseAndUpdateViolation(responseBody);
+            }
+        } catch (HttpClientErrorException e) {
+            String responseBody = e.getResponseBodyAsString();
+            log.error("异步调用AI用户申诉接口返回客户端错误,状态码:{},响应体:{},URL:{}",
+                    e.getStatusCode(), responseBody, aiUserAuditTaskUrl, e);
+            // 即使返回错误,也尝试解析响应体(可能包含审核结果)
+            if (StringUtils.hasText(responseBody)) {
+                parseAndUpdateViolation(responseBody);
+            }
+        } catch (Exception e) {
+            log.error("异步调用AI用户申诉接口异常,URL:{},异常信息:{}", aiUserAuditTaskUrl, e.getMessage(), e);
+        }
+    }
+
+    @Override
+    @Async("lawyerTaskExecutor")
+    public void asyncAiReview(Map<String, Object> requestBody, String accessToken) {
+        log.info("开始异步调用AI申诉接口,请求URL:{},请求参数:{}", aiUserAuditTaskUrl, requestBody);
+
+        try {
+            // 初始化请求头(使用已获取的访问令牌)
+            HttpHeaders aiHeaders = new HttpHeaders();
+            aiHeaders.setContentType(MediaType.APPLICATION_JSON);
+            aiHeaders.set("Authorization", "Bearer " + accessToken);
+
+            HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, aiHeaders);
+
+            log.info("异步调用AI用户申诉接口,URL:{},请求头:{}", aiUserAuditTaskUrl, aiHeaders);
+
+            // 调用AI服务
+            ResponseEntity<String> responseEntity = restTemplate.postForEntity(
+                    aiAutoReviewUrl, request, String.class);
+
+            log.info("异步调用AI用户申诉接口成功,响应状态:{},响应体:{}",
+                    responseEntity.getStatusCode(), responseEntity.getBody());
+
+        } catch (HttpServerErrorException e) {
+            log.error("异步调用AI申诉接口返回服务器错误,状态码:{},响应体:{},URL:{}",
+                    e.getStatusCode(), e.getResponseBodyAsString(), aiAutoReviewUrl, e);
+        } catch (HttpClientErrorException e) {
+            log.error("异步调用AI用户申诉接口返回客户端错误,状态码:{},响应体:{},URL:{}",
+                    e.getStatusCode(), e.getResponseBodyAsString(), aiAutoReviewUrl, e);
+        } catch (Exception e) {
+            log.error("异步调用AI申诉接口异常,URL:{},异常信息:{}", aiAutoReviewUrl, e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 解析AI响应并更新举报记录
+     *
+     * @param responseBody 响应体JSON字符串
+     */
+    private void parseAndUpdateViolation(String responseBody) {
+        if (!StringUtils.hasText(responseBody)) {
+            log.warn("AI响应体为空,无法解析");
+            return;
+        }
+
+        try {
+            // 解析JSON响应
+            JSONObject responseJson = JSONObject.parseObject(responseBody);
+
+            // 检查响应码
+            Integer code = responseJson.getInteger("code");
+            if (code == null || code != 200) {
+                log.warn("AI响应码异常,code:{},message:{}", code, responseJson.getString("message"));
+                return;
+            }
+
+            // 获取data对象
+            JSONObject data = responseJson.getJSONObject("data");
+            if (data == null) {
+                log.warn("AI响应data为空,无法获取审核结果");
+                return;
+            }
+
+            // 获取订单号
+            String orderNumber = data.getString("order_number");
+            if (!StringUtils.hasText(orderNumber)) {
+                log.warn("AI响应订单号为空,无法更新举报记录");
+                return;
+            }
+
+            // 获取result对象
+            JSONObject result = data.getJSONObject("result");
+            if (result == null) {
+                log.warn("AI响应result为空,订单号:{}", orderNumber);
+                return;
+            }
+
+            // 获取处理状态和审核结果
+            Integer processingStatus = result.getInteger("processing_status");
+            Boolean isReportValid = result.getBoolean("is_report_valid");
+            String decisionReason = result.getString("decision_reason");
+
+            log.info("解析AI响应成功,订单号:{},processing_status:{},is_report_valid:{},decision_reason:{}",
+                    orderNumber, processingStatus, isReportValid, decisionReason);
+
+            if (processingStatus != null) {
+                if (processingStatus == 1 && StringUtils.hasText(orderNumber)) {
+                    // processing_status = 1 表示已通过,进行退款操作
+                    processRefund(orderNumber);
+                    
+                    // 发送举报成功通知
+                    //sendReportSuccessNotifications(orderNumber);
+                } else if (processingStatus == 2 && StringUtils.hasText(orderNumber)) {
+                    // processing_status = 2 表示未违规(举报失败),发送举报失败通知
+                    //sendReportFailureNotification(orderNumber, decisionReason);
+                }
+            } else {
+                log.warn("processing_status为空,订单号:{}", orderNumber);
+            }
+
+        } catch (Exception e) {
+            log.error("解析AI响应并更新举报记录异常,响应体:{},异常信息:{}", responseBody, e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 处理订单退款
+     * <p>
+     * 根据订单号查询订单信息,调用支付宝退款接口进行退款
+     * 如果退款失败,会抛出异常以触发事务回滚
+     * </p>
+     *
+     * @param orderNumber 订单号
+     * @throws RuntimeException 当退款失败时抛出异常,触发事务回滚
+     */
+    private void processRefund(String orderNumber) {
+        if (!StringUtils.hasText(orderNumber)) {
+            log.error("处理退款失败:订单号为空");
+            throw new RuntimeException("订单号不能为空,无法处理退款");
+        }
+
+        try {
+            // 查询订单信息
+            LambdaQueryWrapper<LawyerConsultationOrder> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(LawyerConsultationOrder::getOrderNumber, orderNumber)
+                    .eq(LawyerConsultationOrder::getDeleteFlag, 0)
+                    .last("LIMIT 1");
+            LawyerConsultationOrder order = consultationOrderMapper.selectOne(queryWrapper);
+
+            if (order == null) {
+                log.error("处理退款失败:订单不存在,订单号:{}", orderNumber);
+                throw new RuntimeException("订单不存在,无法处理退款,订单号:" + orderNumber);
+            }
+
+            // 检查订单是否有支付宝交易号
+            if (!StringUtils.hasText(order.getAlipayNo())) {
+                log.error("处理退款失败:订单无支付宝交易号,订单号:{}", orderNumber);
+                throw new RuntimeException("订单无支付宝交易号,无法处理退款,订单号:" + orderNumber);
+            }
+
+            // 检查订单金额
+            if (order.getOrderAmount() == null || order.getOrderAmount() <= 0) {
+                log.error("处理退款失败:订单金额无效,订单号:{},订单金额:{}", orderNumber, order.getOrderAmount());
+                throw new RuntimeException("订单金额无效,无法处理退款,订单号:" + orderNumber);
+            }
+
+            // 将订单金额从分转换为元
+            BigDecimal refundAmount = new BigDecimal(order.getOrderAmount())
+                    .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
+
+            // 退款原因
+            String refundReason = "举报审核通过,订单退款";
+
+            // 调用支付宝退款接口
+            log.info("开始处理订单退款,订单号:{},支付宝交易号:{},退款金额:{}元",
+                    orderNumber, order.getAlipayNo(), refundAmount.toString());
+
+            String refundResult = aliApi.processRefund(order.getAlipayNo(), refundAmount.toString(), refundReason, "");
+
+            if ("调用成功".equals(refundResult)) {
+                log.info("订单退款成功,订单号:{},退款金额:{}元", orderNumber, refundAmount.toString());
+            } else {
+                log.error("订单退款失败,订单号:{},退款结果:{}", orderNumber, refundResult);
+                // 退款失败时抛出异常,触发事务回滚
+                throw new RuntimeException("订单退款失败,订单号:" + orderNumber + ",退款结果:" + refundResult);
+            }
+
+        } catch (RuntimeException e) {
+            // 重新抛出RuntimeException,触发事务回滚
+            log.error("处理订单退款失败,订单号:{},异常信息:{}", orderNumber, e.getMessage(), e);
+            throw e;
+        } catch (Exception e) {
+            // 其他异常也转换为RuntimeException,触发事务回滚
+            log.error("处理订单退款异常,订单号:{},异常信息:{}", orderNumber, e.getMessage(), e);
+            throw new RuntimeException("处理订单退款时发生异常,订单号:" + orderNumber + ",异常信息:" + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 发送举报失败通知
+     * <p>
+     * 当举报审核未通过时,向举报用户发送通知
+     * </p>
+     *
+     * @param orderNumber   订单号
+     * @param decisionReason 拒绝原因
+     */
+    private void sendReportFailureNotification(String orderNumber, String decisionReason) {
+        try {
+            // 根据订单号查询最新的举报记录
+            LambdaQueryWrapper<LawyerUserViolation> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(LawyerUserViolation::getOrderNumber, orderNumber)
+                    .orderByDesc(LawyerUserViolation::getId)
+                    .last("LIMIT 1");
+
+            LawyerUserViolation violation = lawyerUserViolationService.getOne(queryWrapper);
+            if (violation == null) {
+                log.warn("未找到举报记录,订单号:{}", orderNumber);
+                return;
+            }
+
+            // 获取举报人接收ID
+            String receiverId = getReporterReceiverId(violation);
+            if (!StringUtils.hasText(receiverId)) {
+                log.warn("获取举报人接收ID失败,举报ID:{}", violation.getId());
+                return;
+            }
+
+            // 获取举报理由文本
+            String violationReasonText = getViolationReasonText(violation);
+
+            // 构建通知内容
+            StringBuilder messageBuilder = new StringBuilder();
+            messageBuilder.append("您举报律师").append(violationReasonText)
+                    .append(",经核实,不存在此行为,订单金额不予退还。");
+            
+            // 添加拒绝原因
+            if (StringUtils.hasText(decisionReason)) {
+                messageBuilder.append("拒绝原因:").append(decisionReason);
+            } else {
+                // 如果没有拒绝原因,使用默认值
+                messageBuilder.append("拒绝原因:举报不实");
+            }
+
+            String message = messageBuilder.toString();
+
+            // 创建并保存通知
+            LifeNotice lifeNotice = createLifeNotice(violation.getId(), receiverId, "举报失败通知", message);
+            lifeNoticeMapper.insert(lifeNotice);
+
+            // 发送WebSocket消息
+            sendWebSocketMessage(receiverId, lifeNotice);
+
+            log.info("举报失败通知发送成功,订单号:{},接收人ID:{}", orderNumber, receiverId);
+
+        } catch (Exception e) {
+            log.error("发送举报失败通知异常,订单号:{},异常信息:{}", orderNumber, e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 发送举报成功通知
+     * <p>
+     * 当举报审核通过时,向举报用户和被举报律师发送通知
+     * </p>
+     *
+     * @param orderNumber 订单号
+     */
+    private void sendReportSuccessNotifications(String orderNumber) {
+        try {
+            // 根据订单号查询最新的举报记录
+            LambdaQueryWrapper<LawyerUserViolation> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(LawyerUserViolation::getOrderNumber, orderNumber).eq(LawyerUserViolation::getDeleteFlag, 0)
+                    .orderByDesc(LawyerUserViolation::getId)
+                    .last("LIMIT 1");
+
+            LawyerUserViolation violation = lawyerUserViolationService.getOne(queryWrapper);
+            if (violation == null) {
+                log.warn("未找到举报记录,订单号:{}", orderNumber);
+                return;
+            }
+
+            // 发送举报人通知
+            sendReporterNotification(violation);
+
+            // 发送被举报人通知
+            sendReportedNotification(violation, orderNumber);
+
+        } catch (Exception e) {
+            log.error("发送举报成功通知异常,订单号:{},异常信息:{}", orderNumber, e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 发送举报人通知
+     *
+     * @param violation 举报记录
+     */
+    private void sendReporterNotification(LawyerUserViolation violation) {
+        try {
+            // 获取举报人接收ID
+            String receiverId = getReporterReceiverId(violation);
+            if (!StringUtils.hasText(receiverId)) {
+                log.warn("获取举报人接收ID失败,举报ID:{}", violation.getId());
+                return;
+            }
+
+            // 获取举报理由文本
+            String violationReasonText = getViolationReasonText(violation);
+
+            // 构建通知内容
+            String message = String.format("您举报律师%s,经核实,确实存在此行为,订单金额将在1-3个工作日原路返还,请注意查收。",
+                    violationReasonText);
+
+            // 创建并保存通知
+            LifeNotice lifeNotice = createLifeNotice(violation.getId(), receiverId, "举报成功通知", message);
+            lifeNoticeMapper.insert(lifeNotice);
+
+            // 发送WebSocket消息
+            sendWebSocketMessage(receiverId, lifeNotice);
+
+            log.info("举报人通知发送成功,订单号:{},接收人ID:{}", violation.getOrderNumber(), receiverId);
+
+        } catch (Exception e) {
+            log.error("发送举报人通知异常,举报ID:{},异常信息:{}", violation.getId(), e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 发送被举报人通知
+     *
+     * @param violation   举报记录
+     * @param orderNumber 订单号
+     */
+    private void sendReportedNotification(LawyerUserViolation violation, String orderNumber) {
+        try {
+            // 获取被举报人接收ID
+            String receiverId = getReportedReceiverId(violation);
+            if (!StringUtils.hasText(receiverId)) {
+                log.warn("获取被举报人接收ID失败,举报ID:{}", violation.getId());
+                return;
+            }
+
+            // 构建通知内容
+            String message = String.format("用户对编号为%s的订单进行申诉,经核实,用户举报属实,订单金额将会退还给用户。",
+                    orderNumber);
+
+            // 创建并保存通知
+            LifeNotice lifeNotice = createLifeNotice(violation.getId(), receiverId, "被举报成功通知", message);
+            lifeNoticeMapper.insert(lifeNotice);
+
+            // 发送WebSocket消息
+            sendWebSocketMessage(receiverId, lifeNotice);
+
+            log.info("被举报人通知发送成功,订单号:{},接收人ID:{}", orderNumber, receiverId);
+
+        } catch (Exception e) {
+            log.error("发送被举报人通知异常,举报ID:{},异常信息:{}", violation.getId(), e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 获取举报理由文本
+     *
+     * @param violation 举报记录
+     * @return 举报理由文本
+     */
+    private String getViolationReasonText(LawyerUserViolation violation) {
+        String violationReasonId = violation.getViolationReason();
+        if (!StringUtils.hasText(violationReasonId)) {
+            return "其他原因";
+        }
+
+        switch (violationReasonId) {
+            case "1":
+                return "服务态度差";
+            case "2":
+                return "专业能力差";
+            case "3":
+                return "响应时间超过24小时";
+            case "4":
+                // 其他原因,使用用户填写的内容
+                String otherReason = violation.getOtherReasonContent();
+                return StringUtils.hasText(otherReason) ? otherReason : "其他原因";
+            default:
+                return "其他原因";
+        }
+    }
+
+    /**
+     * 获取举报人接收ID
+     *
+     * @param violation 举报记录
+     * @return 接收人ID,格式:lawyer_/store_/user_ + 手机号
+     */
+    private String getReporterReceiverId(LawyerUserViolation violation) {
+        String reportingUserType = violation.getReportingUserType();
+        String reportingUserId = violation.getReportingUserId();
+
+        if (!StringUtils.hasText(reportingUserId)) {
+            log.warn("举报用户ID为空");
+            return null;
+        }
+
+        // 用户类型:1-商户用户,2-普通用户,3-律师用户
+        if ("3".equals(reportingUserType)) {
+            // 律师用户
+            LawyerUser lawyerUser = lawyerUserMapper.selectById(reportingUserId);
+            if (lawyerUser != null && StringUtils.hasText(lawyerUser.getPhone())) {
+                return "lawyer_" + lawyerUser.getPhone();
+            }
+        } else if ("1".equals(reportingUserType)) {
+            // 商户用户
+            StoreUser storeUser = storeUserMapper.selectById(reportingUserId);
+            if (storeUser != null && StringUtils.hasText(storeUser.getPhone())) {
+                return "store_" + storeUser.getPhone();
+            }
+        } else if ("2".equals(reportingUserType)) {
+            // 普通用户
+            LifeUser lifeUser = lifeUserMapper.selectById(reportingUserId);
+            if (lifeUser != null && StringUtils.hasText(lifeUser.getUserPhone())) {
+                return "user_" + lifeUser.getUserPhone();
+            }
+        }
+
+        log.warn("获取举报人手机号失败,用户类型:{},用户ID:{}", reportingUserType, reportingUserId);
+        return null;
+    }
+
+    /**
+     * 获取被举报人接收ID
+     *
+     * @param violation 举报记录
+     * @return 接收人ID,格式:lawyer_/store_/user_ + 手机号
+     */
+    private String getReportedReceiverId(LawyerUserViolation violation) {
+        String reportedUserType = violation.getReportedUserType();
+        String reportedUserId = violation.getReportedUserId();
+
+        if (!StringUtils.hasText(reportedUserId)) {
+            log.warn("被举报用户ID为空");
+            return null;
+        }
+
+        // 用户类型:1-商户用户,2-普通用户,3-律师用户
+        if ("3".equals(reportedUserType)) {
+            // 律师用户
+            LawyerUser lawyerUser = lawyerUserMapper.selectById(reportedUserId);
+            if (lawyerUser != null && StringUtils.hasText(lawyerUser.getPhone())) {
+                return "lawyer_" + lawyerUser.getPhone();
+            }
+        } else if ("1".equals(reportedUserType)) {
+            // 商户用户
+            StoreUser storeUser = storeUserMapper.selectById(reportedUserId);
+            if (storeUser != null && StringUtils.hasText(storeUser.getPhone())) {
+                return "store_" + storeUser.getPhone();
+            }
+        } else if ("2".equals(reportedUserType)) {
+            // 普通用户
+            LifeUser lifeUser = lifeUserMapper.selectById(reportedUserId);
+            if (lifeUser != null && StringUtils.hasText(lifeUser.getUserPhone())) {
+                return "user_" + lifeUser.getUserPhone();
+            }
+        }
+
+        log.warn("获取被举报人手机号失败,用户类型:{},用户ID:{}", reportedUserType, reportedUserId);
+        return null;
+    }
+
+    /**
+     * 创建通知对象
+     *
+     * @param businessId 业务ID(举报ID)
+     * @param receiverId 接收人ID
+     * @param title      通知标题
+     * @param message    通知消息
+     * @return 通知对象
+     */
+    private LifeNotice createLifeNotice(Integer businessId, String receiverId, String title, String message) {
+        LifeNotice lifeNotice = new LifeNotice();
+        lifeNotice.setSenderId(SYSTEM_SENDER_ID);
+        lifeNotice.setBusinessId(businessId);
+        lifeNotice.setReceiverId(receiverId);
+        lifeNotice.setTitle(title);
+        lifeNotice.setNoticeType(1);
+        lifeNotice.setIsRead(0);
+        lifeNotice.setBusinessType(1);
+
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("message", message);
+        lifeNotice.setContext(jsonObject.toJSONString());
+
+        return lifeNotice;
+    }
+
+    /**
+     * 发送WebSocket消息
+     *
+     * @param receiverId 接收人ID
+     * @param lifeNotice 通知对象
+     */
+    private void sendWebSocketMessage(String receiverId, LifeNotice lifeNotice) {
+        try {
+            WebSocketVo webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId(SYSTEM_SENDER_ID);
+            webSocketVo.setReceiverId(receiverId);
+            webSocketVo.setCategory("notice");
+            webSocketVo.setNoticeType("1");
+            webSocketVo.setIsRead(0);
+            webSocketVo.setText(JSONObject.from(lifeNotice).toJSONString());
+
+            webSocketProcess.sendMessage(receiverId, JSONObject.from(webSocketVo).toJSONString());
+        } catch (Exception e) {
+            log.error("发送WebSocket消息异常,接收人ID:{},异常信息:{}", receiverId, e.getMessage(), e);
+        }
+    }
+}
+

+ 581 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/CommentAppealServiceImpl.java

@@ -0,0 +1,581 @@
+package shop.alien.lawyer.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.nacos.client.naming.utils.RandomUtils;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.*;
+import shop.alien.entity.store.vo.CommentAppealVo;
+import shop.alien.entity.store.vo.WebSocketVo;
+import shop.alien.lawyer.config.WebSocketProcess;
+import shop.alien.mapper.*;
+import shop.alien.lawyer.service.CommentAppealService;
+import shop.alien.lawyer.service.OrderReviewService;
+import shop.alien.lawyer.service.ReviewCommentService;
+import shop.alien.util.common.JwtUtil;
+import shop.alien.util.common.ListToPage;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 评论申诉表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class CommentAppealServiceImpl extends ServiceImpl<CommentAppealMapper, CommentAppeal> implements CommentAppealService {
+
+    private final OrderReviewService orderReviewService;
+    private final ReviewCommentService reviewCommentService;
+    private final WebSocketProcess webSocketProcess;
+    private final LifeNoticeMapper lifeNoticeMapper;
+    private final OrderReviewMapper orderReviewMapper;
+    private final LifeUserMapper lifeUserMapper;
+    private final LawyerUserMapper lawyerUserMapper;
+    private final LawyerConsultationOrderMapper lawyerConsultationOrderMapper;
+
+
+    @Override
+    public R<CommentAppeal> submitAppeal(CommentAppeal commentAppeal) {
+        log.info("CommentAppealServiceImpl.submitAppeal?commentAppeal={}", commentAppeal);
+
+        // 校验必填字段
+        if (commentAppeal.getCommentId() == null) {
+            return R.fail("评论ID不能为空");
+        }
+        if (!StringUtils.hasText(commentAppeal.getAppealReason())) {
+            return R.fail("申诉理由不能为空");
+        }
+
+        // 检查该评论是否已有待处理或已通过的申诉
+        LambdaQueryWrapper<CommentAppeal> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(CommentAppeal::getCommentId, commentAppeal.getCommentId());
+        wrapper.eq(CommentAppeal::getDeleteFlag, 0);
+        wrapper.in(CommentAppeal::getStatus, 0, 1); // 待处理或已通过
+        CommentAppeal existingAppeal = this.getOne(wrapper);
+        if (existingAppeal != null) {
+            return R.fail("该评论已有申诉记录,无法重复申诉");
+        }
+
+        OrderReview orderReview = orderReviewMapper.selectById(commentAppeal.getCommentId());
+        if(orderReview != null){
+            if (orderReview.getOrderId() != null) {
+                commentAppeal.setOrderId(orderReview.getOrderId());
+            }
+        }
+
+        // 设置默认值
+        commentAppeal.setStatus(0); // 待处理
+        if (commentAppeal.getDeleteFlag() == null) {
+            commentAppeal.setDeleteFlag(0);
+        }
+        commentAppeal.setAppealTime(new Date());
+        commentAppeal.setAppealNumber(generateOrderNumber());
+
+        // 保存申诉
+        boolean result = this.save(commentAppeal);
+        if (result) {
+            log.info("提交申诉成功,id={}", commentAppeal.getId());
+            if(orderReview != null){
+                orderReview.setAppealId(commentAppeal.getId().toString());
+                orderReviewMapper.updateById(orderReview);
+            }
+            
+            // 更新订单表的申诉状态为2(申诉中)
+            if (commentAppeal.getOrderId() != null) {
+                LambdaUpdateWrapper<LawyerConsultationOrder> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+                lambdaUpdateWrapper.eq(LawyerConsultationOrder::getId, commentAppeal.getOrderId())
+                        .set(LawyerConsultationOrder::getIsAppealed, 2);
+                lawyerConsultationOrderMapper.update(null, lambdaUpdateWrapper);
+                log.info("更新订单申诉状态成功,orderId={}, isAppealed=2", commentAppeal.getOrderId());
+            }
+
+            // 发送通知给评价用户,告知其评价被申诉了
+            sendSubmitAppealNotification(commentAppeal);
+
+            return R.data(commentAppeal, "申诉提交成功");
+        } else {
+            return R.fail("申诉提交失败");
+        }
+    }
+
+    @Override
+    public R<Boolean> auditAppeal(Integer id, Integer status, String reviewReasons) {
+        log.info("CommentAppealServiceImpl.auditAppeal?id={}, status={}, reviewReasons={}", id, status, reviewReasons);
+
+        // 校验参数
+        if (id == null) {
+            return R.fail("申诉ID不能为空");
+        }
+
+        if (status == null || (status != 1 && status != 2)) {
+            return R.fail("审核状态不正确,必须是1(已通过)或2(已驳回)");
+        }
+
+        // 从 token 获取当前登录用户ID(审核人)
+        Integer auditUserId = null;
+        try {
+            com.alibaba.fastjson.JSONObject userInfo = JwtUtil.getCurrentUserInfo();
+            if (userInfo != null && userInfo.getString("userId") != null) {
+                auditUserId = userInfo.getInteger("userId");
+            }
+        } catch (Exception e) {
+            log.warn("获取当前用户信息失败,将使用默认值", e);
+        }
+
+        // 查询申诉记录
+        CommentAppeal appeal = this.getById(id);
+        if (appeal == null || appeal.getDeleteFlag() == 1) {
+            return R.fail("申诉记录不存在或已被删除");
+        }
+
+        // 检查状态
+        if (appeal.getStatus() != 0) {
+            return R.fail("该申诉已处理,无法重复审核");
+        }
+
+        // 查询评价信息
+        OrderReview orderReview = orderReviewMapper.selectById(appeal.getCommentId());
+        if (orderReview == null) {
+            return R.fail("评价记录不存在");
+        }
+
+        // 更新状态
+        CommentAppeal updateAppeal = new CommentAppeal();
+        updateAppeal.setId(id);
+        updateAppeal.setStatus(status);
+        updateAppeal.setAppealReviewTime(new Date());
+        if (auditUserId != null) {
+            updateAppeal.setUpdatedUserId(auditUserId);
+        }
+        // 设置审核原因
+        if (StringUtils.hasText(reviewReasons)) {
+            updateAppeal.setReviewReasons(reviewReasons);
+        }
+
+        boolean result = this.updateById(updateAppeal);
+        if (result) {
+            String statusText = status == 1 ? "已通过" : "已驳回";
+            log.info("审核申诉成功,id={}, status={}, auditUserId={}", id, statusText, auditUserId);
+
+            // 如果申诉通过,删除评价及该评价下的所有评论和回复
+            if (status == 1) {
+//                deleteReviewAndRelatedData(appeal.getCommentId());
+                orderReviewService.deleteReviewByAdmin(appeal.getCommentId());
+            }
+
+            // 发送通知
+            sendAuditNotification(appeal, orderReview, status, reviewReasons);
+
+            return R.success("申诉" + statusText);
+        } else {
+            return R.fail("审核失败");
+        }
+    }
+
+    /**
+     * 发送提交申诉通知(给评价用户)
+     *
+     * @param appeal 申诉记录
+     */
+    private void sendSubmitAppealNotification(CommentAppeal appeal) {
+        try {
+            // 查询律师用户信息
+            LambdaQueryWrapper<LawyerUser> lawyerUser = new LambdaQueryWrapper<>();
+            lawyerUser.eq(LawyerUser::getId, appeal.getLawyerUserId());
+            LawyerUser lifeUser = lawyerUserMapper.selectOne(lawyerUser);
+            if (lifeUser == null) {
+                log.warn("评价用户不存在,userId={}", lifeUser.getId());
+                return;
+            }
+            LambdaQueryWrapper<OrderReview> orderReviewLambdaQueryWrapper = new LambdaQueryWrapper<>();
+            orderReviewLambdaQueryWrapper.eq(OrderReview::getId, appeal.getCommentId());
+            OrderReview orderReview = orderReviewMapper.selectOne(orderReviewLambdaQueryWrapper);
+            String orderNumber = "";
+            if (orderReview != null) {
+                orderNumber = orderReview.getOrderNumber();
+            }
+            String receiverId = "lawyer_" + lifeUser.getPhone();
+            String message = String.format("您提交的差评申诉信息,订单编号为" + orderNumber + ",已提交至平台审核,1-3个工作日会发送您审核结果,请注意查收。");
+
+            LifeNotice lifeNotice = new LifeNotice();
+            lifeNotice.setReceiverId(receiverId);
+            lifeNotice.setBusinessId(appeal.getId());
+            lifeNotice.setTitle("申诉通知");
+            lifeNotice.setSenderId("system");
+            lifeNotice.setIsRead(0);
+            lifeNotice.setNoticeType(1);
+            lifeNotice.setDeleteFlag(0);
+            lifeNotice.setBusinessType(1);
+
+            com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
+            jsonObject.put("message", message);
+//            jsonObject.put("orderNumber", orderReview.getOrderNumber());
+//            jsonObject.put("appealId", appeal.getId());
+//            jsonObject.put("status", "pending"); // 待审核状态
+            lifeNotice.setContext(jsonObject.toJSONString());
+
+            lifeNoticeMapper.insert(lifeNotice);
+
+            // 发送 WebSocket 消息
+            sendWebSocketNotification(receiverId, lifeNotice);
+            log.info("提交申诉通知发送成功,receiverId={}, appealId={}", receiverId, appeal.getId());
+        } catch (Exception e) {
+            log.error("发送提交申诉通知失败,appealId={}, error={}", appeal.getId(), e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 发送审核通知
+     *
+     * @param appeal      申诉记录
+     * @param orderReview 评价记录
+     * @param status      审核状态 1:已通过, 2:已驳回
+     */
+    private void sendAuditNotification(CommentAppeal appeal, OrderReview orderReview, Integer status, String reviewReasons) {
+        try {
+            if (status == 1) {
+                // 审核通过:给评价用户发通知(评价被删除)
+                sendNotificationToReviewUser(orderReview, appeal);
+                // 审核通过:给律师发通知(申诉成功)
+                sendNotificationToLawyer(orderReview, appeal, true, null);
+            } else {
+                // 审核驳回:给律师发通知(申诉失败)
+                sendNotificationToLawyer(orderReview, appeal, false, reviewReasons);
+            }
+        } catch (Exception e) {
+            log.error("发送审核通知失败,appealId={}, status={}, error={}", appeal.getId(), status, e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 给评价用户发送通知(评价被删除)
+     *
+     * @param orderReview 评价记录
+     * @param appeal      申诉记录
+     */
+    private void sendNotificationToReviewUser(OrderReview orderReview, CommentAppeal appeal) {
+        try {
+            LifeUser lifeUser = lifeUserMapper.selectById(orderReview.getUserId());
+            if (lifeUser == null) {
+                log.warn("评价用户不存在,userId={}", orderReview.getUserId());
+                return;
+            }
+
+            String receiverId = "user_" + lifeUser.getUserPhone();
+            String message = String.format("您对订单编号为" + orderReview.getOrderNumber() + "的评价,律师已进行申诉,经核实,您的评价不实,平台已删除此条评价及回复。");
+
+            LifeNotice lifeNotice = new LifeNotice();
+            lifeNotice.setReceiverId(receiverId);
+            lifeNotice.setBusinessId(appeal.getId());
+            lifeNotice.setTitle("评价被申诉成功通知");
+            lifeNotice.setSenderId("system");
+            lifeNotice.setIsRead(0);
+            lifeNotice.setNoticeType(1);
+            lifeNotice.setDeleteFlag(0);
+            lifeNotice.setBusinessType(1);
+
+            com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
+            jsonObject.put("message", message);
+            lifeNotice.setContext(jsonObject.toJSONString());
+
+            lifeNoticeMapper.insert(lifeNotice);
+
+            // 发送 WebSocket 消息
+            sendWebSocketNotification(receiverId, lifeNotice);
+            log.info("评价用户通知发送成功,receiverId={}", receiverId);
+        } catch (Exception e) {
+            log.error("给评价用户发送通知失败,userId={}, error={}", orderReview.getUserId(), e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 给律师发送通知(申诉成功/失败)
+     *
+     * @param orderReview 评价记录
+     * @param appeal      申诉记录
+     * @param isSuccess   是否成功
+     */
+    private void sendNotificationToLawyer(OrderReview orderReview, CommentAppeal appeal, boolean isSuccess, String reviewReasons) {
+        try {
+            LawyerUser lawyerUser = lawyerUserMapper.selectById(orderReview.getLawyerUserId());
+            if (lawyerUser == null) {
+                log.warn("律师用户不存在,lawyerUserId={}", orderReview.getLawyerUserId());
+                return;
+            }
+
+            // 律师的接收ID格式可能是 "lawyer_" + phone 或其他格式,需要根据实际情况调整
+            String receiverId = "lawyer_" + lawyerUser.getPhone();
+            String title = isSuccess ? "申诉成功通知" : "申诉失败通知";
+            String message = isSuccess
+                    ? String.format("您的编号" + orderReview.getOrderNumber() + "的订单,提交的差评申诉信息,经核实,评价内容不实,平台已删除此条评价。")
+                    : String.format("您的编号" + orderReview.getOrderNumber() + "的订单,提交的差评申诉信息,经核实,评价内容属实。失败原因:" + reviewReasons + "。");
+
+            LifeNotice lifeNotice = new LifeNotice();
+            lifeNotice.setReceiverId(receiverId);
+            lifeNotice.setBusinessId(appeal.getId());
+            lifeNotice.setTitle(title);
+            lifeNotice.setSenderId("system");
+            lifeNotice.setIsRead(0);
+            lifeNotice.setNoticeType(1);
+            lifeNotice.setDeleteFlag(0);
+            lifeNotice.setBusinessType(1);
+
+            com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
+            jsonObject.put("message", message);
+            lifeNotice.setContext(jsonObject.toJSONString());
+
+            lifeNoticeMapper.insert(lifeNotice);
+
+            // 发送 WebSocket 消息
+            sendWebSocketNotification(receiverId, lifeNotice);
+            log.info("律师通知发送成功,receiverId={}, isSuccess={}", receiverId, isSuccess);
+        } catch (Exception e) {
+            log.error("给律师发送通知失败,lawyerUserId={}, error={}", orderReview.getLawyerUserId(), e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 发送 WebSocket 通知
+     *
+     * @param receiverId 接收人ID
+     * @param lifeNotice 通知内容
+     */
+    private void sendWebSocketNotification(String receiverId, LifeNotice lifeNotice) {
+        try {
+            WebSocketVo webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId("system");
+            webSocketVo.setReceiverId(receiverId);
+            webSocketVo.setCategory("notice");
+            webSocketVo.setNoticeType("1");
+            webSocketVo.setIsRead(0);
+            webSocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeNotice).toJSONString());
+
+            webSocketProcess.sendMessage(receiverId, com.alibaba.fastjson2.JSONObject.from(webSocketVo).toJSONString());
+        } catch (Exception e) {
+            log.error("发送WebSocket通知失败,receiverId={}, error={}", receiverId, e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public R<IPage<CommentAppealVo>> getAppealPage(Integer pageNum, Integer pageSize, Integer status,
+                                                   String orderNumber, String userName, String userPhone, String lawyerName, String lawyerPhone,
+                                                   String startTime, String endTime) {
+        log.info("CommentAppealServiceImpl.getAppealPage?pageNum={}, pageSize={}, status={}, orderNumber={}, userName={}, userPhone={}, lawyerName={}, lawyerPhone={}, startTime={}, endTime={}",
+                pageNum, pageSize, status, orderNumber, userName, userPhone, lawyerName, lawyerPhone, startTime, endTime);
+
+        Page<CommentAppealVo> page = new Page<>(pageNum, pageSize);
+        IPage<CommentAppealVo> pageResult = baseMapper.getAppealPageWithFilters(page, status, orderNumber, userName, userPhone, lawyerName, lawyerPhone, startTime, endTime);
+
+        // 处理数据转换
+        for (CommentAppealVo vo : pageResult.getRecords()) {
+            processAppealVo(vo);
+        }
+
+        return R.data(pageResult);
+    }
+
+    @Override
+    public R<CommentAppealVo> getAppealDetail(Integer id) {
+        log.info("CommentAppealServiceImpl.getAppealDetail?id={}", id);
+
+        if (id == null) {
+            return R.fail("申诉ID不能为空");
+        }
+
+        // 查询申诉详情(包含评价和用户信息)
+        CommentAppealVo vo = baseMapper.getAppealDetailById(id);
+        if (vo == null) {
+            return R.fail("申诉记录不存在或已被删除");
+        }
+
+        // 处理数据转换(复用历史列表的处理逻辑)
+        processAppealVo(vo);
+
+        return R.data(vo, "查询成功");
+    }
+
+    /**
+     * 处理申诉VO数据转换(状态字符串、图片列表等)
+     *
+     * @param vo 申诉VO对象
+     */
+    private void processAppealVo(CommentAppealVo vo) {
+        // 设置状态字符串
+        if (vo.getStatus() != null) {
+            switch (vo.getStatus()) {
+                case 0:
+                    vo.setStatusStr("审核中");
+                    break;
+                case 1:
+                    vo.setStatusStr("已通过");
+                    vo.setAppealResult("申诉成功");
+                    break;
+                case 2:
+                    vo.setStatusStr("被驳回");
+                    vo.setAppealResult("申诉失败");
+                    break;
+                default:
+                    vo.setStatusStr("未知");
+            }
+        }
+
+        // 处理申诉图片列表
+        if (StringUtils.hasText(vo.getImgUrl())) {
+            try {
+                // 如果是 JSON 数组格式,解析为列表
+                if (vo.getImgUrl().trim().startsWith("[")) {
+                    List<String> imgList = JSON.parseArray(vo.getImgUrl(), String.class);
+                    vo.setImgList(imgList != null ? imgList : new ArrayList<>());
+                } else {
+                    // 如果是单个 URL 或逗号分隔的字符串
+                    List<String> imgList = new ArrayList<>();
+                    String[] urls = vo.getImgUrl().split(",");
+                    for (String url : urls) {
+                        if (StringUtils.hasText(url.trim())) {
+                            imgList.add(url.trim());
+                        }
+                    }
+                    vo.setImgList(imgList);
+                }
+            } catch (Exception e) {
+                log.warn("解析申诉图片失败,imgUrl={}", vo.getImgUrl(), e);
+                List<String> imgList = new ArrayList<>();
+                imgList.add(vo.getImgUrl());
+                vo.setImgList(imgList);
+            }
+        } else {
+            vo.setImgList(new ArrayList<>());
+        }
+
+        // 处理评价图片列表
+        if (StringUtils.hasText(vo.getReviewImages())) {
+            try {
+                // 如果是 JSON 数组格式,解析为列表
+                if (vo.getReviewImages().trim().startsWith("[")) {
+                    List<String> reviewImgList = JSON.parseArray(vo.getReviewImages(), String.class);
+                    vo.setReviewImgList(reviewImgList != null ? reviewImgList : new ArrayList<>());
+                } else {
+                    // 如果是单个 URL 或逗号分隔的字符串
+                    List<String> reviewImgList = new ArrayList<>();
+                    String[] urls = vo.getReviewImages().split(",");
+                    for (String url : urls) {
+                        if (StringUtils.hasText(url.trim())) {
+                            reviewImgList.add(url.trim());
+                        }
+                    }
+                    vo.setReviewImgList(reviewImgList);
+                }
+            } catch (Exception e) {
+                log.warn("解析评价图片失败,reviewImages={}", vo.getReviewImages(), e);
+                vo.setReviewImgList(new ArrayList<>());
+            }
+        } else {
+            vo.setReviewImgList(new ArrayList<>());
+        }
+    }
+
+    /**
+     * 删除评价及该评价下的所有评论和回复
+     *
+     * @param reviewId 评价ID
+     */
+    private void deleteReviewAndRelatedData(Integer reviewId) {
+        log.info("删除评价及关联数据,reviewId={}, userId={}", reviewId);
+
+        if (reviewId == null) {
+            log.warn("评价ID为空,无法删除");
+            return;
+        }
+
+        try {
+            // 1. 查询该评价下的所有评论
+            LambdaQueryWrapper<ReviewComment> commentWrapper = new LambdaQueryWrapper<>();
+            commentWrapper.eq(ReviewComment::getReviewId, reviewId)
+                    .eq(ReviewComment::getDeleteFlag, 0);
+            List<ReviewComment> comments = reviewCommentService.list(commentWrapper);
+
+            // 2. 删除每个评论下的所有回复
+            for (ReviewComment comment : comments) {
+                // 删除评论下的所有回复(逻辑删除)
+                LambdaUpdateWrapper<ReviewComment> replyUpdateWrapper = new LambdaUpdateWrapper<>();
+                replyUpdateWrapper.eq(ReviewComment::getHeadId, comment.getId())
+                        .eq(ReviewComment::getHeadType, 1)
+                        .eq(ReviewComment::getDeleteFlag, 0);
+                replyUpdateWrapper.set(ReviewComment::getDeleteFlag, 1);
+                replyUpdateWrapper.set(ReviewComment::getUpdatedTime, new Date());
+                reviewCommentService.update(replyUpdateWrapper);
+                log.info("删除评论下的回复,commentId={}, 回复数已删除", comment.getId());
+            }
+
+            // 3. 删除所有评论(逻辑删除)
+            for (ReviewComment comment : comments) {
+                comment.setDeleteFlag(1);
+                comment.setUpdatedTime(new Date());
+                reviewCommentService.updateById(comment);
+            }
+            log.info("删除评价下的评论,reviewId={}, 评论数={}", reviewId, comments.size());
+
+            // 4. 删除评价本身(逻辑删除)
+            OrderReview review = orderReviewService.getById(reviewId);
+            if (review != null && review.getDeleteFlag() == 0) {
+                review.setDeleteFlag(1);
+                review.setUpdatedTime(new Date());
+                orderReviewService.updateById(review);
+                log.info("删除评价成功,reviewId={}", reviewId);
+            } else {
+                log.warn("评价不存在或已删除,reviewId={}", reviewId);
+            }
+        } catch (Exception e) {
+            log.error("删除评价及关联数据失败,reviewId={}, error={}", reviewId, e.getMessage(), e);
+            throw new RuntimeException("删除评价及关联数据失败:" + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public List<CommentAppealVo> getAppealHistory(Integer pageNum, Integer pageSize, Integer status, Integer lawyerUserId) {
+        log.info("CommentAppealServiceImpl.getAppealHistory?pageNum={}, pageSize={}, status={}, lawyerUserId={}",
+                pageNum, pageSize, status, lawyerUserId);
+        List<CommentAppealVo> appealList = new ArrayList<>();
+        //status 3 查全部
+        if (status == 3) {
+            appealList = baseMapper.getAppealHistoryList(null, lawyerUserId);
+        } else {
+            // 查询申诉历史列表
+            appealList = baseMapper.getAppealHistoryList(status, lawyerUserId);
+        }
+
+        // 处理数据转换(图片列表等)
+        for (CommentAppealVo vo : appealList) {
+            processAppealVo(vo);
+        }
+
+        ListToPage.setPage(appealList, pageNum, pageSize);
+
+        return appealList;
+    }
+
+    private String generateOrderNumber() {
+        String dateStr = new SimpleDateFormat("yyyyMMdd").format(new Date());
+        String randomStr = String.format("%05d", RandomUtils.nextInt(100000));
+        return "LAW" + dateStr + randomStr;
+    }
+}
+
+

+ 34 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawFirmPaymentServiceImpl.java

@@ -0,0 +1,34 @@
+package shop.alien.lawyer.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.store.LawFirmPayment;
+import shop.alien.lawyer.service.LawFirmPaymentService;
+import shop.alien.mapper.LawFirmPaymentMapper;
+
+/**
+ * 律所子表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawFirmPaymentServiceImpl extends ServiceImpl<LawFirmPaymentMapper, LawFirmPayment> implements LawFirmPaymentService {
+
+    private final LawFirmPaymentMapper lawFirmPaymentMapper;
+
+}
+
+
+
+
+
+
+
+

+ 330 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawFirmReconciliationServiceImpl.java

@@ -0,0 +1,330 @@
+package shop.alien.lawyer.service.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawFirm;
+import shop.alien.entity.store.LawyerUser;
+import shop.alien.entity.store.vo.LawFirmListVO;
+import shop.alien.entity.store.vo.LawFirmReconciliationVO;
+import shop.alien.entity.store.vo.LawyerListVO;
+import shop.alien.lawyer.service.LawFirmReconciliationService;
+import shop.alien.lawyer.service.LawFirmService;
+import shop.alien.lawyer.service.LawyerUserService;
+import shop.alien.mapper.LawFirmMapper;
+import shop.alien.util.excel.EasyExcelUtil;
+
+import javax.servlet.http.HttpServletResponse;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 律所对账结算服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawFirmReconciliationServiceImpl implements LawFirmReconciliationService {
+
+    private final LawFirmMapper lawFirmMapper;
+    private final LawFirmService lawFirmService;
+    private final LawyerUserService lawyerUserService;
+
+    @Override
+    public R<LawFirmReconciliationVO> getLawFirmReconciliation(
+            Integer firmId,
+            String firmName,
+            Date startDate,
+            Date endDate) {
+        log.info("LawFirmReconciliationServiceImpl.getLawFirmReconciliation?firmId={},firmName={},startDate={},endDate={}",
+                firmId, firmName, startDate, endDate);
+
+        // 查询统计信息
+        java.util.Map<String, Object> statistics = lawFirmMapper.getLawFirmReconciliationStatistics(firmId, firmName, startDate, endDate);
+        long totalOrderCount = statistics != null && statistics.get("order_count") != null
+                ? ((Number) statistics.get("order_count")).longValue() : 0L;
+        long totalOrderAmount = statistics != null && statistics.get("total_amount") != null
+                ? ((Number) statistics.get("total_amount")).longValue() : 0L;
+        long platformServiceFee = statistics != null && statistics.get("total_platform_service_fee") != null
+                ? ((Number) statistics.get("total_platform_service_fee")).longValue() : 0L;
+
+        // 获取律所名称
+        String resultFirmName = "所有律所";
+
+        // 如果 firmId 有值,查询当前律所信息并验证佣金比例
+        if (firmId != null) {
+            LawFirm lawFirm = lawFirmService.getById(firmId);
+            if (lawFirm == null || (lawFirm.getDeleteFlag() != null && lawFirm.getDeleteFlag() == 1)) {
+                return R.fail("律所不存在或已被删除");
+            }
+            resultFirmName = lawFirm.getFirmName();
+//            if (lawFirm.getPlatformCommissionRatio() == null) {
+//                return R.fail("律所[" + resultFirmName + "]的平台佣金比例未设置,无法计算对账信息");
+//            }
+        } else if (firmName != null && !firmName.trim().isEmpty()) {
+            // 如果只传了 firmName,使用传入的名称
+            resultFirmName = firmName;
+        }
+
+        // 构建返回对象
+        LawFirmReconciliationVO vo = new LawFirmReconciliationVO();
+        vo.setFirmId(firmId);
+        vo.setFirmName(resultFirmName);
+        vo.setTotalOrderCount(totalOrderCount);
+        vo.setTotalOrderAmountYuan(convertFenToYuan(totalOrderAmount));
+        vo.setPlatformServiceFeeYuan(convertFenToYuan(platformServiceFee));
+
+        return R.data(vo, "查询成功");
+    }
+
+
+
+    @Override
+    public R<IPage<LawFirmListVO>> getAllLawFirmReconciliationList(
+            String firmName,
+            Date startDate,
+            Date endDate,
+            Integer pageNum,
+            Integer pageSize) {
+        log.info("LawFirmReconciliationServiceImpl.getAllLawFirmReconciliationList?firmName={},startDate={},endDate={},pageNum={},pageSize={}",
+                firmName, startDate, endDate, pageNum, pageSize);
+
+        // 查询所有律所的统计信息(SQL中已直接计算为元)
+        List<LawFirmListVO> firmList = lawFirmMapper.getAllLawFirmReconciliationStatistics(firmName, startDate, endDate);
+
+        // 手动分页
+        Page<LawFirmListVO> page = new Page<>();
+        if (pageNum != null && pageSize != null && pageNum > 0 && pageSize > 0) {
+            page.setCurrent(pageNum);
+            page.setSize(pageSize);
+            page.setTotal(firmList.size());
+
+            int start = (pageNum - 1) * pageSize;
+            int end = Math.min(start + pageSize, firmList.size());
+            if (start < firmList.size()) {
+                page.setRecords(firmList.subList(start, end));
+            } else {
+                page.setRecords(Collections.emptyList());
+            }
+        } else {
+            page.setCurrent(1);
+            page.setSize(firmList.size());
+            page.setTotal(firmList.size());
+            page.setRecords(firmList);
+        }
+
+        return R.data(page, "查询成功");
+    }
+
+    @Override
+    public R<IPage<LawyerListVO>> getLawyerReconciliationList(
+            Integer firmId,
+            Date startDate,
+            Date endDate,
+            Integer pageNum,
+            Integer pageSize) {
+        log.info("LawFirmReconciliationServiceImpl.getLawyerReconciliationList?firmId={},startDate={},endDate={},pageNum={},pageSize={}",
+                firmId, startDate, endDate, pageNum, pageSize);
+
+        // 验证律所是否存在
+        if (firmId == null) {
+            return R.fail("律所ID不能为空");
+        }
+        LawFirm lawFirm = lawFirmService.getById(firmId);
+        if (lawFirm == null || (lawFirm.getDeleteFlag() != null && lawFirm.getDeleteFlag() == 1)) {
+            return R.fail("律所不存在或已被删除");
+        }
+
+        // 查询律所下所有律师的统计信息(SQL中已直接计算为元)
+        List<LawyerListVO> lawyerList = lawFirmMapper.getLawyerReconciliationStatistics(firmId, startDate, endDate);
+
+        // 手动分页
+        Page<LawyerListVO> page = new Page<>();
+        if (pageNum != null && pageSize != null && pageNum > 0 && pageSize > 0) {
+            page.setCurrent(pageNum);
+            page.setSize(pageSize);
+            page.setTotal(lawyerList.size());
+
+            int start = (pageNum - 1) * pageSize;
+            int end = Math.min(start + pageSize, lawyerList.size());
+            if (start < lawyerList.size()) {
+                page.setRecords(lawyerList.subList(start, end));
+            } else {
+                page.setRecords(Collections.emptyList());
+            }
+        } else {
+            page.setCurrent(1);
+            page.setSize(lawyerList.size());
+            page.setTotal(lawyerList.size());
+            page.setRecords(lawyerList);
+        }
+
+        return R.data(page, "查询成功");
+    }
+
+    @Override
+    public R<IPage<LawyerListVO>> getLawyerReconciliationListWithName(
+            Integer firmId,
+            String lawyerName,
+            Date startDate,
+            Date endDate,
+            Integer pageNum,
+            Integer pageSize) {
+        log.info("LawFirmReconciliationServiceImpl.getLawyerReconciliationListWithName?firmId={},lawyerName={},startDate={},endDate={},pageNum={},pageSize={}",
+                firmId, lawyerName, startDate, endDate, pageNum, pageSize);
+
+        // 验证律所是否存在
+        if (firmId == null) {
+            return R.fail("律所ID不能为空");
+        }
+        LawFirm lawFirm = lawFirmService.getById(firmId);
+        if (lawFirm == null || (lawFirm.getDeleteFlag() != null && lawFirm.getDeleteFlag() == 1)) {
+            return R.fail("律所不存在或已被删除");
+        }
+
+        // 查询律所下所有律师的统计信息(SQL中已直接计算为元,支持律师名称模糊查询)
+        List<LawyerListVO> lawyerList = lawFirmMapper.getLawyerReconciliationStatisticsWithName(firmId, lawyerName, startDate, endDate);
+
+        // 手动分页
+        Page<LawyerListVO> page = new Page<>();
+        if (pageNum != null && pageSize != null && pageNum > 0 && pageSize > 0) {
+            page.setCurrent(pageNum);
+            page.setSize(pageSize);
+            page.setTotal(lawyerList.size());
+
+            int start = (pageNum - 1) * pageSize;
+            int end = Math.min(start + pageSize, lawyerList.size());
+            if (start < lawyerList.size()) {
+                page.setRecords(lawyerList.subList(start, end));
+            } else {
+                page.setRecords(Collections.emptyList());
+            }
+        } else {
+            page.setCurrent(1);
+            page.setSize(lawyerList.size());
+            page.setTotal(lawyerList.size());
+            page.setRecords(lawyerList);
+        }
+
+        return R.data(page, "查询成功");
+    }
+
+    @Override
+    public R<IPage<LawFirmReconciliationVO>> getLawyerOrderList(
+            Integer lawyerId,
+            Integer pageNum,
+            Integer pageSize) {
+        log.info("LawFirmReconciliationServiceImpl.getLawyerOrderList?lawyerId={},pageNum={},pageSize={}",
+                lawyerId, pageNum, pageSize);
+
+        // 验证律师是否存在
+        if (lawyerId == null) {
+            return R.fail("律师ID不能为空");
+        }
+        LawyerUser lawyerUser = lawyerUserService.getById(lawyerId);
+        if (lawyerUser == null || (lawyerUser.getDeleteFlag() != null && lawyerUser.getDeleteFlag() == 1)) {
+            return R.fail("律师不存在或已被删除");
+        }
+
+        // 创建分页对象
+        int pageNumValue = pageNum != null && pageNum > 0 ? pageNum : 1;
+        int pageSizeValue = pageSize != null && pageSize > 0 ? pageSize : 10;
+        Page<LawFirmReconciliationVO> page = new Page<>(pageNumValue, pageSizeValue);
+
+        // 查询律师的已完成订单列表
+        IPage<LawFirmReconciliationVO> orderPage = lawFirmMapper.getLawyerOrderList(page, lawyerId);
+
+        return R.data(orderPage, "查询成功");
+    }
+
+    @Override
+    public void exportAllLawFirmList(
+            HttpServletResponse response,
+            String firmName,
+            Date startDate,
+            Date endDate,
+            Integer pageNum,
+            Integer pageSize) throws Exception {
+        log.info("LawFirmReconciliationServiceImpl.exportAllLawFirmList?firmName={},startDate={},endDate={},pageNum={},pageSize={}",
+                firmName, startDate, endDate, pageNum, pageSize);
+
+        // 调用查询接口获取数据
+        R<IPage<LawFirmListVO>> result = getAllLawFirmReconciliationList(firmName, startDate, endDate, pageNum, pageSize);
+
+        if (result == null || !result.isSuccess()) {
+            log.warn("获取律所对账列表失败:{}", result != null ? result.getMsg() : "返回结果为空");
+            throw new RuntimeException(result != null ? result.getMsg() : "获取数据失败,请稍后重试");
+        }
+
+        IPage<LawFirmListVO> page = result.getData();
+        if (page == null || page.getRecords() == null || page.getRecords().isEmpty()) {
+            log.warn("律所对账列表导出:无数据可导出");
+            throw new IllegalArgumentException("暂无数据可导出");
+        }
+
+        List<LawFirmListVO> exportList = page.getRecords();
+        log.info("律所对账列表导出:查询到{}条数据,导出{}条数据", page.getTotal(), exportList.size());
+
+        // 使用EasyExcelUtil导出
+        EasyExcelUtil.exportExcel(response, exportList, LawFirmListVO.class, "律所对账列表", "律所对账列表");
+    }
+
+    @Override
+    public void exportLawyerListWithName(
+            HttpServletResponse response,
+            Integer firmId,
+            String lawyerName,
+            Date startDate,
+            Date endDate,
+            Integer pageNum,
+            Integer pageSize) throws Exception {
+        log.info("LawFirmReconciliationServiceImpl.exportLawyerListWithName?firmId={},lawyerName={},startDate={},endDate={},pageNum={},pageSize={}",
+                firmId, lawyerName, startDate, endDate, pageNum, pageSize);
+
+        // 调用查询接口获取数据
+        R<IPage<LawyerListVO>> result = getLawyerReconciliationListWithName(firmId, lawyerName, startDate, endDate, pageNum, pageSize);
+
+        if (result == null || !result.isSuccess()) {
+            log.warn("获取律师对账列表失败:{}", result != null ? result.getMsg() : "返回结果为空");
+            throw new RuntimeException(result != null ? result.getMsg() : "获取数据失败,请稍后重试");
+        }
+
+        IPage<LawyerListVO> page = result.getData();
+        if (page == null || page.getRecords() == null || page.getRecords().isEmpty()) {
+            log.warn("律师对账列表导出:无数据可导出,firmId={}", firmId);
+            throw new IllegalArgumentException("暂无数据可导出");
+        }
+
+        List<LawyerListVO> exportList = page.getRecords();
+        log.info("律师对账列表导出:查询到{}条数据,导出{}条数据,firmId={}", page.getTotal(), exportList.size(), firmId);
+
+        // 使用EasyExcelUtil导出
+        EasyExcelUtil.exportExcel(response, exportList, LawyerListVO.class, "律师对账列表", "律师对账列表");
+    }
+
+    /**
+     * 将分转换为元(字符串格式)
+     *
+     * @param fen 分
+     * @return 元(字符串格式,保留2位小数)
+     */
+    private String convertFenToYuan(Long fen) {
+        if (fen == null || fen == 0) {
+            return "0.00";
+        }
+        BigDecimal yuan = BigDecimal.valueOf(fen).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
+        return yuan.toString();
+    }
+}
+

+ 1403 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawFirmServiceImpl.java

@@ -0,0 +1,1403 @@
+package shop.alien.lawyer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawFirm;
+import shop.alien.entity.store.LawFirmPayment;
+import shop.alien.entity.store.excelVo.LawFirmExcelVo;
+import shop.alien.entity.store.excelVo.util.ExcelHeader;
+import shop.alien.entity.store.vo.LawFirmPaymentVO;
+import shop.alien.lawyer.service.LawFirmPaymentService;
+import shop.alien.lawyer.service.LawFirmService;
+import shop.alien.mapper.LawFirmMapper;
+import shop.alien.mapper.LawFirmPaymentMapper;
+import shop.alien.util.excel.EasyExcelUtil;
+
+import org.apache.poi.ss.usermodel.DateUtil;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 律所表 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawFirmServiceImpl extends ServiceImpl<LawFirmMapper, LawFirm> implements LawFirmService {
+
+    private final LawFirmMapper lawFirmMapper;
+    private final LawFirmPaymentService lawFirmPaymentService;
+    private final LawFirmPaymentMapper lawFirmPaymentMapper;
+
+    @Override
+    public R<IPage<LawFirm>> getLawFirmList(int pageNum, int pageSize, String firmName, Integer status) {
+        log.info("LawFirmServiceImpl.getLawFirmList?pageNum={},pageSize={},firmName={},status={}", pageNum, pageSize, firmName, status);
+        Page<LawFirm> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<LawFirm> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawFirm::getDeleteFlag, 0);
+        if (StringUtils.hasText(firmName)) {
+            queryWrapper.like(LawFirm::getFirmName, firmName);
+        }
+        if (status != null) {
+            queryWrapper.eq(LawFirm::getStatus, status);
+        }
+        queryWrapper.orderByDesc(LawFirm::getCreatedTime);
+        IPage<LawFirm> pageResult = this.page(page, queryWrapper);
+        return R.data(pageResult);
+    }
+
+    @Override
+    public R<LawFirm> addLawFirm(LawFirm lawFirm) {
+        log.info("LawFirmServiceImpl.addLawFirm?lawFirm={}, platformCommissionRatio={}", lawFirm, lawFirm.getPlatformCommissionRatio());
+        
+        // 第一步:先校验收款账号,如果 paymentAccount 在数据库中存在,不让新增
+        if (lawFirm.getPaymentList() != null && !lawFirm.getPaymentList().isEmpty()) {
+            for (LawFirmPayment payment : lawFirm.getPaymentList()) {
+                if (StringUtils.hasText(payment.getPaymentAccount())) {
+                    String paymentAccount = payment.getPaymentAccount().trim();
+                    
+                    // 检查收款账号是否在数据库中存在
+                    LambdaQueryWrapper<LawFirmPayment> queryWrapper = new LambdaQueryWrapper<>();
+                    queryWrapper.eq(LawFirmPayment::getPaymentAccount, paymentAccount);
+                    queryWrapper.eq(LawFirmPayment::getDeleteFlag, 0);
+                    long count = lawFirmPaymentService.count(queryWrapper);
+                    if (count > 0) {
+                        return R.fail("收款账号[" + paymentAccount + "]在数据库中已存在,不能添加");
+                    }
+                }
+            }
+        }
+        
+        // 第二步:paymentAccount 校验通过后,检查统一社会信用代码是否已存在
+        LawFirm existingLawFirm = null;
+        if (StringUtils.hasText(lawFirm.getCreditCode())) {
+            LambdaQueryWrapper<LawFirm> creditCodeWrapper = new LambdaQueryWrapper<>();
+            creditCodeWrapper.eq(LawFirm::getCreditCode, lawFirm.getCreditCode());
+            creditCodeWrapper.eq(LawFirm::getDeleteFlag, 0);
+            existingLawFirm = this.getOne(creditCodeWrapper);
+        }
+        
+        // existingLawFirm 不为空执行更新主表和子表,否则执行新增主子表操作
+        if (existingLawFirm != null) {
+            // 更新主表和子表操作
+            Integer firmId = existingLawFirm.getId();
+            log.info("统一社会信用代码[{}]已存在,更新主表和子表数据,律所ID={}", lawFirm.getCreditCode(), firmId);
+            
+            // 更新主表数据:将新传入的字段更新到现有记录中
+            lawFirm.setId(firmId);
+            // 保留原有的一些字段(如果新数据没有提供)
+            if (!StringUtils.hasText(lawFirm.getFirmName())) {
+                lawFirm.setFirmName(existingLawFirm.getFirmName());
+            }
+            if (lawFirm.getStatus() == null) {
+                lawFirm.setStatus(existingLawFirm.getStatus());
+            }
+            if (lawFirm.getDeleteFlag() == null) {
+                lawFirm.setDeleteFlag(existingLawFirm.getDeleteFlag());
+            }
+            
+            // 更新主表
+            boolean updateResult = this.updateById(lawFirm);
+            if (!updateResult) {
+                log.error("更新律所主表失败,firmId={}", firmId);
+                return R.fail("更新律所信息失败");
+            }
+            log.info("更新律所主表成功,firmId={}", firmId);
+            
+            // 保存子表数据
+            if (lawFirm.getPaymentList() != null && !lawFirm.getPaymentList().isEmpty()) {
+                for (LawFirmPayment payment : lawFirm.getPaymentList()) {
+                    if (!StringUtils.hasText(payment.getPaymentAccount())) {
+                        continue;
+                    }
+                    
+                    String paymentAccount = payment.getPaymentAccount().trim();
+                    
+                    // 再次检查账号是否已存在(防止并发问题)
+                    LambdaQueryWrapper<LawFirmPayment> checkWrapper = new LambdaQueryWrapper<>();
+                    checkWrapper.eq(LawFirmPayment::getPaymentAccount, paymentAccount);
+                    checkWrapper.eq(LawFirmPayment::getDeleteFlag, 0);
+                    long checkCount = lawFirmPaymentService.count(checkWrapper);
+                    if (checkCount > 0) {
+                        return R.fail("收款账号[" + paymentAccount + "]在数据库中已存在,不能添加");
+                    }
+                    
+                    // 新增收款账号
+                    LawFirmPayment newPayment = new LawFirmPayment();
+                    newPayment.setFirmId(firmId);
+                    newPayment.setPaymentAccount(paymentAccount);
+                    if (StringUtils.hasText(payment.getAddress())) {
+                        newPayment.setAddress(payment.getAddress().trim());
+                    }
+                    newPayment.setDeleteFlag(0);
+                    
+                    boolean saveResult = lawFirmPaymentService.save(newPayment);
+                    if (!saveResult) {
+                        log.error("保存收款账号失败,firmId={}, paymentAccount={}", firmId, paymentAccount);
+                        return R.fail("保存收款账号失败:" + paymentAccount);
+                    }
+                    log.info("保存收款账号成功,firmId={}, paymentAccount={}, id={}", 
+                            firmId, paymentAccount, newPayment.getId());
+                }
+            }
+            
+            // 重新查询更新后的数据返回
+            LawFirm updatedLawFirm = this.getById(firmId);
+            return R.data(updatedLawFirm, "统一社会信用代码已存在,已更新律所信息和收款账号列表");
+        } else {
+            // 新增主子表操作
+            // 保存主表
+            log.info("新增律所,platformCommissionRatio={}", lawFirm.getPlatformCommissionRatio());
+            boolean result = this.save(lawFirm);
+            if (!result) {
+                return R.fail("新增失败");
+            }
+            Integer firmId = lawFirm.getId();
+            
+            // 验证 platformCommissionRatio 是否保存成功
+            LawFirm savedLawFirm = this.getById(firmId);
+            if (savedLawFirm != null) {
+                log.info("保存后的律所,platformCommissionRatio={}", savedLawFirm.getPlatformCommissionRatio());
+            }
+            
+            // 保存子表数据
+            if (lawFirm.getPaymentList() != null && !lawFirm.getPaymentList().isEmpty()) {
+                for (LawFirmPayment payment : lawFirm.getPaymentList()) {
+                    if (!StringUtils.hasText(payment.getPaymentAccount())) {
+                        continue;
+                    }
+                    
+                    String paymentAccount = payment.getPaymentAccount().trim();
+                    
+                    // 再次检查账号是否已存在(防止并发问题)
+                    LambdaQueryWrapper<LawFirmPayment> checkWrapper = new LambdaQueryWrapper<>();
+                    checkWrapper.eq(LawFirmPayment::getPaymentAccount, paymentAccount);
+                    checkWrapper.eq(LawFirmPayment::getDeleteFlag, 0);
+                    long checkCount = lawFirmPaymentService.count(checkWrapper);
+                    if (checkCount > 0) {
+                        return R.fail("收款账号[" + paymentAccount + "]在数据库中已存在,不能添加");
+                    }
+                    
+                    // 创建新的对象,避免污染原对象
+                    LawFirmPayment newPayment = new LawFirmPayment();
+                    newPayment.setFirmId(firmId);
+                    newPayment.setPaymentAccount(paymentAccount);
+                    if (StringUtils.hasText(payment.getAddress())) {
+                        newPayment.setAddress(payment.getAddress().trim());
+                    }
+                    newPayment.setDeleteFlag(0);
+
+                    boolean saveResult = lawFirmPaymentService.save(newPayment);
+                    if (!saveResult) {
+                        log.error("保存收款账号失败,firmId={}, paymentAccount={}", firmId, paymentAccount);
+                        return R.fail("保存收款账号失败:" + paymentAccount);
+                    }
+                    log.info("保存收款账号成功,firmId={}, paymentAccount={}, id={}", 
+                            firmId, paymentAccount, newPayment.getId());
+                }
+            }
+            
+            return R.data(lawFirm);
+        }
+    }
+
+    @Override
+    public R<LawFirm> editLawFirm(LawFirm lawFirm) {
+        log.info("LawFirmServiceImpl.editLawFirm?lawFirm={}", lawFirm);
+        
+        // 校验律所ID
+        if (lawFirm.getId() == null) {
+            return R.fail("律所ID不能为空");
+        }
+        
+        // 检查律所是否存在
+        LawFirm existingLawFirm = this.getById(lawFirm.getId());
+        if (existingLawFirm == null || existingLawFirm.getDeleteFlag() == 1) {
+            return R.fail("律所不存在");
+        }
+        
+        // 第一步:先校验收款账号,如果 paymentAccount 在数据库中存在,不让新增或修改
+        if (lawFirm.getPaymentList() != null && !lawFirm.getPaymentList().isEmpty()) {
+            // 查询当前律所下现有的收款账号记录(用于判断是新增还是更新)
+            LambdaQueryWrapper<LawFirmPayment> existingWrapper = new LambdaQueryWrapper<>();
+            existingWrapper.eq(LawFirmPayment::getFirmId, lawFirm.getId());
+            existingWrapper.eq(LawFirmPayment::getDeleteFlag, 0);
+            List<LawFirmPayment> existingPayments = lawFirmPaymentService.list(existingWrapper);
+            Map<Integer, String> existingPaymentMap = existingPayments.stream()
+                    .collect(Collectors.toMap(LawFirmPayment::getId, LawFirmPayment::getPaymentAccount));
+            
+            for (LawFirmPayment payment : lawFirm.getPaymentList()) {
+                if (StringUtils.hasText(payment.getPaymentAccount())) {
+                    String paymentAccount = payment.getPaymentAccount().trim();
+                    
+                    // 如果是更新操作,检查账号是否被修改
+                    if (payment.getId() != null) {
+                        String originalAccount = existingPaymentMap.get(payment.getId());
+                        // 如果账号被修改了,需要检查新账号是否已存在
+                        if (originalAccount != null && !originalAccount.equals(paymentAccount)) {
+                            // 检查新账号是否在数据库中存在(排除当前记录)
+                            LambdaQueryWrapper<LawFirmPayment> queryWrapper = new LambdaQueryWrapper<>();
+                            queryWrapper.eq(LawFirmPayment::getPaymentAccount, paymentAccount);
+                            queryWrapper.eq(LawFirmPayment::getDeleteFlag, 0);
+                            queryWrapper.ne(LawFirmPayment::getId, payment.getId());
+                            long count = lawFirmPaymentService.count(queryWrapper);
+                            if (count > 0) {
+                                return R.fail("新收款账号数据库已存在");
+                            }
+                        }
+                    } else {
+                        // 新增操作:检查账号是否在数据库中存在
+                        LambdaQueryWrapper<LawFirmPayment> queryWrapper = new LambdaQueryWrapper<>();
+                        queryWrapper.eq(LawFirmPayment::getPaymentAccount, paymentAccount);
+                        queryWrapper.eq(LawFirmPayment::getDeleteFlag, 0);
+                        long count = lawFirmPaymentService.count(queryWrapper);
+                        if (count > 0) {
+                            return R.fail("收款账号[" + paymentAccount + "]在数据库中已存在,不能添加");
+                        }
+                    }
+                }
+            }
+        }
+        
+        // 第二步:更新主表
+        boolean result = this.updateById(lawFirm);
+        if (!result) {
+        return R.fail("修改失败");
+        }
+        
+        // 第三步:处理子表数据
+        if (lawFirm.getPaymentList() != null) {
+            // 查询现有的收款账号记录
+            LambdaQueryWrapper<LawFirmPayment> existingWrapper = new LambdaQueryWrapper<>();
+            existingWrapper.eq(LawFirmPayment::getFirmId, lawFirm.getId());
+            existingWrapper.eq(LawFirmPayment::getDeleteFlag, 0);
+            List<LawFirmPayment> existingPayments = lawFirmPaymentService.list(existingWrapper);
+            
+            // 收集前端传来的收款账号ID
+            Set<Integer> incomingIds = lawFirm.getPaymentList().stream()
+                    .filter(p -> p.getId() != null)
+                    .map(LawFirmPayment::getId)
+                    .collect(Collectors.toSet());
+            
+            // 删除不在前端列表中的记录(逻辑删除)
+            for (LawFirmPayment existing : existingPayments) {
+                if (!incomingIds.contains(existing.getId())) {
+                    existing.setDeleteFlag(1);
+                    lawFirmPaymentService.updateById(existing);
+                }
+            }
+            
+            // 新增或更新收款账号记录
+            for (LawFirmPayment payment : lawFirm.getPaymentList()) {
+                if (!StringUtils.hasText(payment.getPaymentAccount())) {
+                    continue;
+                }
+                
+                String paymentAccount = payment.getPaymentAccount().trim();
+                
+                try {
+                    if (payment.getId() != null) {
+                        // 更新:获取原始记录
+                        LawFirmPayment existingPayment = existingPayments.stream()
+                                .filter(p -> p.getId().equals(payment.getId()))
+                                .findFirst()
+                                .orElse(null);
+                        
+                        if (existingPayment != null) {
+                            String originalAccount = existingPayment.getPaymentAccount();
+                            
+                            // 如果 paymentAccount 被修改了,不更新 paymentAccount 字段,保持原值
+                            LawFirmPayment updatePayment = new LawFirmPayment();
+                            updatePayment.setId(payment.getId());
+                            updatePayment.setFirmId(lawFirm.getId());
+                            
+                            // 如果账号被修改了,保持原值;否则使用新值
+                            if (!originalAccount.equals(paymentAccount)) {
+                                updatePayment.setPaymentAccount(originalAccount); // 保持原值
+                            } else {
+                                updatePayment.setPaymentAccount(paymentAccount);
+                            }
+                            
+                            // 更新其他字段
+                            if (StringUtils.hasText(payment.getAddress())) {
+                                updatePayment.setAddress(payment.getAddress().trim());
+                            } else {
+                                updatePayment.setAddress(existingPayment.getAddress());
+                            }
+                            
+                            boolean updateResult = lawFirmPaymentService.updateById(updatePayment);
+                            if (!updateResult) {
+                                log.error("更新收款账号失败,firmId={}, paymentAccount={}, id={}", 
+                                        lawFirm.getId(), paymentAccount, payment.getId());
+                                return R.fail("更新收款账号失败:" + paymentAccount);
+                            }
+                            log.info("更新收款账号成功,firmId={}, paymentAccount={}, id={}", 
+                                    lawFirm.getId(), existingPayment.getPaymentAccount(), updatePayment.getId());
+                        }
+                    } else {
+                        // 新增:再次检查账号是否已存在(防止并发问题)
+                        LambdaQueryWrapper<LawFirmPayment> checkWrapper = new LambdaQueryWrapper<>();
+                        checkWrapper.eq(LawFirmPayment::getPaymentAccount, paymentAccount);
+                        checkWrapper.eq(LawFirmPayment::getDeleteFlag, 0);
+                        long checkCount = lawFirmPaymentService.count(checkWrapper);
+                        if (checkCount > 0) {
+                            return R.fail("收款账号[" + paymentAccount + "]在数据库中已存在,不能添加");
+                        }
+                        
+                        // 新增收款账号
+                        LawFirmPayment newPayment = new LawFirmPayment();
+                        newPayment.setFirmId(lawFirm.getId());
+                        newPayment.setPaymentAccount(paymentAccount);
+                        if (StringUtils.hasText(payment.getAddress())) {
+                            newPayment.setAddress(payment.getAddress().trim());
+                        }
+                        newPayment.setDeleteFlag(0);
+                        
+                        boolean saveResult = lawFirmPaymentService.save(newPayment);
+                        if (!saveResult) {
+                            log.error("保存收款账号失败,firmId={}, paymentAccount={}", lawFirm.getId(), paymentAccount);
+                            return R.fail("保存收款账号失败:" + paymentAccount);
+                        }
+                        log.info("保存收款账号成功,firmId={}, paymentAccount={}, id={}", 
+                                lawFirm.getId(), paymentAccount, newPayment.getId());
+                    }
+                } catch (Exception e) {
+                    log.error("处理收款账号异常,firmId={}, paymentAccount={}", lawFirm.getId(), paymentAccount, e);
+                    return R.fail("处理收款账号异常:" + e.getMessage());
+                }
+            }
+        }
+        
+        return R.data(lawFirm);
+    }
+
+    @Override
+    public R<Boolean> deleteLawFirm(Integer id) {
+        log.info("LawFirmServiceImpl.deleteLawFirm?paymentId={}", id);
+        
+        // 1. 根据律所子表id查询子表记录
+        LawFirmPayment payment = lawFirmPaymentService.getById(id);
+        if (payment == null || payment.getDeleteFlag() == 1) {
+            return R.fail("收款账号记录不存在或已被删除");
+        }
+        
+        Integer firmId = payment.getFirmId();
+        if (firmId == null) {
+            return R.fail("律所ID为空,无法删除");
+        }
+        
+        // 2. 删除子表记录(逻辑删除)
+        boolean deletePaymentResult = lawFirmPaymentService.removeById(id);
+        if (!deletePaymentResult) {
+            return R.fail("删除收款账号失败");
+        }
+        log.info("删除收款账号成功,paymentId={}, firmId={}", id, firmId);
+        
+        // 3. 查询该律所是否还有关联的子表(未删除的)
+        LambdaQueryWrapper<LawFirmPayment> paymentWrapper = new LambdaQueryWrapper<>();
+        paymentWrapper.eq(LawFirmPayment::getFirmId, firmId);
+        paymentWrapper.eq(LawFirmPayment::getDeleteFlag, 0);
+        long remainingPaymentCount = lawFirmPaymentService.count(paymentWrapper);
+        
+        // 4. 如果没有子表了,继续删除主表(逻辑删除)
+        if (remainingPaymentCount == 0) {
+            log.info("律所[{}]没有关联的收款账号了,删除主表", firmId);
+            boolean deleteFirmResult = this.removeById(firmId);
+            if (!deleteFirmResult) {
+                log.warn("删除律所主表失败,firmId={}", firmId);
+                return R.success("收款账号删除成功,但删除律所主表失败");
+            }
+            log.info("删除律所主表成功,firmId={}", firmId);
+            return R.success("删除成功,已同时删除律所主表");
+        } else {
+            log.info("律所[{}]还有{}个收款账号,保留主表", firmId, remainingPaymentCount);
+            return R.success("删除成功");
+        }
+    }
+
+    @Override
+    public R<LawFirm> register(LawFirm lawFirm) {
+        log.info("LawFirmServiceImpl.register?lawFirm={}", lawFirm);
+        
+        // 校验必填字段
+        if (!StringUtils.hasText(lawFirm.getFirmName())) {
+            return R.fail("律所名称不能为空");
+        }
+        if (!StringUtils.hasText(lawFirm.getCreditCode())) {
+            return R.fail("统一社会信用代码不能为空");
+        }
+        if (!StringUtils.hasText(lawFirm.getPhone())) {
+            return R.fail("联系电话不能为空");
+        }
+        if (!StringUtils.hasText(lawFirm.getDirectorName())) {
+            return R.fail("负责人姓名不能为空");
+        }
+        if (!StringUtils.hasText(lawFirm.getDirectorPhone())) {
+            return R.fail("负责人电话不能为空");
+        }
+        
+        // 校验统一社会信用代码是否已存在
+        LambdaQueryWrapper<LawFirm> creditCodeWrapper = new LambdaQueryWrapper<>();
+        creditCodeWrapper.eq(LawFirm::getCreditCode, lawFirm.getCreditCode());
+        creditCodeWrapper.eq(LawFirm::getDeleteFlag, 0);
+        long creditCodeCount = this.count(creditCodeWrapper);
+        if (creditCodeCount > 0) {
+            return R.fail("该统一社会信用代码已注册,请勿重复注册");
+        }
+        
+        // 校验联系电话是否已存在
+        if (StringUtils.hasText(lawFirm.getPhone())) {
+            LambdaQueryWrapper<LawFirm> phoneWrapper = new LambdaQueryWrapper<>();
+            phoneWrapper.eq(LawFirm::getPhone, lawFirm.getPhone());
+            phoneWrapper.eq(LawFirm::getDeleteFlag, 0);
+            long phoneCount = this.count(phoneWrapper);
+            if (phoneCount > 0) {
+                return R.fail("该联系电话已被使用,请更换其他号码");
+            }
+        }
+        
+        // 校验负责人电话是否已存在
+        if (StringUtils.hasText(lawFirm.getDirectorPhone())) {
+            LambdaQueryWrapper<LawFirm> directorPhoneWrapper = new LambdaQueryWrapper<>();
+            directorPhoneWrapper.eq(LawFirm::getDirectorPhone, lawFirm.getDirectorPhone());
+            directorPhoneWrapper.eq(LawFirm::getDeleteFlag, 0);
+            long directorPhoneCount = this.count(directorPhoneWrapper);
+            if (directorPhoneCount > 0) {
+                return R.fail("该负责人电话已被使用,请更换其他号码");
+            }
+        }
+        
+        // 校验邮箱是否已存在
+        if (StringUtils.hasText(lawFirm.getEmail())) {
+            LambdaQueryWrapper<LawFirm> emailWrapper = new LambdaQueryWrapper<>();
+            emailWrapper.eq(LawFirm::getEmail, lawFirm.getEmail());
+            emailWrapper.eq(LawFirm::getDeleteFlag, 0);
+            long emailCount = this.count(emailWrapper);
+            if (emailCount > 0) {
+                return R.fail("该邮箱已被使用,请更换其他邮箱");
+            }
+        }
+        
+        // 设置注册时的初始状态
+        lawFirm.setStatus(1); // 启用状态
+        lawFirm.setCertificationStatus(0); // 未认证状态
+        lawFirm.setDeleteFlag(0); // 未删除
+        lawFirm.setIsRecommended(0); // 默认不推荐
+        if (lawFirm.getLawyerCount() == null) {
+            lawFirm.setLawyerCount(0);
+        }
+        if (lawFirm.getPartnerCount() == null) {
+            lawFirm.setPartnerCount(0);
+        }
+        
+        // 保存律所信息
+        boolean result = this.save(lawFirm);
+        if (result) {
+            return R.data(lawFirm, "注册成功,请等待审核");
+        }
+        return R.fail("注册失败,请稍后重试");
+    }
+
+    @Override
+    public void exportLawFirm(HttpServletResponse response, Integer firmId, Integer pageNum, Integer pageSize) throws IOException {
+        log.info("LawFirmServiceImpl.exportLawFirm?firmId={},pageNum={},pageSize={}", firmId, pageNum, pageSize);
+        try {
+            // 如果 pageSize 为 null,则导出全部数据;否则使用分页
+            int currentPage = (pageNum != null && pageNum > 0) ? pageNum : 1;
+            int size = (pageSize != null && pageSize > 0) ? pageSize : Integer.MAX_VALUE;
+            
+            Page<LawFirmPaymentVO> page = new Page<>(currentPage, size);
+            IPage<LawFirmPaymentVO> pageResult = lawFirmPaymentMapper.selectPaymentPageWithFirm(
+                    page, firmId, null, null, null, null, null, null, null, null
+            );
+
+            List<LawFirmPaymentVO> paymentList = pageResult.getRecords();
+
+            if (paymentList == null || paymentList.isEmpty()) {
+                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+                response.getWriter().write("暂无数据可导出");
+                return;
+            }
+
+            // 使用 EasyExcelUtil 导出
+            EasyExcelUtil.exportExcel(response, paymentList, LawFirmPaymentVO.class, "律所列表", "律所列表");
+        } catch (Exception e) {
+            log.error("导出律所数据失败", e);
+            if (!response.isCommitted()) {
+                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                response.getWriter().write("导出失败:" + e.getMessage());
+            }
+            throw new IOException("导出失败:" + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public R<String> importLawFirm(MultipartFile file) {
+        log.info("LawFirmServiceImpl.importLawFirm fileName={}", file.getOriginalFilename());
+        try {
+            if (file == null || file.isEmpty()) {
+                return R.fail("文件不能为空");
+            }
+
+            String fileName = file.getOriginalFilename();
+            if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) {
+                return R.fail("文件格式不正确,请上传Excel文件");
+            }
+
+            List<LawFirmExcelVo> excelVoList = new ArrayList<>();
+            List<String> errorMessages = new ArrayList<>();
+
+            // 使用POI读取Excel
+            try (InputStream inputStream = file.getInputStream();
+                 Workbook workbook = new XSSFWorkbook(inputStream)) {
+                Sheet sheet = workbook.getSheetAt(0);
+
+                // 获取表头
+                Row headerRow = sheet.getRow(0);
+                if (headerRow == null) {
+                    return R.fail("Excel文件格式不正确,缺少表头");
+                }
+
+                // 构建字段映射(表头名称 -> 列索引)
+                Map<String, Integer> headerMap = new HashMap<>();
+                Field[] fields = LawFirmExcelVo.class.getDeclaredFields();
+                for (int i = 0; i < headerRow.getLastCellNum(); i++) {
+                    Cell cell = headerRow.getCell(i);
+                    if (cell != null) {
+                        String headerName = cell.getStringCellValue().trim();
+                        headerMap.put(headerName, i);
+                    }
+                }
+
+                // 读取数据行
+                for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
+                    Row row = sheet.getRow(rowIndex);
+                    if (row == null) {
+                        continue;
+                    }
+
+                    // 检查是否为空行
+                    boolean isEmptyRow = true;
+                    for (int i = 0; i < row.getLastCellNum(); i++) {
+                        Cell cell = row.getCell(i);
+                        if (cell != null && cell.getCellType() != CellType.BLANK) {
+                            isEmptyRow = false;
+                            break;
+                        }
+                    }
+                    if (isEmptyRow) {
+                        continue;
+                    }
+
+                    LawFirmExcelVo excelVo = new LawFirmExcelVo();
+
+                    // 读取每个字段
+                    for (Field field : fields) {
+                        if (!field.isAnnotationPresent(ExcelHeader.class)) {
+                            continue;
+                        }
+                        ExcelHeader excelHeader = field.getAnnotation(ExcelHeader.class);
+                        String headerName = excelHeader.value();
+                        Integer colIndex = headerMap.get(headerName);
+                        if (colIndex == null) {
+                            continue;
+                        }
+
+                        Cell cell = row.getCell(colIndex);
+                        if (cell == null) {
+                            continue;
+                        }
+
+                        field.setAccessible(true);
+                        try {
+                            String cellValue = getCellValueAsString(cell);
+                            if (cellValue != null && !cellValue.trim().isEmpty()) {
+                                // 根据字段类型设置值
+                                Class<?> fieldType = field.getType();
+                                if (fieldType == String.class) {
+                                    field.set(excelVo, cellValue.trim());
+                                } else if (fieldType == Integer.class) {
+                                    try {
+                                        field.set(excelVo, Integer.parseInt(cellValue.trim()));
+                                    } catch (NumberFormatException e) {
+                                        // 忽略
+                                    }
+                                }
+                            }
+                        } catch (Exception e) {
+                            log.warn("读取字段{}失败:{}", headerName, e.getMessage());
+                        }
+                    }
+
+                    // 校验数据
+                    String errorMsg = validateExcelData(excelVo, rowIndex + 1);
+                    if (errorMsg != null) {
+                        errorMessages.add(errorMsg);
+                    } else {
+                        excelVoList.add(excelVo);
+                    }
+                }
+            }
+
+            if (!errorMessages.isEmpty()) {
+                return R.fail("数据校验失败:\n" + String.join("\n", errorMessages));
+            }
+
+            if (excelVoList.isEmpty()) {
+                return R.fail("Excel文件中没有有效数据");
+            }
+
+            // 收集所有导入数据中的收款账号(用于检查重复)
+            Set<String> allImportAccounts = new HashSet<>();
+            Map<String, String> accountToFirmNameMap = new HashMap<>(); // 账号 -> 律所名称,用于错误提示
+
+            // 先收集所有账号,检查导入数据中的重复(不允许逗号分隔)
+            for (LawFirmExcelVo excelVo : excelVoList) {
+                String paymentAccount = excelVo.getPaymentAccount();
+                if (StringUtils.hasText(paymentAccount)) {
+                    String trimmedAccount = paymentAccount.trim();
+                    if (!trimmedAccount.isEmpty()) {
+                        if (allImportAccounts.contains(trimmedAccount)) {
+                            // 发现重复账号
+                            String existingFirmName = accountToFirmNameMap.get(trimmedAccount);
+                            return R.fail(String.format("导入数据中存在重复的收款账号[%s],律所[%s]和律所[%s]使用了相同的账号,无法导入",
+                                    trimmedAccount, existingFirmName, excelVo.getFirmName()));
+                        }
+                        allImportAccounts.add(trimmedAccount);
+                        accountToFirmNameMap.put(trimmedAccount, excelVo.getFirmName());
+                    }
+                }
+            }
+
+            // 检查数据库中是否已存在这些账号
+            if (!allImportAccounts.isEmpty()) {
+                // 查询数据库中所有已存在的收款账号(从子表查询)
+                LambdaQueryWrapper<LawFirmPayment> paymentWrapper = new LambdaQueryWrapper<>();
+                paymentWrapper.eq(LawFirmPayment::getDeleteFlag, 0);
+                paymentWrapper.isNotNull(LawFirmPayment::getPaymentAccount);
+                paymentWrapper.ne(LawFirmPayment::getPaymentAccount, "");
+                List<LawFirmPayment> existingPayments = lawFirmPaymentService.list(paymentWrapper);
+
+                // 收集数据库中所有已存在的账号
+                Set<String> existingAccounts = new HashSet<>();
+                for (LawFirmPayment payment : existingPayments) {
+                    String paymentAccount = payment.getPaymentAccount();
+                    if (StringUtils.hasText(paymentAccount)) {
+                        existingAccounts.add(paymentAccount.trim());
+                    }
+                }
+
+                // 检查导入的账号是否已存在
+                Set<String> duplicateAccounts = new HashSet<>();
+                for (String importAccount : allImportAccounts) {
+                    if (existingAccounts.contains(importAccount)) {
+                        duplicateAccounts.add(importAccount);
+                    }
+                }
+
+                if (!duplicateAccounts.isEmpty()) {
+                    String duplicateAccountList = String.join("、", duplicateAccounts);
+                    return R.fail(String.format("收款账号[%s]在数据库中已存在,无法导入。请检查并修改账号后重新导入", duplicateAccountList));
+                }
+            }
+
+            // 按统一社会信用代码分组,合并数据
+            // 先检查是否有空的信用代码
+            for (LawFirmExcelVo excelVo : excelVoList) {
+                if (!StringUtils.hasText(excelVo.getCreditCode())) {
+                    return R.fail("导入数据中存在空的统一社会信用代码,请检查第" + (excelVoList.indexOf(excelVo) + 2) + "行数据");
+                }
+            }
+
+            // 按统一社会信用代码分组
+            Map<String, List<LawFirmExcelVo>> creditCodeMap = excelVoList.stream()
+                    .filter(vo -> StringUtils.hasText(vo.getCreditCode()))
+                    .collect(Collectors.groupingBy(LawFirmExcelVo::getCreditCode));
+
+            int successCount = 0;
+            int failCount = 0;
+            int updateCount = 0;
+            List<String> importErrors = new ArrayList<>();
+
+            for (Map.Entry<String, List<LawFirmExcelVo>> entry : creditCodeMap.entrySet()) {
+                String creditCode = entry.getKey();
+                List<LawFirmExcelVo> voList = entry.getValue();
+
+                // 合并收款账号,并维护账号和地址的对应关系(不允许逗号分隔)
+                // 使用LinkedHashMap保持顺序,后出现的账号地址会覆盖先出现的(如果地址不为空)
+                Map<String, String> accountAddressMap = new LinkedHashMap<>();
+                for (LawFirmExcelVo vo : voList) {
+                    String paymentAccount = vo.getPaymentAccount();
+                    String address = vo.getAddress();
+                    if (StringUtils.hasText(paymentAccount)) {
+                        String trimmedAccount = paymentAccount.trim();
+                        if (!trimmedAccount.isEmpty()) {
+                            // 如果账号已存在但地址为空,且当前地址不为空,则更新地址
+                            // 如果账号不存在,直接添加
+                            if (!accountAddressMap.containsKey(trimmedAccount) || 
+                                (StringUtils.hasText(address) && !StringUtils.hasText(accountAddressMap.get(trimmedAccount)))) {
+                                accountAddressMap.put(trimmedAccount, address != null ? address.trim() : "");
+                            }
+                        }
+                    }
+                }
+
+                // 使用第一个数据作为主数据,其他数据补充缺失字段
+                LawFirmExcelVo firstVo = voList.get(0);
+                LawFirm lawFirm = convertToLawFirm(firstVo);
+                
+                // 如果第一个数据缺少某些字段,尝试从其他数据中获取
+                for (int i = 1; i < voList.size(); i++) {
+                    LawFirmExcelVo otherVo = voList.get(i);
+                    // 补充缺失的字段(这里可以根据需要补充其他字段)
+                    if (!StringUtils.hasText(lawFirm.getFirmName()) && StringUtils.hasText(otherVo.getFirmName())) {
+                        lawFirm.setFirmName(otherVo.getFirmName());
+                    }
+                    if (!StringUtils.hasText(lawFirm.getPhone()) && StringUtils.hasText(otherVo.getPhone())) {
+                        lawFirm.setPhone(otherVo.getPhone());
+                    }
+                    if (!StringUtils.hasText(lawFirm.getDirectorName()) && StringUtils.hasText(otherVo.getDirectorName())) {
+                        lawFirm.setDirectorName(otherVo.getDirectorName());
+                    }
+                    if (!StringUtils.hasText(lawFirm.getDirectorPhone()) && StringUtils.hasText(otherVo.getDirectorPhone())) {
+                        lawFirm.setDirectorPhone(otherVo.getDirectorPhone());
+                    }
+                }
+
+                // 检查统一社会信用代码是否已存在
+                LambdaQueryWrapper<LawFirm> creditCodeWrapper = new LambdaQueryWrapper<>();
+                creditCodeWrapper.eq(LawFirm::getCreditCode, creditCode);
+                creditCodeWrapper.eq(LawFirm::getDeleteFlag, 0);
+                LawFirm existingLawFirm = this.getOne(creditCodeWrapper);
+                
+                boolean isUpdate = false;
+                if (existingLawFirm != null) {
+                    // 如果已存在,更新现有记录
+                    isUpdate = true;
+                    lawFirm.setId(existingLawFirm.getId());
+                    // 保留原有的一些字段(如果新数据没有提供)
+                    if (!StringUtils.hasText(lawFirm.getFirmName())) {
+                        lawFirm.setFirmName(existingLawFirm.getFirmName());
+                    }
+                    if (!StringUtils.hasText(lawFirm.getPhone())) {
+                        lawFirm.setPhone(existingLawFirm.getPhone());
+                    }
+                    if (lawFirm.getStatus() == null) {
+                        lawFirm.setStatus(existingLawFirm.getStatus());
+                    }
+                } else {
+                    // 设置默认值(新增时)
+                if (lawFirm.getStatus() == null) {
+                    lawFirm.setStatus(1);
+                }
+                }
+                
+                if (lawFirm.getDeleteFlag() == null) {
+                    lawFirm.setDeleteFlag(0);
+                }
+                
+                // 保存或更新主表
+                boolean saveResult;
+                if (isUpdate) {
+                    saveResult = this.updateById(lawFirm);
+                } else {
+                    saveResult = this.save(lawFirm);
+                }
+                
+                if (!saveResult) {
+                    importErrors.add("统一社会信用代码[" + creditCode + "]" + (isUpdate ? "更新" : "保存") + "失败");
+                    failCount++;
+                    continue;
+                }
+                
+                // 处理子表收款账号
+                if (!accountAddressMap.isEmpty()) {
+                    // 查询现有的收款账号
+                    LambdaQueryWrapper<LawFirmPayment> existingPaymentWrapper = new LambdaQueryWrapper<>();
+                    existingPaymentWrapper.eq(LawFirmPayment::getFirmId, lawFirm.getId());
+                    existingPaymentWrapper.eq(LawFirmPayment::getDeleteFlag, 0);
+                    List<LawFirmPayment> existingPayments = lawFirmPaymentService.list(existingPaymentWrapper);
+                    
+                    // 收集现有的收款账号
+                    Set<String> existingAccountSet = existingPayments.stream()
+                            .map(LawFirmPayment::getPaymentAccount)
+                            .filter(Objects::nonNull)
+                            .filter(acc -> !acc.trim().isEmpty())
+                            .collect(Collectors.toSet());
+                    
+                    // 添加新的收款账号(排除已存在的),每个账号使用其对应的地址
+                    for (Map.Entry<String, String> accountEntry : accountAddressMap.entrySet()) {
+                        String trimmedAccount = accountEntry.getKey();
+                        String address = accountEntry.getValue();
+                        
+                        if (!trimmedAccount.isEmpty() && !existingAccountSet.contains(trimmedAccount)) {
+                            // 检查账号是否已存在(全局唯一性校验)
+                            LambdaQueryWrapper<LawFirmPayment> accountWrapper = new LambdaQueryWrapper<>();
+                            accountWrapper.eq(LawFirmPayment::getPaymentAccount, trimmedAccount);
+                            accountWrapper.eq(LawFirmPayment::getDeleteFlag, 0);
+                            long accountCount = lawFirmPaymentService.count(accountWrapper);
+                            if (accountCount == 0) {
+                                // 账号不存在,可以添加
+                                LawFirmPayment payment = new LawFirmPayment();
+                                payment.setFirmId(lawFirm.getId());
+                                payment.setPaymentAccount(trimmedAccount);
+                                // 设置详细地址(使用该账号对应的地址)
+                                if (StringUtils.hasText(address)) {
+                                    payment.setAddress(address.trim());
+                                }
+                                payment.setDeleteFlag(0);
+                                lawFirmPaymentService.save(payment);
+                            } else {
+                                // 账号已存在,记录警告但不阻止导入
+                                log.warn("收款账号[{}]已存在,跳过添加", trimmedAccount);
+                            }
+                        }
+                    }
+                }
+
+                if (isUpdate) {
+                    updateCount++;
+                } else {
+                    successCount++;
+                }
+            }
+
+            String resultMsg = String.format("导入完成:新增%d条,更新%d条,失败%d条", successCount, updateCount, failCount);
+            if (!importErrors.isEmpty()) {
+                resultMsg += "\n失败详情:\n" + String.join("\n", importErrors);
+            }
+
+            return R.success(resultMsg);
+        } catch (Exception e) {
+            log.error("导入律所数据失败", e);
+            return R.fail("导入失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 生成带合并单元格的Excel
+     */
+    private void generateExcelWithMerge(HttpServletResponse response, List<LawFirmExcelVo> excelVoList,
+                                        Map<String, List<LawFirm>> firmNameMap) throws IOException {
+        XSSFWorkbook workbook = new XSSFWorkbook();
+        Sheet sheet = workbook.createSheet("律所列表");
+
+        // 获取字段
+        Field[] fields = LawFirmExcelVo.class.getDeclaredFields();
+        List<Field> excelFields = Arrays.stream(fields)
+                .filter(f -> f.isAnnotationPresent(ExcelHeader.class))
+                .collect(Collectors.toList());
+
+        // 创建表头样式
+        Font headerFont = workbook.createFont();
+        headerFont.setBold(true);
+        CellStyle headerCellStyle = workbook.createCellStyle();
+        headerCellStyle.setFont(headerFont);
+        headerCellStyle.setAlignment(HorizontalAlignment.CENTER);
+        headerCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+
+        // 创建数据样式
+        CellStyle dataCellStyle = workbook.createCellStyle();
+        dataCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        dataCellStyle.setWrapText(true);
+
+        // 创建表头
+        Row headerRow = sheet.createRow(0);
+        for (int i = 0; i < excelFields.size(); i++) {
+            Field field = excelFields.get(i);
+            ExcelHeader excelHeader = field.getAnnotation(ExcelHeader.class);
+            Cell cell = headerRow.createCell(i);
+            cell.setCellValue(excelHeader.value());
+            cell.setCellStyle(headerCellStyle);
+        }
+
+        // 填充数据
+        int rowNum = 1;
+        Map<String, Integer> firmNameRowMap = new HashMap<>(); // 记录每个律所名称的第一行
+        Map<String, Integer> firmNameCountMap = new HashMap<>(); // 记录每个律所名称的行数
+
+        for (LawFirmExcelVo excelVo : excelVoList) {
+            Row row = sheet.createRow(rowNum);
+            String firmName = excelVo.getFirmName();
+
+            if (!firmNameRowMap.containsKey(firmName)) {
+                firmNameRowMap.put(firmName, rowNum);
+                // 计算该律所名称有多少行
+                long count = excelVoList.stream()
+                        .filter(vo -> firmName.equals(vo.getFirmName()))
+                        .count();
+                firmNameCountMap.put(firmName, (int) count);
+            }
+
+            for (int i = 0; i < excelFields.size(); i++) {
+                Field field = excelFields.get(i);
+                field.setAccessible(true);
+                Cell cell = row.createCell(i);
+                cell.setCellStyle(dataCellStyle);
+                try {
+                    Object value = field.get(excelVo);
+                    if (value != null) {
+                        cell.setCellValue(value.toString());
+                    }
+                } catch (IllegalAccessException e) {
+                    log.error("获取字段值失败", e);
+                }
+            }
+            rowNum++;
+        }
+
+        // 合并相同律所名称的单元格(除了账号列)
+        int firmNameColIndex = -1;
+        int paymentAccountColIndex = -1;
+        for (int i = 0; i < excelFields.size(); i++) {
+            Field field = excelFields.get(i);
+            ExcelHeader excelHeader = field.getAnnotation(ExcelHeader.class);
+            if ("律所名称".equals(excelHeader.value())) {
+                firmNameColIndex = i;
+            } else if ("律师事务所收款账号".equals(excelHeader.value())) {
+                paymentAccountColIndex = i;
+            }
+        }
+
+        // 合并律所名称列
+        if (firmNameColIndex >= 0) {
+            for (Map.Entry<String, Integer> entry : firmNameRowMap.entrySet()) {
+                String firmName = entry.getKey();
+                int firstRow = entry.getValue();
+                int rowCount = firmNameCountMap.get(firmName);
+                if (rowCount > 1) {
+                    sheet.addMergedRegion(new CellRangeAddress(firstRow, firstRow + rowCount - 1, firmNameColIndex, firmNameColIndex));
+                }
+            }
+        }
+
+        // 合并其他列(除了账号列)
+        for (int i = 0; i < excelFields.size(); i++) {
+            if (i == firmNameColIndex || i == paymentAccountColIndex) {
+                continue;
+            }
+            for (Map.Entry<String, Integer> entry : firmNameRowMap.entrySet()) {
+                String firmName = entry.getKey();
+                int firstRow = entry.getValue();
+                int rowCount = firmNameCountMap.get(firmName);
+                if (rowCount > 1) {
+                    sheet.addMergedRegion(new CellRangeAddress(firstRow, firstRow + rowCount - 1, i, i));
+                }
+            }
+        }
+
+        // 设置列宽
+        for (int i = 0; i < excelFields.size(); i++) {
+            sheet.setColumnWidth(i, 20 * 256);
+        }
+
+        // 设置响应头
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setCharacterEncoding("utf-8");
+        String fileName = URLEncoder.encode("律所列表", "UTF-8").replaceAll("\\+", "%20");
+        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
+
+        // 输出(不要关闭 response 的输出流,由容器管理)
+        OutputStream outputStream = response.getOutputStream();
+        workbook.write(outputStream);
+        outputStream.flush();
+        workbook.close();
+    }
+
+    /**
+     * 转换为ExcelVo
+     */
+    private LawFirmExcelVo convertToExcelVo(LawFirm lawFirm, int serialNumber,
+                                           SimpleDateFormat dateFormat, SimpleDateFormat dateTimeFormat) {
+        LawFirmExcelVo excelVo = new LawFirmExcelVo();
+        BeanUtils.copyProperties(lawFirm, excelVo);
+        excelVo.setSerialNumber(serialNumber);
+
+        // 格式化日期
+        if (lawFirm.getEstablishmentDate() != null) {
+            excelVo.setEstablishmentDate(dateFormat.format(lawFirm.getEstablishmentDate()));
+        }
+
+        // 格式化类型
+        if (lawFirm.getFirmType() != null) {
+            switch (lawFirm.getFirmType()) {
+                case 1:
+                    excelVo.setFirmType("合伙制");
+                    break;
+                case 2:
+                    excelVo.setFirmType("个人制");
+                    break;
+                case 3:
+                    excelVo.setFirmType("国资制");
+                    break;
+            }
+        }
+
+        // 格式化规模
+        if (lawFirm.getFirmScale() != null) {
+            switch (lawFirm.getFirmScale()) {
+                case 1:
+                    excelVo.setFirmScale("小型(1-10人)");
+                    break;
+                case 2:
+                    excelVo.setFirmScale("中型(11-50人)");
+                    break;
+                case 3:
+                    excelVo.setFirmScale("大型(51-200人)");
+                    break;
+                case 4:
+                    excelVo.setFirmScale("超大型(200人以上)");
+                    break;
+            }
+        }
+
+        // 格式化状态
+        if (lawFirm.getStatus() != null) {
+            excelVo.setStatus(lawFirm.getStatus() == 1 ? "启用" : "禁用");
+        }
+
+        // 格式化认证状态
+        if (lawFirm.getCertificationStatus() != null) {
+            switch (lawFirm.getCertificationStatus()) {
+                case 0:
+                    excelVo.setCertificationStatus("未认证");
+                    break;
+                case 1:
+                    excelVo.setCertificationStatus("认证中");
+                    break;
+                case 2:
+                    excelVo.setCertificationStatus("已认证");
+                    break;
+                case 3:
+                    excelVo.setCertificationStatus("认证失败");
+                    break;
+            }
+        }
+
+        // 格式化佣金比例
+        if (lawFirm.getPlatformCommissionRatio() != null) {
+            excelVo.setPlatformCommissionRatio(lawFirm.getPlatformCommissionRatio().toString() + "%");
+        }
+
+        return excelVo;
+    }
+
+    /**
+     * 校验Excel数据
+     */
+    private String validateExcelData(LawFirmExcelVo excelVo, int rowIndex) {
+        if (!StringUtils.hasText(excelVo.getFirmName())) {
+            return "第" + rowIndex + "行:律所名称不能为空";
+        }
+        if (!StringUtils.hasText(excelVo.getCreditCode())) {
+            return "第" + rowIndex + "行:统一社会信用代码不能为空";
+        }
+//        if (!StringUtils.hasText(excelVo.getPhone())) {
+//            return "第" + rowIndex + "行:联系电话不能为空";
+//        }
+        if (!StringUtils.hasText(excelVo.getDirectorName())) {
+            return "第" + rowIndex + "行:负责人姓名不能为空";
+        }
+//        if (!StringUtils.hasText(excelVo.getDirectorPhone())) {
+//            return "第" + rowIndex + "行:负责人电话不能为空";
+//        }
+
+        // 校验收款账号(不允许逗号分隔,必须是单个16位或19位账号)
+        if (StringUtils.hasText(excelVo.getPaymentAccount())) {
+            String paymentAccount = excelVo.getPaymentAccount().trim();
+            // 不允许包含逗号
+            if (paymentAccount.contains(",")) {
+                return "第" + rowIndex + "行:收款账号不允许包含逗号,每行只能填写一个账号";
+            }
+            // 校验长度(必须是16位或19位)
+            int length = paymentAccount.length();
+            if (length != 16 && length != 19) {
+                return "第" + rowIndex + "行:收款账号[" + paymentAccount + "]长度不正确,必须是16位或19位,当前为" + length + "位";
+            }
+        }
+
+        // 校验邮箱格式
+//        if (StringUtils.hasText(excelVo.getEmail())) {
+//            if (!excelVo.getEmail().matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")) {
+//                return "第" + rowIndex + "行:邮箱格式不正确";
+//            }
+//        }
+//        if (StringUtils.hasText(excelVo.getDirectorEmail())) {
+//            if (!excelVo.getDirectorEmail().matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")) {
+//                return "第" + rowIndex + "行:负责人邮箱格式不正确";
+//            }
+//        }
+
+        return null;
+    }
+
+    /**
+     * 转换为LawFirm实体
+     */
+    private LawFirm convertToLawFirm(LawFirmExcelVo excelVo) {
+        LawFirm lawFirm = new LawFirm();
+        BeanUtils.copyProperties(excelVo, lawFirm);
+
+        // 解析类型
+        if (StringUtils.hasText(excelVo.getFirmType())) {
+            switch (excelVo.getFirmType()) {
+                case "合伙制":
+                    lawFirm.setFirmType(1);
+                    break;
+                case "个人制":
+                    lawFirm.setFirmType(2);
+                    break;
+                case "国资制":
+                    lawFirm.setFirmType(3);
+                    break;
+            }
+        }
+
+        // 解析规模
+        if (StringUtils.hasText(excelVo.getFirmScale())) {
+            if (excelVo.getFirmScale().contains("小型")) {
+                lawFirm.setFirmScale(1);
+            } else if (excelVo.getFirmScale().contains("中型")) {
+                lawFirm.setFirmScale(2);
+            } else if (excelVo.getFirmScale().contains("大型") && !excelVo.getFirmScale().contains("超大型")) {
+                lawFirm.setFirmScale(3);
+            } else if (excelVo.getFirmScale().contains("超大型")) {
+                lawFirm.setFirmScale(4);
+            }
+        }
+
+        // 解析状态
+        if (StringUtils.hasText(excelVo.getStatus())) {
+            lawFirm.setStatus("启用".equals(excelVo.getStatus()) ? 1 : 0);
+        }
+
+        // 解析认证状态
+        if (StringUtils.hasText(excelVo.getCertificationStatus())) {
+            switch (excelVo.getCertificationStatus()) {
+                case "未认证":
+                    lawFirm.setCertificationStatus(0);
+                    break;
+                case "认证中":
+                    lawFirm.setCertificationStatus(1);
+                    break;
+                case "已认证":
+                    lawFirm.setCertificationStatus(2);
+                    break;
+                case "认证失败":
+                    lawFirm.setCertificationStatus(3);
+                    break;
+            }
+        }
+
+        // 解析日期
+        if (StringUtils.hasText(excelVo.getEstablishmentDate())) {
+            try {
+                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+                lawFirm.setEstablishmentDate(dateFormat.parse(excelVo.getEstablishmentDate()));
+            } catch (Exception e) {
+                log.warn("解析成立日期失败:{}", excelVo.getEstablishmentDate());
+            }
+        }
+
+        return lawFirm;
+    }
+
+    /**
+     * 获取单元格值作为字符串
+     */
+    private String getCellValueAsString(Cell cell) {
+        if (cell == null) {
+            return null;
+        }
+        switch (cell.getCellType()) {
+            case STRING:
+                return cell.getStringCellValue();
+            case NUMERIC:
+                if (DateUtil.isCellDateFormatted(cell)) {
+                    return new SimpleDateFormat("yyyy-MM-dd").format(cell.getDateCellValue());
+                } else {
+                    double numericValue = cell.getNumericCellValue();
+                    // 如果是整数,返回整数格式
+                    if (numericValue == (long) numericValue) {
+                        return String.valueOf((long) numericValue);
+                    } else {
+                        return String.valueOf(numericValue);
+                    }
+                }
+            case BOOLEAN:
+                return String.valueOf(cell.getBooleanCellValue());
+            case FORMULA:
+                return cell.getCellFormula();
+            default:
+                return null;
+        }
+    }
+
+    @Override
+    public R<LawFirm> getLawFirmDetail(Integer id) {
+        log.info("LawFirmServiceImpl.getLawFirmDetail?id={}", id);
+
+        if (id == null) {
+            return R.fail("律所ID不能为空");
+        }
+
+        // 使用 MyBatis-Plus 的 getById 方法获取律所详情
+        LawFirm lawFirm = this.getById(id);
+
+        if (lawFirm == null) {
+            return R.fail("律所不存在");
+        }
+
+        // 检查是否已删除
+        if (lawFirm.getDeleteFlag() != null && lawFirm.getDeleteFlag() == 1) {
+            return R.fail("律所已被删除");
+        }
+
+        // 查询收款账号列表
+        LambdaQueryWrapper<LawFirmPayment> paymentWrapper = new LambdaQueryWrapper<>();
+        paymentWrapper.eq(LawFirmPayment::getFirmId, id);
+        paymentWrapper.eq(LawFirmPayment::getDeleteFlag, 0);
+        List<LawFirmPayment> paymentList = lawFirmPaymentService.list(paymentWrapper);
+        lawFirm.setPaymentList(paymentList);
+
+        return R.data(lawFirm, "查询成功");
+    }
+
+    @Override
+    public void downloadTemplate(HttpServletResponse response) throws IOException {
+        log.info("LawFirmServiceImpl.downloadTemplate");
+        
+        XSSFWorkbook workbook = new XSSFWorkbook();
+        try {
+            Sheet sheet = workbook.createSheet("律所导入模板");
+
+            // 获取带有 @ExcelHeader 注解的字段
+            Field[] fields = LawFirmExcelVo.class.getDeclaredFields();
+            List<Field> excelFields = Arrays.stream(fields)
+                    .filter(f -> f.isAnnotationPresent(ExcelHeader.class))
+                    .collect(Collectors.toList());
+
+            // 创建表头样式
+            Font headerFont = workbook.createFont();
+            headerFont.setBold(true);
+            CellStyle headerCellStyle = workbook.createCellStyle();
+            headerCellStyle.setFont(headerFont);
+            headerCellStyle.setAlignment(HorizontalAlignment.CENTER);
+            headerCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+
+            // 创建表头
+            Row headerRow = sheet.createRow(0);
+            for (int i = 0; i < excelFields.size(); i++) {
+                Field field = excelFields.get(i);
+                ExcelHeader excelHeader = field.getAnnotation(ExcelHeader.class);
+                Cell cell = headerRow.createCell(i);
+                cell.setCellValue(excelHeader.value());
+                cell.setCellStyle(headerCellStyle);
+            }
+
+            // 设置列宽
+            for (int i = 0; i < excelFields.size(); i++) {
+                sheet.setColumnWidth(i, 20 * 256);
+            }
+
+            // 设置响应头(必须在获取输出流之前设置)
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
+            String fileName = URLEncoder.encode("律所导入模板", "UTF-8").replaceAll("\\+", "%20");
+            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
+
+            // 输出到响应流
+            OutputStream outputStream = response.getOutputStream();
+            workbook.write(outputStream);
+            outputStream.flush();
+        } finally {
+            // 确保 workbook 被正确关闭
+            if (workbook != null) {
+                workbook.close();
+            }
+        }
+    }
+
+    /**
+     * 批量填充律所的收款账号列表
+     *
+     * @param lawFirmList 律所列表
+     */
+    @Override
+    public void fillPaymentList(List<LawFirm> lawFirmList) {
+        if (lawFirmList == null || lawFirmList.isEmpty()) {
+            return;
+        }
+
+        // 收集所有律所ID
+        List<Integer> firmIds = lawFirmList.stream()
+                .map(LawFirm::getId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (firmIds.isEmpty()) {
+            return;
+        }
+
+        // 批量查询所有收款账号
+        LambdaQueryWrapper<LawFirmPayment> paymentWrapper = new LambdaQueryWrapper<>();
+        paymentWrapper.in(LawFirmPayment::getFirmId, firmIds);
+        paymentWrapper.eq(LawFirmPayment::getDeleteFlag, 0);
+        List<LawFirmPayment> allPayments = lawFirmPaymentService.list(paymentWrapper);
+
+        // 按律所ID分组
+        Map<Integer, List<LawFirmPayment>> paymentMap = allPayments.stream()
+                .collect(Collectors.groupingBy(LawFirmPayment::getFirmId));
+
+        // 为每个律所设置收款账号列表
+        lawFirmList.forEach(lawFirm -> {
+            List<LawFirmPayment> paymentList = paymentMap.getOrDefault(lawFirm.getId(), Collections.emptyList());
+            lawFirm.setPaymentList(paymentList);
+        });
+    }
+
+    @Override
+    public R<IPage<LawFirmPaymentVO>> getPaymentPageWithFirm(
+            int pageNum, int pageSize,
+            Integer firmId, String paymentAccount, String firmName, String creditCode,
+            Integer status, Integer certificationStatus, String directorName,
+            String createdTimeStart, String createdTimeEnd) {
+        log.info("LawFirmServiceImpl.getPaymentPageWithFirm?pageNum={},pageSize={},firmId={},paymentAccount={},firmName={},creditCode={},status={},certificationStatus={},directorName={},createdTimeStart={},createdTimeEnd={}",
+                pageNum, pageSize, firmId, paymentAccount, firmName, creditCode, status, certificationStatus, directorName, createdTimeStart, createdTimeEnd);
+        
+        Page<LawFirmPaymentVO> page = new Page<>(pageNum, pageSize);
+        IPage<LawFirmPaymentVO> pageResult = lawFirmPaymentMapper.selectPaymentPageWithFirm(
+                page, firmId, paymentAccount, firmName, creditCode, status, certificationStatus, directorName, createdTimeStart, createdTimeEnd
+        );
+        return R.data(pageResult);
+    }
+}
+
+

+ 187 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerAiInteractionLogServiceImpl.java

@@ -0,0 +1,187 @@
+package shop.alien.lawyer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerAiInteractionLog;
+import shop.alien.entity.store.vo.LawyerAiInteractionLogVo;
+import shop.alien.lawyer.service.LawyerAiInteractionLogService;
+import shop.alien.mapper.LawyerAiInteractionLogMapper;
+import shop.alien.util.common.ListToPage;
+
+import java.util.*;
+
+/**
+ * AI交互日志 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawyerAiInteractionLogServiceImpl extends ServiceImpl<LawyerAiInteractionLogMapper, LawyerAiInteractionLog> implements LawyerAiInteractionLogService {
+
+    private final LawyerAiInteractionLogMapper aiInteractionLogMapper;
+
+    @Override
+    public R<IPage<LawyerAiInteractionLog>> getAiInteractionLogList(int pageNum, int pageSize, Integer clientUserId,
+                                                               String conversationId, Integer problemScenarioId) {
+        log.info("LawyerAiInteractionLogServiceImpl.getAiInteractionLogList?pageNum={},pageSize={},clientUserId={},conversationId={},problemScenarioId={}",
+                pageNum, pageSize, clientUserId, conversationId, problemScenarioId);
+        Page<LawyerAiInteractionLog> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<LawyerAiInteractionLog> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerAiInteractionLog::getDeleteFlag, 0);
+        if (clientUserId != null) {
+            queryWrapper.eq(LawyerAiInteractionLog::getClientUserId, clientUserId);
+        }
+        if (StringUtils.hasText(conversationId)) {
+            queryWrapper.eq(LawyerAiInteractionLog::getConversationId, conversationId);
+        }
+        if (problemScenarioId != null) {
+            queryWrapper.eq(LawyerAiInteractionLog::getProblemScenarioId, problemScenarioId);
+        }
+        queryWrapper.orderByDesc(LawyerAiInteractionLog::getInteractionTime);
+        IPage<LawyerAiInteractionLog> pageResult = this.page(page, queryWrapper);
+        return R.data(pageResult);
+    }
+
+    @Override
+    public List<LawyerAiInteractionLog> getListByConversationId(String conversationId) {
+        log.info("LawyerAiInteractionLogServiceImpl.getListByConversationId?conversationId={}", conversationId);
+        LambdaQueryWrapper<LawyerAiInteractionLog> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerAiInteractionLog::getConversationId, conversationId)
+                .eq(LawyerAiInteractionLog::getDeleteFlag, 0)
+                .orderByAsc(LawyerAiInteractionLog::getInteractionTime);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public R<LawyerAiInteractionLog> addAiInteractionLog(LawyerAiInteractionLog aiInteractionLog) {
+        log.info("LawyerAiInteractionLogServiceImpl.addAiInteractionLog?aiInteractionLog={}", aiInteractionLog);
+        boolean result = this.save(aiInteractionLog);
+        if (result) {
+            return R.data(aiInteractionLog);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<LawyerAiInteractionLog> editAiInteractionLog(LawyerAiInteractionLog aiInteractionLog) {
+        log.info("LawyerAiInteractionLogServiceImpl.editAiInteractionLog?aiInteractionLog={}", aiInteractionLog);
+        boolean result = this.updateById(aiInteractionLog);
+        if (result) {
+            return R.data(aiInteractionLog);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteAiInteractionLog(Integer id) {
+        log.info("LawyerAiInteractionLogServiceImpl.deleteAiInteractionLog?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @Override
+    public R<Map<String, Object>> sendAIMessage(String message, String sessionId, Integer clientUserId) {
+        log.info("LawyerAiInteractionLogServiceImpl.sendAIMessage?message={},sessionId={},clientUserId={}", message, sessionId, clientUserId);
+
+        // 如果没有sessionId,生成一个新的
+        if (sessionId == null || sessionId.isEmpty()) {
+            sessionId = UUID.randomUUID().toString().replace("-", "");
+        }
+
+        // TODO: 这里需要调用实际的AI服务接口,目前返回模拟数据
+        String aiReply = "您好!我是AI助手UBAO,正在为您分析问题...";
+
+        // 保存AI交互日志
+        LawyerAiInteractionLog log = new LawyerAiInteractionLog();
+        log.setClientUserId(clientUserId);
+        log.setConversationId(sessionId);
+        log.setQueryText(message);
+        log.setResponseText(aiReply);
+        log.setInteractionTime(new Date());
+        this.save(log);
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("reply", aiReply);
+        result.put("sessionId", sessionId);
+        result.put("hasRecommendLawyers", false);
+        result.put("recommendLawyerIds", new ArrayList<>());
+
+        return R.data(result);
+    }
+
+    @Override
+    public R<Map<String, Object>> saveChatLog(LawyerAiInteractionLog aiInteractionLog) {
+        log.info("LawyerAiInteractionLogController.saveChatLog?aiInteractionLog={}", aiInteractionLog);
+        Map<String, Object> result = new HashMap<>();
+//        LawyerAiInteractionLogVo log = new LawyerAiInteractionLogVo();
+//        log.setClientUserId(clientUserId);
+//        log.setQueryText(queryText);
+//        log.setResponseText(responseText);
+//        log.setInteractionTime(new Date());
+//        log.setDeleteFlag(0);
+//        log.setCreatedTime(new Date());
+//        log.setUpdatedTime(new Date());
+//        int num =aiInteractionLogMapper.insertLog(log);
+        LawyerAiInteractionLog log = new LawyerAiInteractionLog();
+        log.setClientUserId(aiInteractionLog.getClientUserId());
+        log.setQueryText(aiInteractionLog.getQueryText());
+        log.setResponseText(aiInteractionLog.getResponseText());
+        log.setInteractionTime(new Date());
+        log.setDeleteFlag(0);
+        log.setSceneId(aiInteractionLog.getSceneId());
+        log.setCreatedTime(new Date());
+        log.setUpdatedTime(new Date());
+        //向表中插入一条新的数据,插入成功后获取插入该条数据的id
+        boolean A=this.save(log);
+        if (A) {
+            result.put("result","聊天记录保存成功");
+            result.put("log",log);
+            return R.data(result);
+        }
+        result.put("result","聊天记录保存失败");
+        return R.data(result);
+    }
+
+    @Override
+    public R<Map<String, Object>> updatedLog(Integer id) {
+        log.info("LawyerAiInteractionLogController.updatedLog?id={}", id);
+        Map<String, Object> result = new HashMap<>();
+        LawyerAiInteractionLogVo log = new LawyerAiInteractionLogVo();
+        log.setId(id);
+        log.setDeleteFlag(1);
+        log.setUpdatedTime(new Date());
+        int num=aiInteractionLogMapper.updateLog(log);
+        if (num>0) {
+            result.put("result","删除成功");
+            result.put("log",log);
+            return R.data(result);
+        }
+        result.put("result","删除失败");
+        return R.data(result);
+    }
+
+    @Override
+    public R<IPage<LawyerAiInteractionLog>> getLogList(Integer clientUserId,Integer page, Integer size) {
+        List<LawyerAiInteractionLog> list = aiInteractionLogMapper.getLogList(clientUserId,page,size);
+        IPage<LawyerAiInteractionLog> iPag =ListToPage.setPage(list,page, size);
+        return R.data(iPag);
+    }
+
+
+}
+

+ 95 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerChatMessageServiceImpl.java

@@ -0,0 +1,95 @@
+package shop.alien.lawyer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerChatMessage;
+import shop.alien.lawyer.service.LawyerChatMessageService;
+import shop.alien.mapper.LawyerChatMessageMapper;
+
+
+import java.util.List;
+
+/**
+ * 聊天消息 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawyerChatMessageServiceImpl extends ServiceImpl<LawyerChatMessageMapper, LawyerChatMessage> implements LawyerChatMessageService {
+
+    private final LawyerChatMessageMapper chatMessageMapper;
+
+    @Override
+    public R<IPage<LawyerChatMessage>> getChatMessageList(int pageNum, int pageSize, Integer chatSessionId,
+                                                     Integer consultationOrderId, Integer senderType) {
+        log.info("LawyerChatMessageServiceImpl.getChatMessageList?pageNum={},pageSize={},chatSessionId={},consultationOrderId={},senderType={}",
+                pageNum, pageSize, chatSessionId, consultationOrderId, senderType);
+        Page<LawyerChatMessage> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<LawyerChatMessage> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerChatMessage::getDeleteFlag, 0);
+        if (chatSessionId != null) {
+            queryWrapper.eq(LawyerChatMessage::getChatSessionId, chatSessionId);
+        }
+        if (consultationOrderId != null) {
+            queryWrapper.eq(LawyerChatMessage::getConsultationOrderId, consultationOrderId);
+        }
+        if (senderType != null) {
+            queryWrapper.eq(LawyerChatMessage::getSenderType, senderType);
+        }
+        queryWrapper.orderByAsc(LawyerChatMessage::getMessageTimestamp);
+        IPage<LawyerChatMessage> pageResult = this.page(page, queryWrapper);
+        return R.data(pageResult);
+    }
+
+    @Override
+    public List<LawyerChatMessage> getListBySessionId(Integer chatSessionId) {
+        log.info("LawyerChatMessageServiceImpl.getListBySessionId?chatSessionId={}", chatSessionId);
+        LambdaQueryWrapper<LawyerChatMessage> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerChatMessage::getChatSessionId, chatSessionId)
+                .eq(LawyerChatMessage::getDeleteFlag, 0)
+                .orderByAsc(LawyerChatMessage::getMessageTimestamp);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public R<LawyerChatMessage> addChatMessage(LawyerChatMessage chatMessage) {
+        log.info("LawyerChatMessageServiceImpl.addChatMessage?chatMessage={}", chatMessage);
+        boolean result = this.save(chatMessage);
+        if (result) {
+            return R.data(chatMessage);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<LawyerChatMessage> editChatMessage(LawyerChatMessage chatMessage) {
+        log.info("LawyerChatMessageServiceImpl.editChatMessage?chatMessage={}", chatMessage);
+        boolean result = this.updateById(chatMessage);
+        if (result) {
+            return R.data(chatMessage);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteChatMessage(Integer id) {
+        log.info("LawyerChatMessageServiceImpl.deleteChatMessage?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+}
+

+ 245 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerChatSessionServiceImpl.java

@@ -0,0 +1,245 @@
+package shop.alien.lawyer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerChatMessage;
+import shop.alien.entity.store.LawyerChatSession;
+import shop.alien.lawyer.service.LawyerChatMessageService;
+import shop.alien.lawyer.service.LawyerChatSessionService;
+import shop.alien.mapper.LawyerChatSessionMapper;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 聊天会话 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawyerChatSessionServiceImpl extends ServiceImpl<LawyerChatSessionMapper, LawyerChatSession> implements LawyerChatSessionService {
+
+    private final LawyerChatSessionMapper chatSessionMapper;
+    private final LawyerChatMessageService chatMessageService;
+    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+
+    @Override
+    public R<IPage<LawyerChatSession>> getChatSessionList(int pageNum, int pageSize, Integer consultationOrderId,
+                                                          Integer clientUserId, Integer lawyerUserId, Integer status) {
+        log.info("LawyerChatSessionServiceImpl.getChatSessionList?pageNum={},pageSize={},consultationOrderId={},clientUserId={},lawyerUserId={},status={}",
+                pageNum, pageSize, consultationOrderId, clientUserId, lawyerUserId, status);
+        Page<LawyerChatSession> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<LawyerChatSession> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerChatSession::getDeleteFlag, 0);
+        if (consultationOrderId != null) {
+            queryWrapper.eq(LawyerChatSession::getConsultationOrderId, consultationOrderId);
+        }
+        if (clientUserId != null) {
+            queryWrapper.eq(LawyerChatSession::getClientUserId, clientUserId);
+        }
+        if (lawyerUserId != null) {
+            queryWrapper.eq(LawyerChatSession::getLawyerUserId, lawyerUserId);
+        }
+        if (status != null) {
+            queryWrapper.eq(LawyerChatSession::getStatus, status);
+        }
+        queryWrapper.orderByDesc(LawyerChatSession::getLastMessageTime);
+        IPage<LawyerChatSession> pageResult = this.page(page, queryWrapper);
+        return R.data(pageResult);
+    }
+
+    @Override
+    public LawyerChatSession getByOrderId(Integer consultationOrderId) {
+        log.info("LawyerChatSessionServiceImpl.getByOrderId?consultationOrderId={}", consultationOrderId);
+        LambdaQueryWrapper<LawyerChatSession> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerChatSession::getConsultationOrderId, consultationOrderId)
+                .eq(LawyerChatSession::getDeleteFlag, 0)
+                .last("LIMIT 1");
+        return this.getOne(queryWrapper);
+    }
+
+    @Override
+    public R<LawyerChatSession> addChatSession(LawyerChatSession chatSession) {
+        log.info("LawyerChatSessionServiceImpl.addChatSession?chatSession={}", chatSession);
+        boolean result = this.save(chatSession);
+        if (result) {
+            return R.data(chatSession);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<LawyerChatSession> editChatSession(LawyerChatSession chatSession) {
+        log.info("LawyerChatSessionServiceImpl.editChatSession?chatSession={}", chatSession);
+        boolean result = this.updateById(chatSession);
+        if (result) {
+            return R.data(chatSession);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteChatSession(Integer id) {
+        log.info("LawyerChatSessionServiceImpl.deleteChatSession?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @Override
+    public R<Map<String, Object>> getChatHistory(String sessionId, int page, int pageSize) {
+        log.info("LawyerChatSessionServiceImpl.getChatHistory?sessionId={},page={},pageSize={}", sessionId, page, pageSize);
+
+        Map<String, Object> result = new HashMap<>();
+
+        if (StringUtils.hasText(sessionId)) {
+            // 返回该会话的消息列表
+            LawyerChatMessage query = new LawyerChatMessage();
+            query.setChatSessionId(Integer.parseInt(sessionId));
+
+            int pageNum = page > 0 ? page : 1;
+            int pageSizeNum = pageSize > 0 ? pageSize : 20;
+
+            IPage<LawyerChatMessage> pageResult = QueryBuilder.of(query)
+                    .page(pageNum, pageSizeNum)
+                    .build()
+                    .page(chatMessageService);
+
+            List<Map<String, Object>> messages = pageResult.getRecords().stream().map(msg -> {
+                Map<String, Object> msgMap = new HashMap<>();
+                msgMap.put("id", msg.getId());
+                msgMap.put("content", msg.getMessageContent());
+                msgMap.put("type", msg.getSenderType() == 0 ? "user" : "ai");  // 0:客户端用户, 1:律师用户,这里假设AI消息
+                msgMap.put("time", msg.getMessageTimestamp() != null ?
+                        DATE_FORMAT.format(msg.getMessageTimestamp()) : "");
+                msgMap.put("sessionId", sessionId);
+                // TODO: 处理图片和视频URL数组
+                msgMap.put("images", new ArrayList<>());
+                msgMap.put("videos", new ArrayList<>());
+                return msgMap;
+            }).collect(Collectors.toList());
+
+            result.put("messages", messages);
+        } else {
+            // 返回会话列表
+            QueryWrapper<LawyerChatSession> queryWrapper = new QueryWrapper<>();
+            queryWrapper.eq("delete_flag", 0)
+                    .orderByDesc("last_message_time", "created_time");
+
+            int pageNum = page > 0 ? page : 1;
+            int pageSizeNum = pageSize > 0 ? pageSize : 20;
+            Page<LawyerChatSession> pageObj = new Page<>(pageNum, pageSizeNum);
+            IPage<LawyerChatSession> pageResult = this.page(pageObj, queryWrapper);
+
+            List<Map<String, Object>> sessions = pageResult.getRecords().stream().map(session -> {
+                Map<String, Object> sessionMap = new HashMap<>();
+                sessionMap.put("id", session.getId().toString());
+                // TODO: 获取会话标题(通常是第一条消息)
+                sessionMap.put("title", "咨询会话");
+                // TODO: 获取最后一条消息内容
+                sessionMap.put("lastMessage", "");
+                sessionMap.put("lastMessageTime", session.getLastMessageTime() != null ?
+                        DATE_FORMAT.format(session.getLastMessageTime()) : "");
+                // TODO: 获取消息数量
+                sessionMap.put("messageCount", 0);
+                return sessionMap;
+            }).collect(Collectors.toList());
+
+            result.put("sessions", sessions);
+        }
+
+        return R.data(result);
+    }
+
+    @Override
+    public R<Map<String, Object>> saveChatMessage(String sessionId, String content, String type, Integer clientUserId, Integer consultationOrderId) {
+        log.info("LawyerChatSessionServiceImpl.saveChatMessage?sessionId={},content={},type={},clientUserId={},consultationOrderId={}",
+                sessionId, content, type, clientUserId, consultationOrderId);
+
+        Integer chatSessionId = null;
+        if (StringUtils.hasText(sessionId)) {
+            chatSessionId = Integer.parseInt(sessionId);
+        } else {
+            // 创建新会话
+            LawyerChatSession newSession = new LawyerChatSession();
+            newSession.setClientUserId(clientUserId);
+            newSession.setConsultationOrderId(consultationOrderId);
+            newSession.setStatus(1);  // 进行中
+            this.save(newSession);
+            chatSessionId = newSession.getId();
+        }
+
+        // 保存消息
+        LawyerChatMessage message = new LawyerChatMessage();
+        message.setChatSessionId(chatSessionId);
+        message.setConsultationOrderId(consultationOrderId);
+        message.setSenderId(clientUserId);
+        message.setSenderType("user".equals(type) ? 0 : 1);  // 0:客户端用户, 1:律师用户
+        message.setMessageContent(content);
+        message.setMessageType(0);  // 文本消息
+        message.setMessageTimestamp(new Date());
+        message.setReadStatus(0);  // 未读
+
+        chatMessageService.save(message);
+
+        // 更新会话的最后消息时间和ID
+        LawyerChatSession session = this.getById(chatSessionId);
+        if (session != null) {
+            session.setLastMessageId(message.getId());
+            session.setLastMessageTime(new Date());
+            this.updateById(session);
+        }
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("messageId", message.getId());
+        result.put("sessionId", chatSessionId.toString());
+        result.put("time", DATE_FORMAT.format(message.getMessageTimestamp()));
+
+        return R.data(result);
+    }
+
+    @Override
+    public R<String> deleteChatMessages(String messageIds, String sessionId) {
+        log.info("LawyerChatSessionServiceImpl.deleteChatMessages?messageIds={},sessionId={}", messageIds, sessionId);
+
+        String[] ids = messageIds.split(",");
+        List<Integer> idList = Arrays.stream(ids)
+                .map(String::trim)
+                .filter(s -> !s.isEmpty())
+                .map(Integer::parseInt)
+                .collect(Collectors.toList());
+
+        if (!idList.isEmpty()) {
+            chatMessageService.removeByIds(idList);
+        }
+
+        return R.success("删除成功");
+    }
+
+    @Override
+    public R<String> deleteChatSessionBySessionId(String sessionId) {
+        log.info("LawyerChatSessionServiceImpl.deleteChatSessionBySessionId?sessionId={}", sessionId);
+
+        this.removeById(Integer.parseInt(sessionId));
+
+        return R.success("删除成功");
+    }
+}
+

+ 1855 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerClientConsultationOrderServiceImpl.java

@@ -0,0 +1,1855 @@
+package shop.alien.lawyer.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.alibaba.nacos.common.utils.CollectionUtils;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.*;
+import shop.alien.entity.store.vo.LawyerConsultationOrderVO;
+import shop.alien.entity.store.vo.WebSocketVo;
+import shop.alien.lawyer.config.WebSocketProcess;
+import shop.alien.lawyer.feign.AlienStoreFeign;
+import shop.alien.lawyer.service.LawyerClientConsultationOrderService;
+import shop.alien.lawyer.service.LawyerUserService;
+import shop.alien.lawyer.service.OrderExpirationService;
+import shop.alien.mapper.*;
+import shop.alien.util.common.constant.LawyerStatusEnum;
+import shop.alien.util.common.constant.OrderActionType;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 咨询订单 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawyerClientConsultationOrderServiceImpl extends ServiceImpl<LawyerConsultationOrderMapper, LawyerConsultationOrder> implements LawyerClientConsultationOrderService {
+
+    private final LawyerConsultationOrderMapper consultationOrderMapper;
+    private final LawyerUserService lawyerUserService;
+    private final LawyerServiceAreaMapper lawyerServiceAreaMapper;
+    private final LawyerUserMapper lawyerUserMapper;
+    private final OrderExpirationService orderExpirationService;
+    private final AlienStoreFeign alienStoreFeign;
+    private final LifeNoticeMapper lifeNoticeMapper;
+    private final LifeUserMapper lifeUserMapper;
+    private final WebSocketProcess webSocketProcess;
+    private final LifeMessageMapper lifeMessageMapper;
+    private final LawFirmMapper lawFirmMapper;
+
+    /**
+     * 系统发送者ID常量
+     */
+    private static final String SYSTEM_SENDER_ID = "system";
+
+    /**
+     * 同意退款动作标识
+     */
+    private static final String ACTION_AGREE = "1";
+
+    /**
+     * 拒绝退款动作标识
+     */
+    private static final String ACTION_REJECT = "2";
+
+    /**
+     * 已申请退款状态
+     */
+    private static final String REFUND_STATUS_APPLIED = "1";
+
+    /**
+     * 律师已拒绝退款状态
+     */
+    private static final String REFUND_STATUS_REJECTED = "2";
+
+    /**
+     * 律师已同意退款状态
+     */
+    private static final String REFUND_STATUS_AGREED = "3";
+
+    /**
+     * 分钟转秒的转换系数
+     */
+    private static final long MINUTE_TO_SECOND = 60L;
+
+    /**
+     * 律师发送者ID前缀
+     */
+    private static final String LAWYER_SENDER_PREFIX = "lawyer_";
+
+    /**
+     * 用户接收者ID前缀
+     */
+    private static final String USER_RECEIVER_PREFIX = "user_";
+
+    /**
+     * 删除标记:未删除
+     */
+    private static final Integer DELETE_FLAG_NOT_DELETED = 0;
+
+    @Value("${order.coefficient}")
+    private String coefficient;
+    @Value("${order.coefficients}")
+    private String coefficients;
+
+
+
+    /**
+     * 删除咨询订单
+     * <p>
+     * 删除前会进行以下校验:
+     * 1. 参数校验:订单ID不能为空
+     * 2. 订单存在性校验:订单必须存在
+     * 3. 订单状态校验:进行中的订单不允许删除
+     * 4. 如果订单是待支付状态,会取消Redis中的支付超时计时器
+     * </p>
+     *
+     * @param id 订单ID
+     * @return 删除结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R<Boolean> deleteConsultationOrder(Integer id) {
+        log.info("开始删除咨询订单,订单ID={}", id);
+
+        // 参数校验
+        if (id == null) {
+            log.warn("删除咨询订单失败:订单ID为空");
+            return R.fail("订单ID不能为空");
+        }
+
+        // 查询订单信息
+        LawyerConsultationOrder order = consultationOrderMapper.selectById(id);
+        if (order == null) {
+            log.warn("删除咨询订单失败:订单不存在,订单ID={}", id);
+            return R.fail("订单不存在");
+        }
+
+        // 检查订单状态:进行中的订单不允许删除
+        Integer orderStatus = order.getOrderStatus();
+        Integer inProgressStatus = LawyerStatusEnum.INPROGRESS.getStatus(); // 2:进行中
+        if (inProgressStatus.equals(orderStatus)) {
+            log.warn("删除咨询订单失败:订单进行中,不允许删除,订单ID={}, 订单编号={}",
+                    id, order.getOrderNumber());
+            return R.fail("订单进行中,不允许删除");
+        }
+
+        // 如果订单是待支付状态,取消Redis中的订单支付超时计时
+        Integer waitPayStatus = LawyerStatusEnum.WAIT_PAY.getStatus(); // 0:待支付
+        if (waitPayStatus.equals(orderStatus) && order.getOrderNumber() != null) {
+            try {
+                orderExpirationService.cancelOrderPaymentTimeout(order.getOrderNumber());
+                log.info("已取消订单支付超时计时,订单编号={}", order.getOrderNumber());
+            } catch (Exception e) {
+                log.error("取消订单支付超时计时失败,订单编号={}", order.getOrderNumber(), e);
+                // 继续执行删除操作,不因取消计时器失败而中断
+            }
+        }
+
+        // 执行删除操作
+        boolean result = this.removeById(id);
+        if (result) {
+            log.info("删除咨询订单成功,订单ID={}, 订单编号={}", id, order.getOrderNumber());
+            return R.data(true, "删除成功");
+        } else {
+            log.error("删除咨询订单失败:数据库操作失败,订单ID={}, 订单编号={}", id, order.getOrderNumber());
+            return R.fail("删除失败");
+        }
+    }
+
+    /**
+     * 填充律师服务领域信息到订单VO列表
+     *
+     * @param voPage 订单VO分页对象
+     */
+    private void fillLawyerServiceArea(IPage<LawyerConsultationOrderVO> voPage) {
+        List<LawyerConsultationOrderVO> orderList = voPage.getRecords();
+        if (CollectionUtils.isEmpty(orderList)) {
+            return;
+        }
+
+        // 提取所有律师ID
+        List<Integer> lawyerIdList = orderList.stream()
+                .map(LawyerConsultationOrderVO::getLawyerUserId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (CollectionUtils.isEmpty(lawyerIdList)) {
+            return;
+        }
+
+        // 批量查询律师问题场景
+        Map<String, List<String>> serviceAreaMap = getLawyerServiceArea(lawyerIdList);
+        if (serviceAreaMap.isEmpty()) {
+            return;
+        }
+
+        // 填充问题场景
+        orderList.forEach(order -> {
+            Integer lawyerUserId = order.getLawyerUserId();
+            if (lawyerUserId != null) {
+                String lawyerUserIdStr = String.valueOf(lawyerUserId);
+                List<String> serviceAreaList = serviceAreaMap.get(lawyerUserIdStr);
+                if (CollectionUtils.isNotEmpty(serviceAreaList)) {
+                    order.setLawyerLegalProblemScenarioList(serviceAreaList);
+                }
+            }
+        });
+    }
+
+    /**
+     * 批量查询律师问题场景
+     *
+     * @param lawyerIdList 律师ID列表
+     * @return 律师问题场景Map,key为律师ID(String),value为问题场景名称列表
+     */
+    private Map<String, List<String>> getLawyerServiceArea(List<Integer> lawyerIdList) {
+        Map<String, List<String>> serviceAreaMap = new HashMap<>();
+        if (CollectionUtils.isEmpty(lawyerIdList)) {
+            return serviceAreaMap;
+        }
+
+        QueryWrapper<LawyerServiceArea> wrapper = new QueryWrapper<>();
+        wrapper.in("lsa.lawyer_user_id", lawyerIdList)
+                .eq("lsa.delete_flag", 0)
+                .eq("lsa.status", 1);
+
+        List<Map<String, Object>> serviceAreaDataList = lawyerServiceAreaMapper.getLawyerLegalProblemScenarioList(wrapper);
+        if (CollectionUtils.isEmpty(serviceAreaDataList)) {
+            return serviceAreaMap;
+        }
+
+        for (Map<String, Object> row : serviceAreaDataList) {
+            Object lawyerUserIdObj = row.get("lawyer_user_id");
+            if (lawyerUserIdObj == null) {
+                continue;
+            }
+
+            String lawyerUserId = String.valueOf(lawyerUserIdObj);
+            String scenarioName = (String) row.get("name");
+            String scenarioNameValue = StringUtils.hasText(scenarioName) ? scenarioName : "";
+
+            serviceAreaMap.computeIfAbsent(lawyerUserId, k -> new ArrayList<>())
+                    .add(scenarioNameValue);
+        }
+
+        return serviceAreaMap;
+    }
+
+    /**
+     * 获取咨询订单详情
+     * <p>
+     * 查询订单基本信息、律师信息和律师问题场景信息
+     * </p>
+     *
+     * @param lawyerOrderId 订单ID
+     * @return 咨询订单详情VO,如果订单不存在或参数为空则返回空对象
+     */
+    @Override
+    public LawyerConsultationOrderVO getConsultationOrderDetail(String lawyerOrderId) {
+        log.info("获取咨询订单详情,订单ID={}", lawyerOrderId);
+
+        // 参数校验
+        if (!StringUtils.hasText(lawyerOrderId)) {
+            log.warn("获取咨询订单详情失败:订单ID为空");
+            return new LawyerConsultationOrderVO();
+        }
+
+        // 查询订单信息
+        LawyerConsultationOrder order = consultationOrderMapper.selectById(lawyerOrderId);
+        if (order == null) {
+            log.warn("获取咨询订单详情失败:订单不存在,订单ID={}", lawyerOrderId);
+            return new LawyerConsultationOrderVO();
+        }
+
+        // 复制订单基本信息
+        LawyerConsultationOrderVO orderVO = new LawyerConsultationOrderVO();
+        BeanUtils.copyProperties(order, orderVO);
+
+        // 填充律师相关信息
+        fillLawyerRelatedInfo(orderVO, order.getLawyerUserId());
+
+        return orderVO;
+    }
+
+    /**
+     * 填充律师相关信息到订单VO
+     * <p>
+     * 包括律师基本信息和律师问题场景列表
+     * </p>
+     *
+     * @param orderVO    订单VO对象
+     * @param lawyerUserId 律师用户ID
+     */
+    private void fillLawyerRelatedInfo(LawyerConsultationOrderVO orderVO, Integer lawyerUserId) {
+        if (lawyerUserId == null) {
+            return;
+        }
+
+        // 查询并填充律师基本信息
+        LawyerUser lawyerUser = lawyerUserMapper.selectById(lawyerUserId);
+        if (lawyerUser != null) {
+            fillLawyerInfo(orderVO, lawyerUser);
+        }
+
+        // 查询并填充律师问题场景
+        fillLawyerServiceAreaForOrder(orderVO, lawyerUserId);
+    }
+
+    /**
+     * 填充律师问题场景信息到订单VO
+     *
+     * @param orderVO     订单VO对象
+     * @param lawyerUserId 律师用户ID
+     */
+    private void fillLawyerServiceAreaForOrder(LawyerConsultationOrderVO orderVO, Integer lawyerUserId) {
+        List<Integer> lawyerIdList = Collections.singletonList(lawyerUserId);
+        Map<String, List<String>> serviceAreaMap = getLawyerServiceArea(lawyerIdList);
+
+        if (serviceAreaMap.isEmpty()) {
+            return;
+        }
+
+        String lawyerUserIdStr = String.valueOf(lawyerUserId);
+        List<String> serviceAreaList = serviceAreaMap.get(lawyerUserIdStr);
+        if (CollectionUtils.isNotEmpty(serviceAreaList)) {
+            orderVO.setLawyerLegalProblemScenarioList(serviceAreaList);
+        }
+    }
+
+    /**
+     * 查询咨询订单信息(律师端)
+     *
+     * @param pageNum        页码
+     * @param pageSize       页容量
+     * @param startDate      开始时间
+     * @param endDate        结束时间
+     * @param clientUserName 客户端用户名
+     * @param orderStatus    订单状态
+     * @param lawyerId       律师ID
+     * @return 订单信息Map,包含订单列表、总数、进行中数量、已完成数量
+     */
+    @Override
+    public Map<String, Object> getLawyerConsultationOrderInfo(int pageNum, int pageSize, String startDate,
+            String endDate, String clientUserName, String orderStatus, String lawyerId) {
+        log.info("查询咨询订单信息(律师端)- pageNum={}, pageSize={}, startDate={}, endDate={}, "
+                + "clientUserName={}, orderStatus={}, lawyerId={}",
+                pageNum, pageSize, startDate, endDate, clientUserName, orderStatus, lawyerId);
+
+        // 参数校验:律师ID不能为空
+        if (!StringUtils.hasText(lawyerId)) {
+            log.warn("查询咨询订单信息失败:律师ID为空");
+            return buildEmptyResultMap(pageNum, pageSize);
+        }
+
+        // 创建分页对象
+        Page<LawyerConsultationOrderVO> page = new Page<>(pageNum, pageSize);
+
+        // 构建查询条件
+        QueryWrapper<LawyerConsultationOrderVO> queryWrapper = buildOrderQueryWrapper(
+                lawyerId, orderStatus, clientUserName, startDate, endDate);
+        QueryWrapper<LawyerConsultationOrderVO> statisticsWrapper = buildStatisticsQueryWrapper(
+                lawyerId, clientUserName,startDate, endDate);
+
+        // 查询订单列表
+        IPage<LawyerConsultationOrderVO> voPage = consultationOrderMapper.getLawyerConsultationOrderInfo(
+                page, queryWrapper);
+
+        // 填充律师问题场景信息
+        if (voPage != null) {
+            fillLawyerServiceArea(voPage);
+            // 为待支付订单计算倒计时(30分钟有效期)
+            calculateCountdownForPendingOrders(voPage);
+            //填充未读消息
+            fillUnreadMessage(voPage,lawyerId);
+        }
+
+        // 获取统计信息
+        statisticsWrapper.groupBy("lco.order_status");
+        List<Map<String, Object>> statisticsInfo = consultationOrderMapper.getLawyerStatisticsInfo(
+                statisticsWrapper);
+
+        // 构建状态统计Map并计算各状态订单数量
+        Map<Integer, Integer> statusCountMap = buildStatusCountMap(statisticsInfo);
+        Map<String, Integer> orderStatistics = calculateOrderStatistics(statusCountMap);
+
+        // 构建返回结果
+        return buildResultMap(voPage, orderStatistics);
+    }
+
+    /**
+     * 填充未读消息数量
+     *
+     * @param voPage 订单VO分页对象
+     */
+    private void fillUnreadMessage(IPage<LawyerConsultationOrderVO> voPage,String lawyerId) {
+        List<LawyerConsultationOrderVO> orderList = voPage.getRecords();
+        if (CollectionUtils.isEmpty(orderList)) {
+            return;
+        }
+
+        // 提取所有律师ID,并加上lawyer_前缀
+        List<String> lawyerIdList = orderList.stream()
+                .map(LawyerConsultationOrderVO::getClientUserPhone)
+                .filter(Objects::nonNull)
+                .map(phone -> "user_" + phone)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (CollectionUtils.isEmpty(lawyerIdList)) {
+            return;
+        }
+
+        LawyerUser lawyerUser = lawyerUserMapper.selectById(lawyerId);
+        String phone = lawyerUser.getPhone();
+
+        LambdaQueryWrapper<LifeMessage> lifeMessageLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lifeMessageLambdaQueryWrapper.in(LifeMessage::getSenderId, lawyerIdList);
+        lifeMessageLambdaQueryWrapper.eq(LifeMessage::getDeleteFlag, 0);
+        lifeMessageLambdaQueryWrapper.eq(LifeMessage::getIsRead, 0);
+        lifeMessageLambdaQueryWrapper.eq(LifeMessage::getReceiverId, "lawyer_" + phone);
+        List<LifeMessage> lifeMessageList = lifeMessageMapper.selectList(lifeMessageLambdaQueryWrapper);
+
+
+        // 按照senderId进行分组,返回senderId和数量的map
+        Map<String, Long> senderIdCountMap = new HashMap<>();
+        if(CollectionUtils.isNotEmpty(lifeMessageList)){
+            senderIdCountMap = lifeMessageList.stream()
+                    .collect(Collectors.groupingBy(LifeMessage::getSenderId, Collectors.counting()));
+        }
+
+        for(LawyerConsultationOrderVO lawyerConsultationOrderVO : voPage.getRecords()){
+            String lawyerPhone = "user_" + lawyerConsultationOrderVO.getClientUserPhone();
+            if(!senderIdCountMap.isEmpty() && senderIdCountMap.containsKey(lawyerPhone) && lawyerConsultationOrderVO.getOrderStatus() == 2){
+                long messageCount = senderIdCountMap.get(lawyerPhone);
+                lawyerConsultationOrderVO.setUnreadMessage(messageCount);
+            } else {
+                lawyerConsultationOrderVO.setUnreadMessage(0L);
+            }
+        }
+    }
+
+    /**
+     * 构建空结果Map
+     *
+     * @param pageNum  页码
+     * @param pageSize 页容量
+     * @return 空结果Map
+     */
+    private Map<String, Object> buildEmptyResultMap(int pageNum, int pageSize) {
+        Map<String, Object> resultMap = new HashMap<>();
+        Page<LawyerConsultationOrderVO> emptyPage = new Page<>(pageNum, pageSize);
+        emptyPage.setRecords(Collections.emptyList());
+        emptyPage.setTotal(0);
+        resultMap.put("lawyerOrderCount", 0L);
+        resultMap.put("lawyerInProgressOrderCount", 0);
+        resultMap.put("lawyerCompleteOrderCount", 0);
+        resultMap.put("lawyerWaitAcceptStatusOrderCount", 0);
+        resultMap.put("lawyerRefundedStatusOrderCount", 0);
+        resultMap.put("lawyerConsultationOrderList", Collections.emptyList());
+        return resultMap;
+    }
+
+    /**
+     * 计算订单统计信息
+     *
+     * @param statusCountMap 状态统计Map
+     * @return 订单统计信息Map
+     */
+    private Map<String, Integer> calculateOrderStatistics(Map<Integer, Integer> statusCountMap) {
+        Map<String, Integer> statistics = new HashMap<>();
+        Integer waitAcceptStatus = LawyerStatusEnum.WAIT_ACCEPT.getStatus();
+        Integer inProgressStatus = LawyerStatusEnum.INPROGRESS.getStatus();
+        Integer completeStatus = LawyerStatusEnum.COMPLETE.getStatus();
+        Integer refundedStatus = LawyerStatusEnum.REFUNDED.getStatus();
+        Integer cancelStatus = LawyerStatusEnum.CANCEL.getStatus();
+
+        int waitAcceptCount = statusCountMap.getOrDefault(waitAcceptStatus, 0);
+        int inProgressCount = statusCountMap.getOrDefault(inProgressStatus, 0);
+        int completeCount = statusCountMap.getOrDefault(completeStatus, 0);
+        int refundedCount = statusCountMap.getOrDefault(refundedStatus, 0);
+        int cancelCount = statusCountMap.getOrDefault(cancelStatus, 0);
+
+        statistics.put("waitAcceptCount", waitAcceptCount);
+        statistics.put("inProgressCount", inProgressCount);
+        statistics.put("completeCount", completeCount);
+        statistics.put("refundedCount", refundedCount);
+        statistics.put("cancelCount", cancelCount);
+        statistics.put("totalCount", waitAcceptCount + inProgressCount + completeCount + refundedCount );
+
+        return statistics;
+    }
+
+    /**
+     * 构建返回结果Map
+     *
+     * @param voPage         订单分页对象
+     * @param orderStatistics 订单统计信息
+     * @return 结果Map
+     */
+    private Map<String, Object> buildResultMap(IPage<LawyerConsultationOrderVO> voPage,
+            Map<String, Integer> orderStatistics) {
+        Map<String, Object> resultMap = new HashMap<>();
+
+        // 设置统计信息
+        resultMap.put("lawyerInProgressOrderCount", orderStatistics.get("inProgressCount"));
+        resultMap.put("lawyerCompleteOrderCount", orderStatistics.get("completeCount"));
+        resultMap.put("lawyerWaitAcceptStatusOrderCount", orderStatistics.get("waitAcceptCount"));
+        resultMap.put("lawyerRefundedStatusOrderCount", orderStatistics.get("refundedCount"));
+        resultMap.put("lawyerOrderCount", (long) orderStatistics.get("totalCount"));
+
+        // 设置订单列表
+        List<LawyerConsultationOrderVO> orderList = (voPage != null && voPage.getRecords() != null)
+                ? voPage.getRecords() : Collections.emptyList();
+        resultMap.put("lawyerConsultationOrderList", orderList);
+
+        return resultMap;
+    }
+
+    /**
+     * 构建订单查询条件
+     *
+     * @param lawyerId       律师ID
+     * @param orderStatus    订单状态
+     * @param clientUserName 客户端用户名
+     * @param startDate      开始时间
+     * @param endDate        结束时间
+     * @return 查询条件包装器
+     */
+    private QueryWrapper<LawyerConsultationOrderVO> buildOrderQueryWrapper(String lawyerId, String orderStatus,
+            String clientUserName, String startDate, String endDate) {
+        QueryWrapper<LawyerConsultationOrderVO> queryWrapper = new QueryWrapper<>();
+
+        // 订单状态条件
+        if (StringUtils.hasText(orderStatus)) {
+            queryWrapper.in("lco.order_status", Collections.singletonList(orderStatus));
+        } else {
+            // 默认查询非待支付和已取消的订单
+            Integer waitPayStatus = LawyerStatusEnum.WAIT_PAY.getStatus();
+            Integer cancelStatus = LawyerStatusEnum.CANCEL.getStatus();
+            queryWrapper.notIn("lco.order_status",
+                    Arrays.asList(String.valueOf(cancelStatus), String.valueOf(waitPayStatus)));
+        }
+
+        // 律师ID条件
+        queryWrapper.eq("lco.lawyer_user_id", lawyerId);
+
+        // 客户端用户名条件
+        if (StringUtils.hasText(clientUserName)) {
+            queryWrapper.like("lur.user_name", clientUserName);
+        }
+
+        // 删除标记条件
+        queryWrapper.eq("lco.delete_flag", 0);
+
+        // 时间范围条件
+        if (StringUtils.hasText(startDate)) {
+            queryWrapper.ge("lco.payment_time", startDate);
+        }
+
+        if (StringUtils.hasText(endDate)) {
+            queryWrapper.le("lco.payment_time", endDate);
+        }
+
+        // 排序条件
+        queryWrapper.orderByDesc("lco.created_time");
+
+        return queryWrapper;
+    }
+
+    /**
+     * 构建统计查询条件
+     *
+     * @param lawyerId       律师ID
+     * @param clientUserName 客户端用户名
+     * @return 统计查询条件包装器
+     */
+    private QueryWrapper<LawyerConsultationOrderVO> buildStatisticsQueryWrapper(String lawyerId,
+            String clientUserName, String startDate, String endDate) {
+        QueryWrapper<LawyerConsultationOrderVO> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("lco.lawyer_user_id", lawyerId);
+        queryWrapper.eq("lco.delete_flag", 0);
+
+        // 时间范围条件
+        if (StringUtils.hasText(startDate)) {
+            queryWrapper.ge("lco.payment_time", startDate);
+        }
+
+        if (StringUtils.hasText(endDate)) {
+            queryWrapper.le("lco.payment_time", endDate);
+        }
+
+        if (StringUtils.hasText(clientUserName)) {
+            queryWrapper.like("lur.user_name", clientUserName);
+        }
+
+        return queryWrapper;
+    }
+
+    /**
+     * 构建状态统计Map
+     *
+     * @param statisticsInfo 统计信息列表
+     * @return 状态统计Map,key为订单状态,value为订单数量
+     */
+    private Map<Integer, Integer> buildStatusCountMap(List<Map<String, Object>> statisticsInfo) {
+        Map<Integer, Integer> statusCountMap = new HashMap<>();
+        if (CollectionUtils.isEmpty(statisticsInfo)) {
+            return statusCountMap;
+        }
+
+        for (Map<String, Object> map : statisticsInfo) {
+            Object statusObj = map.get("order_status");
+            Object countObj = map.get("order_count");
+
+            if (statusObj == null || countObj == null) {
+                continue;
+            }
+
+            Integer status = (Integer) statusObj;
+            // COUNT(*) 返回类型可能是Long
+            Long countLong = (Long) countObj;
+            statusCountMap.put(status, countLong.intValue());
+        }
+
+        return statusCountMap;
+    }
+
+    /**
+     * 为待支付订单计算倒计时(30分钟有效期)
+     *
+     * @param voPage 订单分页对象
+     */
+    private void calculateCountdownForPendingOrders(IPage<LawyerConsultationOrderVO> voPage) {
+        if (voPage == null || voPage.getRecords() == null) {
+            return;
+        }
+
+        Date now = new Date();
+        long validitySeconds = 30 * 60; // 30分钟 = 1800秒
+
+        for (LawyerConsultationOrderVO vo : voPage.getRecords()) {
+            // 仅对待支付订单(orderStatus=0)计算倒计时
+            if (vo.getOrderStatus() != null && vo.getOrderStatus() == 0) {
+                Date orderTime = vo.getOrderTime();
+                if (orderTime != null) {
+                    long elapsedSeconds = (now.getTime() - orderTime.getTime()) / 1000;
+                    long remainingSeconds = validitySeconds - elapsedSeconds;
+                    // 如果已过期,设置为0
+                    vo.setCountdownSeconds(Math.max(0, remainingSeconds));
+                } else {
+                    vo.setCountdownSeconds(0L);
+                }
+            } else {
+                // 非待支付订单,倒计时为null
+                vo.setCountdownSeconds(null);
+            }
+        }
+    }
+
+    /**
+     * 填充律师信息到订单VO
+     *
+     * @param orderVO    订单VO对象
+     * @param lawyerUser 律师用户对象
+     */
+    private void fillLawyerInfo(LawyerConsultationOrderVO orderVO, LawyerUser lawyerUser) {
+        orderVO.setLawyerName(lawyerUser.getName());
+        orderVO.setLawyerPhone(lawyerUser.getPhone());
+        orderVO.setLawyerEmail(lawyerUser.getEmail());
+        orderVO.setLawyerCertificateNo(lawyerUser.getLawyerCertificateNo());
+        // 通过firm_id查询律所名称
+        if (lawyerUser.getFirmId() != null) {
+            LawFirm lawFirm = lawFirmMapper.selectById(lawyerUser.getFirmId());
+            orderVO.setLawFirm(lawFirm != null && (lawFirm.getDeleteFlag() == null || lawFirm.getDeleteFlag() == 0) 
+                    ? lawFirm.getFirmName() : null);
+        } else {
+            orderVO.setLawFirm(null);
+        }
+        orderVO.setPracticeYears(lawyerUser.getPracticeYears());
+        orderVO.setSpecialtyFields(lawyerUser.getSpecialtyFields());
+        orderVO.setCertificationStatus(lawyerUser.getCertificationStatus());
+        orderVO.setServiceCount(lawyerUser.getServiceCount());
+        orderVO.setServiceScore(lawyerUser.getServiceScore());
+        orderVO.setLawyerConsultationFee(lawyerUser.getConsultationFee());
+        orderVO.setProvince(lawyerUser.getProvince());
+        orderVO.setCity(lawyerUser.getCity());
+        orderVO.setDistrict(lawyerUser.getDistrict());
+        orderVO.setAddress(lawyerUser.getAddress());
+        orderVO.setHeadImg(lawyerUser.getHeadImg());
+        orderVO.setNickName(lawyerUser.getNickName());
+        orderVO.setPersonalIntroduction(lawyerUser.getPersonalIntroduction());
+    }
+
+    /**
+     * 律师确认订单
+     * <p>
+     * 支持两种操作类型:
+     * 1. 接单:将订单状态从待接单更新为进行中
+     * 2. 取消:将订单状态更新为已取消
+     * </p>
+     *
+     * @param id        订单ID
+     * @param actionType 操作类型:1-接单,2-取消
+     * @return 确认结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R<Boolean> confirmOrder(String id, String actionType) {
+        log.info("开始律师确认订单,订单ID={}, 操作类型={}", id, actionType);
+
+        // 参数校验
+        if (id == null || id.trim().isEmpty()) {
+            log.warn("律师确认订单失败:订单ID为空");
+            return R.fail("订单ID不能为空");
+        }
+
+        if (actionType == null || actionType.trim().isEmpty()) {
+            log.warn("律师确认订单失败:操作类型为空");
+            return R.fail("操作类型不能为空");
+        }
+
+        // 操作类型格式校验
+        String trimmedActionType = actionType.trim();
+        OrderActionType actionTypeEnum = OrderActionType.getByCode(trimmedActionType);
+        if (actionTypeEnum == null) {
+            log.warn("律师确认订单失败:操作类型无效,actionType={}", actionType);
+            return R.fail("操作类型无效,1-接单,2-取消");
+        }
+
+        // 将订单ID转换为Integer
+        Integer orderId;
+        try {
+            orderId = Integer.parseInt(id.trim());
+        } catch (NumberFormatException e) {
+            log.warn("律师确认订单失败:订单ID格式错误,id={}", id, e);
+            return R.fail("订单ID格式错误");
+        }
+
+        // 查询订单信息
+        LawyerConsultationOrder order = consultationOrderMapper.selectById(orderId);
+        if (order == null) {
+            log.warn("律师确认订单失败:订单不存在,订单ID={}", orderId);
+            return R.fail("订单不存在");
+        }
+
+        // 清除redis缓存,避免调用订单超时处理
+        orderExpirationService.cancelOrderAcceptTimeout(order.getOrderNumber());
+
+        // 验证订单状态是否为待接单
+        Integer orderStatus = order.getOrderStatus();
+        Integer waitAcceptStatus = LawyerStatusEnum.WAIT_ACCEPT.getStatus();
+        if (!waitAcceptStatus.equals(orderStatus)) {
+            log.warn("律师确认订单失败:订单状态不是待接单,订单ID={}, 订单编号={}, 当前状态={}",
+                    orderId, order.getOrderNumber(), orderStatus);
+            return R.fail("订单状态不是待接单,无法确认");
+        }
+
+        // 根据操作类型处理
+        if (actionTypeEnum == OrderActionType.ACCEPT) {
+            // 接单:更新订单状态为进行中
+            Integer inProgressStatus = LawyerStatusEnum.INPROGRESS.getStatus();
+            Date acceptTime = new Date();
+            order.setOrderStatus(inProgressStatus);
+            order.setAcceptOrdersTime(acceptTime);
+            order.setUpdatedTime(acceptTime);
+            LocalDateTime now = LocalDateTime.now();
+            LocalDateTime validityDateTime = now.plusDays(3)
+                    .withHour(0)
+                    .withMinute(0)
+                    .withSecond(0)
+                    .withNano(0);
+            order.setValidityPeriod(Date.from(validityDateTime.atZone(ZoneId.systemDefault()).toInstant()));
+
+            // 执行更新操作
+            boolean result = this.updateById(order);
+            if (result) {
+                log.info("律师接单成功,订单ID={}, 订单编号={}, 状态从{}变更为{}",
+                        orderId, order.getOrderNumber(), waitAcceptStatus, inProgressStatus);
+                
+                // 发送接单通知
+                sendAcceptOrderNotice(order, acceptTime);
+                
+                return R.data(true, "接单成功");
+            } else {
+                log.error("律师接单失败:数据库操作失败,订单ID={}, 订单编号={}", orderId, order.getOrderNumber());
+                return R.fail("接单失败");
+            }
+        } else {
+            // 取消:更新订单状态为已取消
+            Integer cancelStatus = LawyerStatusEnum.REFUNDED.getStatus();
+            order.setOrderStatus(cancelStatus);
+            order.setUpdatedTime(new Date());
+
+            // 执行更新操作
+            boolean result = this.updateById(order);
+            if (result) {
+                log.info("律师取消订单成功,订单ID={}, 订单编号={}, 状态从{}变更为{}",
+                        orderId, order.getOrderNumber(), waitAcceptStatus, cancelStatus);
+                
+                // 发送拒绝接单通知
+                sendRejectOrderNotice(order);
+                
+                return R.data(true, "取消订单成功");
+            } else {
+                log.error("律师取消订单失败:数据库操作失败,订单ID={}, 订单编号={}", orderId, order.getOrderNumber());
+                return R.fail("取消订单失败");
+            }
+        }
+    }
+
+    /**
+     * 订单开始计时(律师端)
+     * <p>
+     * 订单开始计时功能说明:
+     * 1. 通过订单ID查询订单信息,验证订单是否存在
+     * 2. 校验订单状态,必须是进行中状态才能开始计时
+     * 3. 判断订单是否按分钟收费(chargeMinute和minuteNum都不为空)
+     * 4. 如果按分钟收费,计算超时时间并调用订单完成超时方法设置超时限制
+     * 5. 超时后系统会自动将订单状态更新为已完成
+     * </p>
+     *
+     * @param id 订单ID,不能为空
+     * @return R<Boolean> 操作结果,true表示成功,false表示失败
+     * @throws IllegalArgumentException 当订单ID为空或无效时抛出
+     * @author system
+     * @since 2025-01-XX
+     */
+    @Override
+    public R<Boolean> orderStartTiming(String id) {
+        log.info("订单开始计时请求开始,订单ID={}", id);
+
+        // 参数校验
+        validateOrderId(id);
+
+        try {
+            // 查询并校验订单
+            LawyerConsultationOrder order = queryAndValidateOrder(id);
+
+            // 校验订单状态
+            validateOrderStatus(order, id);
+
+            // 处理订单计时逻辑
+            return processOrderTiming(order, id);
+
+        } catch (IllegalArgumentException e) {
+            log.warn("订单开始计时参数校验失败,订单ID={},错误信息:{}", id, e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("订单开始计时异常,订单ID={},异常信息:{}", id, e.getMessage(), e);
+            return R.fail("订单开始计时失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 校验订单ID
+     * <p>
+     * 检查订单ID是否为空或无效
+     * </p>
+     *
+     * @param id 订单ID
+     * @throws IllegalArgumentException 当订单ID为空或无效时抛出
+     */
+    private void validateOrderId(String id) {
+        if (StringUtils.isEmpty(id)) {
+            log.warn("订单开始计时参数校验失败:订单ID为空");
+            throw new IllegalArgumentException("订单ID不能为空");
+        }
+
+        // 去除前后空格后再次检查
+        String trimmedId = id.trim();
+        if (trimmedId.isEmpty()) {
+            log.warn("订单开始计时参数校验失败:订单ID为空字符串");
+            throw new IllegalArgumentException("订单ID不能为空");
+        }
+    }
+
+    /**
+     * 查询并校验订单
+     * <p>
+     * 根据订单ID查询订单信息,并校验订单是否存在
+     * </p>
+     *
+     * @param id 订单ID
+     * @return 订单对象
+     * @throws IllegalArgumentException 当订单不存在时抛出
+     */
+    private LawyerConsultationOrder queryAndValidateOrder(String id) {
+        LawyerConsultationOrder order = this.getById(id);
+        if (order == null) {
+            log.warn("订单开始计时失败:订单不存在,订单ID={}", id);
+            throw new IllegalArgumentException("订单不存在");
+        }
+
+        log.debug("订单查询成功,订单ID={}, 订单编号={}, 订单状态={}", 
+                id, order.getOrderNumber(), order.getOrderStatus());
+        return order;
+    }
+
+    /**
+     * 校验订单状态
+     * <p>
+     * 检查订单状态是否为进行中,只有进行中的订单才能开始计时
+     * </p>
+     *
+     * @param order 订单对象
+     * @param id    订单ID(用于日志记录)
+     * @throws IllegalArgumentException 当订单状态不是进行中时抛出
+     */
+    private void validateOrderStatus(LawyerConsultationOrder order, String id) {
+        Integer inProgressStatus = LawyerStatusEnum.INPROGRESS.getStatus();
+        Integer currentOrderStatus = order.getOrderStatus();
+
+        if (!inProgressStatus.equals(currentOrderStatus)) {
+            log.warn("订单开始计时失败:订单状态不是进行中,订单ID={}, 订单编号={}, 当前状态={}, 期望状态={}",
+                    id, order.getOrderNumber(), currentOrderStatus, inProgressStatus);
+            throw new IllegalArgumentException("订单状态不是进行中,无法开始计时");
+        }
+
+        log.debug("订单状态校验通过,订单ID={}, 订单编号={}, 订单状态=进行中", id, order.getOrderNumber());
+    }
+
+    /**
+     * 处理订单计时逻辑
+     * <p>
+     * 判断订单是否按分钟收费,如果是则设置超时限制
+     * </p>
+     *
+     * @param order 订单对象
+     * @param id    订单ID(用于日志记录)
+     * @return 操作结果
+     */
+    private R<Boolean> processOrderTiming(LawyerConsultationOrder order, String id) {
+        // 判断订单是否按分钟收费
+        if (isMinuteBasedCharging(order)) {
+            // 按分钟收费,设置超时限制
+            return setMinuteBasedTimeout(order, id);
+        } else {
+            // 不是按分钟收费,无需设置超时限制
+            log.info("订单不是按分钟收费,无需设置超时限制,订单ID={}, 订单编号={}", 
+                    id, order.getOrderNumber());
+            return R.data(true, "订单不是按分钟收费,无需设置超时限制");
+        }
+    }
+
+    /**
+     * 判断订单是否按分钟收费
+     * <p>
+     * 通过检查chargeMinute和minuteNum字段是否都不为空来判断
+     * </p>
+     *
+     * @param order 订单对象
+     * @return true表示按分钟收费,false表示不是按分钟收费
+     */
+    private boolean isMinuteBasedCharging(LawyerConsultationOrder order) {
+        return order.getChargeMinute() != null && order.getMinuteNum() != null;
+    }
+
+    /**
+     * 设置按分钟收费订单的超时限制
+     * <p>
+     * 计算超时时间(分钟数转换为秒),并调用订单完成超时方法设置超时限制
+     * </p>
+     *
+     * @param order 订单对象
+     * @param id    订单ID(用于日志记录)
+     * @return 操作结果
+     */
+    private R<Boolean> setMinuteBasedTimeout(LawyerConsultationOrder order, String id) {
+        try {
+            // 计算超时时间(分钟数转换为秒)
+            long timeoutSeconds = calculateTimeoutSeconds(order.getMinuteNum());
+
+            // 调用订单完成超时方法设置超时限制
+            orderExpirationService.setOrderCompleteTimeout(order.getOrderNumber(), timeoutSeconds);
+
+            log.info("订单开始计时成功,订单ID={}, 订单编号={}, 超时时间={}秒({}分钟)",
+                    id, order.getOrderNumber(), timeoutSeconds, order.getMinuteNum());
+
+            return R.data(true, "订单开始计时成功");
+
+        } catch (Exception e) {
+            log.error("设置订单超时限制失败,订单ID={}, 订单编号={},异常信息:{}",
+                    id, order.getOrderNumber(), e.getMessage(), e);
+            throw new RuntimeException("设置订单超时限制失败:" + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 计算超时时间(秒)
+     * <p>
+     * 将分钟数转换为秒数
+     * </p>
+     *
+     * @param minuteNum 分钟数,必须大于0
+     * @return 超时时间(秒)
+     * @throws IllegalArgumentException 当分钟数无效时抛出
+     */
+    private long calculateTimeoutSeconds(Integer minuteNum) {
+        if (minuteNum == null || minuteNum <= 0) {
+            log.warn("计算超时时间失败:分钟数无效,minuteNum={}", minuteNum);
+            throw new IllegalArgumentException("分钟数必须大于0");
+        }
+
+        long timeoutSeconds = minuteNum * MINUTE_TO_SECOND;
+        log.debug("计算超时时间,分钟数={}, 超时时间={}秒", minuteNum, timeoutSeconds);
+        return timeoutSeconds;
+    }
+
+    /**
+     * 申请退款
+     * <p>
+     * 申请退款前会进行以下校验:
+     * 1. 参数校验:订单ID不能为空,退款原因不能为空
+     * 2. 订单存在性校验:订单必须存在
+     * 3. 订单支付状态校验:只有已支付的订单才能申请退款
+     * 4. 订单状态校验:待支付、已取消的订单不允许申请退款
+     * </p>
+     *
+     * @param id 订单ID
+     * @param refundReason 退款原因
+     * @return 申请退款结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R<Boolean> requestRefund(Integer id, String refundReason) {
+        log.info("开始申请退款,订单ID={}, 退款原因={}", id, refundReason);
+
+        // 参数校验
+        if (id == null) {
+            log.warn("申请退款失败:订单ID为空");
+            return R.fail("订单ID不能为空");
+        }
+
+        if (!StringUtils.hasText(refundReason)) {
+            log.warn("申请退款失败:退款原因为空,订单ID={}", id);
+            return R.fail("退款原因不能为空");
+        }
+
+        // 查询订单信息
+        LawyerConsultationOrder order = consultationOrderMapper.selectById(id);
+        if (order == null) {
+            log.warn("申请退款失败:订单不存在,订单ID={}", id);
+            return R.fail("订单不存在");
+        }
+
+        // 检查订单支付状态:只有已支付的订单才能申请退款
+        Integer paymentStatus = order.getPaymentStatus();
+        Integer paidStatus = 1; // 1:已支付
+        if (paymentStatus == 0 || !paidStatus.equals(paymentStatus)) {
+            log.warn("申请退款失败:订单未支付,无法申请退款,订单ID={}, 订单编号={}, 支付状态={}",
+                    id, order.getOrderNumber(), paymentStatus);
+            return R.fail("订单未支付,无法申请退款");
+        }
+
+        // 检查订单状态:待支付、已取消的订单不允许申请退款
+        Integer orderStatus = order.getOrderStatus();
+        Integer waitPayStatus = LawyerStatusEnum.WAIT_PAY.getStatus(); // 0:待支付
+        Integer cancelStatus = LawyerStatusEnum.CANCEL.getStatus(); // 4:已取消
+
+        if (waitPayStatus.equals(orderStatus)) {
+            log.warn("申请退款失败:订单待支付,无法申请退款,订单ID={}, 订单编号={}", id, order.getOrderNumber());
+            return R.fail("订单待支付,无法申请退款");
+        }
+
+        if (cancelStatus.equals(orderStatus)) {
+            log.warn("申请退款失败:订单已取消,无法申请退款,订单ID={}, 订单编号={}", id, order.getOrderNumber());
+            return R.fail("订单已取消,无法申请退款");
+        }
+
+        // 检查订单是否已经申请过退款(这里可以根据业务需求添加退款状态字段来判断)
+        // 目前暂时允许重复申请,实际业务中可能需要添加退款状态字段来防止重复申请
+
+        // 更新订单状态为退款中或已退款(根据业务需求,这里暂时不更新订单状态,只记录退款申请)
+        // 如果需要更新订单状态,可以添加退款状态字段,例如:refundStatus (0:未退款, 1:退款中, 2:已退款, 3:退款失败)
+        
+        // 记录退款申请信息(这里可以根据业务需求创建退款记录表,或者更新订单表的退款相关字段)
+        log.info("申请退款成功,订单ID={}, 订单编号={}, 订单金额={}分, 退款原因={}",
+                id, order.getOrderNumber(), order.getOrderAmount(), refundReason);
+
+        // 这里可以调用实际的退款接口处理退款
+        // 例如:调用支付宝退款接口、更新退款状态等
+        // 目前先返回成功,实际业务中需要根据退款接口的返回结果来判断
+
+        return R.data(true, "退款申请已提交,请等待处理");
+    }
+
+    /**
+     * 退款申请处理(律师端)
+     * <p>
+     * 处理退款申请前会进行以下校验:
+     * 1. 参数校验:律师ID、订单ID、处理动作不能为空
+     * 2. 订单存在性校验:订单必须存在
+     * 3. 订单归属校验:订单必须属于该律师
+     * 4. 退款状态校验:订单必须处于已申请退款状态
+     * 5. 处理动作校验:处理动作必须为同意(1)或拒绝(2)
+     * 6. 拒绝原因校验:拒绝时必须提供拒绝原因
+     * </p>
+     * <p>
+     * 处理逻辑:
+     * - 同意退款:将订单状态设置为已退款(5),申请退款状态设置为已同意(3),并发送同意通知
+     * - 拒绝退款:将申请退款状态设置为已拒绝(2),更新拒绝原因,并发送拒绝通知
+     * </p>
+     *
+     * @param lawyerConsultationOrder 订单对象
+     * @return 处理结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R<Boolean> refundApplyProcess(LawyerConsultationOrder lawyerConsultationOrder) {
+        Integer lawyerId = lawyerConsultationOrder.getLawyerUserId();
+        Integer orderId = lawyerConsultationOrder.getId();
+        String processAction = lawyerConsultationOrder.getProcessAction();
+        String rejectRefundReason = lawyerConsultationOrder.getRejectRefundReason();
+        
+        log.info("开始处理退款申请,律师ID={}, 订单ID={}, 处理动作={}", lawyerId, orderId, processAction);
+
+        // 1. 参数校验
+        R<Boolean> paramValidateResult = validateRefundProcessParams(lawyerId, orderId, processAction, rejectRefundReason);
+        if (paramValidateResult != null) {
+            return paramValidateResult;
+        }
+
+        // 2. 查询订单信息
+        LawyerConsultationOrder order = consultationOrderMapper.selectById(orderId);
+        if (Objects.isNull(order)) {
+            log.warn("处理退款申请失败:订单不存在,订单ID={}", orderId);
+            return R.fail("订单不存在");
+        }
+
+        // 3. 业务校验
+        R<Boolean> businessValidateResult = validateRefundProcessBusiness(lawyerId, order);
+        if (businessValidateResult != null) {
+            return businessValidateResult;
+        }
+
+        // 4. 取消退款超时计时器
+        try {
+            orderExpirationService.cancelOrderRefundTimeout(order.getOrderNumber());
+        } catch (Exception e) {
+            log.error("取消退款超时计时器失败,订单编号={}", order.getOrderNumber(), e);
+            // 继续执行,不因取消计时器失败而中断
+        }
+
+        // 5. 根据处理动作进行相应处理
+        if (ACTION_AGREE.equals(processAction)) {
+            return processAgreeRefund(order);
+        } else {
+            return processRejectRefund(order, rejectRefundReason);
+        }
+    }
+
+    /**
+     * 校验退款处理参数
+     *
+     * @param lawyerId          律师ID
+     * @param orderId           订单ID
+     * @param processAction     处理动作
+     * @param rejectRefundReason 拒绝原因
+     * @return 校验失败时返回错误结果,校验通过返回null
+     */
+    private R<Boolean> validateRefundProcessParams(Integer lawyerId, Integer orderId, 
+                                                     String processAction, String rejectRefundReason) {
+        // 律师ID校验
+        if (Objects.isNull(lawyerId) || lawyerId <= 0) {
+            log.warn("处理退款申请失败:律师ID为空或无效,lawyerId={}", lawyerId);
+            return R.fail("律师ID不能为空");
+        }
+
+        // 订单ID校验
+        if (Objects.isNull(orderId) || orderId <= 0) {
+            log.warn("处理退款申请失败:订单ID为空或无效,orderId={}", orderId);
+            return R.fail("订单ID不能为空");
+        }
+
+        // 处理动作校验
+        if (!StringUtils.hasText(processAction)) {
+            log.warn("处理退款申请失败:处理动作为空,订单ID={}", orderId);
+            return R.fail("处理动作不能为空");
+        }
+
+        // 处理动作值校验
+        if (!ACTION_AGREE.equals(processAction) && !ACTION_REJECT.equals(processAction)) {
+            log.warn("处理退款申请失败:处理动作无效,订单ID={}, 处理动作={}", orderId, processAction);
+            return R.fail("处理动作无效,1-同意,2-拒绝");
+        }
+
+        // 拒绝原因校验:拒绝时必须提供拒绝原因
+        if (ACTION_REJECT.equals(processAction) && !StringUtils.hasText(rejectRefundReason)) {
+            log.warn("处理退款申请失败:拒绝退款时必须提供拒绝原因,订单ID={}", orderId);
+            return R.fail("拒绝退款时必须提供拒绝原因");
+        }
+
+        return null;
+    }
+
+    /**
+     * 校验退款处理业务规则
+     *
+     * @param lawyerId 律师ID
+     * @param order    订单对象
+     * @return 校验失败时返回错误结果,校验通过返回null
+     */
+    private R<Boolean> validateRefundProcessBusiness(Integer lawyerId, LawyerConsultationOrder order) {
+        Integer orderId = order.getId();
+        
+        // 订单归属校验:订单必须属于该律师
+        Integer orderLawyerId = order.getLawyerUserId();
+        if (Objects.isNull(orderLawyerId) || !lawyerId.equals(orderLawyerId)) {
+            log.warn("处理退款申请失败:订单不属于该律师,订单ID={}, 订单律师ID={}, 请求律师ID={}", 
+                    orderId, orderLawyerId, lawyerId);
+            return R.fail("订单不属于该律师,无法处理退款申请");
+        }
+
+        // 退款状态校验:订单必须处于已申请退款状态
+        String applyRefundStatus = order.getApplyRefundStatus();
+        if (!REFUND_STATUS_APPLIED.equals(applyRefundStatus)) {
+            log.warn("处理退款申请失败:订单未申请退款或已处理,订单ID={}, 订单编号={}, 退款状态={}", 
+                    orderId, order.getOrderNumber(), applyRefundStatus);
+            return R.fail("订单未申请退款或已处理,无法重复处理");
+        }
+
+        return null;
+    }
+
+    /**
+     * 处理同意退款
+     * <p>
+     * 1. 更新订单状态为已退款(5)
+     * 2. 更新申请退款状态为已同意(3)
+     * 3. 发送同意退款通知给申请人
+     * </p>
+     *
+     * @param order 订单对象
+     * @return 处理结果
+     */
+    private R<Boolean> processAgreeRefund(LawyerConsultationOrder order) {
+        log.info("开始处理同意退款,订单ID={}, 订单编号={}", order.getId(), order.getOrderNumber());
+
+        try {
+            // 1. 更新订单状态和退款状态
+            Date processTime = new Date();
+            LambdaUpdateWrapper<LawyerConsultationOrder> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.eq(LawyerConsultationOrder::getId, order.getId())
+                    .set(LawyerConsultationOrder::getOrderStatus, LawyerStatusEnum.REFUNDED.getStatus())
+                    .set(LawyerConsultationOrder::getApplyRefundStatus, REFUND_STATUS_AGREED)
+                    .set(LawyerConsultationOrder::getApplyRefundProcessTime, processTime)
+                    .set(LawyerConsultationOrder::getApplyRefundTime, processTime)
+                    .set(LawyerConsultationOrder::getUpdatedTime, processTime);
+
+            int updateCount = consultationOrderMapper.update(null, updateWrapper);
+            if (updateCount <= 0) {
+                log.error("处理同意退款失败:更新订单状态失败,订单ID={}, 订单编号={}", 
+                        order.getId(), order.getOrderNumber());
+                return R.fail("处理同意退款失败,请稍后重试");
+            }
+
+            // 2. 发送同意退款通知给申请人
+            sendRefundAgreeNotice(order);
+
+            log.info("处理同意退款成功,订单ID={}, 订单编号={}", order.getId(), order.getOrderNumber());
+            return R.data(true, "已同意退款申请");
+        } catch (Exception e) {
+            log.error("处理同意退款异常,订单ID={}, 订单编号={}, 异常信息={}", 
+                    order.getId(), order.getOrderNumber(), e.getMessage(), e);
+            return R.fail("处理同意退款失败,请稍后重试");
+        }
+    }
+
+    /**
+     * 处理拒绝退款
+     * <p>
+     * 1. 更新申请退款状态为已拒绝(2)
+     * 2. 更新拒绝退款原因
+     * 3. 发送拒绝退款通知给申请人
+     * </p>
+     *
+     * @param order              订单对象
+     * @param rejectRefundReason 拒绝原因
+     * @return 处理结果
+     */
+    private R<Boolean> processRejectRefund(LawyerConsultationOrder order, String rejectRefundReason) {
+        log.info("开始处理拒绝退款,订单ID={}, 订单编号={}, 拒绝原因={}", 
+                order.getId(), order.getOrderNumber(), rejectRefundReason);
+
+        try {
+            // 1. 更新订单退款状态和拒绝原因
+            Date processTime = new Date();
+            LambdaUpdateWrapper<LawyerConsultationOrder> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.eq(LawyerConsultationOrder::getId, order.getId())
+                    .set(LawyerConsultationOrder::getApplyRefundStatus, REFUND_STATUS_REJECTED)
+                    .set(LawyerConsultationOrder::getRejectRefundReason, rejectRefundReason)
+                    .set(LawyerConsultationOrder::getApplyRefundProcessTime, processTime)
+                    .set(LawyerConsultationOrder::getUpdatedTime, processTime);
+
+            int updateCount = consultationOrderMapper.update(null, updateWrapper);
+            if (updateCount <= 0) {
+                log.error("处理拒绝退款失败:更新订单状态失败,订单ID={}, 订单编号={}", 
+                        order.getId(), order.getOrderNumber());
+                return R.fail("处理拒绝退款失败,请稍后重试");
+            }
+
+            // 2. 发送拒绝退款通知给申请人
+            sendRefundRejectNotice(order, rejectRefundReason);
+
+            log.info("处理拒绝退款成功,订单ID={}, 订单编号={}", order.getId(), order.getOrderNumber());
+            return R.data(true, "已拒绝退款申请");
+        } catch (Exception e) {
+            log.error("处理拒绝退款异常,订单ID={}, 订单编号={}, 异常信息={}", 
+                    order.getId(), order.getOrderNumber(), e.getMessage(), e);
+            return R.fail("处理拒绝退款失败,请稍后重试");
+        }
+    }
+
+    /**
+     * 发送同意退款通知给申请人
+     *
+     * @param order 订单对象
+     */
+    private void sendRefundAgreeNotice(LawyerConsultationOrder order) {
+        try {
+            Integer clientUserId = order.getClientUserId();
+            if (clientUserId == null) {
+                log.warn("发送同意退款通知失败:客户端用户ID为空,订单ID={}", order.getId());
+                return;
+            }
+
+            // 获取申请人接收ID
+            String receiverId = getClientReceiverId(clientUserId);
+            if (!StringUtils.hasText(receiverId)) {
+                log.warn("发送同意退款通知失败:获取申请人接收ID失败,订单ID={}, 用户ID={}", 
+                        order.getId(), clientUserId);
+                return;
+            }
+
+            // 构建通知消息
+            String orderNumber = order.getOrderNumber() != null ? order.getOrderNumber() : "";
+            String message = String.format("您的编号为%s的订单,律师已同意您的退款申请,订单金额将在1-3个工作日原路返还,请注意查收。", 
+                    orderNumber);
+
+            // 创建并保存通知
+            LifeNotice lifeNotice = createRefundNotice(order.getId(), receiverId, "退款申请处理通知", message);
+            int noticeResult = lifeNoticeMapper.insert(lifeNotice);
+            if (noticeResult <= 0) {
+                log.warn("发送同意退款通知失败:保存通知失败,订单ID={}", order.getId());
+                return;
+            }
+
+            // 发送WebSocket消息
+            sendWebSocketMessage(receiverId, lifeNotice);
+
+            log.info("发送同意退款通知成功,接收人ID={}, 订单编号={}", receiverId, orderNumber);
+        } catch (Exception e) {
+            log.error("发送同意退款通知异常,订单ID={}, 异常信息={}", order.getId(), e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 发送拒绝退款通知给申请人
+     *
+     * @param order              订单对象
+     * @param rejectRefundReason 拒绝原因
+     */
+    private void sendRefundRejectNotice(LawyerConsultationOrder order, String rejectRefundReason) {
+        try {
+            Integer clientUserId = order.getClientUserId();
+            if (clientUserId == null) {
+                log.warn("发送拒绝退款通知失败:客户端用户ID为空,订单ID={}", order.getId());
+                return;
+            }
+
+            // 获取申请人接收ID
+            String receiverId = getClientReceiverId(clientUserId);
+            if (!StringUtils.hasText(receiverId)) {
+                log.warn("发送拒绝退款通知失败:获取申请人接收ID失败,订单ID={}, 用户ID={}", 
+                        order.getId(), clientUserId);
+                return;
+            }
+
+            // 构建通知消息
+            String orderNumber = order.getOrderNumber() != null ? order.getOrderNumber() : "";
+            String rejectReason = StringUtils.hasText(rejectRefundReason) ? rejectRefundReason : "无";
+            String message = String.format("您的编号为%s的订单,律师已拒绝您的退款申请。拒绝原因:%s。如您仍有疑问,可通过举报的方式提交平台审核", 
+                    orderNumber, rejectReason);
+
+            // 创建并保存通知
+            LifeNotice lifeNotice = createRefundNotice(order.getId(), receiverId, "拒绝退款通知", message);
+            int noticeResult = lifeNoticeMapper.insert(lifeNotice);
+            if (noticeResult <= 0) {
+                log.warn("发送拒绝退款通知失败:保存通知失败,订单ID={}", order.getId());
+                return;
+            }
+
+            // 发送WebSocket消息
+            sendWebSocketMessage(receiverId, lifeNotice);
+
+            log.info("发送拒绝退款通知成功,接收人ID={}, 订单编号={}", receiverId, orderNumber);
+        } catch (Exception e) {
+            log.error("发送拒绝退款通知异常,订单ID={}, 异常信息={}", order.getId(), e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 获取客户端用户接收ID
+     *
+     * @param clientUserId 客户端用户ID
+     * @return 接收人ID,格式:user_ + 手机号
+     */
+    private String getClientReceiverId(Integer clientUserId) {
+        if (clientUserId == null || clientUserId <= 0) {
+            log.warn("获取客户端用户接收ID失败:用户ID为空或无效");
+            return null;
+        }
+
+        try {
+            LifeUser lifeUser = lifeUserMapper.selectById(clientUserId);
+            if (lifeUser != null && StringUtils.hasText(lifeUser.getUserPhone())) {
+                return "user_" + lifeUser.getUserPhone();
+            }
+        } catch (Exception e) {
+            log.error("获取客户端用户手机号异常,用户ID={}, 异常信息={}", clientUserId, e.getMessage(), e);
+        }
+
+        log.warn("获取客户端用户手机号失败,用户ID={}", clientUserId);
+        return null;
+    }
+
+    /**
+     * 创建退款通知对象
+     *
+     * @param businessId 业务ID(订单ID)
+     * @param receiverId 接收人ID
+     * @param title      通知标题
+     * @param message    通知消息
+     * @return 通知对象
+     */
+    private LifeNotice createRefundNotice(Integer businessId, String receiverId, String title, String message) {
+        LifeNotice lifeNotice = new LifeNotice();
+        lifeNotice.setSenderId(SYSTEM_SENDER_ID);
+        lifeNotice.setBusinessId(businessId);
+        lifeNotice.setReceiverId(receiverId);
+        lifeNotice.setTitle(title);
+        lifeNotice.setNoticeType(1);
+        lifeNotice.setIsRead(0);
+
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("message", message);
+        lifeNotice.setContext(jsonObject.toJSONString());
+        lifeNotice.setBusinessType(1);
+
+        return lifeNotice;
+    }
+
+    /**
+     * 发送WebSocket消息
+     *
+     * @param receiverId 接收人ID
+     * @param lifeNotice 通知对象
+     */
+    private void sendWebSocketMessage(String receiverId, LifeNotice lifeNotice) {
+        try {
+            WebSocketVo webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId(SYSTEM_SENDER_ID);
+            webSocketVo.setReceiverId(receiverId);
+            webSocketVo.setCategory("notice");
+            webSocketVo.setNoticeType("1");
+            webSocketVo.setIsRead(0);
+            webSocketVo.setText(JSON.toJSONString(lifeNotice));
+            webSocketProcess.sendMessage(receiverId, JSON.toJSONString(webSocketVo));
+        } catch (Exception e) {
+            log.error("发送WebSocket消息异常,接收人ID={}, 异常信息={}", receiverId, e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 发送接单通知给客户端用户
+     *
+     * @param order      订单对象
+     * @param acceptTime 接单时间
+     */
+    private void sendAcceptOrderNotice(LawyerConsultationOrder order, Date acceptTime) {
+        try {
+            Integer clientUserId = order.getClientUserId();
+            if (clientUserId == null) {
+                log.warn("发送接单通知失败:客户端用户ID为空,订单ID={}", order.getId());
+                return;
+            }
+
+            // 获取客户端用户接收ID
+            String receiverId = getClientReceiverId(clientUserId);
+            if (!StringUtils.hasText(receiverId)) {
+                log.warn("发送接单通知失败:获取客户端用户接收ID失败,订单ID={}, 用户ID={}", 
+                        order.getId(), clientUserId);
+                return;
+            }
+
+            // 格式化日期
+            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            String acceptTimeStr = dateFormat.format(acceptTime);
+            
+            // 计算当前时间三天后的23:59:59
+            LocalDateTime now = LocalDateTime.now();
+            LocalDateTime validityDateTime = now.plusDays(3)
+                    .withHour(23)
+                    .withMinute(59)
+                    .withSecond(59)
+                    .withNano(0);
+            Date validityDate = Date.from(validityDateTime.atZone(ZoneId.systemDefault()).toInstant());
+            String validityPeriodStr = dateFormat.format(validityDate);
+
+            // 构建通知消息
+            String orderNumber = order.getOrderNumber() != null ? order.getOrderNumber() : "";
+            String message = String.format("您的编号为%s的订单已于%s被律师接单,有效期至%s。请在有效期内与律师沟通。", 
+                    orderNumber, acceptTimeStr, validityPeriodStr);
+
+            // 创建并保存通知
+            LifeNotice lifeNotice = createOrderNotice(order.getId(), receiverId, "接单通知", message);
+            int noticeResult = lifeNoticeMapper.insert(lifeNotice);
+            if (noticeResult <= 0) {
+                log.warn("发送接单通知失败:保存通知失败,订单ID={}", order.getId());
+                return;
+            }
+
+            // 发送WebSocket消息
+            sendWebSocketMessage(receiverId, lifeNotice);
+
+            log.info("发送接单通知成功,接收人ID={}, 订单编号={}", receiverId, orderNumber);
+        } catch (Exception e) {
+            log.error("发送接单通知异常,订单ID={}, 异常信息={}", order.getId(), e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 发送拒绝接单通知给客户端用户
+     *
+     * @param order 订单对象
+     */
+    private void sendRejectOrderNotice(LawyerConsultationOrder order) {
+        try {
+            Integer clientUserId = order.getClientUserId();
+            if (clientUserId == null) {
+                log.warn("发送拒绝接单通知失败:客户端用户ID为空,订单ID={}", order.getId());
+                return;
+            }
+
+            // 获取客户端用户接收ID
+            String receiverId = getClientReceiverId(clientUserId);
+            if (!StringUtils.hasText(receiverId)) {
+                log.warn("发送拒绝接单通知失败:获取客户端用户接收ID失败,订单ID={}, 用户ID={}", 
+                        order.getId(), clientUserId);
+                return;
+            }
+
+            // 获取拒绝原因
+            String rejectReason = order.getReasonOrderRefusal();
+            if (!StringUtils.hasText(rejectReason)) {
+                rejectReason = "无";
+            }
+
+            // 构建通知消息
+            String orderNumber = order.getOrderNumber() != null ? order.getOrderNumber() : "";
+            String message = String.format("您的编号为%s的订单已被拒绝,拒绝原因:%s。订单金额将在1-3个工作日原路返还,请注意查收。", 
+                    orderNumber, rejectReason);
+
+            // 创建并保存通知
+            LifeNotice lifeNotice = createOrderNotice(order.getId(), receiverId, "拒绝接单通知", message);
+            int noticeResult = lifeNoticeMapper.insert(lifeNotice);
+            if (noticeResult <= 0) {
+                log.warn("发送拒绝接单通知失败:保存通知失败,订单ID={}", order.getId());
+                return;
+            }
+
+            // 发送WebSocket消息
+            sendWebSocketMessage(receiverId, lifeNotice);
+
+            log.info("发送拒绝接单通知成功,接收人ID={}, 订单编号={}", receiverId, orderNumber);
+        } catch (Exception e) {
+            log.error("发送拒绝接单通知异常,订单ID={}, 异常信息={}", order.getId(), e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 创建订单通知对象
+     *
+     * @param businessId 业务ID(订单ID)
+     * @param receiverId 接收人ID
+     * @param title      通知标题
+     * @param message    通知消息
+     * @return 通知对象
+     */
+    private LifeNotice createOrderNotice(Integer businessId, String receiverId, String title, String message) {
+        LifeNotice lifeNotice = new LifeNotice();
+        lifeNotice.setSenderId(SYSTEM_SENDER_ID);
+        lifeNotice.setBusinessId(businessId);
+        lifeNotice.setReceiverId(receiverId);
+        lifeNotice.setTitle(title);
+        lifeNotice.setNoticeType(1);
+        lifeNotice.setIsRead(0);
+
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("message", message);
+        lifeNotice.setContext(jsonObject.toJSONString());
+        lifeNotice.setBusinessType(1);
+
+        return lifeNotice;
+    }
+
+    /**
+     * 检查订单接单后是否有消息记录
+     * <p>
+     * 根据订单ID查询订单信息,获取律师接单时间、用户ID和律师ID,
+     * 然后查询 life_message 表,检查是否存在律师发送给用户的消息记录,
+     * 且消息创建时间大于接单时间。
+     * </p>
+     * <p>
+     * 处理流程:
+     * 1. 参数校验:订单ID不能为空
+     * 2. 查询订单信息:获取接单时间、用户ID、律师ID
+     * 3. 校验订单信息:订单必须存在,且必须有接单时间
+     * 4. 查询律师信息:获取律师手机号
+     * 5. 查询用户信息:获取用户手机号
+     * 6. 构建消息查询条件:sender_id=律师phone,receiver_id=用户phone,created_time>接单时间
+     * 7. 查询消息记录:如果存在记录返回true,否则返回false
+     * </p>
+     *
+     * @param id 订单ID,不能为空
+     * @return true表示有消息记录,false表示无消息记录
+     * @throws IllegalArgumentException 当订单ID无效或订单不存在时抛出
+     * @throws RuntimeException        当查询异常时抛出
+     * @author system
+     * @since 2025-01-XX
+     */
+    @Override
+    public Boolean checkMessageAfterAccept(String id) {
+        log.info("检查订单接单后是否有消息记录开始,订单ID={}", id);
+
+        // 参数校验
+        validateOrderId(id);
+
+        try {
+            // 查询订单信息
+            LawyerConsultationOrder order = queryOrderById(id);
+
+            // 校验订单信息
+            validateOrderInfo(order, id);
+
+            // 获取接单时间、用户ID、律师ID
+            Date acceptOrdersTime = order.getAcceptOrdersTime();
+            Integer clientUserId = order.getClientUserId();
+            Integer lawyerUserId = order.getLawyerUserId();
+
+            // 查询律师和用户信息,获取手机号
+            String lawyerPhone = queryLawyerPhone(lawyerUserId, id);
+            String userPhone = queryUserPhone(clientUserId, id);
+
+            // 构建消息查询条件并查询
+            boolean hasMessage = queryMessageAfterAccept(lawyerPhone, userPhone, acceptOrdersTime, id);
+
+            log.info("检查订单接单后是否有消息记录完成,订单ID={},是否有消息={}", id, hasMessage);
+            return hasMessage;
+
+        } catch (IllegalArgumentException e) {
+            log.warn("检查订单接单后是否有消息记录参数校验失败,订单ID={},错误信息:{}", id, e.getMessage());
+            throw e;
+        } catch (RuntimeException e) {
+            log.error("检查订单接单后是否有消息记录异常,订单ID={},异常信息:{}", id, e.getMessage(), e);
+            throw new RuntimeException("检查消息记录失败:" + e.getMessage(), e);
+        } catch (Exception e) {
+            log.error("检查订单接单后是否有消息记录未知异常,订单ID={},异常信息:{}", id, e.getMessage(), e);
+            throw new RuntimeException("检查消息记录失败,请稍后重试", e);
+        }
+    }
+
+    /**
+     * 根据订单ID查询订单信息
+     * <p>
+     * 查询订单的详细信息
+     * </p>
+     *
+     * @param id 订单ID
+     * @return 订单信息
+     * @throws RuntimeException 当查询异常时抛出
+     */
+    private LawyerConsultationOrder queryOrderById(String id) {
+        try {
+            LawyerConsultationOrder order = consultationOrderMapper.selectById(id);
+            if (order == null) {
+                log.warn("检查订单接单后是否有消息记录:订单不存在,订单ID={}", id);
+                throw new IllegalArgumentException("订单不存在,订单ID=" + id);
+            }
+            log.debug("查询订单信息成功,订单ID={}", id);
+            return order;
+        } catch (IllegalArgumentException e) {
+            throw e;
+        } catch (Exception e) {
+            log.error("查询订单信息异常,订单ID={},异常信息:{}", id, e.getMessage(), e);
+            throw new RuntimeException("查询订单信息失败:" + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 校验订单信息
+     * <p>
+     * 检查订单是否包含必要的信息(接单时间、用户ID、律师ID)
+     * </p>
+     *
+     * @param order 订单信息
+     * @param id    订单ID
+     * @throws IllegalArgumentException 当订单信息不完整时抛出
+     */
+    private void validateOrderInfo(LawyerConsultationOrder order, String id) {
+        if (order.getAcceptOrdersTime() == null) {
+            log.warn("检查订单接单后是否有消息记录:订单接单时间为空,订单ID={}", id);
+            throw new IllegalArgumentException("订单接单时间为空,订单ID=" + id);
+        }
+
+        if (order.getClientUserId() == null) {
+            log.warn("检查订单接单后是否有消息记录:订单用户ID为空,订单ID={}", id);
+            throw new IllegalArgumentException("订单用户ID为空,订单ID=" + id);
+        }
+
+        if (order.getLawyerUserId() == null) {
+            log.warn("检查订单接单后是否有消息记录:订单律师ID为空,订单ID={}", id);
+            throw new IllegalArgumentException("订单律师ID为空,订单ID=" + id);
+        }
+    }
+
+    /**
+     * 查询律师手机号
+     * <p>
+     * 根据律师ID查询律师信息,获取手机号
+     * </p>
+     *
+     * @param lawyerUserId 律师ID
+     * @param orderId      订单ID(用于日志)
+     * @return 律师手机号
+     * @throws RuntimeException 当查询异常或律师不存在时抛出
+     */
+    private String queryLawyerPhone(Integer lawyerUserId, String orderId) {
+        try {
+            LawyerUser lawyerUser = lawyerUserMapper.selectById(lawyerUserId);
+            if (lawyerUser == null) {
+                log.warn("检查订单接单后是否有消息记录:律师不存在,律师ID={},订单ID={}", lawyerUserId, orderId);
+                throw new RuntimeException("律师不存在,律师ID=" + lawyerUserId);
+            }
+
+            String phone = lawyerUser.getPhone();
+            if (phone == null || phone.trim().isEmpty()) {
+                log.warn("检查订单接单后是否有消息记录:律师手机号为空,律师ID={},订单ID={}", lawyerUserId, orderId);
+                throw new RuntimeException("律师手机号为空,律师ID=" + lawyerUserId);
+            }
+
+            log.debug("查询律师手机号成功,律师ID={},手机号={}", lawyerUserId, phone);
+            return phone;
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            log.error("查询律师手机号异常,律师ID={},订单ID={},异常信息:{}", lawyerUserId, orderId, e.getMessage(), e);
+            throw new RuntimeException("查询律师手机号失败:" + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 查询用户手机号
+     * <p>
+     * 根据用户ID查询用户信息,获取手机号
+     * </p>
+     *
+     * @param clientUserId 用户ID
+     * @param orderId      订单ID(用于日志)
+     * @return 用户手机号
+     * @throws RuntimeException 当查询异常或用户不存在时抛出
+     */
+    private String queryUserPhone(Integer clientUserId, String orderId) {
+        try {
+            LifeUser lifeUser = lifeUserMapper.selectById(clientUserId);
+            if (lifeUser == null) {
+                log.warn("检查订单接单后是否有消息记录:用户不存在,用户ID={},订单ID={}", clientUserId, orderId);
+                throw new RuntimeException("用户不存在,用户ID=" + clientUserId);
+            }
+
+            String phone = lifeUser.getUserPhone();
+            if (phone == null || phone.trim().isEmpty()) {
+                log.warn("检查订单接单后是否有消息记录:用户手机号为空,用户ID={},订单ID={}", clientUserId, orderId);
+                throw new RuntimeException("用户手机号为空,用户ID=" + clientUserId);
+            }
+
+            log.debug("查询用户手机号成功,用户ID={},手机号={}", clientUserId, phone);
+            return phone;
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            log.error("查询用户手机号异常,用户ID={},订单ID={},异常信息:{}", clientUserId, orderId, e.getMessage(), e);
+            throw new RuntimeException("查询用户手机号失败:" + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 查询接单后的消息记录
+     * <p>
+     * 查询 life_message 表,条件:
+     * - sender_id = 律师phone(格式:lawyer_phone)
+     * - receiver_id = 用户phone(格式:user_phone)
+     * - created_time > 接单时间
+     * </p>
+     *
+     * @param lawyerPhone      律师手机号
+     * @param userPhone        用户手机号
+     * @param acceptOrdersTime 接单时间
+     * @param orderId          订单ID(用于日志)
+     * @return true表示有消息记录,false表示无消息记录
+     */
+    private boolean queryMessageAfterAccept(String lawyerPhone, String userPhone, Date acceptOrdersTime, String orderId) {
+        try {
+            // 构建发送者ID和接收者ID
+            String senderId = LAWYER_SENDER_PREFIX + lawyerPhone;
+            String receiverId = USER_RECEIVER_PREFIX + userPhone;
+
+            // 构建查询条件
+            LambdaQueryWrapper<LifeMessage> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(LifeMessage::getSenderId, senderId)
+                    .eq(LifeMessage::getReceiverId, receiverId)
+                    .gt(LifeMessage::getCreatedTime, acceptOrdersTime)
+                    .eq(LifeMessage::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
+                    .last("LIMIT 1");
+
+            // 查询消息记录(只需要判断是否存在,使用 selectOne 并判断是否为 null)
+            LifeMessage message = lifeMessageMapper.selectOne(queryWrapper);
+
+            boolean hasMessage = message != null;
+            log.debug("查询接单后消息记录完成,订单ID={},发送者ID={},接收者ID={},接单时间={},是否有消息={}",
+                    orderId, senderId, receiverId, acceptOrdersTime, hasMessage);
+
+            return hasMessage;
+        } catch (Exception e) {
+            log.error("查询接单后消息记录异常,订单ID={},律师手机号={},用户手机号={},接单时间={},异常信息:{}",
+                    orderId, lawyerPhone, userPhone, acceptOrdersTime, e.getMessage(), e);
+            // 异常时返回 false,避免影响主流程
+            return false;
+        }
+    }
+}
+

+ 117 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerCommonQuestionServiceImpl.java

@@ -0,0 +1,117 @@
+package shop.alien.lawyer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerCommonQuestion;
+import shop.alien.lawyer.service.LawyerCommonQuestionService;
+import shop.alien.mapper.LawyerCommonQuestionMapper;
+
+import java.util.List;
+
+/**
+ * 常见问题 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawyerCommonQuestionServiceImpl extends ServiceImpl<LawyerCommonQuestionMapper, LawyerCommonQuestion> implements LawyerCommonQuestionService {
+
+    private final LawyerCommonQuestionMapper commonQuestionMapper;
+
+    @Override
+    public R<IPage<LawyerCommonQuestion>> getCommonQuestionList(int pageNum, int pageSize, String questionText,
+                                                                Integer categoryType, Integer problemScenarioId, Integer status) {
+        log.info("LawyerCommonQuestionServiceImpl.getCommonQuestionList?pageNum={},pageSize={},questionText={},categoryType={},problemScenarioId={},status={}",
+                pageNum, pageSize, questionText, categoryType, problemScenarioId, status);
+        Page<LawyerCommonQuestion> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<LawyerCommonQuestion> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerCommonQuestion::getDeleteFlag, 0);
+        if (StringUtils.hasText(questionText)) {
+            queryWrapper.like(LawyerCommonQuestion::getQuestionText, questionText);
+        }
+        if (categoryType != null) {
+            queryWrapper.eq(LawyerCommonQuestion::getCategoryType, categoryType);
+        }
+        if (problemScenarioId != null) {
+            queryWrapper.eq(LawyerCommonQuestion::getProblemScenarioId, problemScenarioId);
+        }
+        if (status != null) {
+            queryWrapper.eq(LawyerCommonQuestion::getStatus, status);
+        }
+        queryWrapper.orderByAsc(LawyerCommonQuestion::getSortOrder);
+        IPage<LawyerCommonQuestion> pageResult = this.page(page, queryWrapper);
+        return R.data(pageResult);
+    }
+
+    @Override
+    public List<LawyerCommonQuestion> getListByCategoryType(Integer categoryType) {
+        log.info("LawyerCommonQuestionServiceImpl.getListByCategoryType?categoryType={}", categoryType);
+        LambdaQueryWrapper<LawyerCommonQuestion> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerCommonQuestion::getCategoryType, categoryType)
+                .eq(LawyerCommonQuestion::getDeleteFlag, 0)
+                .eq(LawyerCommonQuestion::getStatus, 1)
+                .orderByAsc(LawyerCommonQuestion::getSortOrder);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public R<LawyerCommonQuestion> addCommonQuestion(LawyerCommonQuestion commonQuestion) {
+        log.info("LawyerCommonQuestionServiceImpl.addCommonQuestion?commonQuestion={}", commonQuestion);
+        boolean result = this.save(commonQuestion);
+        if (result) {
+            return R.data(commonQuestion);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<LawyerCommonQuestion> editCommonQuestion(LawyerCommonQuestion commonQuestion) {
+        log.info("LawyerCommonQuestionServiceImpl.editCommonQuestion?commonQuestion={}", commonQuestion);
+        boolean result = this.updateById(commonQuestion);
+        if (result) {
+            return R.data(commonQuestion);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteCommonQuestion(Integer id) {
+        log.info("LawyerCommonQuestionServiceImpl.deleteCommonQuestion?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @Override
+    public R<List<LawyerCommonQuestion>> getCommonQuestionListByNum(Integer num) {
+        log.info("LawyerCommonQuestionServiceImpl.getCommonQuestionListByNum?num={}", num);
+
+        // 参数校验:如果 num 小于等于0,返回空列表
+        if (num == null || num <= 0) {
+            return R.data(new java.util.ArrayList<>());
+        }
+
+        // 构建查询条件:只查询未删除的记录,随机排序
+        LambdaQueryWrapper<LawyerCommonQuestion> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerCommonQuestion::getDeleteFlag, 0)
+                .last("ORDER BY RAND() LIMIT " + num);
+
+        List<LawyerCommonQuestion> list = this.list(queryWrapper);
+        return R.data(list);
+    }
+}
+

+ 2368 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerConsultationOrderServiceImpl.java

@@ -0,0 +1,2368 @@
+package shop.alien.lawyer.service.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.alibaba.nacos.common.utils.CollectionUtils;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.math.RandomUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.*;
+import shop.alien.entity.store.dto.LawyerConsultationOrderDto;
+import shop.alien.entity.store.dto.PayStatusRequest;
+import shop.alien.entity.store.vo.LawyerConsultationOrderVO;
+import shop.alien.entity.store.vo.OrderRevenueVO;
+import shop.alien.entity.store.vo.WebSocketVo;
+import shop.alien.lawyer.config.WebSocketProcess;
+import shop.alien.lawyer.payment.PaymentStrategyFactory;
+import shop.alien.lawyer.service.LawyerConsultationOrderService;
+import shop.alien.lawyer.service.LawyerUserService;
+import shop.alien.lawyer.service.OrderExpirationService;
+import shop.alien.lawyer.util.DateUtils;
+import shop.alien.mapper.*;
+import shop.alien.util.common.constant.LawyerStatusEnum;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 咨询订单 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsultationOrderMapper, LawyerConsultationOrder> implements LawyerConsultationOrderService {
+
+    private final LawyerConsultationOrderMapper consultationOrderMapper;
+    private final LawyerUserService lawyerUserService;
+    private final LawyerServiceAreaMapper lawyerServiceAreaMapper;
+    private final LawyerUserMapper lawyerUserMapper;
+    private final LawyerExpertiseAreaMapper lawyerExpertiseAreaMapper;
+    private final OrderExpirationService orderExpirationService;
+//    private final AliApi aliApi;
+    private final LawFirmMapper lawFirmMapper;
+    private final LifeNoticeMapper lifeNoticeMapper;
+    private final LifeUserMapper lifeUserMapper;
+    private final WebSocketProcess webSocketProcess;
+
+    private final LifeMessageMapper lifeMessageMapper;
+    private final StoreCommentMapper storeCommentMapper;
+    private final LawyerUserViolationMapper lawyerUserViolationMapper;
+    private final CommentAppealMapper commentAppealMapper;
+    private final OrderReviewMapper orderReviewMapper;
+
+
+    private final PaymentStrategyFactory paymentStrategyFactory;
+
+    /**
+     * 系统发送者ID
+     */
+    private static final String SYSTEM_SENDER_ID = "system";
+
+    /**
+     * 删除标记:未删除
+     */
+    private static final Integer DELETE_FLAG_NOT_DELETED = 0;
+
+    /**
+     * 金额单位转换:分转元
+     */
+    private static final int FEN_TO_YUAN = 100;
+
+    /**
+     * 默认价格字符串(当所有收费字段都为空时)
+     */
+    private static final String DEFAULT_PRICE_STR = "";
+
+    /**
+     * 默认格式化价格(当价格为null时)
+     */
+    private static final String DEFAULT_FORMATTED_PRICE = "0";
+
+    /**
+     * 价格格式:分钟收费
+     */
+    private static final String PRICE_FORMAT_MINUTE = "¥%s/%d分钟";
+
+    /**
+     * 价格格式:次数收费
+     */
+    private static final String PRICE_FORMAT_TIME = "¥%s/%d次";
+
+    /**
+     * 小数位数:保留2位小数
+     */
+    private static final int DECIMAL_SCALE = 2;
+
+    @Value("${order.coefficients}")
+    private String coefficients;
+
+    @Override
+    public R<IPage<LawyerConsultationOrderVO>> getConsultationOrderList(int pageNum, int pageSize, String orderNumber,
+                                                                        Integer clientUserId, Integer lawyerUserId, String lawyerName, Integer orderStatus) {
+        log.info("LawyerConsultationOrderServiceImpl.getConsultationOrderList?pageNum={},pageSize={},orderNumber={},clientUserId={},lawyerUserId={},lawyerName={},orderStatus={}",
+                pageNum, pageSize, orderNumber, clientUserId, lawyerUserId, lawyerName, orderStatus);
+
+        // 創建分頁對象
+        Page<LawyerConsultationOrderVO> page = new Page<>(pageNum, pageSize);
+
+        // 如果按律師姓名搜索,先查詢匹配的律師ID列表
+        List<Integer> lawyerUserIds = null;
+        if (StringUtils.hasText(lawyerName)) {
+            LambdaQueryWrapper<LawyerUser> lawyerQueryWrapper = new LambdaQueryWrapper<>();
+            lawyerQueryWrapper.eq(LawyerUser::getDeleteFlag, 0);
+            lawyerQueryWrapper.like(LawyerUser::getName, lawyerName);
+            List<LawyerUser> lawyerUsers = lawyerUserService.list(lawyerQueryWrapper);
+            if (lawyerUsers != null && !lawyerUsers.isEmpty()) {
+                lawyerUserIds = lawyerUsers.stream()
+                        .map(LawyerUser::getId)
+                        .collect(Collectors.toList());
+            } else {
+                // 如果沒有找到匹配的律師,返回空結果
+                Page<LawyerConsultationOrderVO> emptyPage = new Page<>(pageNum, pageSize);
+                emptyPage.setRecords(Collections.emptyList());
+                emptyPage.setTotal(0);
+                return R.data(emptyPage);
+            }
+        }
+
+        // 使用JOIN查詢,直接聯查律師信息
+        IPage<LawyerConsultationOrderVO> voPage = consultationOrderMapper.getConsultationOrderListWithLawyer(
+                page, orderNumber, clientUserId, lawyerUserId, lawyerName, orderStatus, lawyerUserIds);
+
+        // 為待支付訂單計算倒計時(30分鐘有效期)
+        if (voPage != null && voPage.getRecords() != null) {
+            Date now = new Date();
+            long validitySeconds = 30 * 60; // 30分鐘 = 1800秒
+
+            for (LawyerConsultationOrderVO vo : voPage.getRecords()) {
+                // 僅對待支付訂單(orderStatus=0)計算倒計時
+                if (vo.getOrderStatus() != null && vo.getOrderStatus() == 0) {
+                    Date orderTime = vo.getOrderTime();
+                    if (orderTime != null) {
+                        long elapsedSeconds = (now.getTime() - orderTime.getTime()) / 1000;
+                        long remainingSeconds = validitySeconds - elapsedSeconds;
+                        // 如果已過期,設置為0
+                        vo.setCountdownSeconds(Math.max(0, remainingSeconds));
+                    } else {
+                        vo.setCountdownSeconds(0L);
+                    }
+                } else {
+                    // 非待支付訂單,倒計時為null
+                    vo.setCountdownSeconds(null);
+                }
+            }
+        }
+
+        return R.data(voPage);
+    }
+
+    /**
+     * 將訂單分頁結果轉換為VO並填充律師信息
+     */
+    private IPage<LawyerConsultationOrderVO> convertToVO(IPage<LawyerConsultationOrder> pageResult) {
+        // 創建VO分頁對象
+        Page<LawyerConsultationOrderVO> voPage = new Page<>(pageResult.getCurrent(), pageResult.getSize(), pageResult.getTotal());
+
+        List<LawyerConsultationOrder> records = pageResult.getRecords();
+        if (records == null || records.isEmpty()) {
+            voPage.setRecords(Collections.emptyList());
+            return voPage;
+        }
+
+        // 收集所有律師ID
+        Set<Integer> lawyerUserIds = records.stream()
+                .map(LawyerConsultationOrder::getLawyerUserId)
+                .filter(id -> id != null)
+                .collect(Collectors.toSet());
+
+        // 批量查詢律師信息
+        Map<Integer, LawyerUser> lawyerMap = new HashMap<>();
+        if (!lawyerUserIds.isEmpty()) {
+            LambdaQueryWrapper<LawyerUser> lawyerWrapper = new LambdaQueryWrapper<>();
+            lawyerWrapper.in(LawyerUser::getId, lawyerUserIds);
+            lawyerWrapper.eq(LawyerUser::getDeleteFlag, 0);
+            List<LawyerUser> lawyerUsers = lawyerUserService.list(lawyerWrapper);
+            if (lawyerUsers != null && !lawyerUsers.isEmpty()) {
+                lawyerMap = lawyerUsers.stream()
+                        .collect(Collectors.toMap(LawyerUser::getId, lawyer -> lawyer, (k1, k2) -> k1));
+            }
+        }
+
+        // 批量查询律所信息
+        Map<Integer, LawFirm> lawFirmMap = new HashMap<>();
+        if (lawyerMap != null && !lawyerMap.isEmpty()) {
+            Set<Integer> firmIds = lawyerMap.values().stream()
+                    .map(LawyerUser::getFirmId)
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toSet());
+            if (!firmIds.isEmpty()) {
+                List<LawFirm> lawFirms = lawFirmMapper.selectBatchIds(firmIds);
+                if (lawFirms != null) {
+                    lawFirmMap = lawFirms.stream()
+                            .filter(firm -> firm.getDeleteFlag() == null || firm.getDeleteFlag() == 0)
+                            .collect(Collectors.toMap(LawFirm::getId, firm -> firm, (k1, k2) -> k1));
+                }
+            }
+        }
+
+        // 轉換為VO並填充律師信息
+        final Map<Integer, LawyerUser> finalLawyerMap = lawyerMap;
+        final Map<Integer, LawFirm> finalLawFirmMap = lawFirmMap;
+        List<LawyerConsultationOrderVO> voList = records.stream()
+                .map(order -> {
+                    LawyerConsultationOrderVO vo = new LawyerConsultationOrderVO();
+                    // 複製訂單基本信息
+                    BeanUtils.copyProperties(order, vo);
+
+                    // 填充律師信息
+                    LawyerUser lawyer = finalLawyerMap.get(order.getLawyerUserId());
+                    if (lawyer != null) {
+                        vo.setLawyerName(lawyer.getName());
+                        vo.setLawyerPhone(lawyer.getPhone());
+                        vo.setLawyerEmail(lawyer.getEmail());
+                        vo.setLawyerCertificateNo(lawyer.getLawyerCertificateNo());
+                        // 通过firm_id查询律所名称
+                        if (lawyer.getFirmId() != null) {
+                            LawFirm lawFirm = finalLawFirmMap.get(lawyer.getFirmId());
+                            vo.setLawFirm(lawFirm != null ? lawFirm.getFirmName() : null);
+                        } else {
+                            vo.setLawFirm(null);
+                        }
+                        vo.setPracticeYears(lawyer.getPracticeYears());
+                        vo.setSpecialtyFields(lawyer.getSpecialtyFields());
+                        vo.setCertificationStatus(lawyer.getCertificationStatus());
+                        vo.setServiceScore(lawyer.getServiceScore());
+                        vo.setServiceCount(lawyer.getServiceCount());
+                        vo.setLawyerConsultationFee(lawyer.getConsultationFee());
+                        vo.setProvince(lawyer.getProvince());
+                        vo.setCity(lawyer.getCity());
+                        vo.setDistrict(lawyer.getDistrict());
+                        vo.setAddress(lawyer.getAddress());
+                        vo.setHeadImg(lawyer.getHeadImg());
+                        vo.setNickName(lawyer.getNickName());
+                        vo.setPersonalIntroduction(lawyer.getPersonalIntroduction());
+                    }
+
+                    return vo;
+                })
+                .collect(Collectors.toList());
+
+        voPage.setRecords(voList);
+        return voPage;
+    }
+
+    @Override
+    public R<LawyerConsultationOrder> addConsultationOrder(LawyerConsultationOrder consultationOrder) {
+        log.info("LawyerConsultationOrderServiceImpl.addConsultationOrder?consultationOrder={}", consultationOrder);
+        boolean result = this.save(consultationOrder);
+        if (result) {
+            return R.data(consultationOrder);
+        }
+        return R.fail("新增失败");
+    }
+
+    /**
+     * 编辑咨询订单
+     * <p>
+     * 编辑前会进行以下校验:
+     * 1. 参数校验:订单ID不能为空
+     * 2. 订单存在性校验:订单必须存在
+     * 3. 订单状态校验:已完成的订单不允许修改关键信息
+     * </p>
+     *
+     * @param consultationOrder 咨询订单
+     * @return 编辑结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R<LawyerConsultationOrder> editConsultationOrder(LawyerConsultationOrder consultationOrder) {
+        Integer orderId = consultationOrder.getId();
+        log.info("开始编辑咨询订单,订单ID={}", orderId);
+
+        // 参数校验:订单ID不能为空
+        if (orderId == null) {
+            log.warn("编辑咨询订单失败:订单ID为空");
+            return R.fail("订单ID不能为空");
+        }
+
+        // 查询订单信息,验证订单是否存在
+        LawyerConsultationOrder existingOrder = consultationOrderMapper.selectById(orderId);
+        if (existingOrder == null) {
+            log.warn("编辑咨询订单失败:订单不存在,订单ID={}", orderId);
+            return R.fail("订单不存在");
+        }
+
+        // 记录修改前的订单状态
+        Integer oldOrderStatus = existingOrder.getOrderStatus();
+        Integer oldPaymentStatus = existingOrder.getPaymentStatus();
+        Integer newOrderStatus = consultationOrder.getOrderStatus();
+        Integer newPaymentStatus = consultationOrder.getPaymentStatus();
+
+        log.info("编辑咨询订单,订单ID={}, 订单编号={}, 原订单状态={}, 新订单状态={}, 原支付状态={}, 新支付状态={}",
+                orderId, existingOrder.getOrderNumber(), oldOrderStatus, newOrderStatus,
+                oldPaymentStatus, newPaymentStatus);
+
+        // 订单状态校验:已完成的订单不允许修改关键信息(订单状态、支付状态、订单金额等)
+        Integer completedStatus = LawyerStatusEnum.COMPLETE.getStatus(); // 3:已完成
+        if (completedStatus.equals(oldOrderStatus)) {
+            // 如果订单已完成,只允许修改评价相关字段(rating, comment)
+            if (newOrderStatus != null && !newOrderStatus.equals(oldOrderStatus)) {
+                log.warn("编辑咨询订单失败:订单已完成,不允许修改订单状态,订单ID={}, 订单编号={}",
+                        orderId, existingOrder.getOrderNumber());
+                return R.fail("订单已完成,不允许修改订单状态");
+            }
+            if (newPaymentStatus != null && !newPaymentStatus.equals(oldPaymentStatus)) {
+                log.warn("编辑咨询订单失败:订单已完成,不允许修改支付状态,订单ID={}, 订单编号={}",
+                        orderId, existingOrder.getOrderNumber());
+                return R.fail("订单已完成,不允许修改支付状态");
+            }
+            if (consultationOrder.getOrderAmount() != null &&
+                    !consultationOrder.getOrderAmount().equals(existingOrder.getOrderAmount())) {
+                log.warn("编辑咨询订单失败:订单已完成,不允许修改订单金额,订单ID={}, 订单编号={}",
+                        orderId, existingOrder.getOrderNumber());
+                return R.fail("订单已完成,不允许修改订单金额");
+            }
+        }
+
+        // 已取消的订单不允许修改为其他状态(除非是管理员操作,这里暂不限制)
+        Integer cancelStatus = LawyerStatusEnum.CANCEL.getStatus(); // 4:已取消
+        if (cancelStatus.equals(oldOrderStatus) && newOrderStatus != null &&
+                !cancelStatus.equals(newOrderStatus)) {
+            log.warn("编辑咨询订单警告:订单已取消,尝试修改订单状态,订单ID={}, 订单编号={}, 原状态={}, 新状态={}",
+                    orderId, existingOrder.getOrderNumber(), oldOrderStatus, newOrderStatus);
+            // 这里可以根据业务需求决定是否允许,暂时允许修改
+        }
+
+        // 如果订单状态从待支付变为已支付,需要更新支付时间
+        Integer waitPayStatus = LawyerStatusEnum.WAIT_PAY.getStatus(); // 0:待支付
+        if (waitPayStatus.equals(oldOrderStatus) && newOrderStatus != null &&
+                !waitPayStatus.equals(newOrderStatus) && newPaymentStatus != null && newPaymentStatus == 1) {
+            if (consultationOrder.getPaymentTime() == null) {
+                consultationOrder.setPaymentTime(new Date());
+                log.info("订单状态从待支付变为已支付,自动设置支付时间,订单ID={}, 订单编号={}",
+                        orderId, existingOrder.getOrderNumber());
+            }
+        }
+
+        // 设置更新时间
+        consultationOrder.setUpdatedTime(new Date());
+
+        // 执行更新操作
+        boolean result = this.updateById(consultationOrder);
+        if (result) {
+            log.info("编辑咨询订单成功,订单ID={}, 订单编号={}, 原订单状态={}, 新订单状态={}",
+                    orderId, existingOrder.getOrderNumber(), oldOrderStatus, newOrderStatus);
+            // 重新查询更新后的订单信息
+            LawyerConsultationOrder updatedOrder = consultationOrderMapper.selectById(orderId);
+            return R.data(updatedOrder != null ? updatedOrder : consultationOrder);
+        } else {
+            log.error("编辑咨询订单失败:数据库更新失败,订单ID={}, 订单编号={}",
+                    orderId, existingOrder.getOrderNumber());
+            return R.fail("修改失败");
+        }
+    }
+
+    /**
+     * 删除咨询订单
+     * <p>
+     * 删除前会进行以下校验:
+     * 1. 参数校验:订单ID不能为空
+     * 2. 订单存在性校验:订单必须存在
+     * 3. 订单状态校验:进行中的订单不允许删除
+     * 4. 如果订单是待支付状态,会取消Redis中的支付超时计时器
+     * </p>
+     *
+     * @param id 订单ID
+     * @return 删除结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R<Boolean> deleteConsultationOrder(Integer id) {
+        log.info("开始删除咨询订单,订单ID={}", id);
+
+        // 参数校验
+        if (id == null) {
+            log.warn("删除咨询订单失败:订单ID为空");
+            return R.fail("订单ID不能为空");
+        }
+
+        // 查询订单信息
+        LawyerConsultationOrder order = consultationOrderMapper.selectById(id);
+        if (order == null) {
+            log.warn("删除咨询订单失败:订单不存在,订单ID={}", id);
+            return R.fail("订单不存在");
+        }
+
+        // 检查订单状态:进行中的订单不允许删除
+        Integer orderStatus = order.getOrderStatus();
+        Integer inProgressStatus = LawyerStatusEnum.INPROGRESS.getStatus(); // 2:进行中
+        if (inProgressStatus.equals(orderStatus)) {
+            log.warn("删除咨询订单失败:订单进行中,不允许删除,订单ID={}, 订单编号={}",
+                    id, order.getOrderNumber());
+            return R.fail("订单进行中,不允许删除");
+        }
+
+        // 如果订单是待支付状态,取消Redis中的订单支付超时计时
+        Integer waitPayStatus = LawyerStatusEnum.WAIT_PAY.getStatus(); // 0:待支付
+        if (waitPayStatus.equals(orderStatus) && order.getOrderNumber() != null) {
+            try {
+                orderExpirationService.cancelOrderPaymentTimeout(order.getOrderNumber());
+                log.info("已取消订单支付超时计时,订单编号={}", order.getOrderNumber());
+            } catch (Exception e) {
+                log.error("取消订单支付超时计时失败,订单编号={}", order.getOrderNumber(), e);
+                // 继续执行删除操作,不因取消计时器失败而中断
+            }
+        }
+
+        // 执行删除操作
+        boolean result = this.removeById(id);
+        if (result) {
+            log.info("删除咨询订单成功,订单ID={}, 订单编号={}", id, order.getOrderNumber());
+            return R.data(true, "删除成功");
+        } else {
+            log.error("删除咨询订单失败:数据库操作失败,订单ID={}, 订单编号={}", id, order.getOrderNumber());
+            return R.fail("删除失败");
+        }
+    }
+
+    /*@Override
+    public R<Map<String, Object>> startConsultation(Integer lawyerId, String question, String sessionId, Integer clientUserId, Integer problemScenarioId) {
+        log.info("LawyerConsultationOrderServiceImpl.startConsultation?lawyerId={},question={},sessionId={},clientUserId={},problemScenarioId={}",
+                lawyerId, question, sessionId, clientUserId, problemScenarioId);
+
+        // 创建咨询订单
+        LawyerConsultationOrder order = new LawyerConsultationOrder();
+        order.setLawyerUserId(lawyerId);
+        order.setClientUserId(clientUserId);
+        order.setProblemScenarioId(problemScenarioId);
+        order.setProblemDescription(question);
+        order.setOrderStatus(0);  // 待支付
+        order.setPaymentStatus(0);  // 未支付
+
+        boolean saved = this.save(order);
+        if (!saved) {
+            return R.fail("创建咨询订单失败");
+        }
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("consultationId", order.getId());
+        result.put("lawyerId", lawyerId);
+        result.put("status", "pending");  // pending(待响应), active(进行中), closed(已结束)
+        result.put("createTime", order.getCreatedTime() != null ?
+                new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(order.getCreatedTime()) : "");
+
+        return R.data(result);
+    }*/
+
+
+    @Override
+    public R<LawyerConsultationOrderDto> consultNow(LawyerConsultationOrder lawyerConsultationOrder) {
+            log.info("LawyerConsultationOrderServiceImpl.consultNow?lawyerConsultationOrder={}", lawyerConsultationOrder);
+
+        // 参数校验
+        if (lawyerConsultationOrder == null) {
+            log.warn("创建咨询订单失败:订单信息为空");
+            return R.fail("订单信息不能为空");
+        }
+
+        Integer clientUserId = lawyerConsultationOrder.getClientUserId();
+        Integer lawyerUserId = lawyerConsultationOrder.getLawyerUserId();
+        Integer orderAmount = lawyerConsultationOrder.getOrderAmount();
+
+        if (clientUserId == null || clientUserId <= 0) {
+            log.warn("创建咨询订单失败:客户端用户ID无效,clientUserId={}", clientUserId);
+            return R.fail("客户端用户ID不能为空");
+        }
+
+        if (lawyerUserId == null || lawyerUserId <= 0) {
+            log.warn("创建咨询订单失败:律师用户ID无效,lawyerUserId={}", lawyerUserId);
+            return R.fail("律师用户ID不能为空");
+        }
+
+        if (orderAmount == null || orderAmount <= 0) {
+            log.warn("创建咨询订单失败:订单金额无效,orderAmount={}", orderAmount);
+            return R.fail("订单金额不能为空且必须大于0");
+        }
+
+         LawyerUser  lawuer = lawyerUserMapper.selectById(lawyerUserId);
+
+
+
+        // 创建订单DTO对象
+        LawyerConsultationOrderDto order = new LawyerConsultationOrderDto();
+        Date now = new Date();
+
+        // 设置订单基本信息
+        order.setClientUserId(clientUserId);
+        order.setLawyerUserId(lawyerUserId);
+        order.setProblemScenarioId(lawyerConsultationOrder.getProblemScenarioId());
+        order.setProblemDescription(lawyerConsultationOrder.getProblemDescription());
+        order.setOrderAmount(orderAmount);
+        order.setAlipayNo(lawyerConsultationOrder.getAlipayNo());
+        order.setOrderStr(lawyerConsultationOrder.getOrderStr());
+        order.setPlaceId(lawyerConsultationOrder.getPlaceId());
+        order.setPayType(lawyerConsultationOrder.getPayType());
+
+        if (lawyerConsultationOrder.getChargeMinute() != null) {
+            order.setChargeMinute(lawyerConsultationOrder.getChargeMinute());
+        }
+        if (lawyerConsultationOrder.getMinuteNum() != null) {
+            order.setMinuteNum(lawyerConsultationOrder.getMinuteNum());
+        }
+        if (lawyerConsultationOrder.getChargeTime() != null) {
+            order.setChargeTime(lawyerConsultationOrder.getChargeTime());
+        }
+        if (lawyerConsultationOrder.getTimeNum() != null) {
+            order.setTimeNum(lawyerConsultationOrder.getTimeNum());
+        }
+        if (lawuer != null) {
+            if (lawuer.getFirmId() != null){
+                order.setPlaceId(lawuer.getFirmId());
+            }
+        }
+
+       String serviceType =lawyerConsultationOrder.getServiceType();
+
+        if (serviceType.contains("次")){
+            //对serviceType进行截取,比如"10次",想要10,则截取字符串"10",并转换为整型10
+            serviceType = serviceType.substring(0, serviceType.indexOf("次"));
+            order.setTimeNum(Integer.valueOf(serviceType));
+
+            order.setChargeTime(lawyerConsultationOrder.getPrice());
+        }
+        if (serviceType.contains("分钟")){
+            serviceType = serviceType.substring(0, serviceType.indexOf("分钟"));
+            order.setMinuteNum(Integer.valueOf(serviceType));
+            order.setChargeMinute(lawyerConsultationOrder.getPrice());
+        }
+
+
+        // 设置订单状态
+        order.setOrderStatus(0); // 待支付
+        order.setPaymentStatus(0); // 未支付
+        order.setOrderTime(now);
+        order.setCreatedTime(now);
+        order.setUpdatedTime(now);
+        order.setDeleteFlag(0);
+
+        // 生成订单编号:LAW + 年月日(8位数字)+ 随机5位数字
+        String orderNumber = generateOrderNumber();
+        order.setOrderNumber(orderNumber);
+        log.info("生成订单编号:orderNumber={}", orderNumber);
+
+        // 计算本单收益(平台收益)
+        Integer consultationFee = calculateConsultationFee(lawyerUserId, orderAmount);
+        order.setConsultationFee(consultationFee);
+        log.info("计算咨询费用:lawyerUserId={}, orderAmount={}, consultationFee={}",
+                lawyerUserId, orderAmount, consultationFee);
+
+        // 计算律师收益(订单金额-平台收益)
+        Integer lawyerEarnings =  orderAmount - consultationFee;
+        order.setLawyerEarnings(lawyerEarnings);
+        // 插入订单
+        int insertCount = consultationOrderMapper.insertOrder(order);
+        if (insertCount > 0) {
+            log.info("创建咨询订单成功:orderNumber={}, orderId={}", orderNumber, order.getId());
+            return R.data(order);
+        } else {
+            log.error("创建咨询订单失败:数据库插入失败,orderNumber={}", orderNumber);
+            return R.fail("创建订单失败");
+        }
+    }
+
+    /**
+     * 生成订单编号
+     * <p>
+     * 格式:LAW + 年月日(8位数字)+ 随机5位数字
+     * 示例:LAW2025011512345
+     * </p>
+     *
+     * @return 订单编号
+     */
+    private String generateOrderNumber() {
+        String dateStr = new SimpleDateFormat("yyyyMMdd").format(new Date());
+        String randomStr = String.format("%05d", RandomUtils.nextInt(100000));
+        return "LAW" + dateStr + randomStr;
+    }
+
+    /**
+     * 计算咨询费用(本单收益)
+     * <p>
+     * 根据律所的平台佣金比例计算咨询费用
+     * 如果律师没有关联律所或律所没有设置佣金比例,则使用默认佣金比例3%
+     * </p>
+     *
+     * @param lawyerUserId 律师用户ID
+     * @param orderAmount  订单金额(单位:分)
+     * @return 咨询费用(单位:分)
+     */
+    private Integer calculateConsultationFee(Integer lawyerUserId, Integer orderAmount) {
+        // 默认佣金比例:3%
+        float defaultCommissionRate = 3.0F;
+        float commissionRate = defaultCommissionRate;
+
+        // 查询律师信息
+        LawyerUser lawyerUser = lawyerUserMapper.selectById(lawyerUserId);
+        if (lawyerUser != null) {
+            // 获取律所信息
+            Integer firmId = lawyerUser.getFirmId();
+            if (firmId != null && firmId > 0) {
+                LawFirm lawyerFirm = lawFirmMapper.selectById(firmId);
+                if (lawyerFirm != null && lawyerFirm.getPlatformCommissionRatio() != null
+                        && lawyerFirm.getPlatformCommissionRatio().floatValue() > 0) {
+                    commissionRate = lawyerFirm.getPlatformCommissionRatio().floatValue();
+                }
+            }
+        }
+
+        // 计算咨询费用:订单金额 * 佣金比例 / 100,四舍五入
+        int consultationFee = Math.round(orderAmount * commissionRate / 100);
+        return consultationFee;
+    }
+
+    /**
+     * 更新订单支付状态
+     * <p>
+     * 根据支付状态更新订单信息,包括订单状态、支付状态、支付时间等
+     * </p>
+     *
+     * @param request 支付状态更新请求对象,包含订单编号、支付状态、订单状态等信息,不能为空
+     * @return 更新结果,成功返回订单信息,失败返回错误信息
+     */
+    @Override
+    public R<LawyerConsultationOrderDto> payStatus(PayStatusRequest request) {
+        log.info("LawyerConsultationOrderServiceImpl.payStatus?orderNumber={},paymentStatus={},orderStatus={}",
+                request.getOrderNumber(), request.getPaymentStatus(), request.getOrderStatus());
+
+        LawyerConsultationOrderDto order = new LawyerConsultationOrderDto();
+        if (null != request.getOrderStr()){
+            order.setOrderStr(request.getOrderStr());
+        }
+        if (null != request.getAlipayNo()){
+            order.setAlipayNo(request.getAlipayNo());
+        }
+        if (null != request.getPayType()){
+            order.setPayType(request.getPayType());
+        }
+        order.setOrderNumber(request.getOrderNumber());
+        order.setPaymentStatus(1);
+        order.setOrderStatus(1);
+        order.setUpdatedTime(new Date());
+        order.setDeleteFlag(0);
+        order.setPaymentTime(new Date());
+        order.setStartTime(new Date());
+//        LocalDateTime now = LocalDateTime.now();
+//        LocalDateTime validityDateTime = now.plusDays(7)
+//                .withHour(0)
+//                .withMinute(0)
+//                .withSecond(0)
+//                .withNano(0);
+//        order.setValidityPeriod(Date.from(validityDateTime.atZone(ZoneId.systemDefault()).toInstant()));
+//        boolean result = this.updateById(order);
+        Integer result = consultationOrderMapper.updateOrder(order);
+
+        if (result > 0) {
+            return R.data(order);
+        }
+        return R.fail("失败");
+    }
+
+    /**
+     * 根据用户ID查询订单列表(包含律师信息)
+     *
+     * @param pageNum     页码
+     * @param pageSize    页容量
+     * @param userId      用户ID
+     * @param orderStatus 订单状态
+     * @param lawyerName  律师姓名(支持模糊查询)
+     * @return 分页订单列表
+     */
+    @Override
+    public Map<String, Object> getConsultationOrderListById(int pageNum, int pageSize,
+                                                             String userId, String orderStatus,
+                                                             String lawyerName) {
+        log.info("查询咨询订单信息(律师端)- pageNum={}, pageSize={}, userId={}, orderStatus={}, lawyerName={}",
+                pageNum, pageSize, userId, orderStatus, lawyerName);
+
+        // 参数校验:用户ID不能为空
+        if (!StringUtils.hasText(userId)) {
+            log.warn("查询咨询订单信息失败:用户ID为空");
+            return buildEmptyResultMap(pageNum, pageSize);
+        }
+
+        // 创建分页对象
+        Page<LawyerConsultationOrderVO> page = new Page<>(pageNum, pageSize);
+
+        // 构建查询条件
+        QueryWrapper<LawyerConsultationOrderVO> queryWrapper = buildOrderQueryWrapper(
+                lawyerName, orderStatus, userId);
+        QueryWrapper<LawyerConsultationOrderVO> statisticsWrapper = buildStatisticsQueryWrapper(
+                lawyerName, userId);
+
+        // 查询订单列表
+        IPage<LawyerConsultationOrderVO> voPage = consultationOrderMapper.getLawyerConsultationOrderList(
+                page, queryWrapper);
+
+        if (voPage != null) {
+            // 填充法律场景信息和倒计时
+            fillLegalSceneArea(voPage);
+            calculateCountdownForPendingOrders(voPage);
+
+            //填充未读消息
+            fillUnreadMessage(voPage,userId);
+
+            // 设置订单价格字符串
+            setOrderPriceStrForOrderList(voPage);
+
+            // 设置评价状态
+            setCommonStatusForOrderList(voPage);
+        }
+
+        // 判断工作日信息
+        calculateWorkDayInfo(voPage);
+
+
+        // 获取统计信息
+        statisticsWrapper.groupBy("lco.order_status");
+        List<Map<String, Object>> statisticsInfo = consultationOrderMapper.getLawyerStatisticsInfo(
+                statisticsWrapper);
+
+        // 构建状态统计Map并填充返回结果
+        Map<Integer, Integer> statusCountMap = buildStatusCountMap(statisticsInfo);
+        Map<String, Object> resultMap = buildOrderStatisticsResult(statusCountMap, voPage);
+
+        return resultMap;
+    }
+
+    /**
+     * 构建订单统计结果Map
+     *
+     * @param statusCountMap 状态统计Map
+     * @param voPage        订单分页对象
+     * @return 订单统计结果Map
+     */
+    private Map<String, Object> buildOrderStatisticsResult(Map<Integer, Integer> statusCountMap,
+                                                            IPage<LawyerConsultationOrderVO> voPage) {
+        Map<String, Object> resultMap = new HashMap<>(16);
+
+        // 获取各状态订单数量
+        Integer waitPayStatus = LawyerStatusEnum.WAIT_PAY.getStatus();
+        int waitPayStatusCount = statusCountMap.getOrDefault(waitPayStatus, 0);
+
+        Integer waitAcceptStatus = LawyerStatusEnum.WAIT_ACCEPT.getStatus();
+        int waitAcceptStatusCount = statusCountMap.getOrDefault(waitAcceptStatus, 0);
+
+        Integer inProgressStatus = LawyerStatusEnum.INPROGRESS.getStatus();
+        int inProgressCount = statusCountMap.getOrDefault(inProgressStatus, 0);
+
+        Integer completeStatus = LawyerStatusEnum.COMPLETE.getStatus();
+        int completeCount = statusCountMap.getOrDefault(completeStatus, 0);
+
+        Integer cancelStatus = LawyerStatusEnum.CANCEL.getStatus();
+        int cancelStatusCount = statusCountMap.getOrDefault(cancelStatus, 0);
+
+        Integer refundedStatus = LawyerStatusEnum.REFUNDED.getStatus();
+        int refundedStatusCount = statusCountMap.getOrDefault(refundedStatus, 0);
+
+        // 计算订单总数
+        long totalOrderCount = (long) (waitPayStatusCount + waitAcceptStatusCount + inProgressCount
+                + completeCount + cancelStatusCount + refundedStatusCount);
+
+        // 填充返回结果
+        resultMap.put("lawyerWaitPayStatusOrderCount", waitPayStatusCount);
+        resultMap.put("lawyerWaitAcceptStatusOrderCount", waitAcceptStatusCount);
+        resultMap.put("lawyerInProgressOrderCount", inProgressCount);
+        resultMap.put("lawyerCompleteOrderCount", completeCount);
+        resultMap.put("lawyerCancelStatusOrderCount", cancelStatusCount);
+        resultMap.put("lawyerRefundedStatusOrderCount", refundedStatusCount);
+        resultMap.put("total", totalOrderCount);
+
+        // 设置订单列表
+        List<LawyerConsultationOrderVO> orderList = (voPage != null && voPage.getRecords() != null)
+                ? voPage.getRecords() : Collections.emptyList();
+        resultMap.put("lawyerConsultationOrderList", orderList);
+
+        return resultMap;
+    }
+
+    /**
+     * 构建状态统计Map
+     *
+     * @param statisticsInfo 统计信息列表
+     * @return 状态统计Map,key为订单状态,value为订单数量
+     */
+    private Map<Integer, Integer> buildStatusCountMap(List<Map<String, Object>> statisticsInfo) {
+        Map<Integer, Integer> statusCountMap = new HashMap<>(16);
+        if (CollectionUtils.isEmpty(statisticsInfo)) {
+            return statusCountMap;
+        }
+
+        for (Map<String, Object> map : statisticsInfo) {
+            Object statusObj = map.get("order_status");
+            Object countObj = map.get("order_count");
+
+            if (statusObj == null || countObj == null) {
+                continue;
+            }
+
+            Integer status = (Integer) statusObj;
+            // COUNT(*) 返回类型可能是Long
+            Long countLong = (Long) countObj;
+            statusCountMap.put(status, countLong.intValue());
+        }
+
+        return statusCountMap;
+    }
+
+    /**
+     * 构建统计查询条件
+     *
+     * @param lawyerName 律师名称
+     * @param userId     客户端用户ID
+     * @return 统计查询条件包装器
+     */
+    private QueryWrapper<LawyerConsultationOrderVO> buildStatisticsQueryWrapper(String lawyerName,
+                                                                                String userId) {
+        QueryWrapper<LawyerConsultationOrderVO> queryWrapper = new QueryWrapper<>();
+        if (StringUtils.hasText(lawyerName)) {
+            queryWrapper.like("lu.name", lawyerName);
+        }
+        queryWrapper.eq("lco.client_user_id", userId);
+        queryWrapper.eq("lco.delete_flag", 0);
+        return queryWrapper;
+    }
+
+
+    /**
+     * 构建空结果Map
+     *
+     * @param pageNum  页码
+     * @param pageSize 页容量
+     * @return 空结果Map
+     */
+    private Map<String, Object> buildEmptyResultMap(int pageNum, int pageSize) {
+        Map<String, Object> resultMap = new HashMap<>(16);
+        Page<LawyerConsultationOrderVO> emptyPage = new Page<>(pageNum, pageSize);
+        emptyPage.setRecords(Collections.emptyList());
+        emptyPage.setTotal(0);
+        resultMap.put("lawyerWaitPayStatusOrderCount", 0);
+        resultMap.put("lawyerWaitAcceptStatusOrderCount", 0);
+        resultMap.put("lawyerInProgressOrderCount", 0);
+        resultMap.put("lawyerCompleteOrderCount", 0);
+        resultMap.put("lawyerCancelStatusOrderCount", 0);
+        resultMap.put("lawyerRefundedStatusOrderCount", 0);
+        resultMap.put("lawyerOrderCount", 0L);
+        resultMap.put("lawyerConsultationOrderList", Collections.emptyList());
+        return resultMap;
+    }
+
+    /**
+     * 构建订单查询条件
+     *
+     * @param lawyerName 律师名称
+     * @param orderStatus 订单状态
+     * @param userId      客户端用户ID
+     * @return 查询条件包装器
+     */
+    private QueryWrapper<LawyerConsultationOrderVO> buildOrderQueryWrapper(String lawyerName, String orderStatus,
+                                                                           String userId) {
+        QueryWrapper<LawyerConsultationOrderVO> queryWrapper = new QueryWrapper<>();
+
+        // 订单状态条件
+        if (StringUtils.hasText(orderStatus)) {
+            queryWrapper.in("lco.order_status", Collections.singletonList(orderStatus));
+        }
+
+        // 律师名称条件
+        if (StringUtils.hasText(lawyerName)) {
+            queryWrapper.like("lu.name", lawyerName);
+        }
+
+        // 客户端用户ID条件
+        queryWrapper.eq("lco.client_user_id", userId);
+
+        // 删除标记条件
+        queryWrapper.eq("lco.delete_flag", 0);
+
+        // 排序条件:按创建时间倒序
+        queryWrapper.orderByDesc("lco.created_time");
+        return queryWrapper;
+    }
+
+    /**
+     * 根据律师姓名查询律师ID列表
+     *
+     * @param lawyerName 律师姓名
+     * @return 律师ID列表
+     */
+    private List<Integer> queryLawyerIdsByName(String lawyerName) {
+        LambdaQueryWrapper<LawyerUser> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerUser::getDeleteFlag, 0)
+                .like(LawyerUser::getName, lawyerName);
+        List<LawyerUser> lawyerUsers = lawyerUserService.list(queryWrapper);
+
+        if (CollectionUtils.isEmpty(lawyerUsers)) {
+            return Collections.emptyList();
+        }
+
+        return lawyerUsers.stream()
+                .map(LawyerUser::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 填充律师法律场景到订单VO列表
+     *
+     * @param voPage 订单VO分页对象
+     */
+    private void fillLegalSceneArea(IPage<LawyerConsultationOrderVO> voPage) {
+        List<LawyerConsultationOrderVO> orderList = voPage.getRecords();
+        if (CollectionUtils.isEmpty(orderList)) {
+            return;
+        }
+
+        // 提取所有律师ID
+        List<Integer> lawyerIdList = orderList.stream()
+                .map(LawyerConsultationOrderVO::getLawyerUserId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (CollectionUtils.isEmpty(lawyerIdList)) {
+            return;
+        }
+
+        // 批量查询法律场景
+        Map<String, List<String>> serviceAreaMap = getLawyerServiceArea(lawyerIdList);
+        if (serviceAreaMap.isEmpty()) {
+            return;
+        }
+
+        // 填充问题场景
+        orderList.forEach(order -> {
+            Integer lawyerUserId = order.getLawyerUserId();
+            if (lawyerUserId != null) {
+                String lawyerUserIdStr = String.valueOf(lawyerUserId);
+                List<String> serviceAreaList = serviceAreaMap.get(lawyerUserIdStr);
+                if (CollectionUtils.isNotEmpty(serviceAreaList)) {
+                    order.setLawyerLegalProblemScenarioList(serviceAreaList);
+                }
+            }
+        });
+    }
+
+
+
+    /**
+     * 填充未读消息数量
+     *
+     * @param voPage 订单VO分页对象
+     */
+    private void fillUnreadMessage(IPage<LawyerConsultationOrderVO> voPage,String userId) {
+        List<LawyerConsultationOrderVO> orderList = voPage.getRecords();
+        if (CollectionUtils.isEmpty(orderList)) {
+            return;
+        }
+
+        // 提取所有律师ID,并加上lawyer_前缀
+        List<String> lawyerIdList = orderList.stream()
+                .map(LawyerConsultationOrderVO::getLawyerPhone)
+                .filter(Objects::nonNull)
+                .map(phone -> "lawyer_" + phone)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (CollectionUtils.isEmpty(lawyerIdList)) {
+            return;
+        }
+
+        LifeUser lifeUser = lifeUserMapper.selectById(userId);
+        String phone = lifeUser.getUserPhone();
+
+        LambdaQueryWrapper<LifeMessage> lifeMessageLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lifeMessageLambdaQueryWrapper.in(LifeMessage::getSenderId, lawyerIdList);
+        lifeMessageLambdaQueryWrapper.eq(LifeMessage::getDeleteFlag, 0);
+        lifeMessageLambdaQueryWrapper.eq(LifeMessage::getIsRead, 0);
+        lifeMessageLambdaQueryWrapper.eq(LifeMessage::getReceiverId, "user_" + phone);
+        List<LifeMessage> lifeMessageList = lifeMessageMapper.selectList(lifeMessageLambdaQueryWrapper);
+
+        // 按照senderId进行分组,返回senderId和数量的map
+        Map<String, Long> senderIdCountMap = new HashMap<>();
+        if(CollectionUtils.isNotEmpty(lifeMessageList)){
+            senderIdCountMap = lifeMessageList.stream()
+                    .collect(Collectors.groupingBy(LifeMessage::getSenderId, Collectors.counting()));
+        }
+
+        for(LawyerConsultationOrderVO lawyerConsultationOrderVO : voPage.getRecords()){
+            String lawyerPhone = "lawyer_" + lawyerConsultationOrderVO.getLawyerPhone();
+            if(!senderIdCountMap.isEmpty() && senderIdCountMap.containsKey(lawyerPhone) && lawyerConsultationOrderVO.getOrderStatus() == 2){
+                long messageCount = senderIdCountMap.get(lawyerPhone);
+                lawyerConsultationOrderVO.setUnreadMessage(messageCount);
+            } else {
+                lawyerConsultationOrderVO.setUnreadMessage(0L);
+            }
+        }
+    }
+
+
+    /**
+     * 批量查询律师问题场景
+     *
+     * @param lawyerIdList 律师ID列表
+     * @return 律师问题场景Map,key为律师ID(String),value为问题场景名称列表
+     */
+    private Map<String, List<String>> getLawyerServiceArea(List<Integer> lawyerIdList) {
+        Map<String, List<String>> serviceAreaMap = new HashMap<>();
+        if (CollectionUtils.isEmpty(lawyerIdList)) {
+            return serviceAreaMap;
+        }
+
+        QueryWrapper<LawyerServiceArea> wrapper = new QueryWrapper<>();
+        wrapper.in("lsa.lawyer_user_id", lawyerIdList)
+                .eq("lsa.delete_flag", 0)
+                .eq("lsa.status", 1);
+
+        List<Map<String, Object>> serviceAreaDataList = lawyerServiceAreaMapper.getLawyerLegalProblemScenarioList(wrapper);
+        if (CollectionUtils.isEmpty(serviceAreaDataList)) {
+            return serviceAreaMap;
+        }
+
+        for (Map<String, Object> row : serviceAreaDataList) {
+            Object lawyerUserIdObj = row.get("lawyer_user_id");
+            if (lawyerUserIdObj == null) {
+                continue;
+            }
+
+            String lawyerUserId = String.valueOf(lawyerUserIdObj);
+            String scenarioName = (String) row.get("name");
+            String scenarioNameValue = StringUtils.hasText(scenarioName) ? scenarioName : "";
+
+            serviceAreaMap.computeIfAbsent(lawyerUserId, k -> new ArrayList<>())
+                    .add(scenarioNameValue);
+        }
+
+        return serviceAreaMap;
+    }
+
+    /**
+     * 获取咨询订单详情
+     *
+     * @param lawyerOrderId 订单ID
+     * @return 咨询订单详情VO
+     */
+    @Override
+    public LawyerConsultationOrderVO getConsultationOrderDetail(String lawyerOrderId) {
+        log.info("LawyerConsultationOrderServiceImpl.getConsultationOrderDetail?lawyerOrderId={}", lawyerOrderId);
+
+        LawyerConsultationOrderVO orderVO = new LawyerConsultationOrderVO();
+
+        // 参数校验
+        if (!StringUtils.hasText(lawyerOrderId)) {
+            return orderVO;
+        }
+
+        // 查询订单信息
+        LawyerConsultationOrder order = consultationOrderMapper.selectById(lawyerOrderId);
+        if (order == null) {
+            return orderVO;
+        }
+
+        // 复制订单基本信息
+        BeanUtils.copyProperties(order, orderVO);
+
+        // 查询并填充律师信息
+        Integer lawyerUserId = order.getLawyerUserId();
+        if (lawyerUserId != null) {
+            LawyerUser lawyerUser = lawyerUserMapper.selectById(lawyerUserId);
+            if (lawyerUser != null) {
+                fillLawyerInfo(orderVO, lawyerUser);
+            }
+
+
+            // 查询律师问题场景
+            List<Integer> lawyerIdList = Collections.singletonList(lawyerUserId);
+            Map<String, List<String>> serviceAreaMap = getLawyerServiceArea(lawyerIdList);
+            String lawyerUserIdStr = String.valueOf(lawyerUserId);
+            if (!serviceAreaMap.isEmpty() && serviceAreaMap.containsKey(lawyerUserIdStr)) {
+                orderVO.setLawyerLegalProblemScenarioList(serviceAreaMap.get(lawyerUserIdStr));
+            }
+        }
+
+        return orderVO;
+    }
+
+    /**
+     * 取消律师咨询订单
+     * <p>
+     * 取消订单的业务逻辑:
+     * 1. 参数校验:订单ID不能为空
+     * 2. 订单存在性校验:订单必须存在
+     * 3. 订单状态校验:已取消或已完成的订单不允许再次取消
+     * 4. 如果订单是待支付状态,会取消Redis中的订单支付超时计时器
+     * 5. 根据订单状态更新为相应状态:
+     *    - 待接单状态:更新为已退款状态
+     *    - 待支付状态:更新为已取消状态
+     * </p>
+     *
+     * @param id 订单ID
+     * @return 是否取消成功,true表示成功,false表示失败
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean cancelOrder(String id) {
+        log.info("开始取消订单,订单ID={}", id);
+        try {
+        // 1. 参数校验
+        if (!StringUtils.hasText(id)) {
+            log.warn("取消订单失败:订单ID为空");
+            return false;
+        }
+
+        // 2. 查询订单信息
+        LawyerConsultationOrder order = consultationOrderMapper.selectById(id);
+        if (order == null) {
+            log.warn("取消订单失败:订单不存在,订单ID={}", id);
+            return false;
+        }
+
+        if (order.getOrderStatus() == 1 ) {
+            Map<String, String> params=new HashMap<>();
+            params.put("payType", "1".equals(order.getPayType()) ? "alipay" : "wechatPay");
+            params.put("outTradeNo",order.getAlipayNo());
+            params.put("totalAmount", String.valueOf(order.getOrderAmount()));
+
+            params.put("refundAmount", String.valueOf(order.getOrderAmount()));
+            if ("1".equals( order.getPayType())){
+                params.put("refundAmount", new BigDecimal(order.getOrderAmount()).divide(new BigDecimal(100)).toString());
+            }
+
+            if ("2".equals( order.getPayType())){
+                params.put("refundReason", "用户取消订单");
+            }
+
+            String refundResult =paymentStrategyFactory.getStrategy(params.get("payType")).handleRefund(params);
+
+            if (!"调用成功".equals(refundResult)){
+                log.warn("取消订单失败:订单取消失败,订单ID={}, 订单编号={}", id, order.getOrderNumber());
+                return false;
+            }
+        }
+
+        // 3. 订单状态校验
+        Integer orderStatus = order.getOrderStatus();
+        String orderNumber = order.getOrderNumber();
+        
+        // 提取订单状态常量,避免魔法值
+        Integer cancelStatus = LawyerStatusEnum.CANCEL.getStatus();
+        Integer completedStatus = LawyerStatusEnum.COMPLETE.getStatus();
+        Integer waitAcceptStatus = LawyerStatusEnum.WAIT_ACCEPT.getStatus();
+        Integer waitPayStatus = LawyerStatusEnum.WAIT_PAY.getStatus();
+        Integer refundedStatus = LawyerStatusEnum.REFUNDED.getStatus();
+
+        // 3.1 检查订单是否已取消(使用 Objects.equals 避免空指针异常)
+        if (Objects.equals(cancelStatus, orderStatus)) {
+            log.warn("取消订单失败:订单已取消,订单ID={}, 订单编号={}", id, orderNumber);
+            return false;
+        }
+
+        // 3.2 检查订单是否已完成
+        if (Objects.equals(completedStatus, orderStatus)) {
+            log.warn("取消订单失败:订单已完成,不允许取消,订单ID={}, 订单编号={}", id, orderNumber);
+            return false;
+        }
+
+        // 4. 如果订单是待支付状态,取消Redis中的订单支付超时计时器
+        if (Objects.equals(waitPayStatus, orderStatus) && StringUtils.hasText(orderNumber)) {
+            try {
+                orderExpirationService.cancelOrderPaymentTimeout(orderNumber);
+                log.info("已取消订单支付超时计时器,订单编号={}", orderNumber);
+            } catch (Exception e) {
+                log.error("取消订单支付超时计时器失败,订单编号={},异常信息:{}", orderNumber, e.getMessage(), e);
+                // 继续执行取消订单操作,不因取消计时器失败而中断
+            }
+        }
+
+        // 5. 根据订单状态更新为相应状态
+        Integer targetStatus;
+        if (Objects.equals(waitAcceptStatus, orderStatus)) {
+            // 待接单状态:更新为已退款状态
+            targetStatus = refundedStatus;
+            log.debug("待接单订单取消,将更新为已退款状态,订单ID={}", id);
+        } else if (Objects.equals(waitPayStatus, orderStatus)) {
+            // 待支付状态:更新为已取消状态
+            targetStatus = cancelStatus;
+            log.debug("待支付订单取消,将更新为已取消状态,订单ID={}", id);
+        } else {
+            log.warn("取消订单失败:订单状态不允许取消,订单ID={}, 订单编号={}, 订单状态={}", 
+                    id, orderNumber, orderStatus);
+            return false;
+        }
+
+        // 6. 更新订单状态
+        LambdaUpdateWrapper<LawyerConsultationOrder> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(LawyerConsultationOrder::getId, id)
+                .set(LawyerConsultationOrder::getOrderStatus, targetStatus)
+                .set(LawyerConsultationOrder::getUpdatedTime, new Date());
+
+        int updateCount = consultationOrderMapper.update(null, updateWrapper);
+        boolean success = updateCount > 0;
+
+        // 7. 记录操作结果
+        if (success) {
+            log.info("取消订单成功,订单ID={}, 订单编号={}, 原状态={}, 新状态={}", 
+                    id, orderNumber, orderStatus, targetStatus);
+        } else {
+            log.error("取消订单失败:更新数据库失败,订单ID={}, 订单编号={}, 原状态={}, 目标状态={}", 
+                    id, orderNumber, orderStatus, targetStatus);
+        }
+
+        return success;
+
+        } catch (Exception e) {
+            log.error("取消订单失败:订单ID={}, 错误信息={}", id,  e.getMessage());
+            return false;
+        }
+
+    }
+
+    /**
+     * 查询咨询订单信息(律师端)
+     *
+     * @param pageNum   页码
+     * @param pageSize  页容
+     * @param startDate 开始时间
+     * @param endDate   结束时间
+     * @param lawyerId  律师ID
+     * @return 订单信息Map,包含订单列表、总数、进行中数量、已完成数量
+     */
+    @Override
+    public Map<String, Object> getLawyerConsultationOrderInfo(int pageNum, int pageSize, String startDate, String endDate, String clientUserName, String orderStatus, String lawyerId) {
+        log.info("LawyerConsultationOrderServiceImpl.getLawyerConsultationOrderInfo?pageNum={},pageSize={},startDate={},endDate={},clientUserName={},lawyerId={}",
+                pageNum, pageSize, startDate, endDate, clientUserName, lawyerId);
+
+        Map<String, Object> resultMap = new HashMap<>();
+
+        // 参数校验:律师ID不能为空
+        if (!StringUtils.hasText(lawyerId)) {
+            log.warn("查询咨询订单信息失败:律师ID为空");
+            Page<LawyerConsultationOrderVO> emptyPage = new Page<>(pageNum, pageSize);
+            emptyPage.setRecords(Collections.emptyList());
+            emptyPage.setTotal(0);
+            resultMap.put("lawyerOrderCount", 0L);
+            resultMap.put("lawyerInProgressOrderCount", 0);
+            resultMap.put("lawyerCompleteOrderCount", 0);
+            resultMap.put("lawyerConsultationOrderList", Collections.emptyList());
+            return resultMap;
+        }
+
+        // 创建分页对象
+        Page<LawyerConsultationOrderVO> page = new Page<>(pageNum, pageSize);
+
+        // 构建查询条件:查询进行中(2)和已完成(3)状态的订单
+        QueryWrapper<LawyerConsultationOrderVO> queryWrapper = new QueryWrapper<>();
+        QueryWrapper<LawyerConsultationOrderVO> queryStatisticsWrapper = new QueryWrapper<>();
+
+        if (StringUtils.hasText(orderStatus)) {
+            queryWrapper.in("lco.order_status", Collections.singletonList(orderStatus));
+        } else {
+            queryWrapper.in("lco.order_status", Arrays.asList("2", "3"));
+        }
+        queryWrapper.eq("lco.lawyer_user_id", lawyerId);
+        queryStatisticsWrapper.eq("lco.lawyer_user_id", lawyerId);
+        if (StringUtils.hasText(clientUserName)) {
+            queryWrapper.like("lur.user_name", clientUserName);
+            queryStatisticsWrapper.like("lur.user_name", clientUserName);
+        }
+        queryWrapper.eq("lco.delete_flag", 0);
+        queryStatisticsWrapper.eq("lco.delete_flag", 0);
+        queryWrapper.orderByDesc("lco.created_time");
+        // 时间范围查询:如果开始时间和结束时间都存在,则进行范围查询
+        if (StringUtils.hasText(startDate) && StringUtils.hasText(endDate)) {
+            queryWrapper.between("lco.payment_time", startDate + " 00:00:00", endDate + " 23:59:59");
+        }
+
+        // 查询订单列表
+        IPage<LawyerConsultationOrderVO> voPage = consultationOrderMapper.getLawyerConsultationOrderList(page, queryWrapper);
+
+        // 填充律师问题场景信息
+        if (voPage != null) {
+            fillLegalSceneArea(voPage);
+            // 为待支付订单计算倒计时(30分钟有效期)
+            calculateCountdownForPendingOrders(voPage);
+        }
+
+        //获取统计信息
+        queryStatisticsWrapper.groupBy("lco.order_status");
+        List<Map<String, Object>> statisticsInfo = consultationOrderMapper.getLawyerStatisticsInfo(queryStatisticsWrapper);
+
+        Map<Integer, Integer> statusCountMap = new HashMap<>();
+        for (Map<String, Object> map : statisticsInfo) {
+            Integer status = (Integer) map.get("order_status");
+            Long countLong = (Long) map.get("order_count"); // COUNT(*) 返回类型可能是Long
+            statusCountMap.put(status, countLong.intValue());
+        }
+
+
+        // 统计进行中订单数量
+        int inProgressCount = statusCountMap.getOrDefault(2, 0);
+        resultMap.put("lawyerInProgressOrderCount", inProgressCount);
+
+        // 统计已完成订单数量
+        int completeCount = statusCountMap.getOrDefault(3, 0);
+        resultMap.put("lawyerCompleteOrderCount", completeCount);
+
+        // 统计订单总数
+        resultMap.put("lawyerOrderCount", inProgressCount + completeCount);
+
+        // 设置订单列表
+        List<LawyerConsultationOrderVO> orderList = voPage != null && voPage.getRecords() != null
+                ? voPage.getRecords() : Collections.emptyList();
+        resultMap.put("lawyerConsultationOrderList", orderList);
+
+        return resultMap;
+    }
+
+
+    /**
+     * 为待支付订单计算倒计时(30分钟有效期)
+     *
+     * @param voPage 订单分页对象
+     */
+    private void calculateCountdownForPendingOrders(IPage<LawyerConsultationOrderVO> voPage) {
+        if (voPage == null || voPage.getRecords() == null) {
+            return;
+        }
+
+        Date now = new Date();
+        long validitySeconds = 30 * 60; // 30分钟 = 1800秒
+
+        for (LawyerConsultationOrderVO vo : voPage.getRecords()) {
+            // 仅对待支付订单(orderStatus=0)计算倒计时
+            if (vo.getOrderStatus() != null && vo.getOrderStatus() == 0) {
+                Date orderTime = vo.getOrderTime();
+                if (orderTime != null) {
+                    long elapsedSeconds = (now.getTime() - orderTime.getTime()) / 1000;
+                    long remainingSeconds = validitySeconds - elapsedSeconds;
+                    // 如果已过期,设置为0
+                    vo.setCountdownSeconds(Math.max(0, remainingSeconds));
+                } else {
+                    vo.setCountdownSeconds(0L);
+                }
+            } else {
+                // 非待支付订单,倒计时为null
+                vo.setCountdownSeconds(null);
+            }
+        }
+    }
+
+    /**
+     * 根据工作日判断是否可以申请退款/举报
+     *
+     * @param voPage 订单分页对象
+     */
+    private void calculateWorkDayInfo(IPage<LawyerConsultationOrderVO> voPage) {
+        if (voPage == null || voPage.getRecords() == null) {
+            return;
+        }
+        boolean isCanApplyRefund = DateUtils.getLastThreeWorkdaysRangeInfo(2);
+        boolean isCanApplyViolation = DateUtils.getLastThreeWorkdaysRangeInfo(3);
+
+        for (LawyerConsultationOrderVO vo : voPage.getRecords()) {
+            if(isCanApplyRefund){
+                vo.setIsCanApplyRefund("2");
+            } else {
+                vo.setIsCanApplyRefund("1");
+            }
+
+            if(isCanApplyViolation){
+                vo.setIsCanApplyViolation("2");
+            } else {
+                vo.setIsCanApplyViolation("1");
+            }
+        }
+    }
+
+    /**
+     * 填充律师信息到订单VO
+     *
+     * @param orderVO    订单VO对象
+     * @param lawyerUser 律师用户对象
+     */
+    private void fillLawyerInfo(LawyerConsultationOrderVO orderVO, LawyerUser lawyerUser) {
+        orderVO.setLawyerName(lawyerUser.getName());
+        orderVO.setLawyerPhone(lawyerUser.getPhone());
+        orderVO.setLawyerEmail(lawyerUser.getEmail());
+        orderVO.setLawyerCertificateNo(lawyerUser.getLawyerCertificateNo());
+        // 通过firm_id查询律所名称
+        if (lawyerUser.getFirmId() != null) {
+            LawFirm lawFirm = lawFirmMapper.selectById(lawyerUser.getFirmId());
+            orderVO.setLawFirm(lawFirm != null && (lawFirm.getDeleteFlag() == null || lawFirm.getDeleteFlag() == 0) 
+                    ? lawFirm.getFirmName() : null);
+        } else {
+            orderVO.setLawFirm(null);
+        }
+        orderVO.setPracticeYears(lawyerUser.getPracticeYears());
+        orderVO.setSpecialtyFields(lawyerUser.getSpecialtyFields());
+        orderVO.setCertificationStatus(lawyerUser.getCertificationStatus());
+        orderVO.setServiceCount(lawyerUser.getServiceCount());
+        orderVO.setServiceScore(lawyerUser.getServiceScore());
+        orderVO.setLawyerConsultationFee(lawyerUser.getConsultationFee());
+        orderVO.setProvince(lawyerUser.getProvince());
+        orderVO.setCity(lawyerUser.getCity());
+        orderVO.setDistrict(lawyerUser.getDistrict());
+        orderVO.setAddress(lawyerUser.getAddress());
+        orderVO.setHeadImg(lawyerUser.getHeadImg());
+        orderVO.setNickName(lawyerUser.getNickName());
+        orderVO.setPersonalIntroduction(lawyerUser.getPersonalIntroduction());
+    }
+
+    /**
+     * 创建订单前的校验
+     * <p>
+     * 检查用户是否已存在咨询该律师的订单(订单状态为待接单或进行中)
+     * </p>
+     *
+     * @param clientUserId 客户端用户ID,不能为空
+     * @param lawyerUserId 律师用户ID,不能为空
+     * @return 校验结果,如果已存在订单返回失败,否则返回成功
+     */
+    @Override
+    public R<Map<String, String>> checkOrder(Integer clientUserId, Integer lawyerUserId) {
+        log.info("LawyerConsultationOrderServiceImpl.checkOrder?clientUserId={},lawyerUserId={}", clientUserId, lawyerUserId);
+        
+        // 参数校验
+        if (clientUserId == null) {
+            log.warn("创建订单前校验失败:客户端用户ID为空");
+            return R.fail("客户端用户ID不能为空");
+        }
+        
+        if (lawyerUserId == null) {
+            log.warn("创建订单前校验失败:律师用户ID为空");
+            return R.fail("律师用户ID不能为空");
+        }
+        
+        // 查询是否存在进行中的订单(订单状态为待接单或进行中)
+        List<LawyerConsultationOrder> existingOrders = consultationOrderMapper.selectOrder(clientUserId, lawyerUserId);
+        
+        if (CollectionUtils.isNotEmpty(existingOrders)) {
+            log.warn("创建订单前校验失败:用户已存在咨询该律师的订单,clientUserId={},lawyerUserId={}", clientUserId, lawyerUserId);
+            return R.fail("您已存在咨询该律师的订单");
+        }
+        
+        log.info("创建订单前校验成功:可以咨询该律师,clientUserId={},lawyerUserId={}", clientUserId, lawyerUserId);
+        return R.success("可以咨询该律师");
+    }
+
+    /**
+     * 获取律师订单收益统计
+     * <p>
+     * 统计三个维度的收益数据:
+     * 1. 全部收益:所有已支付订单的订单总数和收益合计
+     * 2. 进行中:订单状态=2(进行中)且已支付的订单总数和收益合计
+     * 3. 已完成:订单状态=3(已完成)且已支付的订单总数和收益合计
+     * </p>
+     *
+     * @param lawyerConsultationOrderVO 律师用户ID
+     * @param lawyerConsultationOrderVO 开始时间(可选,用于筛选支付时间范围)
+     * @param lawyerConsultationOrderVO 结束时间(可选,用于筛选支付时间范围)
+     * @return 订单收益统计信息
+     */
+    @Override
+    public R<Map<String, Object>> getOrderIncome(LawyerConsultationOrderVO lawyerConsultationOrderVO) {
+        log.info("LawyerConsultationOrderController.getOrderIncome?lawyerConsultationOrderVO{}", lawyerConsultationOrderVO);
+
+        // 参数校验
+        if (lawyerConsultationOrderVO.getLawyerUserId() == null) {
+            log.warn("查询订单收益失败:律师用户ID为空");
+            return R.fail("律师用户ID不能为空");
+        }
+
+        Map<String, Object> resultMap = new HashMap<>();
+        OrderRevenueVO revenueVO = new OrderRevenueVO();
+
+        try {
+            // 构建查询条件:查询已支付的订单(paymentStatus = 1)
+            LambdaQueryWrapper<LawyerConsultationOrder> allRevenueWrapper = new LambdaQueryWrapper<>();
+            allRevenueWrapper.eq(LawyerConsultationOrder::getLawyerUserId, lawyerConsultationOrderVO.getLawyerUserId())
+                    .eq(LawyerConsultationOrder::getPaymentStatus, 1) // 已支付
+                    .in(LawyerConsultationOrder::getOrderStatus, 2,3) // 订单状态=3(已完成)2(进行中)
+                    .eq(LawyerConsultationOrder::getDeleteFlag, 0);
+            
+            // 添加时间段筛选条件(根据支付时间)
+            if (lawyerConsultationOrderVO.getStartTimeNew() != null) {
+                allRevenueWrapper.ge(LawyerConsultationOrder::getPaymentTime, lawyerConsultationOrderVO.getStartTimeNew());
+            }
+            if (lawyerConsultationOrderVO.getEndTimeNew()  != null) {
+                allRevenueWrapper.le(LawyerConsultationOrder::getPaymentTime, lawyerConsultationOrderVO.getEndTimeNew() );
+            }
+
+            // 查询全部已支付订单的统计
+            List<LawyerConsultationOrder> allPaidOrders = consultationOrderMapper.selectList(allRevenueWrapper);
+            long allOrderCount = allPaidOrders.size();
+            long allRevenue = allPaidOrders.stream()
+                    .filter(order -> order.getOrderAmount() != null)
+                    .mapToLong(LawyerConsultationOrder::getOrderAmount)
+                    .sum();
+
+            long consultationFee = allPaidOrders.stream()
+                    .filter(order -> order.getConsultationFee() != null)
+                    .mapToLong(LawyerConsultationOrder::getConsultationFee)
+                    .sum();
+
+            revenueVO.setAllOrderCount(allOrderCount);
+            revenueVO.setAllRevenue(allRevenue-consultationFee);
+
+
+            // 查询进行中订单的统计(orderStatus = 2 且已支付)
+            LambdaQueryWrapper<LawyerConsultationOrder> inProgressWrapper = new LambdaQueryWrapper<>();
+            inProgressWrapper.eq(LawyerConsultationOrder::getLawyerUserId, lawyerConsultationOrderVO.getLawyerUserId())
+                    .eq(LawyerConsultationOrder::getOrderStatus, LawyerStatusEnum.INPROGRESS.getStatus()) // 2:进行中
+                    .eq(LawyerConsultationOrder::getPaymentStatus, 1) // 已支付
+                    .eq(LawyerConsultationOrder::getDeleteFlag, 0);
+            
+            // 添加时间段筛选条件(根据支付时间)
+            if (lawyerConsultationOrderVO.getStartTimeNew() != null) {
+                inProgressWrapper.ge(LawyerConsultationOrder::getPaymentTime, lawyerConsultationOrderVO.getStartTimeNew());
+            }
+            if (lawyerConsultationOrderVO.getEndTimeNew()  != null) {
+                inProgressWrapper.le(LawyerConsultationOrder::getPaymentTime, lawyerConsultationOrderVO.getEndTimeNew() );
+            }
+
+            List<LawyerConsultationOrder> inProgressOrders = consultationOrderMapper.selectList(inProgressWrapper);
+            long inProgressOrderCount = inProgressOrders.size();
+
+            long inProgressRevenue = inProgressOrders.stream()
+                    .filter(order -> order.getOrderAmount() != null)
+                    .mapToLong(LawyerConsultationOrder::getOrderAmount)
+                    .sum();
+
+            long inProgressFee = inProgressOrders.stream()
+                    .filter(order -> order.getConsultationFee() != null)
+                    .mapToLong(LawyerConsultationOrder::getConsultationFee)
+                    .sum();
+
+            revenueVO.setInProgressOrderCount(inProgressOrderCount);
+            revenueVO.setInProgressRevenue(inProgressRevenue-inProgressFee);
+
+
+            // 查询已完成订单的统计(orderStatus = 3 且已支付)
+            LambdaQueryWrapper<LawyerConsultationOrder> completedWrapper = new LambdaQueryWrapper<>();
+            completedWrapper.eq(LawyerConsultationOrder::getLawyerUserId, lawyerConsultationOrderVO.getLawyerUserId())
+                    .eq(LawyerConsultationOrder::getOrderStatus, LawyerStatusEnum.COMPLETE.getStatus()) // 3:已完成
+                    .eq(LawyerConsultationOrder::getPaymentStatus, 1) // 已支付
+                    .eq(LawyerConsultationOrder::getDeleteFlag, 0);
+            
+            // 添加时间段筛选条件(根据支付时间)
+            if (lawyerConsultationOrderVO.getStartTimeNew() != null) {
+                completedWrapper.ge(LawyerConsultationOrder::getPaymentTime, lawyerConsultationOrderVO.getStartTimeNew());
+            }
+            if (lawyerConsultationOrderVO.getEndTimeNew()  != null) {
+                completedWrapper.le(LawyerConsultationOrder::getPaymentTime, lawyerConsultationOrderVO.getEndTimeNew() );
+            }
+
+            List<LawyerConsultationOrder> completedOrders = consultationOrderMapper.selectList(completedWrapper);
+            long completedOrderCount = completedOrders.size();
+            long completedRevenue = completedOrders.stream()
+                    .filter(order -> order.getOrderAmount() != null)
+                    .mapToLong(LawyerConsultationOrder::getOrderAmount)
+                    .sum();
+
+            long completedFee = completedOrders.stream()
+                    .filter(order -> order.getConsultationFee() != null)
+                    .mapToLong(LawyerConsultationOrder::getConsultationFee)
+                    .sum();
+
+            revenueVO.setCompletedOrderCount(completedOrderCount);
+            revenueVO.setCompletedRevenue(completedRevenue-completedFee);
+
+            // 构建返回结果
+            Map<String, Object> allRevenueMap = new HashMap<>();
+            allRevenueMap.put("name", "全部收益");
+            allRevenueMap.put("orderCount", revenueVO.getAllOrderCount());
+            allRevenueMap.put("totalRevenue", revenueVO.getAllRevenue());
+            resultMap.put("allRevenue", allRevenueMap);
+
+            Map<String, Object> inProgressMap = new HashMap<>();
+            inProgressMap.put("name", "进行中");
+            inProgressMap.put("orderCount", revenueVO.getInProgressOrderCount());
+            inProgressMap.put("totalRevenue", revenueVO.getInProgressRevenue());
+            resultMap.put("inProgress", inProgressMap);
+
+            Map<String, Object> completedMap = new HashMap<>();
+            completedMap.put("name", "已完成");
+            completedMap.put("orderCount", revenueVO.getCompletedOrderCount());
+            completedMap.put("totalRevenue", revenueVO.getCompletedRevenue());
+            resultMap.put("completed", completedMap);
+
+            log.info("查询订单收益成功,律师ID={}, 全部订单数={}, 全部收益={}, 进行中订单数={}, 进行中收益={}, 已完成订单数={}, 已完成收益={}",
+                    lawyerConsultationOrderVO.getLawyerUserId(), allOrderCount, allRevenue, inProgressOrderCount, inProgressRevenue,
+                    completedOrderCount, completedRevenue);
+
+            return R.data(resultMap);
+
+        } catch (Exception e) {
+            log.error("查询订单收益失败,律师ID={}", lawyerConsultationOrderVO.getLawyerUserId(), e);
+            return R.fail("查询订单收益失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 用户申请退款
+     * <p>
+     * 申请退款前会进行以下校验和处理:
+     * 1. 参数校验:用户ID和订单ID不能为空
+     * 2. 订单存在性校验:订单必须存在
+     * 3. 订单归属校验:订单必须属于该用户
+     * 4. 订单状态校验:只有进行中(2)和已完成(3)状态的订单可以申请退款
+     * 5. 退款状态校验:已申请退款的订单不允许重复申请
+     * 6. 更新订单的申请退款状态为已申请(1),并更新退款申请时间
+     * </p>
+     *
+     * @param clientUserId 客户端用户ID
+     * @param orderId      订单ID
+     * @return 申请退款结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R<Boolean> applyRefund(String clientUserId, String orderId, String applyRefundReason) {
+        log.info("开始申请退款,用户ID={}, 订单ID={}", clientUserId, orderId);
+
+        // 1. 参数校验
+        if (!StringUtils.hasText(clientUserId)) {
+            log.warn("申请退款失败:用户ID为空或无效,clientUserId={}", clientUserId);
+            return R.fail("用户ID不能为空");
+        }
+
+        if (!StringUtils.hasText(orderId)) {
+            log.warn("申请退款失败:订单ID为空或无效,orderId={}", orderId);
+            return R.fail("订单ID不能为空");
+        }
+
+        // 2. 查询订单信息
+        LawyerConsultationOrder order = consultationOrderMapper.selectById(orderId);
+        if (order == null) {
+            log.warn("申请退款失败:订单不存在,订单ID={}", orderId);
+            return R.fail("订单不存在");
+        }
+
+        // 3. 订单归属校验:订单必须属于该用户
+        if (!clientUserId.equals(order.getClientUserId().toString())) {
+            log.warn("申请退款失败:订单不属于该用户,订单ID={}, 订单用户ID={}, 请求用户ID={}",
+                    orderId, order.getClientUserId(), clientUserId);
+            return R.fail("订单不属于该用户,无法申请退款");
+        }
+
+        // 4. 订单状态校验:只有进行中(2)和已完成(3)状态的订单可以申请退款
+        Integer orderStatus = order.getOrderStatus();
+        Integer inProgressStatus = LawyerStatusEnum.INPROGRESS.getStatus(); // 2:进行中
+        Integer completeStatus = LawyerStatusEnum.COMPLETE.getStatus(); // 3:已完成
+
+        if (!inProgressStatus.equals(orderStatus) && !completeStatus.equals(orderStatus)) {
+            log.warn("申请退款失败:订单状态不允许申请退款,订单ID={}, 订单编号={}, 订单状态={}",
+                    orderId, order.getOrderNumber(), orderStatus);
+            return R.fail("只有进行中或已完成的订单可以申请退款");
+        }
+
+        // 5. 退款状态校验:已申请退款的订单不允许重复申请
+        String applyRefundStatus = order.getApplyRefundStatus();
+        String appliedStatus = "1"; // 1:已申请
+        String lawyerAgreedStatus = "3"; // 3:律师已同意
+        if (appliedStatus.equals(applyRefundStatus) || lawyerAgreedStatus.equals(applyRefundStatus)) {
+            log.warn("申请退款失败:订单已申请退款,不允许重复申请,订单ID={}, 订单编号={}, 退款状态={}",
+                    orderId, order.getOrderNumber(), applyRefundStatus);
+            return R.fail("订单已申请退款,不允许重复申请");
+        }
+
+        // 6. 更新订单的申请退款状态为已申请(1),并更新退款申请时间
+        LambdaUpdateWrapper<LawyerConsultationOrder> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(LawyerConsultationOrder::getId, orderId)
+                .set(LawyerConsultationOrder::getApplyRefundStatus, appliedStatus)
+                .set(LawyerConsultationOrder::getApplyRefundTime, new Date())
+                .set(LawyerConsultationOrder::getUpdatedTime, new Date()).set(LawyerConsultationOrder::getApplyRefundReason, applyRefundReason);
+
+        int updateCount = consultationOrderMapper.update(null, updateWrapper);
+        boolean success = updateCount > 0;
+
+        // 7. 记录操作结果并发送通知
+        if (success) {
+            //设置订单超时退款时间
+            orderExpirationService.setOrderRefundTimeout(order.getOrderNumber(), 60 * Long.parseLong(coefficients));
+
+            log.info("申请退款成功,订单ID={}, 订单编号={}, 订单状态={}, 退款状态=已申请",
+                    orderId, order.getOrderNumber(), orderStatus);
+            
+            // 发送退款申请通知给申请人
+            sendRefundApplyNotice(order, clientUserId);
+            
+            return R.data(true, "退款申请已提交,请等待律师处理");
+        } else {
+            log.error("申请退款失败:更新数据库失败,订单ID={}, 订单编号={}", orderId, order.getOrderNumber());
+            return R.fail("申请退款失败,请稍后重试");
+        }
+    }
+
+    /**
+     * 用户端完成订单
+     * <p>
+     * 完成订单前会进行以下校验和处理:
+     * 1. 参数校验:用户ID和订单ID不能为空
+     * 2. 订单存在性校验:订单必须存在
+     * 3. 订单归属校验:订单必须属于该用户
+     * 4. 订单状态校验:只有进行中(2)状态的订单可以完成
+     * 5. 更新订单状态为已完成(3),并更新完成时间
+     * </p>
+     *
+     * @param clientUserId 客户端用户ID
+     * @param orderId      订单ID
+     * @return 完成订单结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R<Boolean> completeOrder(String clientUserId, String orderId) {
+        log.info("开始完成订单,用户ID={}, 订单ID={}", clientUserId, orderId);
+
+        // 1. 参数校验
+        if (StringUtils.isEmpty(clientUserId)) {
+            log.warn("完成订单失败:用户ID为空或无效,clientUserId={}", clientUserId);
+            return R.fail("用户ID不能为空");
+        }
+
+        if (StringUtils.isEmpty(orderId)) {
+            log.warn("完成订单失败:订单ID为空或无效,orderId={}", orderId);
+            return R.fail("订单ID不能为空");
+        }
+
+        // 2. 查询订单信息
+        LawyerConsultationOrder order = consultationOrderMapper.selectById(orderId);
+        if (order == null) {
+            log.warn("完成订单失败:订单不存在,订单ID={}", orderId);
+            return R.fail("订单不存在");
+        }
+
+        // 3. 订单归属校验:订单必须属于该用户
+        if (!clientUserId.equals(order.getClientUserId().toString())) {
+            log.warn("完成订单失败:订单不属于该用户,订单ID={}, 订单用户ID={}, 请求用户ID={}",
+                    orderId, order.getClientUserId(), clientUserId);
+            return R.fail("订单不属于该用户,无法完成订单");
+        }
+
+        // 4. 订单状态校验:只有进行中(2)状态的订单可以完成
+        Integer orderStatus = order.getOrderStatus();
+        Integer inProgressStatus = LawyerStatusEnum.INPROGRESS.getStatus(); // 2:进行中
+        Integer completeStatus = LawyerStatusEnum.COMPLETE.getStatus(); // 3:已完成
+
+        if (!inProgressStatus.equals(orderStatus)) {
+            log.warn("完成订单失败:订单状态不允许完成,订单ID={}, 订单编号={}, 订单状态={}",
+                    orderId, order.getOrderNumber(), orderStatus);
+            return R.fail("只有进行中的订单可以完成");
+        }
+
+        // 5. 更新订单状态为已完成(3),并更新咨询结束时间和更新时间
+        LambdaUpdateWrapper<LawyerConsultationOrder> updateWrapper = new LambdaUpdateWrapper<>();
+        Date now = new Date();
+        updateWrapper.eq(LawyerConsultationOrder::getId, orderId)
+                .set(LawyerConsultationOrder::getOrderStatus, completeStatus)
+                .set(LawyerConsultationOrder::getEndTime, now)
+                .set(LawyerConsultationOrder::getUpdatedTime, now);
+
+        int updateCount = consultationOrderMapper.update(null, updateWrapper);
+        boolean success = updateCount > 0;
+
+        // 6. 记录操作结果
+        if (success) {
+            log.info("完成订单成功,订单ID={}, 订单编号={}, 原状态={}, 新状态=已完成",
+                    orderId, order.getOrderNumber(), orderStatus);
+            return R.data(true, "订单已完成");
+        } else {
+            log.error("完成订单失败:更新数据库失败,订单ID={}, 订单编号={}", orderId, order.getOrderNumber());
+            return R.fail("完成订单失败,请稍后重试");
+        }
+    }
+
+    /**
+     * 获取申请退款订单详情
+     * <p>
+     * 根据订单ID查询订单信息,用于退款申请详情展示
+     * </p>
+     *
+     * @param orderId 订单ID,不能为空
+     * @return 订单信息,如果订单ID为空或订单不存在则返回null
+     */
+    @Override
+    public LawyerConsultationOrder getApplyRefundDetail(String orderId) {
+        log.info("开始查询申请退款订单详情,订单ID={}", orderId);
+
+        // 参数校验:订单ID不能为空
+        if (!StringUtils.hasText(orderId)) {
+            log.warn("查询申请退款订单详情失败:订单ID为空");
+            return null;
+        }
+
+        try {
+            // 查询订单信息
+            LawyerConsultationOrder order = consultationOrderMapper.selectById(orderId);
+            if (order == null) {
+                log.warn("查询申请退款订单详情失败:订单不存在,订单ID={}", orderId);
+                return null;
+            }
+
+            log.info("查询申请退款订单详情成功,订单ID={}, 订单编号={}", orderId, order.getOrderNumber());
+            return order;
+        } catch (Exception e) {
+            log.error("查询申请退款订单详情异常,订单ID={}, 异常信息={}", orderId, e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 格式化收益金额(分转元)
+     *
+     * @param revenueInCents 收益金额(单位:分)
+     * @return 格式化后的金额字符串(单位:元)
+     */
+    private String formatRevenue(long revenueInCents) {
+        if (revenueInCents == 0) {
+            return "0";
+        }
+        BigDecimal revenue = new BigDecimal(revenueInCents);
+        BigDecimal revenueInYuan = revenue.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
+        return revenueInYuan.toString();
+    }
+
+    /**
+     * 发送退款申请通知给申请人
+     *
+     * @param order        订单对象
+     * @param clientUserId 客户端用户ID
+     */
+    private void sendRefundApplyNotice(LawyerConsultationOrder order, String clientUserId) {
+        try {
+            LifeNotice lifeNotice = createRefundApplyNotice(order, clientUserId);
+            if (lifeNotice == null) {
+                log.warn("生成退款申请通知失败,订单ID={}, 用户ID={}", order.getId(), clientUserId);
+                return;
+            }
+
+            int noticeResult = lifeNoticeMapper.insert(lifeNotice);
+            if (noticeResult <= 0) {
+                log.warn("保存退款申请通知失败,订单ID={}, 用户ID={}", order.getId(), clientUserId);
+                return;
+            }
+
+            // 发送WebSocket消息
+            WebSocketVo webSocketVo = buildWebSocketVo(lifeNotice);
+            webSocketProcess.sendMessage(lifeNotice.getReceiverId(),
+                    JSONObject.from(webSocketVo).toJSONString());
+
+            log.info("退款申请通知发送成功,接收人ID={}, 订单编号={}", lifeNotice.getReceiverId(), order.getOrderNumber());
+
+        } catch (Exception e) {
+            log.error("发送退款申请通知异常,订单ID={}, 用户ID={}, 异常信息={}",
+                    order.getId(), clientUserId, e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 创建退款申请通知对象
+     *
+     * @param order        订单对象
+     * @param clientUserId 客户端用户ID
+     * @return 通知对象,如果生成失败返回null
+     */
+    private LifeNotice createRefundApplyNotice(LawyerConsultationOrder order, String clientUserId) {
+        if (order == null || clientUserId == null) {
+            log.warn("创建退款申请通知失败,订单或用户ID为空");
+            return null;
+        }
+
+        try {
+            LifeNotice lifeNotice = new LifeNotice();
+            lifeNotice.setSenderId(SYSTEM_SENDER_ID);
+            lifeNotice.setBusinessId(order.getId());
+            lifeNotice.setTitle("申请退款通知");
+
+            // 获取申请人接收ID
+            String receiverId = getClientReceiverId(clientUserId);
+            if (org.apache.commons.lang3.StringUtils.isEmpty(receiverId)) {
+                log.warn("获取申请人接收ID失败,用户ID={}", clientUserId);
+                return null;
+            }
+            lifeNotice.setReceiverId(receiverId);
+
+            // 构建通知消息内容
+            String orderNumber = order.getOrderNumber();
+            String message = String.format("您的编号为%s的订单,申请退款的信息已提交给律师,等待律师同意。律师同意或48小时未处理,系统会将订单金额原路返还,请注意查收。", 
+                    orderNumber != null ? orderNumber : "");
+
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("title", "申请退款通知");
+            jsonObject.put("message", message);
+            lifeNotice.setContext(jsonObject.toJSONString());
+            lifeNotice.setNoticeType(1);
+            lifeNotice.setBusinessType(1);
+
+            return lifeNotice;
+
+        } catch (Exception e) {
+            log.error("创建退款申请通知异常,订单ID={}, 用户ID={}, 异常信息={}",
+                    order.getId(), clientUserId, e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 获取客户端用户接收ID
+     *
+     * @param clientUserId 客户端用户ID
+     * @return 接收人ID,格式:user_ + 手机号
+     */
+    private String getClientReceiverId(String clientUserId) {
+        try {
+            LifeUser lifeUser = lifeUserMapper.selectById(clientUserId);
+            if (lifeUser != null && org.apache.commons.lang3.StringUtils.isNotEmpty(lifeUser.getUserPhone())) {
+                return "user_" + lifeUser.getUserPhone();
+            }
+        } catch (Exception e) {
+            log.error("获取客户端用户手机号异常,用户ID={}, 异常信息={}", clientUserId, e.getMessage(), e);
+        }
+
+        log.warn("获取客户端用户手机号失败,用户ID={}", clientUserId);
+        return null;
+    }
+
+    /**
+     * 构建WebSocket消息对象
+     *
+     * @param lifeNotice 通知对象
+     * @return WebSocketVo对象
+     */
+    private WebSocketVo buildWebSocketVo(LifeNotice lifeNotice) {
+        WebSocketVo webSocketVo = new WebSocketVo();
+        webSocketVo.setSenderId(SYSTEM_SENDER_ID);
+        webSocketVo.setReceiverId(lifeNotice.getReceiverId());
+        webSocketVo.setCategory("notice");
+        webSocketVo.setNoticeType("1");
+        webSocketVo.setIsRead(0);
+        webSocketVo.setText(JSONObject.from(lifeNotice).toJSONString());
+        return webSocketVo;
+    }
+
+    /**
+     * 为订单列表设置订单价格字符串
+     * <p>
+     * 遍历订单列表,为每个订单设置orderPriceStr字段
+     * </p>
+     *
+     * @param voPage 订单列表分页对象
+     */
+    private void setOrderPriceStrForOrderList(IPage<LawyerConsultationOrderVO> voPage) {
+        if (voPage == null || CollectionUtils.isEmpty(voPage.getRecords())) {
+            return;
+        }
+
+        for (LawyerConsultationOrderVO orderVO : voPage.getRecords()) {
+            setOrderPriceStr(orderVO);
+        }
+    }
+
+    /**
+     * 设置订单价格字符串
+     * <p>
+     * 根据charge_minute、minute_num、charge_time、time_num字段设置orderPriceStr
+     * 优先级:charge_minute和minute_num > charge_time和time_num
+     * chargeMinute和chargeTime单位都是分,需要转换为元并保留两位小数,去掉尾随的0
+     * </p>
+     * <p>
+     * 处理逻辑:
+     * 1. 优先检查是否按分钟收费(chargeMinute和minuteNum都不为空)
+     * 2. 其次检查是否按次数收费(chargeTime和timeNum都不为空)
+     * 3. 如果都不满足,则设置为空字符串
+     * </p>
+     *
+     * @param orderVO 订单VO对象,不能为null
+     */
+    private void setOrderPriceStr(LawyerConsultationOrderVO orderVO) {
+        if (orderVO == null) {
+            log.warn("设置订单价格字符串失败:订单VO对象为null");
+            return;
+        }
+
+        try {
+            // 优先处理按分钟收费
+            if (isMinuteBasedCharging(orderVO)) {
+                String orderPriceStr = buildMinuteBasedPriceStr(orderVO);
+                orderVO.setOrderPriceStr(orderPriceStr);
+                log.debug("设置订单价格字符串成功(按分钟收费),chargeMinute={}, minuteNum={}, orderPriceStr={}",
+                        orderVO.getChargeMinute(), orderVO.getMinuteNum(), orderPriceStr);
+                return;
+            }
+
+            // 其次处理按次数收费
+            if (isTimeBasedCharging(orderVO)) {
+                String orderPriceStr = buildTimeBasedPriceStr(orderVO);
+                orderVO.setOrderPriceStr(orderPriceStr);
+                log.debug("设置订单价格字符串成功(按次数收费),chargeTime={}, timeNum={}, orderPriceStr={}",
+                        orderVO.getChargeTime(), orderVO.getTimeNum(), orderPriceStr);
+                return;
+            }
+
+            // 当都为空则返回空
+            orderVO.setOrderPriceStr(DEFAULT_PRICE_STR);
+            log.debug("订单价格字段都为空,设置默认空字符串");
+
+        } catch (Exception e) {
+            log.error("设置订单价格字符串异常,异常信息:{}", e.getMessage(), e);
+            // 异常时设置默认值,避免影响主流程
+            orderVO.setOrderPriceStr(DEFAULT_PRICE_STR);
+        }
+    }
+
+    /**
+     * 判断是否按分钟收费
+     * <p>
+     * 通过检查chargeMinute和minuteNum是否都不为空来判断
+     * </p>
+     *
+     * @param orderVO 订单VO对象
+     * @return true表示按分钟收费,false表示不是
+     */
+    private boolean isMinuteBasedCharging(LawyerConsultationOrderVO orderVO) {
+        return orderVO != null
+                && orderVO.getChargeMinute() != null
+                && orderVO.getMinuteNum() != null;
+    }
+
+    /**
+     * 判断是否按次数收费
+     * <p>
+     * 通过检查chargeTime和timeNum是否都不为空来判断
+     * </p>
+     *
+     * @param orderVO 订单VO对象
+     * @return true表示按次数收费,false表示不是
+     */
+    private boolean isTimeBasedCharging(LawyerConsultationOrderVO orderVO) {
+        return orderVO != null
+                && orderVO.getChargeTime() != null
+                && orderVO.getTimeNum() != null;
+    }
+
+    /**
+     * 构建按分钟收费的价格字符串
+     * <p>
+     * 格式:¥{价格}/{分钟数}分钟
+     * 例如:¥1.5/30分钟
+     * </p>
+     *
+     * @param orderVO 订单VO对象
+     * @return 格式化后的价格字符串
+     */
+    private String buildMinuteBasedPriceStr(LawyerConsultationOrderVO orderVO) {
+        // chargeMinute单位是分,需要转换为元并保留两位小数,去掉尾随的0
+        BigDecimal chargeMinuteYuan = convertFenToYuan(orderVO.getChargeMinute());
+        String priceStr = formatPrice(chargeMinuteYuan);
+        return String.format(PRICE_FORMAT_MINUTE, priceStr, orderVO.getMinuteNum());
+    }
+
+    /**
+     * 构建按次数收费的价格字符串
+     * <p>
+     * 格式:¥{价格}/{次数}次
+     * 例如:¥5/3次
+     * </p>
+     *
+     * @param orderVO 订单VO对象
+     * @return 格式化后的价格字符串
+     */
+    private String buildTimeBasedPriceStr(LawyerConsultationOrderVO orderVO) {
+        // chargeTime单位是分,需要转换为元并保留两位小数,去掉尾随的0
+        BigDecimal chargeTimeYuan = convertFenToYuan(orderVO.getChargeTime());
+        String priceStr = formatPrice(chargeTimeYuan);
+        return String.format(PRICE_FORMAT_TIME, priceStr, orderVO.getTimeNum());
+    }
+
+    /**
+     * 将分转换为元
+     * <p>
+     * 分转元,保留指定小数位数,使用四舍五入
+     * </p>
+     *
+     * @param fen 金额(分),单位:分
+     * @return 金额(元),单位:元,保留2位小数
+     */
+    private BigDecimal convertFenToYuan(Integer fen) {
+        if (fen == null) {
+            log.warn("转换分转元失败:金额为null,返回0");
+            return BigDecimal.ZERO;
+        }
+
+        if (fen < 0) {
+            log.warn("转换分转元失败:金额为负数,fen={},返回0", fen);
+            return BigDecimal.ZERO;
+        }
+
+        return BigDecimal.valueOf(fen)
+                .divide(BigDecimal.valueOf(FEN_TO_YUAN), DECIMAL_SCALE, RoundingMode.HALF_UP);
+    }
+
+    /**
+     * 格式化价格,去掉尾随的0
+     * <p>
+     * 将BigDecimal价格格式化为字符串,去掉尾随的0
+     * 例如:1.00 -> 1, 1.50 -> 1.5, 1.25 -> 1.25
+     * </p>
+     *
+     * @param price 价格(BigDecimal),可能为null
+     * @return 格式化后的价格字符串,如果price为null则返回"0"
+     */
+    private String formatPrice(BigDecimal price) {
+        if (price == null) {
+            log.debug("格式化价格:价格为null,返回默认值0");
+            return DEFAULT_FORMATTED_PRICE;
+        }
+
+        try {
+            // 去掉尾随的0
+            String formattedPrice = price.stripTrailingZeros().toPlainString();
+            log.debug("格式化价格成功,原始价格={}, 格式化后={}", price, formattedPrice);
+            return formattedPrice;
+        } catch (Exception e) {
+            log.error("格式化价格异常,price={},异常信息:{}", price, e.getMessage(), e);
+            // 异常时返回默认值
+            return DEFAULT_FORMATTED_PRICE;
+        }
+    }
+
+    /**
+     * 设置订单列表的评价状态(commonStatus)
+     * <p>
+     * 根据订单ID查询 lawyer_order_review 和 comment_appeals 表,设置每个订单的评价状态:
+     * 1. 如果 lawyer_order_review 表中 order_id=订单ID 的记录不存在,则 commonStatus=1
+     * 2. 如果 lawyer_order_review 存在,但 comment_appeals 表中 comment_id=lawyer_order_review.id 的记录不存在,
+     *    或者存在但 processing_status(status) 为 0 或 1,则 commonStatus=2
+     * 3. 其他情况 commonStatus=3
+     * </p>
+     *
+     * @param voPage 订单分页对象
+     */
+    private void setCommonStatusForOrderList(IPage<LawyerConsultationOrderVO> voPage) {
+        if (voPage == null || CollectionUtils.isEmpty(voPage.getRecords())) {
+            log.debug("订单列表为空,跳过设置评价状态");
+            return;
+        }
+
+        // 提取订单ID列表
+        List<Integer> orderIds = voPage.getRecords().stream()
+                .map(LawyerConsultationOrderVO::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        if (CollectionUtils.isEmpty(orderIds)) {
+            log.debug("订单ID列表为空,跳过设置评价状态");
+            return;
+        }
+
+        // 查询 lawyer_order_review 表,order_id=订单ID
+        Map<Integer, OrderReview> orderIdToReviewMap = queryOrderReviewByOrderIds(orderIds);
+
+        // 提取 lawyer_order_review 的 ID 列表
+        List<Integer> reviewIds = orderIdToReviewMap.values().stream()
+                .filter(Objects::nonNull)
+                .map(OrderReview::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        // 查询 comment_appeals 表,comment_id=lawyer_order_review.id
+        Map<Integer, CommentAppeal> reviewIdToAppealMap = queryCommentAppealsByReviewIds(reviewIds);
+
+        // 为每个订单设置 commonStatus
+        for (LawyerConsultationOrderVO orderVO : voPage.getRecords()) {
+            if (orderVO == null || orderVO.getId() == null) {
+                continue;
+            }
+
+            String commonStatus = calculateCommonStatus(
+                    orderVO.getId(), orderIdToReviewMap, reviewIdToAppealMap);
+            orderVO.setCommonStatus(commonStatus);
+        }
+
+        log.debug("设置订单评价状态完成,订单数量={}", voPage.getRecords().size());
+    }
+
+    /**
+     * 根据订单ID列表查询 lawyer_order_review 记录
+     * <p>
+     * 查询条件:order_id=订单ID
+     * 如果一个 order_id 存在多条记录,只取最新的一条(按创建时间倒序)
+     * </p>
+     *
+     * @param orderIds 订单ID列表
+     * @return 订单ID到评价记录的映射,key为订单ID,value为评价记录(每个订单ID对应最新的一条评价)
+     */
+    private Map<Integer, OrderReview> queryOrderReviewByOrderIds(List<Integer> orderIds) {
+        if (CollectionUtils.isEmpty(orderIds)) {
+            return Collections.emptyMap();
+        }
+
+        try {
+            LambdaQueryWrapper<OrderReview> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.in(OrderReview::getOrderId, orderIds)
+                    .eq(OrderReview::getDeleteFlag, 0)
+                    .orderByDesc(OrderReview::getCreatedTime);
+
+            List<OrderReview> reviewList = orderReviewMapper.selectList(queryWrapper);
+
+            // 按 orderId 分组,每个 orderId 只保留最新的一条记录
+            // 由于查询已按创建时间倒序排序,同一个 orderId 的第一条记录即为最新的
+            return reviewList.stream()
+                    .filter(Objects::nonNull)
+                    .filter(review -> review.getId() != null && review.getOrderId() != null)
+                    .collect(Collectors.toMap(
+                            OrderReview::getOrderId,
+                            review -> review,
+                            (existing, replacement) -> {
+                                // 由于已按创建时间倒序排序,existing 是先遍历到的记录(即最新的)
+                                // 但为了安全起见,仍然比较创建时间,确保保留最新的记录
+                                if (existing.getCreatedTime() != null && replacement.getCreatedTime() != null) {
+                                    // existing.getCreatedTime() >= replacement.getCreatedTime()(因为已排序)
+                                    return existing.getCreatedTime().after(replacement.getCreatedTime()) 
+                                            || existing.getCreatedTime().equals(replacement.getCreatedTime())
+                                            ? existing 
+                                            : replacement;
+                                } else if (existing.getCreatedTime() != null) {
+                                    // existing 有创建时间,replacement 没有,保留 existing
+                                    return existing;
+                                } else if (replacement.getCreatedTime() != null) {
+                                    // replacement 有创建时间,existing 没有,保留 replacement
+                                    return replacement;
+                                } else {
+                                    // 都没有创建时间,保留已存在的记录(先遍历到的,即排序后的第一条)
+                                    return existing;
+                                }
+                            }
+                    ));
+        } catch (Exception e) {
+            log.error("查询 lawyer_order_review 记录异常,orderIds={},异常信息:{}", orderIds, e.getMessage(), e);
+            return Collections.emptyMap();
+        }
+    }
+
+    /**
+     * 根据评价ID列表查询 comment_appeals 记录
+     * <p>
+     * 查询条件:comment_id=评价ID(lawyer_order_review.id)
+     * </p>
+     *
+     * @param reviewIds 评价ID列表(lawyer_order_review.id)
+     * @return 评价ID到申诉记录的映射,key为评价ID,value为申诉记录
+     */
+    private Map<Integer, CommentAppeal> queryCommentAppealsByReviewIds(List<Integer> reviewIds) {
+        if (CollectionUtils.isEmpty(reviewIds)) {
+            return Collections.emptyMap();
+        }
+
+        try {
+            // 构建查询条件
+            LambdaQueryWrapper<CommentAppeal> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.in(CommentAppeal::getCommentId, reviewIds)
+                    .eq(CommentAppeal::getDeleteFlag, DELETE_FLAG_NOT_DELETED);
+
+            List<CommentAppeal> appealList = commentAppealMapper.selectList(queryWrapper);
+
+            // 构建映射:key为评价ID,value为申诉记录
+            // 如果一个评价有多个申诉记录,只取第一个(通常一个评价应该只有一个申诉记录)
+            Map<Integer, CommentAppeal> resultMap = new HashMap<>();
+
+            for (CommentAppeal appeal : appealList) {
+                if (appeal == null || appeal.getCommentId() == null) {
+                    continue;
+                }
+
+                // 如果已经存在该评价的申诉记录,保留第一个(先查询到的)
+                if (!resultMap.containsKey(appeal.getCommentId())) {
+                    resultMap.put(appeal.getCommentId(), appeal);
+                }
+            }
+
+            log.debug("查询 comment_appeals 记录成功,reviewIds={},查询到记录数={}",
+                    reviewIds, resultMap.size());
+
+            return resultMap;
+        } catch (Exception e) {
+            log.error("查询 comment_appeals 记录异常,reviewIds={},异常信息:{}",
+                    reviewIds, e.getMessage(), e);
+            return Collections.emptyMap();
+        }
+    }
+
+    /**
+     * 计算订单的评价状态
+     * <p>
+     * 根据查询结果计算订单的 commonStatus 值:
+     * 1. 如果 lawyer_order_review 表中没有记录,则 commonStatus=1
+     * 2. 如果 comment_appeals 表中没有记录,或者 processing_status(status) 为 0 或 1,则 commonStatus=2
+     * 3. 其他情况 commonStatus=3
+     * </p>
+     *
+     * @param orderId               订单ID
+     * @param orderIdToReviewMap    订单ID到评价记录的映射
+     * @param reviewIdToAppealMap   评价ID到申诉记录的映射
+     * @return 评价状态字符串("1"、"2"或"3")
+     */
+    private String calculateCommonStatus(Integer orderId,
+                                        Map<Integer, OrderReview> orderIdToReviewMap,
+                                        Map<Integer, CommentAppeal> reviewIdToAppealMap) {
+        // 第一步:查询 lawyer_order_review 表
+        OrderReview review = orderIdToReviewMap.get(orderId);
+
+        // 如果关联不出来记录,则 commonStatus=1
+        if (review == null || review.getId() == null) {
+            log.debug("订单未找到评价记录,设置 commonStatus=1,orderId={}", orderId);
+            return "1";
+        }
+
+        // 第二步:查询 comment_appeals 表
+        CommentAppeal appeal = reviewIdToAppealMap.get(review.getId());
+
+        // 如果关联不出来或者关联出来的 processing_status(status) 为 0 或者 1,则 commonStatus=2
+        if (appeal == null) {
+            log.debug("评价未找到申诉记录,设置 commonStatus=2,orderId={}, reviewId={}",
+                    orderId, review.getId());
+            return "2";
+        }
+
+        Integer status = appeal.getStatus();
+        if (status == null || status == 0 || status == 2) {
+            log.debug("申诉记录处理状态为0或1,设置 commonStatus=2,orderId={}, reviewId={}, status={}",
+                    orderId, review.getId(), status);
+            return "2";
+        }
+
+        // 其他情况 commonStatus=3
+        log.debug("设置 commonStatus=3,orderId={}, reviewId={}, status={}",
+                orderId, review.getId(), status);
+        return "3";
+    }
+
+
+}
+

+ 94 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerConsultationReviewServiceImpl.java

@@ -0,0 +1,94 @@
+package shop.alien.lawyer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerConsultationReview;
+import shop.alien.lawyer.service.LawyerConsultationReviewService;
+import shop.alien.mapper.LawyerConsultationReviewMapper;
+
+import java.util.List;
+
+/**
+ * 咨询评价 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawyerConsultationReviewServiceImpl extends ServiceImpl<LawyerConsultationReviewMapper, LawyerConsultationReview> implements LawyerConsultationReviewService {
+
+    private final LawyerConsultationReviewMapper consultationReviewMapper;
+
+    @Override
+    public R<IPage<LawyerConsultationReview>> getConsultationReviewList(int pageNum, int pageSize, Integer consultationOrderId,
+                                                                        Integer lawyerUserId, Integer clientUserId) {
+        log.info("LawyerConsultationReviewServiceImpl.getConsultationReviewList?pageNum={},pageSize={},consultationOrderId={},lawyerUserId={},clientUserId={}",
+                pageNum, pageSize, consultationOrderId, lawyerUserId, clientUserId);
+        Page<LawyerConsultationReview> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<LawyerConsultationReview> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerConsultationReview::getDeleteFlag, 0);
+        if (consultationOrderId != null) {
+            queryWrapper.eq(LawyerConsultationReview::getConsultationOrderId, consultationOrderId);
+        }
+        if (lawyerUserId != null) {
+            queryWrapper.eq(LawyerConsultationReview::getLawyerUserId, lawyerUserId);
+        }
+        if (clientUserId != null) {
+            queryWrapper.eq(LawyerConsultationReview::getClientUserId, clientUserId);
+        }
+        queryWrapper.orderByDesc(LawyerConsultationReview::getCreatedTime);
+        IPage<LawyerConsultationReview> pageResult = this.page(page, queryWrapper);
+        return R.data(pageResult);
+    }
+
+    @Override
+    public List<LawyerConsultationReview> getListByLawyerId(Integer lawyerUserId) {
+        log.info("LawyerConsultationReviewServiceImpl.getListByLawyerId?lawyerUserId={}", lawyerUserId);
+        LambdaQueryWrapper<LawyerConsultationReview> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerConsultationReview::getLawyerUserId, lawyerUserId)
+                .eq(LawyerConsultationReview::getDeleteFlag, 0)
+                .orderByDesc(LawyerConsultationReview::getCreatedTime);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public R<LawyerConsultationReview> addConsultationReview(LawyerConsultationReview consultationReview) {
+        log.info("LawyerConsultationReviewServiceImpl.addConsultationReview?consultationReview={}", consultationReview);
+        boolean result = this.save(consultationReview);
+        if (result) {
+            return R.data(consultationReview);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<LawyerConsultationReview> editConsultationReview(LawyerConsultationReview consultationReview) {
+        log.info("LawyerConsultationReviewServiceImpl.editConsultationReview?consultationReview={}", consultationReview);
+        boolean result = this.updateById(consultationReview);
+        if (result) {
+            return R.data(consultationReview);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteConsultationReview(Integer id) {
+        log.info("LawyerConsultationReviewServiceImpl.deleteConsultationReview?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+}
+

+ 162 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerExpertiseAreaServiceImpl.java

@@ -0,0 +1,162 @@
+package shop.alien.lawyer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerExpertiseArea;
+import shop.alien.lawyer.service.LawyerExpertiseAreaService;
+import shop.alien.mapper.LawyerExpertiseAreaMapper;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 领域信息 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawyerExpertiseAreaServiceImpl extends ServiceImpl<LawyerExpertiseAreaMapper, LawyerExpertiseArea> implements LawyerExpertiseAreaService {
+
+    private final LawyerExpertiseAreaMapper expertiseAreaMapper;
+
+    @Override
+    public R<IPage<LawyerExpertiseArea>> getExpertiseAreaList(int pageNum, int pageSize, String expertiseAreaInfo) {
+        log.info("LawyerExpertiseAreaServiceImpl.getExpertiseAreaList?pageNum={},pageSize={},expertiseAreaInfo={}",
+                pageNum, pageSize, expertiseAreaInfo);
+        Page<LawyerExpertiseArea> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<LawyerExpertiseArea> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerExpertiseArea::getDeleteFlag, 0);
+        if (StringUtils.isNotBlank(expertiseAreaInfo)) {
+            queryWrapper.like(LawyerExpertiseArea::getExpertiseAreaInfo, expertiseAreaInfo);
+        }
+        queryWrapper.orderByDesc(LawyerExpertiseArea::getCreatedTime);
+        IPage<LawyerExpertiseArea> pageResult = this.page(page, queryWrapper);
+        return R.data(pageResult);
+    }
+
+    @Override
+    public R<LawyerExpertiseArea> addExpertiseArea(LawyerExpertiseArea expertiseArea) {
+        log.info("LawyerExpertiseAreaServiceImpl.addExpertiseArea?expertiseArea={}", expertiseArea);
+
+        // 参数校验
+        if (expertiseArea == null) {
+            log.warn("新增领域信息失败:参数为空");
+            return R.fail("参数不能为空");
+        }
+        // 参数校验
+        if (StringUtils.isBlank(expertiseArea.getExpertiseAreaInfo())) {
+            log.warn("新增领域信息失败:领域名称为空");
+            return R.fail("领域名称不能为空");
+        }
+
+        // 设置默认值
+        if (expertiseArea.getDeleteFlag() == null) {
+            expertiseArea.setDeleteFlag(0);
+        }
+        if (expertiseArea.getCreatedTime() == null) {
+            expertiseArea.setCreatedTime(new Date());
+        }
+        if (expertiseArea.getUpdatedTime() == null) {
+            expertiseArea.setUpdatedTime(new Date());
+        }
+
+        boolean result = this.save(expertiseArea);
+        if (result) {
+            log.info("新增领域信息成功,ID={}", expertiseArea.getId());
+            return R.data(expertiseArea);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<LawyerExpertiseArea> editExpertiseArea(LawyerExpertiseArea expertiseArea) {
+        log.info("LawyerExpertiseAreaServiceImpl.editExpertiseArea?expertiseArea={}", expertiseArea);
+
+        // 参数校验
+        if (expertiseArea == null || expertiseArea.getId() == null) {
+            log.warn("编辑领域信息失败:ID为空");
+            return R.fail("ID不能为空");
+        }
+
+        // 查询记录是否存在
+        LawyerExpertiseArea existingArea = expertiseAreaMapper.selectById(expertiseArea.getId());
+        if (existingArea == null) {
+            log.warn("编辑领域信息失败:记录不存在,ID={}", expertiseArea.getId());
+            return R.fail("记录不存在");
+        }
+
+        // 设置更新时间
+        expertiseArea.setUpdatedTime(new Date());
+
+        boolean result = this.updateById(expertiseArea);
+        if (result) {
+            log.info("编辑领域信息成功,ID={}", expertiseArea.getId());
+            // 重新查询更新后的记录
+            LawyerExpertiseArea updatedArea = expertiseAreaMapper.selectById(expertiseArea.getId());
+            return R.data(updatedArea != null ? updatedArea : expertiseArea);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteExpertiseArea(Integer id) {
+        log.info("LawyerExpertiseAreaServiceImpl.deleteExpertiseArea?id={}", id);
+
+        // 参数校验
+        if (id == null) {
+            log.warn("删除领域信息失败:ID为空");
+            return R.fail("ID不能为空");
+        }
+
+        // 查询记录是否存在
+        LawyerExpertiseArea existingArea = expertiseAreaMapper.selectById(id);
+        if (existingArea == null) {
+            log.warn("删除领域信息失败:记录不存在,ID={}", id);
+            return R.fail("记录不存在");
+        }
+
+        boolean result = this.removeById(id);
+        if (result) {
+            log.info("删除领域信息成功,ID={}", id);
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @Override
+    public R<List<LawyerExpertiseArea>> getList(LawyerExpertiseArea expertiseArea) {
+        log.info("LawyerExpertiseAreaServiceImpl.getList?expertiseArea={}", expertiseArea);
+        List<LawyerExpertiseArea> list = QueryBuilder.of(expertiseArea)
+                .likeFields("expertiseAreaInfo")  // 领域信息支持模糊查询
+                .build()
+                .list(this);
+        return R.data(list);
+    }
+
+    @Override
+    public R<IPage<LawyerExpertiseArea>> getPage(LawyerExpertiseArea expertiseArea, int page, int size) {
+        log.info("LawyerExpertiseAreaServiceImpl.getPage?expertiseArea={},page={},size={}", expertiseArea, page, size);
+        int pageNum = page > 0 ? page : 1;
+        int pageSize = size > 0 ? size : 10;
+        IPage<LawyerExpertiseArea> pageResult = QueryBuilder.of(expertiseArea)
+                .likeFields("expertiseAreaInfo")  // 领域信息支持模糊查询
+                .page(pageNum, pageSize)
+                .build()
+                .page(this);
+        return R.data(pageResult);
+    }
+}
+

+ 140 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerImgServiceImpl.java

@@ -0,0 +1,140 @@
+package shop.alien.lawyer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.store.LawyerImg;
+import shop.alien.entity.store.vo.LawyerImgTypeVo;
+import shop.alien.lawyer.service.LawyerImgService;
+import shop.alien.mapper.LawyerImgMapper;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * 律师图片 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawyerImgServiceImpl extends ServiceImpl<LawyerImgMapper, LawyerImg> implements LawyerImgService {
+
+    private final LawyerImgMapper lawyerImgMapper;
+
+    /**
+     * 获取律师图片
+     *
+     * @param lawyerId 律师ID
+     * @param imgType  图片类型, 0:其他, 1:头像, 2:执业证照片, 3:身份证正面, 4:身份证反面, 5:案例图片, 6:相册图片, 7:资质证书, 8:荣誉证书, 9:工作照片
+     * @return list
+     */
+    @Override
+    public List<LawyerImg> getLawyerImg(Integer lawyerId, Integer imgType) {
+        log.info("LawyerImgServiceImpl.getLawyerImg?lawyerId={}&imgType={}", lawyerId, imgType);
+        LambdaQueryWrapper<LawyerImg> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(LawyerImg::getLawyerId, lawyerId)
+                .eq(LawyerImg::getImgType, imgType)
+                .orderByAsc(LawyerImg::getImgSort);
+        return this.list(lambdaQueryWrapper);
+    }
+
+    /**
+     * 获取所有图片类型和数量
+     *
+     * @param lawyerId 律师ID
+     * @return LawyerImgTypeVo
+     */
+    @Override
+    public List<LawyerImgTypeVo> getLawyerImgTypeCount(Integer lawyerId) {
+        log.info("LawyerImgServiceImpl.getLawyerImgTypeCount?lawyerId={}", lawyerId);
+        List<LawyerImgTypeVo> lawyerImgTypeVoList = lawyerImgMapper.getLawyerImgTypeCount(lawyerId);
+        for (LawyerImgTypeVo lawyerImgTypeVo : lawyerImgTypeVoList) {
+            //图片类型, 0:其他, 1:头像, 2:执业证照片, 3:身份证正面, 4:身份证反面, 5:案例图片, 6:相册图片, 7:资质证书, 8:荣誉证书, 9:工作照片
+            switch (lawyerImgTypeVo.getImgType()) {
+                case 0:
+                    lawyerImgTypeVo.setImgTypeStr("其他");
+                    break;
+                case 1:
+                    lawyerImgTypeVo.setImgTypeStr("头像");
+                    break;
+                case 2:
+                    lawyerImgTypeVo.setImgTypeStr("执业证照片");
+                    break;
+                case 3:
+                    lawyerImgTypeVo.setImgTypeStr("身份证正面");
+                    break;
+                case 4:
+                    lawyerImgTypeVo.setImgTypeStr("身份证反面");
+                    break;
+                case 5:
+                    lawyerImgTypeVo.setImgTypeStr("案例图片");
+                    break;
+                case 6:
+                    lawyerImgTypeVo.setImgTypeStr("相册图片");
+                    break;
+                case 7:
+                    lawyerImgTypeVo.setImgTypeStr("资质证书");
+                    break;
+                case 8:
+                    lawyerImgTypeVo.setImgTypeStr("荣誉证书");
+                    break;
+                case 9:
+                    lawyerImgTypeVo.setImgTypeStr("工作照片");
+                    break;
+            }
+        }
+        return lawyerImgTypeVoList.stream().sorted(Comparator.comparing(LawyerImgTypeVo::getImgType)).collect(Collectors.toList());
+    }
+
+    @Override
+    public List<String> getUrl(String id, Integer imgType) {
+        log.info("LawyerImgServiceImpl.getUrl?id={}&imgType={}", id, imgType);
+        QueryWrapper<LawyerImg> qw = new QueryWrapper<>();
+        qw.eq(Objects.nonNull(id), "lawyer_id", id)
+                .eq(Objects.nonNull(imgType), "img_type", imgType);
+        List<LawyerImg> resDb = lawyerImgMapper.selectList(qw);
+        return resDb.stream().map(LawyerImg::getImgUrl).collect(Collectors.toList());
+    }
+
+    /**
+     * 通过businessId获取图片
+     *
+     * @param lawyerId   律师ID
+     * @param imgType    图片类型
+     * @param businessId 业务ID
+     * @return list
+     */
+    @Override
+    public List<LawyerImg> getByBusinessId(Integer lawyerId, Integer imgType, Integer businessId) {
+        log.info("LawyerImgServiceImpl.getByBusinessId?lawyerId={}&imgType={}&businessId={}", lawyerId, imgType, businessId);
+        LambdaQueryWrapper<LawyerImg> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(LawyerImg::getLawyerId, lawyerId)
+                .eq(LawyerImg::getImgType, imgType)
+                .eq(LawyerImg::getBusinessId, businessId)
+                .orderByAsc(LawyerImg::getImgSort);
+        return this.list(lambdaQueryWrapper);
+    }
+
+    @Override
+    public int saveOrUpdateImg(Integer lawyerId, Integer imgType) {
+        log.info("LawyerImgServiceImpl.saveOrUpdateImg?lawyerId={}&imgType={}", lawyerId, imgType);
+        int result = 0;
+        if (lawyerId != null && lawyerId > 0 && imgType != null) {
+            result = lawyerImgMapper.delete(new LambdaQueryWrapper<LawyerImg>()
+                    .eq(LawyerImg::getLawyerId, lawyerId)
+                    .eq(LawyerImg::getImgType, imgType));
+        }
+        return result;
+    }
+}
+

+ 199 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerLegalProblemScenarioServiceImpl.java

@@ -0,0 +1,199 @@
+package shop.alien.lawyer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerLegalProblemScenario;
+import shop.alien.lawyer.service.LawyerLegalProblemScenarioService;
+import shop.alien.mapper.LawyerLegalProblemScenarioMapper;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 法律问题场景 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawyerLegalProblemScenarioServiceImpl extends ServiceImpl<LawyerLegalProblemScenarioMapper, LawyerLegalProblemScenario> implements LawyerLegalProblemScenarioService {
+
+    @Override
+    public R<LawyerLegalProblemScenario> addLawyerLegalProblemScenario(LawyerLegalProblemScenario lawyerLegalProblemScenario) {
+        boolean result = this.save(lawyerLegalProblemScenario);
+        if (result) {
+            return R.data(lawyerLegalProblemScenario);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<LawyerLegalProblemScenario> editLawyerLegalProblemScenario(LawyerLegalProblemScenario lawyerLegalProblemScenario) {
+        log.info("LawyerLegalProblemScenarioServiceImpl.editLawyerLegalProblemScenario?LawyerLegalProblemScenario={}", lawyerLegalProblemScenario);
+        boolean result = this.updateById(lawyerLegalProblemScenario);
+        if (result) {
+            return R.data(lawyerLegalProblemScenario);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteLawyerLegalProblemScenario(Integer id) {
+        log.info("LawyerLegalProblemScenarioServiceImpl.deleteLawyerLegalProblemScenario?id={}", id);
+        
+        // 参数校验
+        if (id == null) {
+            log.warn("删除法律问题场景失败:ID为空");
+            return R.fail("场景ID不能为空");
+        }
+        
+        // 查询要删除的场景
+        LawyerLegalProblemScenario scenario = this.getById(id);
+        if (scenario == null) {
+            log.warn("删除法律问题场景失败:场景不存在,id={}", id);
+            return R.fail("场景不存在");
+        }
+        
+        // 递归删除所有子场景
+        List<Integer> allIdsToDelete = new ArrayList<>();
+        allIdsToDelete.add(id);
+        collectChildIds(id, allIdsToDelete);
+        
+        log.info("准备删除场景及其子场景,共{}个,ids={}", allIdsToDelete.size(), allIdsToDelete);
+        
+        // 批量删除所有场景(包括当前场景和所有子场景)
+        boolean result = this.removeByIds(allIdsToDelete);
+        if (result) {
+            log.info("删除法律问题场景成功,共删除{}个场景", allIdsToDelete.size());
+            return R.success("删除成功,共删除" + allIdsToDelete.size() + "个场景");
+        }
+        return R.fail("删除失败");
+    }
+    
+    /**
+     * 递归收集所有子场景的ID
+     *
+     * @param parentId 父场景ID
+     * @param idsList  用于收集所有子场景ID的列表
+     */
+    private void collectChildIds(Integer parentId, List<Integer> idsList) {
+        // 查询所有直接子场景(parentId = 当前场景ID)
+        QueryWrapper<LawyerLegalProblemScenario> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("parent_id", parentId)
+                .eq("delete_flag", 0);
+        List<LawyerLegalProblemScenario> children = this.list(queryWrapper);
+        
+        if (!CollectionUtils.isEmpty(children)) {
+            for (LawyerLegalProblemScenario child : children) {
+                Integer childId = child.getId();
+                idsList.add(childId);
+                // 递归查找子场景的子场景
+                collectChildIds(childId, idsList);
+            }
+        }
+    }
+
+    @Override
+    public R<LawyerLegalProblemScenario> getCategoryTreeByFirstLevelId(Integer id) {
+        log.info("LawyerLegalProblemScenarioServiceImpl.getCategoryTreeByFirstLevelId?id={}", id);
+
+        // 验证一级场景是否存在
+        LawyerLegalProblemScenario firstLevel = this.getById(id);
+        if (firstLevel == null) {
+            return R.fail("一级场景不存在");
+        }
+        if (firstLevel.getLevel() == null || firstLevel.getLevel() != 1) {
+            return R.fail("该主键不是一级场景");
+        }
+
+        // 查询所有二级分类(父ID为一级场景ID,level=2)
+        QueryWrapper<LawyerLegalProblemScenario> secondLevelQuery = new QueryWrapper<>();
+        secondLevelQuery.eq("parent_id", id)
+                .eq("level", 2)
+                .eq("delete_flag", 0)
+                .orderByAsc("sort_order", "id");
+        List<LawyerLegalProblemScenario> secondLevelList = this.list(secondLevelQuery);
+
+        // 如果二级分类为空,直接返回一级场景(children为空列表)
+        if (CollectionUtils.isEmpty(secondLevelList)) {
+            firstLevel.setChildren(new ArrayList<>());
+            return R.data(firstLevel);
+        }
+
+        // 获取所有二级分类的ID列表
+        List<Integer> secondLevelIds = secondLevelList.stream()
+                .map(LawyerLegalProblemScenario::getId)
+                .collect(Collectors.toList());
+
+        // 查询所有三级分类(父ID在二级分类ID列表中,level=3)
+        QueryWrapper<LawyerLegalProblemScenario> thirdLevelQuery = new QueryWrapper<>();
+        thirdLevelQuery.in("parent_id", secondLevelIds)
+                .eq("level", 3)
+                .eq("delete_flag", 0)
+                .orderByAsc("sort_order", "id");
+        List<LawyerLegalProblemScenario> thirdLevelList = this.list(thirdLevelQuery);
+
+        // 将三级分类按父ID分组
+        Map<Integer, List<LawyerLegalProblemScenario>> thirdLevelMap = thirdLevelList.stream()
+                .collect(Collectors.groupingBy(LawyerLegalProblemScenario::getParentId));
+
+        // 为每个二级分类设置其下的三级分类列表
+        for (LawyerLegalProblemScenario secondLevel : secondLevelList) {
+            List<LawyerLegalProblemScenario> children = thirdLevelMap.get(secondLevel.getId());
+            secondLevel.setChildren(children != null ? children : new ArrayList<>());
+        }
+
+        // 将二级分类列表设置到一级场景的children中
+        firstLevel.setChildren(secondLevelList);
+
+        return R.data(firstLevel);
+    }
+
+    @Override
+    public R<Boolean> setStatus(Integer id, Integer status) {
+        log.info("LawyerLegalProblemScenarioServiceImpl.setStatus?id={},status={}", id, status);
+
+        // 参数校验
+        if (id == null) {
+            log.warn("设置法律场景状态失败:场景ID为空");
+            return R.fail("场景ID不能为空");
+        }
+
+        if (status == null || (status != 0 && status != 1)) {
+            log.warn("设置法律场景状态失败:状态参数无效,status={}", status);
+            return R.fail("状态参数无效,必须为0(禁用)或1(启用)");
+        }
+
+        // 查询场景信息
+        LawyerLegalProblemScenario scenario = this.getById(id);
+        if (scenario == null) {
+            log.warn("设置法律场景状态失败:场景不存在,场景ID={}", id);
+            return R.fail("场景不存在");
+        }
+
+        // 更新状态
+        scenario.setStatus(status);
+        boolean result = this.updateById(scenario);
+        if (result) {
+            log.info("设置法律场景状态成功:场景ID={},状态={}", id, status);
+            return R.success("设置成功");
+        } else {
+            log.warn("设置法律场景状态失败:更新数据库失败,场景ID={},状态={}", id, status);
+            return R.fail("设置失败");
+        }
+    }
+
+}
+

+ 477 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerNoticeServiceImpl.java

@@ -0,0 +1,477 @@
+package shop.alien.lawyer.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerUserViolation;
+import shop.alien.entity.store.LifeNotice;
+import shop.alien.entity.store.vo.LifeMessageVo;
+import shop.alien.entity.store.vo.LifeNoticeVo;
+import shop.alien.lawyer.service.LawyerNoticeService;
+import shop.alien.mapper.LawyerUserViolationMapper;
+import shop.alien.mapper.LifeMessageMapper;
+import shop.alien.mapper.LifeNoticeMapper;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 律师通知服务实现类
+ *
+ * @author system
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class LawyerNoticeServiceImpl extends ServiceImpl<LifeNoticeMapper, LifeNotice> implements LawyerNoticeService {
+
+    /**
+     * 系统发送者标识
+     */
+    private static final String SYSTEM_SENDER = "system";
+
+    /**
+     * 平台类型:1-平台
+     */
+    private static final String PLATFORM_TYPE_ONE = "1";
+
+    /**
+     * 店铺审核通知标题
+     */
+    private static final String STORE_AUDIT_NOTICE_TITLE = "店铺审核通知";
+
+    /**
+     * 删除标识:0-未删除
+     */
+    private static final Integer DELETE_FLAG_NOT_DELETED = 0;
+
+    /**
+     * 未读标识:0-未读
+     */
+    private static final Integer IS_READ_UNREAD = 0;
+
+    /**
+     * 已读标识:1-已读
+     */
+    private static final Integer IS_READ_READ = 1;
+
+    /**
+     * 发送者ID分隔符
+     */
+    private static final String SENDER_ID_SEPARATOR = "_";
+
+    /**
+     * 店铺类型标识
+     */
+    private static final String STORE_TYPE = "store";
+
+    /**
+     * 用户类型标识
+     */
+    private static final String USER_TYPE = "user";
+
+    /**
+     * 报告上下文类型范围
+     */
+    private static final String REPORT_CONTEXT_TYPE_RANGE = "1,2,3,6";
+
+    private final LifeNoticeMapper lifeNoticeMapper;
+
+    private final LifeMessageMapper lifeMessageMapper;
+
+    private final LawyerUserViolationMapper lawyerUserViolationMapper;
+
+    @Override
+    public R<IPage<LifeNoticeVo>> getLawyerNoticeList(Integer pageNum, Integer pageSize, String receiverId, Integer noticeType) {
+        // 构建查询条件
+        LambdaQueryWrapper<LifeNotice> queryWrapper = buildQueryWrapper(receiverId, noticeType);
+        
+        // 使用分页查询,避免查询全部数据
+        IPage<LifeNotice> noticePage = new Page<>(pageNum, pageSize);
+        IPage<LifeNotice> noticeResult = lifeNoticeMapper.selectPage(noticePage, queryWrapper);
+        
+        List<LifeNotice> lifeNoticeList = noticeResult.getRecords();
+        if (CollectionUtil.isEmpty(lifeNoticeList)) {
+            return R.data(createEmptyPageResult(pageNum, pageSize));
+        }
+
+        // 提取业务ID列表
+        List<Integer> businessIdList = extractBusinessIdList(lifeNoticeList);
+        
+        // 获取违规信息映射
+        Map<Integer, String> lawyerUserViolationMap = getLifeUserViolationMap(businessIdList);
+        
+        // 提取发送者ID并按类型分组
+        Map<String, List<String>> senderIdGroupMap = extractSenderIdGroupMap(lifeNoticeList);
+        
+        // 获取用户信息列表
+        List<LifeMessageVo> userList = getUserList(senderIdGroupMap);
+        
+        // 转换为VO列表
+        List<LifeNoticeVo> noticeVoList = convertToNoticeVoList(lifeNoticeList, userList);
+        
+        // 设置平台类型
+        setPlatformType(noticeVoList, lawyerUserViolationMap);
+        
+        // 构建分页结果
+        IPage<LifeNoticeVo> result = new Page<>(pageNum, pageSize);
+        result.setRecords(noticeVoList);
+        result.setTotal(noticeResult.getTotal());
+        result.setPages(noticeResult.getPages());
+        result.setCurrent(noticeResult.getCurrent());
+        result.setSize(noticeResult.getSize());
+        
+        return R.data(result);
+    }
+
+    /**
+     * 构建查询条件
+     *
+     * @param receiverId 接收人ID
+     * @param noticeType 通知类型
+     * @return 查询条件包装器
+     */
+    private LambdaQueryWrapper<LifeNotice> buildQueryWrapper(String receiverId, Integer noticeType) {
+        LambdaQueryWrapper<LifeNotice> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LifeNotice::getReceiverId, receiverId)
+                .eq(LifeNotice::getNoticeType, noticeType)
+                .eq(LifeNotice::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
+                .orderByDesc(LifeNotice::getCreatedTime);
+        return queryWrapper;
+    }
+
+    /**
+     * 创建空的分页结果
+     *
+     * @param pageNum  页码
+     * @param pageSize 每页大小
+     * @return 空的分页结果
+     */
+    private IPage<LifeNoticeVo> createEmptyPageResult(Integer pageNum, Integer pageSize) {
+        IPage<LifeNoticeVo> result = new Page<>(pageNum, pageSize);
+        result.setRecords(Collections.emptyList());
+        result.setTotal(0L);
+        result.setPages(0);
+        return result;
+    }
+
+    /**
+     * 提取业务ID列表
+     *
+     * @param lifeNoticeList 通知列表
+     * @return 业务ID列表
+     */
+    private List<Integer> extractBusinessIdList(List<LifeNotice> lifeNoticeList) {
+        return lifeNoticeList.stream()
+                .map(LifeNotice::getBusinessId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 获取违规信息映射
+     *
+     * @param businessIdList 业务ID列表
+     * @return 违规信息映射,key为业务ID,value为报告上下文类型
+     */
+    private Map<Integer, String> getLifeUserViolationMap(List<Integer> businessIdList) {
+        if (CollectionUtil.isEmpty(businessIdList)) {
+            return Collections.emptyMap();
+        }
+        
+        List<LawyerUserViolation> lawyerUserViolationList = lawyerUserViolationMapper.selectBatchIds(businessIdList);
+        if (CollectionUtil.isEmpty(lawyerUserViolationList)) {
+            return Collections.emptyMap();
+        }
+        
+        return lawyerUserViolationList.stream()
+                .filter(violation -> violation != null && violation.getId() != null)
+                .collect(Collectors.toMap(
+                        LawyerUserViolation::getId,
+                        violation -> StringUtils.defaultString(violation.getReportContextType(), ""),
+                        (existing, replacement) -> existing
+                ));
+    }
+
+    /**
+     * 提取发送者ID并按类型分组
+     *
+     * @param lifeNoticeList 通知列表
+     * @return 发送者ID分组映射,key为类型(store/user),value为ID列表
+     */
+    private Map<String, List<String>> extractSenderIdGroupMap(List<LifeNotice> lifeNoticeList) {
+        return lifeNoticeList.stream()
+                .map(LifeNotice::getSenderId)
+                .filter(senderId -> StringUtils.isNotBlank(senderId) 
+                        && !SYSTEM_SENDER.equals(senderId) 
+                        && senderId.contains(SENDER_ID_SEPARATOR))
+                .collect(Collectors.groupingBy(
+                        senderId -> senderId.split(SENDER_ID_SEPARATOR)[0],
+                        Collectors.mapping(
+                                senderId -> senderId.split(SENDER_ID_SEPARATOR)[1],
+                                Collectors.toList()
+                        )
+                ));
+    }
+
+    /**
+     * 获取用户信息列表
+     *
+     * @param senderIdGroupMap 发送者ID分组映射
+     * @return 用户信息列表
+     */
+    private List<LifeMessageVo> getUserList(Map<String, List<String>> senderIdGroupMap) {
+        String storePhones = buildPhoneString(senderIdGroupMap.get(STORE_TYPE));
+        String userPhones = buildPhoneString(senderIdGroupMap.get(USER_TYPE));
+        return lifeMessageMapper.getLifeUserAndStoreUserByPhone(storePhones, userPhones);
+    }
+
+    /**
+     * 构建手机号字符串
+     *
+     * @param phoneList 手机号列表
+     * @return 手机号字符串,格式:'phone1','phone2'
+     */
+    private String buildPhoneString(List<String> phoneList) {
+        if (CollectionUtil.isEmpty(phoneList)) {
+            return "''";
+        }
+        return "'" + String.join("','", phoneList) + "'";
+    }
+
+    /**
+     * 转换为通知VO列表
+     *
+     * @param lifeNoticeList 通知列表
+     * @param userList       用户信息列表
+     * @return 通知VO列表
+     */
+    private List<LifeNoticeVo> convertToNoticeVoList(List<LifeNotice> lifeNoticeList, List<LifeMessageVo> userList) {
+        if (CollectionUtil.isEmpty(userList)) {
+            return lifeNoticeList.stream()
+                    .map(this::convertToNoticeVo)
+                    .collect(Collectors.toList());
+        }
+        
+        Map<String, LifeMessageVo> userMap = userList.stream()
+                .filter(user -> user != null && StringUtils.isNotBlank(user.getPhoneId()))
+                .collect(Collectors.toMap(
+                        LifeMessageVo::getPhoneId,
+                        user -> user,
+                        (existing, replacement) -> existing
+                ));
+        
+        return lifeNoticeList.stream()
+                .map(notice -> {
+                    LifeNoticeVo noticeVo = convertToNoticeVo(notice);
+                    LifeMessageVo userInfo = userMap.get(notice.getSenderId());
+                    if (userInfo != null && !SYSTEM_SENDER.equals(noticeVo.getSenderId())) {
+                        noticeVo.setUserName(userInfo.getUserName());
+                        noticeVo.setUserImage(userInfo.getUserImage());
+                    }
+                    return noticeVo;
+                })
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 转换为通知VO
+     *
+     * @param notice 通知实体
+     * @return 通知VO
+     */
+    private LifeNoticeVo convertToNoticeVo(LifeNotice notice) {
+        LifeNoticeVo noticeVo = new LifeNoticeVo();
+        BeanUtils.copyProperties(notice, noticeVo);
+        return noticeVo;
+    }
+
+    /**
+     * 设置平台类型
+     *
+     * @param noticeVoList           通知VO列表
+     * @param lawyerUserViolationMap   违规信息映射
+     */
+    private void setPlatformType(List<LifeNoticeVo> noticeVoList, Map<Integer, String> lawyerUserViolationMap) {
+        for (LifeNoticeVo noticeVo : noticeVoList) {
+            if (noticeVo == null) {
+                continue;
+            }
+            
+            // 业务ID为空时设置为平台类型
+            if (noticeVo.getBusinessId() == null) {
+                noticeVo.setPlatformType(PLATFORM_TYPE_ONE);
+                continue;
+            }
+            
+            // 店铺审核通知设置为平台类型
+            if (STORE_AUDIT_NOTICE_TITLE.equals(noticeVo.getTitle())) {
+                noticeVo.setPlatformType(PLATFORM_TYPE_ONE);
+                continue;
+            }
+            
+            // 根据违规信息设置平台类型
+            String reportContextType = lawyerUserViolationMap.get(noticeVo.getBusinessId());
+            if (StringUtils.isNotBlank(reportContextType) 
+                    && REPORT_CONTEXT_TYPE_RANGE.contains(reportContextType)) {
+                noticeVo.setPlatformType(PLATFORM_TYPE_ONE);
+            }
+        }
+    }
+
+    @Override
+    public R<Boolean> hasUnreadNotice(String receiverId) {
+        log.info("LawyerNoticeServiceImpl.hasUnreadNotice, receiverId={}", receiverId);
+        
+        // 参数校验
+        if (StringUtils.isBlank(receiverId)) {
+            log.warn("接收人ID为空");
+            return R.fail("接收人ID不能为空");
+        }
+        
+        // 构建查询条件:查询未读且未删除的通知,使用LIMIT 1优化性能
+        LambdaQueryWrapper<LifeNotice> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LifeNotice::getReceiverId, receiverId)
+                .eq(LifeNotice::getIsRead, IS_READ_UNREAD)
+                .eq(LifeNotice::getDeleteFlag, DELETE_FLAG_NOT_DELETED)
+                .last("LIMIT 1");
+        
+        // 使用selectOne判断是否存在,数据库找到一条记录即返回,比selectCount更高效
+        LifeNotice notice = lifeNoticeMapper.selectOne(queryWrapper);
+        boolean hasUnread = notice != null;
+        
+        log.debug("LawyerNoticeServiceImpl.hasUnreadNotice, receiverId={}, hasUnread={}", receiverId, hasUnread);
+        return R.data(hasUnread);
+    }
+
+    /**
+     * 根据通知ID标记通知为已读
+     * <p>
+     * 处理流程:
+     * 1. 参数校验:通知ID必须大于0且不能为null
+     * 2. 构建更新条件:根据ID匹配通知记录
+     * 3. 执行更新:将isRead字段设置为1(已读)
+     * 4. 返回更新结果:返回受影响的记录数
+     * </p>
+     * <p>
+     * 注意事项:
+     * - 如果通知不存在或ID无效,更新操作将不会影响任何记录,返回0
+     * - 如果通知已经是已读状态,更新操作仍然会执行但不会改变状态
+     * </p>
+     *
+     * @param id 通知ID,必须大于0且不能为null
+     * @return 受影响的记录数,>0表示更新成功,0表示未找到匹配的记录或更新失败
+     * @throws IllegalArgumentException 当通知ID无效时抛出
+     * @throws RuntimeException        当更新操作异常时抛出
+     * @author system
+     * @since 2025-01-XX
+     */
+    @Override
+    public int readNoticeById(Integer id) {
+        log.info("标记通知为已读开始,id={}", id);
+
+        // 参数校验
+        validateNoticeId(id);
+
+        try {
+            // 构建更新条件
+            LambdaUpdateWrapper<LifeNotice> updateWrapper = buildUpdateWrapper(id);
+
+            // 执行更新操作
+            int affectedRows = executeUpdate(updateWrapper);
+
+            // 记录更新结果
+            logUpdateResult(id, affectedRows);
+
+            return affectedRows;
+        } catch (IllegalArgumentException e) {
+            log.warn("标记通知为已读参数校验失败,id={},错误信息:{}", id, e.getMessage());
+            throw e;
+        } catch (RuntimeException e) {
+            log.error("标记通知为已读异常,id={},异常信息:{}", id, e.getMessage(), e);
+            throw new RuntimeException("标记通知为已读失败:" + e.getMessage(), e);
+        } catch (Exception e) {
+            log.error("标记通知为已读未知异常,id={},异常信息:{}", id, e.getMessage(), e);
+            throw new RuntimeException("标记通知为已读失败,请稍后重试", e);
+        }
+    }
+
+    /**
+     * 校验通知ID
+     * <p>
+     * 检查通知ID是否为空或无效
+     * </p>
+     *
+     * @param id 通知ID
+     * @throws IllegalArgumentException 当通知ID为空或无效时抛出
+     */
+    private void validateNoticeId(Integer id) {
+        if (id == null) {
+            log.warn("标记通知为已读参数校验失败:通知ID为null");
+            throw new IllegalArgumentException("通知ID不能为空");
+        }
+
+        if (id <= 0) {
+            log.warn("标记通知为已读参数校验失败:通知ID无效,id={}", id);
+            throw new IllegalArgumentException("通知ID必须大于0");
+        }
+    }
+
+    /**
+     * 构建更新条件包装器
+     * <p>
+     * 根据通知ID构建更新条件,将isRead字段设置为已读状态
+     * </p>
+     *
+     * @param id 通知ID
+     * @return 更新条件包装器
+     */
+    private LambdaUpdateWrapper<LifeNotice> buildUpdateWrapper(Integer id) {
+        LambdaUpdateWrapper<LifeNotice> wrapper = new LambdaUpdateWrapper<>();
+        wrapper.eq(LifeNotice::getId, id);
+        wrapper.set(LifeNotice::getIsRead, IS_READ_READ);
+
+        log.debug("构建更新条件成功,id={}, isRead={}", id, IS_READ_READ);
+        return wrapper;
+    }
+
+    /**
+     * 执行更新操作
+     * <p>
+     * 调用Mapper执行更新操作,将通知标记为已读
+     * </p>
+     *
+     * @param updateWrapper 更新条件包装器
+     * @return 受影响的记录数
+     */
+    private int executeUpdate(LambdaUpdateWrapper<LifeNotice> updateWrapper) {
+        return lifeNoticeMapper.update(null, updateWrapper);
+    }
+
+    /**
+     * 记录更新结果日志
+     * <p>
+     * 根据更新结果记录相应级别的日志
+     * </p>
+     *
+     * @param id          通知ID
+     * @param affectedRows 受影响的记录数
+     */
+    private void logUpdateResult(Integer id, int affectedRows) {
+        if (affectedRows > 0) {
+            log.info("标记通知为已读成功,id={},受影响记录数={}", id, affectedRows);
+        } else {
+            log.warn("标记通知为已读未找到匹配记录,id={},受影响记录数={},可能是通知不存在或已被删除", id, affectedRows);
+        }
+    }
+}

+ 82 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerPaymentTransactionServiceImpl.java

@@ -0,0 +1,82 @@
+package shop.alien.lawyer.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerPaymentTransaction;
+import shop.alien.lawyer.service.LawyerPaymentTransactionService;
+import shop.alien.mapper.LawyerPaymentTransactionMapper;
+
+/**
+ * 支付交易 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawyerPaymentTransactionServiceImpl extends ServiceImpl<LawyerPaymentTransactionMapper, LawyerPaymentTransaction> implements LawyerPaymentTransactionService {
+
+    private final LawyerPaymentTransactionMapper paymentTransactionMapper;
+
+    @Override
+    public R<IPage<LawyerPaymentTransaction>> getPaymentTransactionList(int pageNum, int pageSize, Integer consultationOrderId,
+                                                                        Integer clientUserId, Integer transactionStatus) {
+        log.info("LawyerPaymentTransactionServiceImpl.getPaymentTransactionList?pageNum={},pageSize={},consultationOrderId={},clientUserId={},transactionStatus={}",
+                pageNum, pageSize, consultationOrderId, clientUserId, transactionStatus);
+        Page<LawyerPaymentTransaction> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<LawyerPaymentTransaction> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerPaymentTransaction::getDeleteFlag, 0);
+        if (consultationOrderId != null) {
+            queryWrapper.eq(LawyerPaymentTransaction::getConsultationOrderId, consultationOrderId);
+        }
+        if (clientUserId != null) {
+            queryWrapper.eq(LawyerPaymentTransaction::getClientUserId, clientUserId);
+        }
+        if (transactionStatus != null) {
+            queryWrapper.eq(LawyerPaymentTransaction::getTransactionStatus, transactionStatus);
+        }
+        queryWrapper.orderByDesc(LawyerPaymentTransaction::getTransactionTime);
+        IPage<LawyerPaymentTransaction> pageResult = this.page(page, queryWrapper);
+        return R.data(pageResult);
+    }
+
+    @Override
+    public R<LawyerPaymentTransaction> addPaymentTransaction(LawyerPaymentTransaction paymentTransaction) {
+        log.info("LawyerPaymentTransactionServiceImpl.addPaymentTransaction?paymentTransaction={}", paymentTransaction);
+        boolean result = this.save(paymentTransaction);
+        if (result) {
+            return R.data(paymentTransaction);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<LawyerPaymentTransaction> editPaymentTransaction(LawyerPaymentTransaction paymentTransaction) {
+        log.info("LawyerPaymentTransactionServiceImpl.editPaymentTransaction?paymentTransaction={}", paymentTransaction);
+        boolean result = this.updateById(paymentTransaction);
+        if (result) {
+            return R.data(paymentTransaction);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deletePaymentTransaction(Integer id) {
+        log.info("LawyerPaymentTransactionServiceImpl.deletePaymentTransaction?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+}
+

+ 56 - 0
alien-branch-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerServiceAreaServiceImpl.java

@@ -0,0 +1,56 @@
+package shop.alien.lawyer.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerServiceArea;
+import shop.alien.lawyer.service.LawyerServiceAreaService;
+import shop.alien.mapper.LawyerServiceAreaMapper;
+
+
+/**
+ * 律师服务领域关联 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawyerServiceAreaServiceImpl extends ServiceImpl<LawyerServiceAreaMapper, LawyerServiceArea> implements LawyerServiceAreaService {
+
+    @Override
+    public R<LawyerServiceArea> addLawyerServiceArea(LawyerServiceArea lawyerServiceArea) {
+        log.info("LawyerServiceAreaServiceImpl.addLawyerServiceArea?lawyerServiceArea={}", lawyerServiceArea);
+        boolean result = this.save(lawyerServiceArea);
+        if (result) {
+            return R.data(lawyerServiceArea);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<LawyerServiceArea> editLawyerServiceArea(LawyerServiceArea lawyerServiceArea) {
+        log.info("LawyerServiceAreaServiceImpl.editLawyerServiceArea?lawyerServiceArea={}", lawyerServiceArea);
+        boolean result = this.updateById(lawyerServiceArea);
+        if (result) {
+            return R.data(lawyerServiceArea);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteLawyerServiceArea(Integer id) {
+        log.info("LawyerServiceAreaServiceImpl.deleteLawyerServiceArea?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+}
+

Някои файлове не бяха показани, защото твърде много файлове са промени