Ver código fonte

生成接口,大致功能已具备,细节字段需要前台配合进行调整
1. [律师推荐接口](#律师推荐接口)
2. [律师用户接口](#律师用户接口)
3. [AI聊天接口](#ai聊天接口)
4. [聊天管理接口](#聊天管理接口)
5. [咨询订单接口](#咨询订单接口)

LuTong 1 mês atrás
pai
commit
90b618ec8e
19 arquivos alterados com 773 adições e 9 exclusões
  1. 9 0
      alien-entity/src/main/java/shop/alien/entity/store/LawyerLegalProblemScenario.java
  2. 4 0
      alien-entity/src/main/java/shop/alien/entity/store/LawyerUser.java
  3. 17 0
      alien-store/src/main/java/shop/alien/store/controller/LawyerAiInteractionLogController.java
  4. 63 0
      alien-store/src/main/java/shop/alien/store/controller/LawyerChatSessionController.java
  5. 3 0
      alien-store/src/main/java/shop/alien/store/controller/LawyerCommonQuestionController.java
  6. 22 0
      alien-store/src/main/java/shop/alien/store/controller/LawyerConsultationOrderController.java
  7. 11 0
      alien-store/src/main/java/shop/alien/store/controller/LawyerLegalProblemScenarioController.java
  8. 60 0
      alien-store/src/main/java/shop/alien/store/controller/LawyerUserController.java
  9. 11 0
      alien-store/src/main/java/shop/alien/store/service/LawyerAiInteractionLogService.java
  10. 40 0
      alien-store/src/main/java/shop/alien/store/service/LawyerChatSessionService.java
  11. 2 0
      alien-store/src/main/java/shop/alien/store/service/LawyerCommonQuestionService.java
  12. 14 0
      alien-store/src/main/java/shop/alien/store/service/LawyerConsultationOrderService.java
  13. 8 0
      alien-store/src/main/java/shop/alien/store/service/LawyerLegalProblemScenarioService.java
  14. 38 0
      alien-store/src/main/java/shop/alien/store/service/LawyerUserService.java
  15. 31 0
      alien-store/src/main/java/shop/alien/store/service/impl/LawyerAiInteractionLogServiceImpl.java
  16. 150 0
      alien-store/src/main/java/shop/alien/store/service/impl/LawyerChatSessionServiceImpl.java
  17. 39 9
      alien-store/src/main/java/shop/alien/store/service/impl/LawyerConsultationOrderServiceImpl.java
  18. 63 0
      alien-store/src/main/java/shop/alien/store/service/impl/LawyerLegalProblemScenarioServiceImpl.java
  19. 188 0
      alien-store/src/main/java/shop/alien/store/service/impl/LawyerUserServiceImpl.java

+ 9 - 0
alien-entity/src/main/java/shop/alien/entity/store/LawyerLegalProblemScenario.java

@@ -9,6 +9,7 @@ import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.util.Date;
+import java.util.List;
 
 /**
  * 法律问题场景表
@@ -81,5 +82,13 @@ public class LawyerLegalProblemScenario extends Model<LawyerLegalProblemScenario
     @TableField("updated_user_id")
     private Integer updatedUserId;
 
+    @ApiModelProperty(value = "图片ID(关联lawyer_img表)")
+    @TableField("img_id")
+    private Integer imgId;
+
+    @ApiModelProperty(value = "子分类列表(用于树形结构)")
+    @TableField(exist = false)
+    private List<LawyerLegalProblemScenario> children;
+
 }
 

+ 4 - 0
alien-entity/src/main/java/shop/alien/entity/store/LawyerUser.java

@@ -264,6 +264,10 @@ public class LawyerUser extends Model<LawyerUser> {
     @TableField("recommend_sort")
     private Integer recommendSort;
 
+    @ApiModelProperty(value = "接单状态, 0:不接单, 1:接单中")
+    @TableField("order_receiving_status")
+    private Integer orderReceivingStatus;
+
     @TableField(exist = false)
     private String userName;
 

+ 17 - 0
alien-store/src/main/java/shop/alien/store/controller/LawyerAiInteractionLogController.java

@@ -11,6 +11,7 @@ import shop.alien.store.service.LawyerAiInteractionLogService;
 import shop.alien.util.myBaticsPlus.QueryBuilder;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * AI交互日志 前端控制器
@@ -111,5 +112,21 @@ public class LawyerAiInteractionLogController {
                 .page(aiInteractionLogService);
         return R.data(pageResult);
     }
+
+    @ApiOperation("AI聊天接口")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "message", value = "用户发送的消息内容", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "sessionId", value = "会话ID(可选)", 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);
+    }
 }
 

+ 63 - 0
alien-store/src/main/java/shop/alien/store/controller/LawyerChatSessionController.java

@@ -11,6 +11,7 @@ import shop.alien.store.service.LawyerChatSessionService;
 import shop.alien.util.myBaticsPlus.QueryBuilder;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 聊天会话 前端控制器
@@ -111,5 +112,67 @@ public class LawyerChatSessionController {
                 .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);
+    }
 }
 

+ 3 - 0
alien-store/src/main/java/shop/alien/store/controller/LawyerCommonQuestionController.java

@@ -10,7 +10,9 @@ import shop.alien.entity.store.LawyerCommonQuestion;
 import shop.alien.store.service.LawyerCommonQuestionService;
 import shop.alien.util.myBaticsPlus.QueryBuilder;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 常见问题 前端控制器
@@ -113,5 +115,6 @@ public class LawyerCommonQuestionController {
                 .page(commonQuestionService);
         return R.data(pageResult);
     }
+
 }
 

+ 22 - 0
alien-store/src/main/java/shop/alien/store/controller/LawyerConsultationOrderController.java

@@ -12,6 +12,7 @@ import shop.alien.store.service.LawyerConsultationOrderService;
 import shop.alien.util.myBaticsPlus.QueryBuilder;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 咨询订单 前端控制器
@@ -144,5 +145,26 @@ public class LawyerConsultationOrderController {
                 .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 = "problemScenarId", 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 problemScenarId) {
+        log.info("LawyerConsultationOrderController.startConsultation?lawyerId={},question={},sessionId={},clientUserId={},problemScenarId={}",
+                lawyerId, question, sessionId, clientUserId, problemScenarId);
+        return consultationOrderService.startConsultation(lawyerId, question, sessionId, clientUserId, problemScenarId);
+    }
 }
 

+ 11 - 0
alien-store/src/main/java/shop/alien/store/controller/LawyerLegalProblemScenarioController.java

@@ -122,5 +122,16 @@ public class LawyerLegalProblemScenarioController {
         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);
+    }
+
 }
 

+ 60 - 0
alien-store/src/main/java/shop/alien/store/controller/LawyerUserController.java

@@ -1,6 +1,8 @@
 package shop.alien.store.controller;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -11,6 +13,7 @@ import shop.alien.store.service.LawyerUserService;
 import shop.alien.util.myBaticsPlus.QueryBuilder;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 律师用户 前端控制器
@@ -113,5 +116,62 @@ public class LawyerUserController {
                 .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 = "keyword", value = "搜索关键词(律师姓名)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "sortBy", value = "排序方式:recommend(推荐), price(价格), experience(经验)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/recommend/list")
+    public R<Map<String, Object>> getRecommendedLawyerList(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) Integer categoryId,
+            @RequestParam(required = false) String keyword,
+            @RequestParam(defaultValue = "recommend") String sortBy) {
+        log.info("LawyerUserController.getRecommendedLawyerList?page={},pageSize={},categoryId={},keyword={},sortBy={}",
+                page, pageSize, categoryId, keyword, sortBy);
+        return lawyerUserService.getRecommendedLawyerList(page, pageSize, categoryId, keyword, sortBy);
+    }
+
+    @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<Map<String, Object>> getRecommendedLawyersBySession(
+            @RequestParam String sessionId,
+            @RequestParam(required = false) Integer messageId) {
+        log.info("LawyerUserController.getRecommendedLawyersBySession?sessionId={},messageId={}", sessionId, messageId);
+        return lawyerUserService.getRecommendedLawyersBySession(sessionId, messageId);
+    }
 }
 

+ 11 - 0
alien-store/src/main/java/shop/alien/store/service/LawyerAiInteractionLogService.java

@@ -6,6 +6,7 @@ import shop.alien.entity.result.R;
 import shop.alien.entity.store.LawyerAiInteractionLog;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * AI交互日志 服务类
@@ -59,5 +60,15 @@ public interface LawyerAiInteractionLogService extends IService<LawyerAiInteract
      * @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);
 }
 

+ 40 - 0
alien-store/src/main/java/shop/alien/store/service/LawyerChatSessionService.java

@@ -6,6 +6,7 @@ import shop.alien.entity.result.R;
 import shop.alien.entity.store.LawyerChatSession;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 聊天会话 服务类
@@ -60,5 +61,44 @@ public interface LawyerChatSessionService extends IService<LawyerChatSession> {
      * @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);
 }
 

+ 2 - 0
alien-store/src/main/java/shop/alien/store/service/LawyerCommonQuestionService.java

@@ -6,6 +6,7 @@ import shop.alien.entity.result.R;
 import shop.alien.entity.store.LawyerCommonQuestion;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 常见问题 服务类
@@ -60,5 +61,6 @@ public interface LawyerCommonQuestionService extends IService<LawyerCommonQuesti
      * @return R<Boolean>
      */
     R<Boolean> deleteCommonQuestion(Integer id);
+
 }
 

+ 14 - 0
alien-store/src/main/java/shop/alien/store/service/LawyerConsultationOrderService.java

@@ -6,6 +6,8 @@ import shop.alien.entity.result.R;
 import shop.alien.entity.store.LawyerConsultationOrder;
 import shop.alien.entity.store.vo.LawyerConsultationOrderVO;
 
+import java.util.Map;
+
 /**
  * 咨询订单 服务类
  *
@@ -52,5 +54,17 @@ public interface LawyerConsultationOrderService extends IService<LawyerConsultat
      * @return R<Boolean>
      */
     R<Boolean> deleteConsultationOrder(Integer id);
+
+    /**
+     * 开始咨询律师
+     *
+     * @param lawyerId        律师ID
+     * @param question        咨询问题(可选)
+     * @param sessionId       关联的AI会话ID(可选)
+     * @param clientUserId    客户端用户ID(可选)
+     * @param problemScenarId 法律问题场景ID(可选)
+     * @return R<Map<String, Object>>
+     */
+    R<Map<String, Object>> startConsultation(Integer lawyerId, String question, String sessionId, Integer clientUserId, Integer problemScenarId);
 }
 

+ 8 - 0
alien-store/src/main/java/shop/alien/store/service/LawyerLegalProblemScenarioService.java

@@ -38,5 +38,13 @@ public interface LawyerLegalProblemScenarioService extends IService<LawyerLegalP
      * @return R<Boolean>
      */
     R<Boolean> deleteLawyerLegalProblemScenario(Integer id);
+
+    /**
+     * 根据一级场景主键获取其下所有分类(包括二级和三级分类的树形结构)
+     *
+     * @param id 一级场景主键
+     * @return R<LawyerLegalProblemScenario> 返回一级场景及其完整的树形结构(一级场景的children包含所有二级分类,每个二级分类的children包含其下的三级分类)
+     */
+    R<LawyerLegalProblemScenario> getCategoryTreeByFirstLevelId(Integer id);
 }
 

+ 38 - 0
alien-store/src/main/java/shop/alien/store/service/LawyerUserService.java

@@ -6,6 +6,7 @@ import shop.alien.entity.result.R;
 import shop.alien.entity.store.LawyerUser;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 律师用户 服务类
@@ -50,5 +51,42 @@ public interface LawyerUserService extends IService<LawyerUser> {
      * @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 keyword    搜索关键词(律师姓名)
+     * @param sortBy     排序方式:recommend(推荐), price(价格), experience(经验)
+     * @return R<Map<String, Object>>
+     */
+    R<Map<String, Object>> getRecommendedLawyerList(int page, int pageSize, Integer categoryId, String keyword, String sortBy);
+
+    /**
+     * 根据会话获取推荐律师列表
+     *
+     * @param sessionId 会话ID
+     * @param messageId 消息ID(可选)
+     * @return R<Map<String, Object>>
+     */
+    R<Map<String, Object>> getRecommendedLawyersBySession(String sessionId, Integer messageId);
 }
 

+ 31 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LawyerAiInteractionLogServiceImpl.java

@@ -14,6 +14,7 @@ import shop.alien.entity.store.LawyerAiInteractionLog;
 import shop.alien.mapper.LawyerAiInteractionLogMapper;
 import shop.alien.store.service.LawyerAiInteractionLogService;
 
+import java.util.*;
 import java.util.List;
 
 /**
@@ -91,5 +92,35 @@ public class LawyerAiInteractionLogServiceImpl extends ServiceImpl<LawyerAiInter
         }
         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);
+    }
 }
 

+ 150 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LawyerChatSessionServiceImpl.java

@@ -1,6 +1,7 @@
 package shop.alien.store.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;
@@ -8,10 +9,18 @@ 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.mapper.LawyerChatSessionMapper;
+import shop.alien.store.service.LawyerChatMessageService;
 import shop.alien.store.service.LawyerChatSessionService;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * 聊天会话 服务实现类
@@ -26,6 +35,8 @@ import shop.alien.store.service.LawyerChatSessionService;
 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,
@@ -91,5 +102,144 @@ public class LawyerChatSessionServiceImpl extends ServiceImpl<LawyerChatSessionM
         }
         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("删除成功");
+    }
 }
 

+ 39 - 9
alien-store/src/main/java/shop/alien/store/service/impl/LawyerConsultationOrderServiceImpl.java

@@ -18,6 +18,7 @@ import shop.alien.mapper.LawyerConsultationOrderMapper;
 import shop.alien.store.service.LawyerConsultationOrderService;
 import shop.alien.store.service.LawyerUserService;
 
+import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -74,10 +75,10 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
         }
         queryWrapper.orderByDesc(LawyerConsultationOrder::getCreatedTime);
         IPage<LawyerConsultationOrder> pageResult = this.page(page, queryWrapper);
-        
+
         // 轉換為VO並填充律師信息
         IPage<LawyerConsultationOrderVO> voPage = convertToVO(pageResult);
-        
+
         return R.data(voPage);
     }
 
@@ -87,19 +88,19 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
     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()) {
@@ -112,7 +113,7 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
                         .collect(Collectors.toMap(LawyerUser::getId, lawyer -> lawyer, (k1, k2) -> k1));
             }
         }
-        
+
         // 轉換為VO並填充律師信息
         final Map<Integer, LawyerUser> finalLawyerMap = lawyerMap;
         List<LawyerConsultationOrderVO> voList = records.stream()
@@ -120,7 +121,7 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
                     LawyerConsultationOrderVO vo = new LawyerConsultationOrderVO();
                     // 複製訂單基本信息
                     BeanUtils.copyProperties(order, vo);
-                    
+
                     // 填充律師信息
                     LawyerUser lawyer = finalLawyerMap.get(order.getLawyerUserId());
                     if (lawyer != null) {
@@ -143,11 +144,11 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
                         vo.setNickName(lawyer.getNickName());
                         vo.setPersonalIntroduction(lawyer.getPersonalIntroduction());
                     }
-                    
+
                     return vo;
                 })
                 .collect(Collectors.toList());
-        
+
         voPage.setRecords(voList);
         return voPage;
     }
@@ -181,5 +182,34 @@ public class LawyerConsultationOrderServiceImpl extends ServiceImpl<LawyerConsul
         }
         return R.fail("删除失败");
     }
+
+    @Override
+    public R<Map<String, Object>> startConsultation(Integer lawyerId, String question, String sessionId, Integer clientUserId, Integer problemScenarId) {
+        log.info("LawyerConsultationOrderServiceImpl.startConsultation?lawyerId={},question={},sessionId={},clientUserId={},problemScenarId={}",
+                lawyerId, question, sessionId, clientUserId, problemScenarId);
+
+        // 创建咨询订单
+        LawyerConsultationOrder order = new LawyerConsultationOrder();
+        order.setLawyerUserId(lawyerId);
+        order.setClientUserId(clientUserId);
+        order.setProblemScenarId(problemScenarId);
+        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);
+    }
 }
 

+ 63 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LawyerLegalProblemScenarioServiceImpl.java

@@ -1,16 +1,23 @@
 package shop.alien.store.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 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.CollectionUtils;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.LawyerLegalProblemScenario;
 import shop.alien.mapper.LawyerLegalProblemScenarioMapper;
 import shop.alien.store.service.LawyerLegalProblemScenarioService;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
 /**
  * 法律问题场景 服务实现类
  *
@@ -53,5 +60,61 @@ public class LawyerLegalProblemScenarioServiceImpl extends ServiceImpl<LawyerLeg
         return R.fail("删除失败");
     }
 
+    @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);
+    }
+
 }
 

+ 188 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LawyerUserServiceImpl.java

@@ -1,5 +1,6 @@
 package shop.alien.store.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -10,10 +11,15 @@ 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.LawyerServiceArea;
 import shop.alien.entity.store.LawyerUser;
 import shop.alien.mapper.LawyerUserMapper;
+import shop.alien.store.service.LawyerServiceAreaService;
 import shop.alien.store.service.LawyerUserService;
 
+import java.util.*;
+import java.util.stream.Collectors;
+
 /**
  * 律师用户 服务实现类
  *
@@ -27,6 +33,7 @@ import shop.alien.store.service.LawyerUserService;
 public class LawyerUserServiceImpl extends ServiceImpl<LawyerUserMapper, LawyerUser> implements LawyerUserService {
 
     private final LawyerUserMapper lawyerUserMapper;
+    private final LawyerServiceAreaService lawyerServiceAreaService;
 
     @Override
     public R<IPage<LawyerUser>> getLawyerUserList(int pageNum, int pageSize, String name, String phone, Integer status) {
@@ -77,5 +84,186 @@ public class LawyerUserServiceImpl extends ServiceImpl<LawyerUserMapper, LawyerU
         }
         return R.fail("删除失败");
     }
+
+    @Override
+    public R<Map<String, Object>> getLawyerDetail(Integer lawyerId) {
+        log.info("LawyerUserServiceImpl.getLawyerDetail?lawyerId={}", lawyerId);
+
+        LawyerUser lawyer = this.getById(lawyerId);
+        if (lawyer == null || lawyer.getDeleteFlag() == 1) {
+            return R.fail("律师不存在");
+        }
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("id", lawyer.getId());
+        result.put("name", lawyer.getName());
+        result.put("avatar", lawyer.getHeadImg() != null ? lawyer.getHeadImg() : "");
+        result.put("intro", lawyer.getPersonalIntroduction() != null ? lawyer.getPersonalIntroduction() :
+                (lawyer.getAccountBlurb() != null ? lawyer.getAccountBlurb() : "Ta还没有个人业务介绍"));
+        result.put("field", lawyer.getSpecialtyFields() != null ? lawyer.getSpecialtyFields() : "");
+        result.put("experience", lawyer.getPracticeYears() != null ? lawyer.getPracticeYears() : 0);
+        result.put("lawFirm", lawyer.getLawFirm() != null ? lawyer.getLawFirm() : "");
+        result.put("online", lawyer.getIsOnline() != null && lawyer.getIsOnline() == 1);
+        result.put("price", lawyer.getConsultationFee() != null ? lawyer.getConsultationFee() / 100 : 0);
+
+        // 获取服务标签
+        QueryWrapper<LawyerServiceArea> tagQuery = new QueryWrapper<>();
+        tagQuery.eq("lawyer_user_id", lawyer.getId())
+                .eq("delete_flag", 0)
+                .eq("status", 1)
+                .orderByAsc("sort_order");
+        List<LawyerServiceArea> serviceAreas = lawyerServiceAreaService.list(tagQuery);
+        result.put("serviceTags", new ArrayList<>());  // TODO: 需要关联查询问题场景名称
+
+        // 额外字段
+        result.put("education", lawyer.getEducationBackground() != null ? lawyer.getEducationBackground() : "");
+        result.put("credentials", new ArrayList<>());  // TODO: 需要查询资质证书
+        result.put("caseCount", lawyer.getServiceCount() != null ? lawyer.getServiceCount() : 0);
+        // 评分:服务评分转换为0-5星评分
+        result.put("rating", lawyer.getServiceScore() != null ? lawyer.getServiceScore() / 20.0 : 0.0);
+        result.put("reviewCount", lawyer.getGoodReviewCount() != null ?
+                (lawyer.getGoodReviewCount() + (lawyer.getMediumReviewCount() != null ? lawyer.getMediumReviewCount() : 0) +
+                 (lawyer.getBadReviewCount() != null ? lawyer.getBadReviewCount() : 0)) : 0);
+
+        return R.data(result);
+    }
+
+    @Override
+    public R<Map<Integer, Boolean>> getLawyerOnlineStatus(String lawyerIds) {
+        log.info("LawyerUserServiceImpl.getLawyerOnlineStatus?lawyerIds={}", lawyerIds);
+
+        String[] ids = lawyerIds.split(",");
+        List<Integer> idList = Arrays.stream(ids)
+                .map(String::trim)
+                .filter(s -> !s.isEmpty())
+                .map(Integer::parseInt)
+                .collect(Collectors.toList());
+
+        if (idList.isEmpty()) {
+            return R.data(new HashMap<>());
+        }
+
+        List<LawyerUser> lawyers = new ArrayList<>(this.listByIds(idList));
+        Map<Integer, Boolean> result = new HashMap<>();
+        for (LawyerUser lawyer : lawyers) {
+            result.put(lawyer.getId(), lawyer.getIsOnline() != null && lawyer.getIsOnline() == 1);
+        }
+
+        return R.data(result);
+    }
+
+    @Override
+    public R<Map<String, Object>> getRecommendedLawyerList(int page, int pageSize, Integer categoryId, String keyword, String sortBy) {
+        log.info("LawyerUserServiceImpl.getRecommendedLawyerList?page={},pageSize={},categoryId={},keyword={},sortBy={}",
+                page, pageSize, categoryId, keyword, sortBy);
+
+        int pageNum = page > 0 ? page : 1;
+        int pageSizeNum = pageSize > 0 ? pageSize : 10;
+
+        // 构建查询条件
+        QueryWrapper<LawyerUser> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("delete_flag", 0)
+                .eq("status", 1)  // 已启用
+                .eq("order_receiving_status", 1);  // 接单中
+
+        // 关键词搜索(律师姓名)
+        if (StringUtils.hasText(keyword)) {
+            queryWrapper.like("name", keyword);
+        }
+
+        // 分类筛选:通过律师服务领域关联表查询
+        if (categoryId != null) {
+            QueryWrapper<LawyerServiceArea> areaQuery = new QueryWrapper<>();
+            areaQuery.eq("problem_scenar_id", categoryId)
+                    .eq("delete_flag", 0)
+                    .eq("status", 1);
+            List<LawyerServiceArea> serviceAreas = lawyerServiceAreaService.list(areaQuery);
+            if (!serviceAreas.isEmpty()) {
+                List<Integer> lawyerIds = serviceAreas.stream()
+                        .map(LawyerServiceArea::getLawyerUserId)
+                        .distinct()
+                        .collect(Collectors.toList());
+                if (!lawyerIds.isEmpty()) {
+                    queryWrapper.in("id", lawyerIds);
+                } else {
+                    // 如果没有匹配的律师,返回空结果
+                    queryWrapper.eq("id", -1);
+                }
+            } else {
+                queryWrapper.eq("id", -1);
+            }
+        }
+
+        // 排序:优先推荐律师 -> 在线律师 -> 创建时间
+        // 基础排序:推荐状态(推荐的在前面)、在线状态(在线的在前面)、创建时间(最老的在前面)
+        queryWrapper.orderByDesc("is_recommended")  // 优先推荐律师(1在前,0在后)
+                .orderByDesc("is_online")  // 然后在线律师(1在前,0在后)
+                .orderByAsc("created_time");  // 最后按创建时间倒序(最新的在前)
+
+        // 根据sortBy参数添加额外排序
+        switch (sortBy) {
+            case "price":
+                queryWrapper.orderByAsc("consultation_fee");  // 价格升序
+                break;
+            case "experience":
+                queryWrapper.orderByDesc("practice_years");  // 经验降序
+                break;
+            case "recommend":
+            default:
+                // 推荐排序:推荐排序号(升序,数字小的在前)、服务评分(降序,分数高的在前)
+                queryWrapper.orderByAsc("recommend_sort")
+                        .orderByDesc("service_score");
+                break;
+        }
+
+        // 分页查询
+        Page<LawyerUser> pageObj = new Page<>(pageNum, pageSizeNum);
+        IPage<LawyerUser> pageResult = this.page(pageObj, queryWrapper);
+
+        // 转换为前端需要的格式
+        List<Map<String, Object>> records = pageResult.getRecords().stream().map(lawyer -> {
+            Map<String, Object> lawyerMap = new HashMap<>();
+            lawyerMap.put("id", lawyer.getId());
+            lawyerMap.put("name", lawyer.getName());
+            lawyerMap.put("avatar", lawyer.getHeadImg() != null ? lawyer.getHeadImg() : "");
+            lawyerMap.put("intro", lawyer.getPersonalIntroduction() != null ? lawyer.getPersonalIntroduction() :
+                    (lawyer.getAccountBlurb() != null ? lawyer.getAccountBlurb() : "Ta还没有个人业务介绍"));
+            lawyerMap.put("field", lawyer.getSpecialtyFields() != null ? lawyer.getSpecialtyFields() : "");
+            lawyerMap.put("experience", lawyer.getPracticeYears() != null ? lawyer.getPracticeYears() : 0);
+            lawyerMap.put("lawFirm", lawyer.getLawFirm() != null ? lawyer.getLawFirm() : "");
+            lawyerMap.put("online", lawyer.getIsOnline() != null && lawyer.getIsOnline() == 1);
+            // 咨询价格从分转换为元
+            lawyerMap.put("price", lawyer.getConsultationFee() != null ? lawyer.getConsultationFee() / 100 : 0);
+
+            // 获取服务标签(通过律师服务领域关联表)
+            QueryWrapper<LawyerServiceArea> tagQuery = new QueryWrapper<>();
+            tagQuery.eq("lawyer_user_id", lawyer.getId())
+                    .eq("delete_flag", 0)
+                    .eq("status", 1)
+                    .orderByAsc("sort_order");
+            List<LawyerServiceArea> serviceAreas = lawyerServiceAreaService.list(tagQuery);
+            // TODO: 这里需要关联查询问题场景名称,暂时返回空数组
+            lawyerMap.put("serviceTags", new ArrayList<>());
+
+            return lawyerMap;
+        }).collect(Collectors.toList());
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("records", records);
+        result.put("total", pageResult.getTotal());
+        result.put("page", pageNum);
+        result.put("pageSize", pageSizeNum);
+
+        return R.data(result);
+    }
+
+    @Override
+    public R<Map<String, Object>> getRecommendedLawyersBySession(String sessionId, Integer messageId) {
+        log.info("LawyerUserServiceImpl.getRecommendedLawyersBySession?sessionId={},messageId={}", sessionId, messageId);
+
+        // TODO: 根据会话ID查询AI交互日志,获取关联的问题场景ID,然后推荐相关律师
+        // 这里暂时返回推荐列表(可以复用上面的逻辑)
+        return getRecommendedLawyerList(1, 10, null, null, "recommend");
+    }
 }