Bladeren bron

用户端律师代码迁移

ldz 1 maand geleden
bovenliggende
commit
32346d5313

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

@@ -0,0 +1,317 @@
+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.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;
+
+/**
+ * 咨询订单 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"律师平台-咨询订单"})
+@ApiSort(14)
+@CrossOrigin
+@RestController
+@RequestMapping("/lawyer/consultationOrder")
+@RequiredArgsConstructor
+public class LawyerConsultationOrderController {
+
+    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"),
+            @ApiImplicitParam(name = "lawyerUserId", value = "律师用户ID", dataType = "Integer", paramType = "query"),
+            @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"),
+            @ApiImplicitParam(name = "orderStr", value = "支付宝订单字符串", dataType = "String", 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(), 1800);
+        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)
+    })
+    @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());
+        }
+        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:已取消", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "lawyerName", value = "律师姓名(支持模糊查询,关联查询)", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getConsultationOrderListById")
+    public R<IPage<LawyerConsultationOrderVO>> getConsultationOrderListById(@RequestParam(defaultValue = "1") int page,
+                                                                        @RequestParam(defaultValue = "10") int size,
+                                                                        @RequestParam(required = false) 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 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));
+    }
+
+    @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);
+        return R.data(consultationOrderService.cancelOrder(id));
+    }
+
+    @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 = "lawyerId", value = "律师ID", dataType = "String", paramType = "query")
+    })
+    @GetMapping("/getLawyerConsultationOrderList")
+    public R<IPage<LawyerConsultationOrderVO>> getLawyerConsultationOrderList(@RequestParam(defaultValue = "1") int page,
+                                                                            @RequestParam(defaultValue = "10") int size,
+                                                                            @RequestParam(required = false) String startDate,
+                                                                            @RequestParam(required = false) String endDate,
+                                                                            @RequestParam(required = true) String lawyerId) {
+        log.info("LawyerConsultationOrderController.getLawyerConsultationOrderList?pageNum={},pageSize={},startDate={},endDate={},lawyerId={}",
+                page, size, startDate, endDate, lawyerId);
+        return consultationOrderService.getLawyerConsultationOrderList(page, size, startDate, endDate, lawyerId);
+    }
+
+}
+

+ 114 - 0
alien-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);
+    }
+}
+

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

@@ -0,0 +1,208 @@
+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.lawyer.service.LawyerUserService;
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+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")
+    })
+    @GetMapping("/recommend/list")
+    public R<IPage<LawyerUser>> getRecommendedLawyerList(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) Integer categoryId) {
+        log.info("LawyerUserController.getRecommendedLawyerList?page={},pageSize={},categoryId={}",
+                page, pageSize, categoryId);
+        return lawyerUserService.getRecommendedLawyerList(page, pageSize, categoryId);
+    }
+
+    @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 = "name", value = "律师姓名(支持模糊查询)", dataType = "String", paramType = "query", required = true),
+            @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 String name,
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int pageSize,
+            @RequestParam(required = false) Integer clientUserId) {
+        log.info("LawyerUserController.searchLawyerByName?name={},page={},pageSize={},clientUserId={}", name, page, pageSize, clientUserId);
+        return lawyerUserService.searchLawyerByName(name, page, pageSize, clientUserId);
+    }
+
+
+    @ApiOperation("根据AI返回场景获取推荐律师列表")
+    @ApiOperationSupport(order = 11)
+    @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);
+    }
+
+}
+

+ 94 - 0
alien-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);
+    }
+}
+

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

@@ -0,0 +1,115 @@
+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.LawyerConsultationOrder;
+import shop.alien.entity.store.dto.LawyerConsultationOrderDto;
+import shop.alien.entity.store.dto.PayStatusRequest;
+import shop.alien.entity.store.vo.LawyerConsultationOrderVO;
+
+/**
+ * 咨询订单 服务类
+ *
+ * @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>
+     */
+    R<IPage<LawyerConsultationOrderVO>> 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>
+     */
+    R<IPage<LawyerConsultationOrderVO>> getLawyerConsultationOrderList(int pageNum, int pageSize, String startDate, String endDate, String lawyerId);
+}
+

+ 39 - 0
alien-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);
+}
+

+ 79 - 0
alien-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);
+}
+

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

@@ -0,0 +1,110 @@
+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 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(可选)
+     * @return R<IPage<LawyerUser>>
+     */
+    R<IPage<LawyerUser>> getRecommendedLawyerList(int page, int pageSize, Integer categoryId);
+
+    /**
+     * 根据会话获取推荐律师列表
+     *
+     * @param sessionId 会话ID
+     * @param messageId 消息ID(可选)
+     * @return R<IPage<LawyerUser>>
+     */
+    R<IPage<LawyerUser>> getRecommendedLawyersBySession(String sessionId, Integer messageId);
+
+    /**
+     * 通过姓名模糊查询律师(并保存搜索历史)
+     *
+     * @param name        律师姓名(支持模糊查询)
+     * @param page        页码
+     * @param pageSize    每页数量
+     * @param clientUserId 客户端用户ID(可选,用于保存搜索历史)
+     * @return R<IPage<LawyerUser>>
+     */
+    R<IPage<LawyerUser>> searchLawyerByName(String name, 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);
+}
+

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

@@ -0,0 +1,814 @@
+package shop.alien.lawyer.service.impl;
+
+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.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerConsultationOrder;
+import shop.alien.entity.store.LawyerServiceArea;
+import shop.alien.entity.store.LawyerUser;
+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.LawyerUserService;
+import shop.alien.lawyer.service.OrderExpirationService;
+import shop.alien.mapper.LawyerConsultationOrderMapper;
+import shop.alien.mapper.LawyerExpertiseAreaMapper;
+import shop.alien.mapper.LawyerServiceAreaMapper;
+import shop.alien.mapper.LawyerUserMapper;
+import shop.alien.util.common.constant.LawyerStatusEnum;
+
+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 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;
+
+    @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));
+            }
+        }
+
+        // 轉換為VO並填充律師信息
+        final Map<Integer, LawyerUser> finalLawyerMap = lawyerMap;
+        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());
+                        vo.setLawFirm(lawyer.getLawFirm());
+                        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.USED.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("LawyerConsultationOrderController.consultNow?lawyerConsultationOrder={}", lawyerConsultationOrder);
+         LawyerConsultationOrderDto order = new LawyerConsultationOrderDto();
+        order.setClientUserId(lawyerConsultationOrder.getClientUserId());
+        order.setLawyerUserId(lawyerConsultationOrder.getLawyerUserId());
+        order.setProblemScenarioId(lawyerConsultationOrder.getProblemScenarioId());
+        order.setProblemDescription(lawyerConsultationOrder.getProblemDescription());
+        order.setOrderAmount(lawyerConsultationOrder.getOrderAmount());
+        order.setOrderStatus(0);
+        order.setPaymentStatus(0);
+        order.setOrderTime(new Date());
+//        order.setValidityPeriod(DateUtils.addDays(new Date(), 7));
+        order.setCreatedTime(new Date());
+        order.setUpdatedTime(new Date());
+        order.setDeleteFlag(0);
+        order.setAlipayNo(lawyerConsultationOrder.getAlipayNo());
+        order.setOrderStr(lawyerConsultationOrder.getOrderStr());
+        //订单编号想要LAW+年月日(8位数字)+随机5位数字这种格式的
+        String orderNumber = "LAW" + new SimpleDateFormat("yyyyMMdd").format(new Date()) + String.format("%05d", RandomUtils.nextInt(100000));
+        order.setOrderNumber(orderNumber);
+        int num = consultationOrderMapper.insertOrder(order);
+
+
+//        boolean result = this.save(order);
+//        if (result) {
+//            return R.data(order);
+//        }
+//        return R.fail("新增失败");
+        if (num >0){
+            return R.data(order);
+        }
+        return R.fail("新增失败");
+    }
+
+
+    @Override
+    public R<LawyerConsultationOrderDto> payStatus(PayStatusRequest request) {
+        log.info("LawyerConsultationOrderServiceImpl.payStatus?orderNumber={},paymentStatus={},orderStatus={}",
+                request.getOrderNumber(), request.getPaymentStatus(), request.getOrderStatus());
+
+        LawyerConsultationOrderDto order = new LawyerConsultationOrderDto();
+        order.setOrderNumber(request.getOrderNumber());
+        order.setPaymentStatus(request.getPaymentStatus());
+        order.setOrderStatus(request.getOrderStatus());
+        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 R<IPage<LawyerConsultationOrderVO>> getConsultationOrderListById(int pageNum, int pageSize, 
+                                                                              String userId, String orderStatus, 
+                                                                              String lawyerName) {
+        Page<LawyerConsultationOrderVO> page = new Page<>(pageNum, pageSize);
+        
+        // 如果按律师姓名搜索,先查询匹配的律师ID列表
+        List<Integer> lawyerUserIds = null;
+        if (StringUtils.hasText(lawyerName)) {
+            lawyerUserIds = queryLawyerIdsByName(lawyerName);
+            // 如果没有找到匹配的律师,返回空结果
+            if (CollectionUtils.isEmpty(lawyerUserIds)) {
+                Page<LawyerConsultationOrderVO> emptyPage = new Page<>(pageNum, pageSize);
+                emptyPage.setRecords(Collections.emptyList());
+                emptyPage.setTotal(0);
+                return R.data(emptyPage);
+            }
+        }
+
+        // 查询订单列表
+        IPage<LawyerConsultationOrderVO> voPage = consultationOrderMapper.getConsultationOrderListById(
+                page, userId, orderStatus, lawyerUserIds);
+        
+        // 填充律师问题场景信息
+        fillLawyerServiceArea(voPage);
+
+        // 為待支付訂單計算倒計時(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);
+    }
+
+    /**
+     * 根据律师姓名查询律师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 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;
+    }
+
+    /**
+     * 获取咨询订单详情
+     *
+     * @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;
+    }
+
+    /**
+     * 取消律师咨询订单
+     *
+     * @param id 订单ID
+     * @return 是否取消成功
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean cancelOrder(String id) {
+        // 参数校验
+        if (!StringUtils.hasText(id)) {
+            log.warn("取消订单失败:订单ID为空");
+            return false;
+        }
+
+        // 查询订单信息
+        LawyerConsultationOrder order = consultationOrderMapper.selectById(id);
+        if (order == null) {
+            log.warn("取消订单失败:订单不存在,订单ID={}", id);
+            return false;
+        }
+
+        // 检查订单状态:已完成或已取消的订单不允许再次取消
+        Integer orderStatus = order.getOrderStatus();
+        Integer cancelStatus = LawyerStatusEnum.CANCEL.getStatus();
+        if (cancelStatus.equals(orderStatus)) {
+            log.warn("取消订单失败:订单已取消,订单ID={}, 订单编号={}", id, order.getOrderNumber());
+            return false;
+        }
+
+        // 如果订单已完成,不允许取消
+        Integer completedStatus = LawyerStatusEnum.COMPLETE.getStatus(); // 3:已完成
+        if (completedStatus.equals(orderStatus)) {
+            log.warn("取消订单失败:订单已完成,不允许取消,订单ID={}, 订单编号={}", id, order.getOrderNumber());
+            return false;
+        }
+
+        // 如果订单是待支付状态,取消Redis中的订单支付超时计时
+        Integer waitPayStatus = LawyerStatusEnum.WAIT_PAY.getStatus(); // 0:待支付
+        if (waitPayStatus.equals(orderStatus) && order.getOrderNumber() != null) {
+            orderExpirationService.cancelOrderPaymentTimeout(order.getOrderNumber());
+            log.info("已取消订单支付超时计时,订单编号={}", order.getOrderNumber());
+        }
+
+        // 更新订单状态为已取消
+        LambdaUpdateWrapper<LawyerConsultationOrder> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(LawyerConsultationOrder::getId, id)
+                .set(LawyerConsultationOrder::getOrderStatus, cancelStatus)
+                .set(LawyerConsultationOrder::getUpdatedTime, new Date());
+
+        int updateCount = consultationOrderMapper.update(null, updateWrapper);
+        boolean success = updateCount > 0;
+
+        if (success) {
+            log.info("取消订单成功,订单ID={}, 订单编号={}, 原状态={}", id, order.getOrderNumber(), orderStatus);
+        } else {
+            log.error("取消订单失败:更新数据库失败,订单ID={}, 订单编号={}", id, order.getOrderNumber());
+        }
+
+        return success;
+    }
+
+    @Override
+    public R<IPage<LawyerConsultationOrderVO>> getLawyerConsultationOrderList(int pageNum, int pageSize, String startDate, String endDate, String lawyerId) {
+        Page<LawyerConsultationOrderVO> page = new Page<>(pageNum, pageSize);
+
+        // 如果按律师姓名搜索,先查询匹配的律师ID列表
+        if (!StringUtils.hasText(lawyerId)) {
+            Page<LawyerConsultationOrderVO> emptyPage = new Page<>(pageNum, pageSize);
+            emptyPage.setRecords(Collections.emptyList());
+            emptyPage.setTotal(0);
+            return R.data(emptyPage);
+        }
+
+        // 查询订单列表
+        QueryWrapper<LawyerConsultationOrderVO> lawyerConsultationOrderQueryWrapper = new QueryWrapper<>();
+        lawyerConsultationOrderQueryWrapper.in("lco.order_status", Arrays.asList("2", "3"));
+        lawyerConsultationOrderQueryWrapper.eq("lco.lawyer_user_id", lawyerId);
+        if(StringUtils.hasText(startDate) && StringUtils.hasText(startDate)){
+            lawyerConsultationOrderQueryWrapper.between("lco.payment_time", startDate + " 00:00:00", endDate + " 23:59:59");
+        }
+        lawyerConsultationOrderQueryWrapper.eq("lco.delete_flag", 0);
+        IPage<LawyerConsultationOrderVO> voPage = consultationOrderMapper.getLawyerConsultationOrderList(
+                page, lawyerConsultationOrderQueryWrapper);
+
+        // 填充律师问题场景信息
+        fillLawyerServiceArea(voPage);
+
+        // 為待支付訂單計算倒計時(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
+     *
+     * @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());
+        orderVO.setLawFirm(lawyerUser.getLawFirm());
+        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());
+    }
+}
+

+ 56 - 0
alien-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("删除失败");
+    }
+}
+

+ 159 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerUserSearchHistoryServiceImpl.java

@@ -0,0 +1,159 @@
+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 shop.alien.entity.result.R;
+import shop.alien.entity.store.LawyerUserSearchHistory;
+import shop.alien.lawyer.service.LawyerUserSearchHistoryService;
+import shop.alien.mapper.LawyerUserSearchHistoryMapper;
+
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 用户搜索历史 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawyerUserSearchHistoryServiceImpl extends ServiceImpl<LawyerUserSearchHistoryMapper, LawyerUserSearchHistory> implements LawyerUserSearchHistoryService {
+
+    private final LawyerUserSearchHistoryMapper userSearchHistoryMapper;
+
+    @Override
+    public R<IPage<LawyerUserSearchHistory>> getUserSearchHistoryList(int pageNum, int pageSize, Integer clientUserId, Integer searchType) {
+        log.info("LawyerUserSearchHistoryServiceImpl.getUserSearchHistoryList?pageNum={},pageSize={},clientUserId={},searchType={}",
+                pageNum, pageSize, clientUserId, searchType);
+        Page<LawyerUserSearchHistory> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<LawyerUserSearchHistory> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerUserSearchHistory::getDeleteFlag, 0);
+        if (clientUserId != null) {
+            queryWrapper.eq(LawyerUserSearchHistory::getClientUserId, clientUserId);
+        }
+        if (searchType != null) {
+            queryWrapper.eq(LawyerUserSearchHistory::getSearchType, searchType);
+        }
+        queryWrapper.orderByDesc(LawyerUserSearchHistory::getSearchTime);
+        IPage<LawyerUserSearchHistory> pageResult = this.page(page, queryWrapper);
+        return R.data(pageResult);
+    }
+
+    @Override
+    public List<LawyerUserSearchHistory> getListByUserId(Integer clientUserId) {
+        log.info("LawyerUserSearchHistoryServiceImpl.getListByUserId?clientUserId={}", clientUserId);
+        LambdaQueryWrapper<LawyerUserSearchHistory> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerUserSearchHistory::getClientUserId, clientUserId)
+                .eq(LawyerUserSearchHistory::getDeleteFlag, 0)
+                .orderByDesc(LawyerUserSearchHistory::getSearchTime)
+                .last("LIMIT 20");
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public R<LawyerUserSearchHistory> addUserSearchHistory(LawyerUserSearchHistory userSearchHistory) {
+        log.info("LawyerUserSearchHistoryServiceImpl.addUserSearchHistory?userSearchHistory={}", userSearchHistory);
+
+        // 设置搜索时间
+        if (userSearchHistory.getSearchTime() == null) {
+            userSearchHistory.setSearchTime(new java.util.Date());
+        }
+
+        // 检查该用户的历史记录数量,如果超过10条则删除最老的记录
+        if (userSearchHistory.getClientUserId() != null) {
+            LambdaQueryWrapper<LawyerUserSearchHistory> countWrapper = new LambdaQueryWrapper<>();
+            countWrapper.eq(LawyerUserSearchHistory::getClientUserId, userSearchHistory.getClientUserId())
+                    .eq(LawyerUserSearchHistory::getDeleteFlag, 0);
+            long count = this.count(countWrapper);
+
+            // 如果已有10条或更多记录,删除最老的记录
+            if (count >= 10) {
+                // 查询需要删除的最老记录(保留9条,加上新的一条共10条)
+                LambdaQueryWrapper<LawyerUserSearchHistory> queryWrapper = new LambdaQueryWrapper<>();
+                queryWrapper.eq(LawyerUserSearchHistory::getClientUserId, userSearchHistory.getClientUserId())
+                        .eq(LawyerUserSearchHistory::getDeleteFlag, 0)
+                        .orderByAsc(LawyerUserSearchHistory::getSearchTime)  // 按搜索时间升序,最老的在前
+                        .orderByAsc(LawyerUserSearchHistory::getCreatedTime)  // 如果搜索时间相同,按创建时间升序
+                        .last("LIMIT " + (count - 9));  // 查询需要删除的记录数
+                List<LawyerUserSearchHistory> toDeleteList = this.list(queryWrapper);
+
+                // 批量删除最老的记录
+                if (!toDeleteList.isEmpty()) {
+                    List<Integer> idsToDelete = toDeleteList.stream()
+                            .map(LawyerUserSearchHistory::getId)
+                            .collect(Collectors.toList());
+                    this.removeByIds(idsToDelete);
+                    log.info("删除用户{}的最老搜索历史记录{}条,当前记录数:{}",
+                            userSearchHistory.getClientUserId(), idsToDelete.size(), count);
+                }
+            }
+        }
+
+        // 保存新记录
+        boolean result = this.save(userSearchHistory);
+        if (result) {
+            return R.data(userSearchHistory);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<Boolean> deleteUserSearchHistory(Integer id) {
+        log.info("LawyerUserSearchHistoryServiceImpl.deleteUserSearchHistory?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    @Override
+    public R<Boolean> clearUserSearchHistory(Integer clientUserId) {
+        log.info("LawyerUserSearchHistoryServiceImpl.clearUserSearchHistory?clientUserId={}", clientUserId);
+        LambdaQueryWrapper<LawyerUserSearchHistory> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerUserSearchHistory::getClientUserId, clientUserId)
+                .eq(LawyerUserSearchHistory::getDeleteFlag, 0);
+        boolean result = this.remove(queryWrapper);
+        if (result) {
+            return R.success("清空成功");
+        }
+        return R.fail("清空失败");
+    }
+
+    @Override
+    public List<LawyerUserSearchHistory> getListWithOrder(LawyerUserSearchHistory userSearchHistory) {
+        log.info("LawyerUserSearchHistoryServiceImpl.getListWithOrder?userSearchHistory={}", userSearchHistory);
+        QueryWrapper<LawyerUserSearchHistory> queryWrapper = QueryBuilder.of(userSearchHistory)
+                .build()
+                .getWrapper();
+        // 添加排序:按创建时间降序(新的在前,老的在后)
+        queryWrapper.orderByDesc("created_time");
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public IPage<LawyerUserSearchHistory> getPageWithOrder(LawyerUserSearchHistory userSearchHistory, int pageNum, int pageSize) {
+        log.info("LawyerUserSearchHistoryServiceImpl.getPageWithOrder?userSearchHistory={},pageNum={},pageSize={}",
+                userSearchHistory, pageNum, pageSize);
+        Page<LawyerUserSearchHistory> page = new Page<>(pageNum, pageSize);
+        QueryWrapper<LawyerUserSearchHistory> queryWrapper = QueryBuilder.of(userSearchHistory)
+                .build()
+                .getWrapper();
+        // 添加排序:按创建时间降序(新的在前,老的在后)
+        queryWrapper.orderByDesc("created_time");
+        return this.page(page, queryWrapper);
+    }
+}
+

+ 389 - 0
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawyerUserServiceImpl.java

@@ -0,0 +1,389 @@
+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.LawyerLegalProblemScenario;
+import shop.alien.entity.store.LawyerServiceArea;
+import shop.alien.entity.store.LawyerUser;
+import shop.alien.entity.store.LawyerUserSearchHistory;
+import shop.alien.lawyer.service.LawyerLegalProblemScenarioService;
+import shop.alien.lawyer.service.LawyerServiceAreaService;
+import shop.alien.lawyer.service.LawyerUserSearchHistoryService;
+import shop.alien.lawyer.service.LawyerUserService;
+import shop.alien.mapper.LawyerUserMapper;
+
+import shop.alien.util.myBaticsPlus.QueryBuilder;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 律师用户 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class LawyerUserServiceImpl extends ServiceImpl<LawyerUserMapper, LawyerUser> implements LawyerUserService {
+
+    private final LawyerUserMapper lawyerUserMapper;
+    private final LawyerServiceAreaService lawyerServiceAreaService;
+    private final LawyerUserSearchHistoryService userSearchHistoryService;
+    private final LawyerLegalProblemScenarioService lawyerLegalProblemScenarioService;
+
+    @Override
+    public R<IPage<LawyerUser>> getLawyerUserList(int pageNum, int pageSize, String name, String phone, Integer status) {
+        log.info("LawyerUserServiceImpl.getLawyerUserList?pageNum={},pageSize={},name={},phone={},status={}", pageNum, pageSize, name, phone, status);
+        Page<LawyerUser> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<LawyerUser> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LawyerUser::getDeleteFlag, 0);
+        if (StringUtils.hasText(name)) {
+            queryWrapper.like(LawyerUser::getName, name);
+        }
+        if (StringUtils.hasText(phone)) {
+            queryWrapper.eq(LawyerUser::getPhone, phone);
+        }
+        if (status != null) {
+            queryWrapper.eq(LawyerUser::getStatus, status);
+        }
+        queryWrapper.orderByDesc(LawyerUser::getCreatedTime);
+        IPage<LawyerUser> pageResult = this.page(page, queryWrapper);
+        return R.data(pageResult);
+    }
+
+    @Override
+    public R<LawyerUser> addLawyerUser(LawyerUser lawyerUser) {
+        log.info("LawyerUserServiceImpl.addLawyerUser?lawyerUser={}", lawyerUser);
+        boolean result = this.save(lawyerUser);
+        if (result) {
+            return R.data(lawyerUser);
+        }
+        return R.fail("新增失败");
+    }
+
+    @Override
+    public R<LawyerUser> editLawyerUser(LawyerUser lawyerUser) {
+        log.info("LawyerUserServiceImpl.editLawyerUser?lawyerUser={}", lawyerUser);
+        boolean result = this.updateById(lawyerUser);
+        if (result) {
+            return R.data(lawyerUser);
+        }
+        return R.fail("修改失败");
+    }
+
+    @Override
+    public R<Boolean> deleteLawyerUser(Integer id) {
+        log.info("LawyerUserServiceImpl.deleteLawyerUser?id={}", id);
+        boolean result = this.removeById(id);
+        if (result) {
+            return R.success("删除成功");
+        }
+        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<IPage<LawyerUser>> getRecommendedLawyerList(int page, int pageSize, Integer categoryId) {
+        log.info("LawyerUserServiceImpl.getRecommendedLawyerList?page={},pageSize={},categoryId={}",
+                page, pageSize, categoryId);
+
+        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 (categoryId != null) {
+            List<Integer> lawyerIds = getLawyerIdsByCategoryId(categoryId);
+            if (lawyerIds.isEmpty()) {
+                // 如果没有匹配的律师,返回空结果
+                return R.data(new Page<>(pageNum, pageSizeNum));
+            }
+            queryWrapper.in("id", lawyerIds);
+        }
+
+        // 排序:优先推荐律师 -> 在线律师 -> 注册时间
+        queryWrapper.orderByDesc("is_recommended")
+                .orderByDesc("is_online")
+                .orderByAsc("created_time");
+
+        // 分页查询
+        Page<LawyerUser> pageObj = new Page<>(pageNum, pageSizeNum);
+        IPage<LawyerUser> pageResult = this.page(pageObj, queryWrapper);
+
+        // 为每个律师设置关联的法律问题场景列表
+        if (pageResult.getRecords() != null && !pageResult.getRecords().isEmpty()) {
+            setLawyerScenarios(pageResult.getRecords());
+        }
+
+        return R.data(pageResult);
+    }
+
+    /**
+     * 根据分类ID获取律师ID列表
+     */
+    private List<Integer> getLawyerIdsByCategoryId(Integer categoryId) {
+        QueryWrapper<LawyerServiceArea> areaQuery = new QueryWrapper<>();
+        areaQuery.eq("problem_scenario_id", categoryId)
+                .eq("delete_flag", 0)
+                .eq("status", 1)
+                .select("lawyer_user_id");
+        List<LawyerServiceArea> serviceAreas = lawyerServiceAreaService.list(areaQuery);
+        return serviceAreas.stream()
+                .map(LawyerServiceArea::getLawyerUserId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 为律师列表设置关联的法律问题场景
+     */
+    private void setLawyerScenarios(List<LawyerUser> lawyers) {
+        List<Integer> lawyerIds = lawyers.stream()
+                .map(LawyerUser::getId)
+                .collect(Collectors.toList());
+
+        // 批量查询律师服务领域关联
+        QueryWrapper<LawyerServiceArea> areaQuery = new QueryWrapper<>();
+        areaQuery.in("lawyer_user_id", lawyerIds)
+                .eq("delete_flag", 0)
+                .eq("status", 1)
+                .orderByAsc("sort_order");
+        List<LawyerServiceArea> serviceAreas = lawyerServiceAreaService.list(areaQuery);
+
+        if (serviceAreas.isEmpty()) {
+            // 如果没有服务领域关联,设置空列表
+            lawyers.forEach(lawyer -> lawyer.setLawyerLegalProblemScenarioList(new ArrayList<>()));
+            return;
+        }
+
+        // 获取所有关联的法律问题场景ID
+        Set<Integer> scenarioIds = serviceAreas.stream()
+                .map(LawyerServiceArea::getProblemScenarioId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+
+        // 批量查询法律问题场景
+        Map<Integer, LawyerLegalProblemScenario> scenarioMap = getScenarioMap(scenarioIds);
+
+        // 按律师ID分组服务领域(已按sort_order排序)
+        Map<Integer, List<LawyerServiceArea>> lawyerAreaMap = serviceAreas.stream()
+                .collect(Collectors.groupingBy(LawyerServiceArea::getLawyerUserId));
+
+        // 为每个律师设置关联的法律问题场景列表
+        lawyers.forEach(lawyer -> {
+            List<LawyerLegalProblemScenario> scenarioList = lawyerAreaMap.getOrDefault(lawyer.getId(), Collections.emptyList())
+                    .stream()
+                    .map(area -> scenarioMap.get(area.getProblemScenarioId()))
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toList());
+            lawyer.setLawyerLegalProblemScenarioList(scenarioList);
+        });
+    }
+
+    /**
+     * 批量查询法律问题场景并转换为Map
+     */
+    private Map<Integer, LawyerLegalProblemScenario> getScenarioMap(Set<Integer> scenarioIds) {
+        if (scenarioIds.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        List<LawyerLegalProblemScenario> scenarios = new ArrayList<>(lawyerLegalProblemScenarioService.listByIds(scenarioIds));
+        return scenarios.stream()
+                .filter(s -> s.getDeleteFlag() != null && s.getDeleteFlag() == 0)
+                .collect(Collectors.toMap(LawyerLegalProblemScenario::getId, s -> s, (k1, k2) -> k1));
+    }
+
+
+
+    @Override
+    public R<IPage<LawyerUser>> getRecommendedLawyersBySession(String sessionId, Integer messageId) {
+        log.info("LawyerUserServiceImpl.getRecommendedLawyersBySession?sessionId={},messageId={}", sessionId, messageId);
+
+        // TODO: 根据会话ID查询AI交互日志,获取关联的问题场景ID,然后推荐相关律师
+        // 这里暂时返回推荐列表(可以复用上面的逻辑)
+        return getRecommendedLawyerList(1, 10, null);
+    }
+
+    @Override
+    public R<IPage<LawyerUser>> searchLawyerByName(String name, int page, int pageSize, Integer clientUserId) {
+        log.info("LawyerUserServiceImpl.searchLawyerByName?name={},page={},pageSize={},clientUserId={}", name, page, pageSize, clientUserId);
+
+        // 参数校验
+        if (name == null || name.trim().isEmpty()) {
+            return R.fail("姓名不能为空");
+        }
+
+        int pageNum = page > 0 ? page : 1;
+        int pageSizeNum = pageSize > 0 ? pageSize : 10;
+
+        // 通过姓名模糊查询律师(分页)
+        LawyerUser query = new LawyerUser();
+        query.setName(name.trim());
+        IPage<LawyerUser> pageResult = QueryBuilder.of(query)
+                .likeFields("name")  // 姓名支持模糊查询
+                .page(pageNum, pageSizeNum)
+                .build()
+                .page(this);
+
+        // 如果提供了用户ID,保存搜索历史(如果不存在则添加)
+        if (clientUserId != null) {
+            try {
+                String keyword = name.trim();
+                // 检查是否已存在相同的搜索历史记录
+                LambdaQueryWrapper<LawyerUserSearchHistory> checkWrapper = new LambdaQueryWrapper<>();
+                checkWrapper.eq(LawyerUserSearchHistory::getClientUserId, clientUserId)
+                        .eq(LawyerUserSearchHistory::getSearchKeyword, keyword)
+                        .eq(LawyerUserSearchHistory::getSearchType, 0)  // 0:律师姓名
+                        .eq(LawyerUserSearchHistory::getDeleteFlag, 0);
+                long count = userSearchHistoryService.count(checkWrapper);
+                
+                // 如果不存在相同的记录,则添加
+                if (count == 0) {
+                    LawyerUserSearchHistory searchHistory = new LawyerUserSearchHistory();
+                    searchHistory.setClientUserId(clientUserId);
+                    searchHistory.setSearchKeyword(keyword);
+                    searchHistory.setSearchType(0);  // 0:律师姓名
+                    userSearchHistoryService.addUserSearchHistory(searchHistory);
+                    log.info("保存搜索历史成功:用户ID={}, 关键词={}", clientUserId, keyword);
+                } else {
+                    log.debug("搜索历史已存在,跳过添加:用户ID={}, 关键词={}", clientUserId, keyword);
+                }
+            } catch (Exception e) {
+                log.error("保存搜索历史失败:用户ID={}, 关键词={}, 错误={}", clientUserId, name, e.getMessage(), e);
+                // 保存历史失败不影响查询结果返回
+            }
+        }
+
+        return R.data(pageResult);
+    }
+
+    /**
+     * 获取AI推荐律师列表
+     * @param page 页码
+     * pageSize 页大小
+     * categoryId 分类ID
+     */
+    @Override
+    public R<IPage<LawyerUser>> getAiRecommendList(int page, int size, Integer categoryId) {
+        log.info("LawyerUserController.aiRecommendList?page={},size={},categoryId={}",
+                page, size, categoryId);
+
+
+        // 构建查询条件
+        QueryWrapper<LawyerUser> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("delete_flag", 0)
+                .eq("status", 1)
+                .eq("order_receiving_status", 1);
+
+        // 分类筛选:通过律师服务领域关联表查询
+        if (categoryId != null) {
+            List<Integer> lawyerIds = getLawyerIdsByCategoryId(categoryId);
+            if (lawyerIds.isEmpty()) {
+                // 如果没有匹配的律师,返回空结果
+                return R.data(new Page<>(page, size));
+            }
+            queryWrapper.in("id", lawyerIds);
+        }
+
+        // 排序:优先推荐律师 -> 在线律师 -> 注册时间
+        queryWrapper.orderByDesc("is_recommended")
+                .orderByDesc("is_online")
+                .orderByAsc("created_time");
+
+        // 分页查询
+        Page<LawyerUser> pageObj = new Page<>(page, size);
+        IPage<LawyerUser> pageResult = this.page(pageObj, queryWrapper);
+
+        // 为每个律师设置关联的法律问题场景列表
+        if (pageResult.getRecords() != null && !pageResult.getRecords().isEmpty()) {
+            setLawyerScenarios(pageResult.getRecords());
+        }
+
+        return R.data(pageResult);
+    }
+}
+