Explorar el Código

律师端申诉评价 列表 详情 中台端审核 列表 详情

qxy hace 3 semanas
padre
commit
02dbcb0fe9

+ 17 - 0
alien-entity/src/main/java/shop/alien/entity/store/CommentAppeal.java

@@ -71,5 +71,22 @@ public class CommentAppeal extends Model<CommentAppeal> {
     @TableField(value = "appeal_time", fill = FieldFill.INSERT)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private Date appealTime;
+
+    @ApiModelProperty(value = "图片路径")
+    @TableField("img_url")
+    private String imgUrl;
+
+    @ApiModelProperty(value = "申诉审核通过 驳回时间")
+    @TableField(value = "appeal_review_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date appealReviewTime;
+
+    @ApiModelProperty(value = "审核原因")
+    @TableField("review_reasons")
+    private String reviewReasons;
+
+    @ApiModelProperty(value = "申诉审核单号")
+    @TableField("appeal_number")
+    private String appealNumber;
 }
 

+ 137 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/CommentAppealVo.java

@@ -0,0 +1,137 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 评论申诉视图对象
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@ApiModel(value = "CommentAppealVo对象", description = "评论申诉视图对象")
+public class CommentAppealVo implements Serializable {
+
+    @ApiModelProperty(value = "主键id")
+    private Integer id;
+
+    @ApiModelProperty(value = "评论id")
+    private Integer commentId;
+
+    @ApiModelProperty(value = "评论内容")
+    private String commentInfo;
+
+    @ApiModelProperty(value = "申诉理由")
+    private String appealReason;
+
+    @ApiModelProperty(value = "申诉处理状态, 0:审核中, 1:已通过, 2:已驳回")
+    private Integer status;
+
+    @ApiModelProperty(value = "申诉处理状态字符串")
+    private String statusStr;
+
+    @ApiModelProperty(value = "申诉时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date appealTime;
+
+    @ApiModelProperty(value = "申诉审核通过/驳回时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date appealReviewTime;
+
+    @ApiModelProperty(value = "审核原因")
+    private String reviewReasons;
+
+    @ApiModelProperty(value = "图片路径")
+    private String imgUrl;
+
+    @ApiModelProperty(value = "图片列表")
+    private List<String> imgList;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "创建人名称")
+    private String createdUserName;
+
+    @ApiModelProperty(value = "更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "更新人ID")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "更新人名称")
+    private String updatedUserName;
+
+    @ApiModelProperty(value = "审核人ID")
+    private Integer auditUserId;
+
+    @ApiModelProperty(value = "审核人名称")
+    private String auditUserName;
+
+    // 评价相关信息
+    @ApiModelProperty(value = "评价用户ID")
+    private Integer reviewUserId;
+
+    @ApiModelProperty(value = "评价用户名称")
+    private String reviewUserName;
+
+    @ApiModelProperty(value = "评价用户头像")
+    private String reviewUserAvatar;
+
+    @ApiModelProperty(value = "总体评分")
+    private Double overallRating;
+
+    @ApiModelProperty(value = "服务态度评分")
+    private Double serviceAttitudeRating;
+
+    @ApiModelProperty(value = "响应时间评分")
+    private Double responseTimeRating;
+
+    @ApiModelProperty(value = "专业能力评分")
+    private Double professionalAbilityRating;
+
+    @ApiModelProperty(value = "评价内容")
+    private String reviewContent;
+
+    @ApiModelProperty(value = "评价时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date reviewTime;
+
+    @ApiModelProperty(value = "评价图片(JSON字符串,用于查询)")
+    private String reviewImages;
+
+    @ApiModelProperty(value = "评价图片列表")
+    private List<String> reviewImgList;
+
+    @ApiModelProperty(value = "申诉结果描述(申诉成功/申诉失败)")
+    private String appealResult;
+
+    // 中台列表展示字段
+    @ApiModelProperty(value = "订单编号")
+    private String orderNumber;
+
+    @ApiModelProperty(value = "用户电话")
+    private String userPhone;
+
+    @ApiModelProperty(value = "律师用户ID")
+    private Integer lawyerUserId;
+
+    @ApiModelProperty(value = "律师姓名")
+    private String lawyerName;
+
+    @ApiModelProperty(value = "律师电话")
+    private String lawyerPhone;
+}
+

+ 46 - 0
alien-entity/src/main/java/shop/alien/mapper/CommentAppealMapper.java

@@ -2,7 +2,11 @@ package shop.alien.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 import shop.alien.entity.store.CommentAppeal;
+import shop.alien.entity.store.vo.CommentAppealVo;
+
+import java.util.List;
 
 /**
  * 评论申诉表 Mapper 接口
@@ -12,5 +16,47 @@ import shop.alien.entity.store.CommentAppeal;
  */
 @Mapper
 public interface CommentAppealMapper extends BaseMapper<CommentAppeal> {
+
+    /**
+     * 查询申诉历史列表(包含评价和用户信息)
+     *
+     * @param status    申诉状态
+     * @param userId 律师用户ID
+     * @return 申诉历史列表
+     */
+    List<CommentAppealVo> getAppealHistoryList(@Param("status") Integer status,
+                                                @Param("userId") Integer userId);
+
+    /**
+     * 根据ID查询申诉详情(包含评价和用户信息)
+     *
+     * @param id 申诉ID
+     * @return 申诉详情
+     */
+    CommentAppealVo getAppealDetailById(@Param("id") Integer id);
+
+    /**
+     * 分页查询申诉列表(中台使用,支持多条件筛选)
+     *
+     * @param page         分页对象
+     * @param status       申诉状态
+     * @param userName     用户姓名(模糊查询)
+     * @param userPhone    用户电话
+     * @param lawyerName   律师姓名
+     * @param lawyerPhone  律师电话
+     * @param startTime    开始时间
+     * @param endTime      结束时间
+     * @return 分页结果
+     */
+    com.baomidou.mybatisplus.core.metadata.IPage<CommentAppealVo> getAppealPageWithFilters(
+            com.baomidou.mybatisplus.core.metadata.IPage<CommentAppealVo> page,
+            @Param("status") Integer status,
+            @Param("userName") String userName,
+            @Param("userPhone") String userPhone,
+            @Param("lawyerName") String lawyerName,
+            @Param("lawyerPhone") String lawyerPhone,
+            @Param("startTime") String startTime,
+            @Param("endTime") String endTime);
 }
 
+

+ 190 - 0
alien-entity/src/main/resources/mapper/CommentAppealMapper.xml

@@ -17,5 +17,195 @@
         <result column="appeal_time" property="appealTime" />
     </resultMap>
 
+    <!-- 申诉历史列表查询结果映射 -->
+    <resultMap id="CommentAppealVoResultMap" type="shop.alien.entity.store.vo.CommentAppealVo">
+        <id column="id" property="id" />
+        <result column="comment_id" property="commentId" />
+        <result column="comment_info" property="commentInfo" />
+        <result column="appeal_reason" property="appealReason" />
+        <result column="status" property="status" />
+        <result column="appeal_time" property="appealTime" />
+        <result column="appeal_review_time" property="appealReviewTime" />
+        <result column="review_reasons" property="reviewReasons" />
+        <result column="img_url" property="imgUrl" />
+        <result column="created_time" property="createdTime" />
+        <result column="created_user_id" property="createdUserId" />
+        <result column="updated_time" property="updatedTime" />
+        <result column="updated_user_id" property="updatedUserId" />
+        <result column="audit_user_id" property="auditUserId" />
+        <!-- 评价相关信息 -->
+        <result column="review_user_id" property="reviewUserId" />
+        <result column="review_user_name" property="reviewUserName" />
+        <result column="review_user_avatar" property="reviewUserAvatar" />
+        <result column="overall_rating" property="overallRating" />
+        <result column="service_attitude_rating" property="serviceAttitudeRating" />
+        <result column="response_time_rating" property="responseTimeRating" />
+        <result column="professional_ability_rating" property="professionalAbilityRating" />
+        <result column="review_content" property="reviewContent" />
+        <result column="review_time" property="reviewTime" />
+        <result column="review_images" property="reviewImages" />
+        <!-- 中台列表展示字段 -->
+        <result column="order_number" property="orderNumber" />
+        <result column="user_phone" property="userPhone" />
+        <result column="lawyer_user_id" property="lawyerUserId" />
+        <result column="lawyer_name" property="lawyerName" />
+        <result column="lawyer_phone" property="lawyerPhone" />
+    </resultMap>
+
+    <!-- 查询申诉历史列表(包含评价和用户信息) -->
+    <select id="getAppealHistoryList" resultMap="CommentAppealVoResultMap">
+        SELECT
+            ca.id,
+            ca.comment_id,
+            ca.comment_info,
+            ca.appeal_reason,
+            ca.status,
+            ca.appeal_time,
+            ca.appeal_review_time,
+            ca.review_reasons,
+            ca.img_url,
+            ca.created_time,
+            ca.created_user_id,
+            ca.updated_time,
+            ca.updated_user_id,
+            ca.updated_user_id AS audit_user_id,
+            orv.user_id AS review_user_id,
+            CASE
+                WHEN orv.is_anonymous = 1 THEN '匿名用户'
+                ELSE lu.user_name
+            END AS review_user_name,
+            CASE
+                WHEN orv.is_anonymous = 1 THEN NULL
+                ELSE lu.user_image
+            END AS review_user_avatar,
+            orv.overall_rating,
+            orv.service_attitude_rating,
+            orv.response_time_rating,
+            orv.professional_ability_rating,
+            orv.review_content,
+            orv.created_time AS review_time,
+            orv.review_images
+        FROM comment_appeals ca
+        LEFT JOIN order_review orv ON orv.id = ca.comment_id AND orv.delete_flag = 0
+        LEFT JOIN life_user lu ON lu.id = orv.user_id AND lu.delete_flag = 0
+        WHERE ca.delete_flag = 0
+        <if test="status != null">
+            AND ca.status = #{status}
+        </if>
+        <if test="userId != null">
+            AND orv.lawyer_user_id = #{userId}
+        </if>
+        ORDER BY ca.appeal_time DESC
+    </select>
+
+    <!-- 根据ID查询申诉详情(包含评价和用户信息) -->
+    <select id="getAppealDetailById" resultMap="CommentAppealVoResultMap">
+        SELECT
+            ca.id,
+            ca.comment_id,
+            ca.comment_info,
+            ca.appeal_reason,
+            ca.status,
+            ca.appeal_time,
+            ca.appeal_review_time,
+            ca.review_reasons,
+            ca.img_url,
+            ca.created_time,
+            ca.created_user_id,
+            ca.updated_time,
+            ca.updated_user_id,
+            ca.updated_user_id AS audit_user_id,
+            orv.user_id AS review_user_id,
+            CASE
+                WHEN orv.is_anonymous = 1 THEN '匿名用户'
+                ELSE lu.user_name
+            END AS review_user_name,
+            CASE
+                WHEN orv.is_anonymous = 1 THEN NULL
+                ELSE lu.user_image
+            END AS review_user_avatar,
+            orv.overall_rating,
+            orv.service_attitude_rating,
+            orv.response_time_rating,
+            orv.professional_ability_rating,
+            orv.review_content,
+            orv.created_time AS review_time,
+            orv.review_images
+        FROM comment_appeals ca
+        LEFT JOIN order_review orv ON orv.id = ca.comment_id AND orv.delete_flag = 0
+        LEFT JOIN life_user lu ON lu.id = orv.user_id AND lu.delete_flag = 0
+        WHERE ca.delete_flag = 0
+        AND ca.id = #{id}
+        LIMIT 1
+    </select>
+
+    <!-- 分页查询申诉列表(中台使用,支持多条件筛选) -->
+    <select id="getAppealPageWithFilters" resultMap="CommentAppealVoResultMap">
+        SELECT
+            ca.id,
+            ca.comment_id,
+            ca.comment_info,
+            ca.appeal_reason,
+            ca.status,
+            ca.appeal_time,
+            ca.appeal_review_time,
+            ca.review_reasons,
+            ca.img_url,
+            ca.created_time,
+            ca.created_user_id,
+            ca.updated_time,
+            ca.updated_user_id,
+            ca.updated_user_id AS audit_user_id,
+            orv.order_number,
+            orv.user_id AS review_user_id,
+            CASE
+                WHEN orv.is_anonymous = 1 THEN '匿名用户'
+                ELSE lu.user_name
+            END AS review_user_name,
+            CASE
+                WHEN orv.is_anonymous = 1 THEN NULL
+                ELSE lu.user_image
+            END AS review_user_avatar,
+            lu.user_phone AS user_phone,
+            orv.lawyer_user_id AS lawyer_user_id,
+            lu2.name AS lawyer_name,
+            lu2.phone AS lawyer_phone,
+            orv.overall_rating,
+            orv.service_attitude_rating,
+            orv.response_time_rating,
+            orv.professional_ability_rating,
+            orv.review_content,
+            orv.created_time AS review_time,
+            orv.review_images
+        FROM comment_appeals ca
+        LEFT JOIN order_review orv ON orv.id = ca.comment_id AND orv.delete_flag = 0
+        LEFT JOIN life_user lu ON lu.id = orv.user_id AND lu.delete_flag = 0
+        LEFT JOIN lawyer_user lu2 ON lu2.id = orv.lawyer_user_id AND lu2.delete_flag = 0
+        WHERE ca.delete_flag = 0
+        <if test="status != null">
+            AND ca.status = #{status}
+        </if>
+        <if test="userName != null and userName != ''">
+            AND lu.user_name LIKE CONCAT('%', #{userName}, '%')
+        </if>
+        <if test="userPhone != null and userPhone != ''">
+            AND lu.user_phone LIKE CONCAT('%', #{userPhone}, '%')
+        </if>
+        <if test="lawyerName != null and lawyerName != ''">
+            AND lu2.name LIKE CONCAT('%', #{lawyerName}, '%')
+        </if>
+        <if test="lawyerPhone != null and lawyerPhone != ''">
+            AND lu2.phone LIKE CONCAT('%', #{lawyerPhone}, '%')
+        </if>
+        <if test="startTime != null and startTime != ''">
+            AND ca.appeal_time &gt;= #{startTime}
+        </if>
+        <if test="endTime != null and endTime != ''">
+            AND ca.appeal_time &lt;= #{endTime}
+        </if>
+        ORDER BY ca.appeal_time DESC
+    </select>
+
 </mapper>
 
+

+ 60 - 14
alien-lawyer/src/main/java/shop/alien/lawyer/controller/CommentAppealController.java

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

+ 33 - 11
alien-lawyer/src/main/java/shop/alien/lawyer/service/CommentAppealService.java

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

+ 367 - 29
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/CommentAppealServiceImpl.java

@@ -1,5 +1,6 @@
 package shop.alien.lawyer.service.impl;
 
+import com.alibaba.fastjson2.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -10,9 +11,20 @@ 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.CommentAppeal;
-import shop.alien.mapper.CommentAppealMapper;
+import shop.alien.entity.store.*;
+import shop.alien.entity.store.vo.CommentAppealVo;
+import shop.alien.entity.store.vo.WebSocketVo;
+import shop.alien.lawyer.config.WebSocketProcess;
+import shop.alien.mapper.*;
 import shop.alien.lawyer.service.CommentAppealService;
+import shop.alien.lawyer.service.OrderReviewService;
+import shop.alien.lawyer.service.ReviewCommentService;
+import shop.alien.util.common.JwtUtil;
+import shop.alien.util.common.ListToPage;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
 
 /**
  * 评论申诉表 服务实现类
@@ -26,6 +38,15 @@ import shop.alien.lawyer.service.CommentAppealService;
 @RequiredArgsConstructor
 public class CommentAppealServiceImpl extends ServiceImpl<CommentAppealMapper, CommentAppeal> implements CommentAppealService {
 
+    private final OrderReviewService orderReviewService;
+    private final ReviewCommentService reviewCommentService;
+    private final CommentReplyMapper commentReplyMapper;
+    private final WebSocketProcess webSocketProcess;
+    private final LifeNoticeMapper lifeNoticeMapper;
+    private final OrderReviewMapper orderReviewMapper;
+    private final LifeUserMapper lifeUserMapper;
+    private final LawyerUserMapper lawyerUserMapper;
+
     @Override
     public R<CommentAppeal> submitAppeal(CommentAppeal commentAppeal) {
         log.info("CommentAppealServiceImpl.submitAppeal?commentAppeal={}", commentAppeal);
@@ -65,18 +86,27 @@ public class CommentAppealServiceImpl extends ServiceImpl<CommentAppealMapper, C
     }
 
     @Override
-    public R<Boolean> auditAppeal(Integer id, Integer status, Integer userId) {
-        log.info("CommentAppealServiceImpl.auditAppeal?id={}, status={}, userId={}", id, status, userId);
+    public R<Boolean> auditAppeal(Integer id, Integer status, String reviewReasons) {
+        log.info("CommentAppealServiceImpl.auditAppeal?id={}, status={}, reviewReasons={}", id, status, reviewReasons);
 
         // 校验参数
         if (id == null) {
             return R.fail("申诉ID不能为空");
         }
+
         if (status == null || (status != 1 && status != 2)) {
             return R.fail("审核状态不正确,必须是1(已通过)或2(已驳回)");
         }
-        if (userId == null) {
-            return R.fail("审核人ID不能为空");
+
+        // 从 token 获取当前登录用户ID(审核人)
+        Integer auditUserId = null;
+        try {
+            com.alibaba.fastjson.JSONObject userInfo = JwtUtil.getCurrentUserInfo();
+            if (userInfo != null && userInfo.getString("userId") != null) {
+                auditUserId = userInfo.getInteger("userId");
+            }
+        } catch (Exception e) {
+            log.warn("获取当前用户信息失败,将使用默认值", e);
         }
 
         // 查询申诉记录
@@ -90,58 +120,366 @@ public class CommentAppealServiceImpl extends ServiceImpl<CommentAppealMapper, C
             return R.fail("该申诉已处理,无法重复审核");
         }
 
+        // 查询评价信息
+        OrderReview orderReview = orderReviewMapper.selectById(appeal.getCommentId());
+        if (orderReview == null) {
+            return R.fail("评价记录不存在");
+        }
+
         // 更新状态
         CommentAppeal updateAppeal = new CommentAppeal();
         updateAppeal.setId(id);
         updateAppeal.setStatus(status);
-        updateAppeal.setUpdatedUserId(userId);
+        updateAppeal.setAppealReviewTime(new Date());
+        if (auditUserId != null) {
+            updateAppeal.setUpdatedUserId(auditUserId);
+        }
+        // 设置审核原因
+        if (StringUtils.hasText(reviewReasons)) {
+            updateAppeal.setReviewReasons(reviewReasons);
+        }
 
         boolean result = this.updateById(updateAppeal);
         if (result) {
             String statusText = status == 1 ? "已通过" : "已驳回";
-            log.info("审核申诉成功,id={}, status={}", id, statusText);
-            return R.success("审核成功,申诉" + statusText);
+            log.info("审核申诉成功,id={}, status={}, auditUserId={}", id, statusText, auditUserId);
+
+            // 如果申诉通过,删除评价及该评价下的所有评论和回复
+            if (status == 1) {
+                deleteReviewAndRelatedData(appeal.getCommentId());
+            }
+
+            // 发送通知
+            sendAuditNotification(appeal, orderReview, status);
+
+            return R.success("申诉" + statusText);
         } else {
             return R.fail("审核失败");
         }
     }
 
-    @Override
-    public R<IPage<CommentAppeal>> getAppealPage(Integer pageNum, Integer pageSize, Integer status, Integer commentId) {
-        log.info("CommentAppealServiceImpl.getAppealPage?pageNum={}, pageSize={}, status={}, commentId={}", 
-                pageNum, pageSize, status, commentId);
+    /**
+     * 发送审核通知
+     *
+     * @param appeal      申诉记录
+     * @param orderReview 评价记录
+     * @param status      审核状态 1:已通过, 2:已驳回
+     */
+    private void sendAuditNotification(CommentAppeal appeal, OrderReview orderReview, Integer status) {
+        try {
+            if (status == 1) {
+                // 审核通过:给评价用户发通知(评价被删除)
+                sendNotificationToReviewUser(orderReview, appeal);
+                // 审核通过:给律师发通知(申诉成功)
+                sendNotificationToLawyer(orderReview, appeal, true);
+            } else {
+                // 审核驳回:给律师发通知(申诉失败)
+                sendNotificationToLawyer(orderReview, appeal, false);
+            }
+        } catch (Exception e) {
+            log.error("发送审核通知失败,appealId={}, status={}, error={}", appeal.getId(), status, e.getMessage(), e);
+        }
+    }
 
-        Page<CommentAppeal> page = new Page<>(pageNum, pageSize);
-        LambdaQueryWrapper<CommentAppeal> wrapper = new LambdaQueryWrapper<>();
-        wrapper.eq(CommentAppeal::getDeleteFlag, 0);
-        
-        if (status != null) {
-            wrapper.eq(CommentAppeal::getStatus, status);
+    /**
+     * 给评价用户发送通知(评价被删除)
+     *
+     * @param orderReview 评价记录
+     * @param appeal      申诉记录
+     */
+    private void sendNotificationToReviewUser(OrderReview orderReview, CommentAppeal appeal) {
+        try {
+            LifeUser lifeUser = lifeUserMapper.selectById(orderReview.getUserId());
+            if (lifeUser == null) {
+                log.warn("评价用户不存在,userId={}", orderReview.getUserId());
+                return;
+            }
+
+            String receiverId = "user_" + lifeUser.getUserPhone();
+            String message = String.format("您对订单编号为"+ orderReview.getOrderNumber() +"的评价,律师已进行申诉,经核实,您的评价不实,平台已删除此条评价及回复。触发条件:平台审核同意时触发",
+                    orderReview.getOrderNumber());
+
+            LifeNotice lifeNotice = new LifeNotice();
+            lifeNotice.setReceiverId(receiverId);
+            lifeNotice.setBusinessId(appeal.getId());
+            lifeNotice.setTitle("评价被申诉成功通知");
+            lifeNotice.setSenderId("system");
+            lifeNotice.setIsRead(0);
+            lifeNotice.setNoticeType(1);
+            lifeNotice.setDeleteFlag(0);
+
+            com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
+            jsonObject.put("message", message);
+            jsonObject.put("orderNumber", orderReview.getOrderNumber());
+            jsonObject.put("appealId", appeal.getId());
+            lifeNotice.setContext(jsonObject.toJSONString());
+
+            lifeNoticeMapper.insert(lifeNotice);
+
+            // 发送 WebSocket 消息
+            sendWebSocketNotification(receiverId, lifeNotice);
+            log.info("评价用户通知发送成功,receiverId={}", receiverId);
+        } catch (Exception e) {
+            log.error("给评价用户发送通知失败,userId={}, error={}", orderReview.getUserId(), e.getMessage(), e);
         }
-        if (commentId != null) {
-            wrapper.eq(CommentAppeal::getCommentId, commentId);
+    }
+
+    /**
+     * 给律师发送通知(申诉成功/失败)
+     *
+     * @param orderReview 评价记录
+     * @param appeal      申诉记录
+     * @param isSuccess   是否成功
+     */
+    private void sendNotificationToLawyer(OrderReview orderReview, CommentAppeal appeal, boolean isSuccess) {
+        try {
+            LawyerUser lawyerUser = lawyerUserMapper.selectById(orderReview.getLawyerUserId());
+            if (lawyerUser == null) {
+                log.warn("律师用户不存在,lawyerUserId={}", orderReview.getLawyerUserId());
+                return;
+            }
+
+            // 律师的接收ID格式可能是 "lawyer_" + phone 或其他格式,需要根据实际情况调整
+            String receiverId = "lawyer_" + lawyerUser.getPhone();
+            String title = isSuccess ? "申诉成功通知" : "申诉失败通知";
+            String message = isSuccess 
+                    ? String.format("您的编号"+ orderReview.getOrderNumber() +"的订单,提交的差评申诉信息,经核实,评价内容不实,平台已删除此条评价。触发条件:平台审核,点击同意按钮时触发。")
+                    : String.format("您的编号"+ orderReview.getOrderNumber() +"的订单,提交的差评申诉信息,经核实,评价内容属实。失败原因:评价属实。触发条件:平台审核,输入拒绝原因,点击确定按钮时触发");
+
+            LifeNotice lifeNotice = new LifeNotice();
+            lifeNotice.setReceiverId(receiverId);
+            lifeNotice.setBusinessId(appeal.getId());
+            lifeNotice.setTitle(title);
+            lifeNotice.setSenderId("system");
+            lifeNotice.setIsRead(0);
+            lifeNotice.setNoticeType(1);
+            lifeNotice.setDeleteFlag(0);
+
+            com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
+            jsonObject.put("message", message);
+            jsonObject.put("orderNumber", orderReview.getOrderNumber());
+            jsonObject.put("appealId", appeal.getId());
+            jsonObject.put("status", isSuccess ? "success" : "rejected");
+            lifeNotice.setContext(jsonObject.toJSONString());
+
+            lifeNoticeMapper.insert(lifeNotice);
+
+            // 发送 WebSocket 消息
+            sendWebSocketNotification(receiverId, lifeNotice);
+            log.info("律师通知发送成功,receiverId={}, isSuccess={}", receiverId, isSuccess);
+        } catch (Exception e) {
+            log.error("给律师发送通知失败,lawyerUserId={}, error={}", orderReview.getLawyerUserId(), e.getMessage(), e);
         }
-        
-        wrapper.orderByDesc(CommentAppeal::getCreatedTime);
-        
-        IPage<CommentAppeal> pageResult = this.page(page, wrapper);
+    }
+
+    /**
+     * 发送 WebSocket 通知
+     *
+     * @param receiverId 接收人ID
+     * @param lifeNotice 通知内容
+     */
+    private void sendWebSocketNotification(String receiverId, LifeNotice lifeNotice) {
+        try {
+            WebSocketVo webSocketVo = new WebSocketVo();
+            webSocketVo.setSenderId("system");
+            webSocketVo.setReceiverId(receiverId);
+            webSocketVo.setCategory("notice");
+            webSocketVo.setNoticeType("1");
+            webSocketVo.setIsRead(0);
+            webSocketVo.setText(com.alibaba.fastjson2.JSONObject.from(lifeNotice).toJSONString());
+
+            webSocketProcess.sendMessage(receiverId, com.alibaba.fastjson2.JSONObject.from(webSocketVo).toJSONString());
+        } catch (Exception e) {
+            log.error("发送WebSocket通知失败,receiverId={}, error={}", receiverId, e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public R<IPage<CommentAppealVo>> getAppealPage(Integer pageNum, Integer pageSize, Integer status,
+                                                     String userName, String userPhone, String lawyerName, String lawyerPhone,
+                                                     String startTime, String endTime) {
+        log.info("CommentAppealServiceImpl.getAppealPage?pageNum={}, pageSize={}, status={}, userName={}, userPhone={}, lawyerName={}, lawyerPhone={}, startTime={}, endTime={}",
+                pageNum, pageSize, status, userName, userPhone, lawyerName, lawyerPhone, startTime, endTime);
+
+        Page<CommentAppealVo> page = new Page<>(pageNum, pageSize);
+        IPage<CommentAppealVo> pageResult = baseMapper.getAppealPageWithFilters(page, status, userName, userPhone, lawyerName, lawyerPhone, startTime, endTime);
+
+        // 处理数据转换
+        for (CommentAppealVo vo : pageResult.getRecords()) {
+            processAppealVo(vo);
+        }
+
         return R.data(pageResult);
     }
 
     @Override
-    public R<CommentAppeal> getAppealDetail(Integer id) {
+    public R<CommentAppealVo> getAppealDetail(Integer id) {
         log.info("CommentAppealServiceImpl.getAppealDetail?id={}", id);
 
         if (id == null) {
             return R.fail("申诉ID不能为空");
         }
 
-        CommentAppeal appeal = this.getById(id);
-        if (appeal == null || appeal.getDeleteFlag() == 1) {
+        // 查询申诉详情(包含评价和用户信息)
+        CommentAppealVo vo = baseMapper.getAppealDetailById(id);
+        if (vo == null) {
             return R.fail("申诉记录不存在或已被删除");
         }
 
-        return R.data(appeal, "查询成功");
+        // 处理数据转换(复用历史列表的处理逻辑)
+        processAppealVo(vo);
+
+        return R.data(vo, "查询成功");
+    }
+
+    /**
+     * 处理申诉VO数据转换(状态字符串、图片列表等)
+     *
+     * @param vo 申诉VO对象
+     */
+    private void processAppealVo(CommentAppealVo vo) {
+        // 设置状态字符串
+        if (vo.getStatus() != null) {
+            switch (vo.getStatus()) {
+                case 0:
+                    vo.setStatusStr("审核中");
+                    break;
+                case 1:
+                    vo.setStatusStr("已通过");
+                    vo.setAppealResult("申诉成功");
+                    break;
+                case 2:
+                    vo.setStatusStr("被驳回");
+                    vo.setAppealResult("申诉失败");
+                    break;
+                default:
+                    vo.setStatusStr("未知");
+            }
+        }
+
+        // 处理申诉图片列表
+        if (StringUtils.hasText(vo.getImgUrl())) {
+            try {
+                // 如果是 JSON 数组格式,解析为列表
+                if (vo.getImgUrl().trim().startsWith("[")) {
+                    List<String> imgList = JSON.parseArray(vo.getImgUrl(), String.class);
+                    vo.setImgList(imgList != null ? imgList : new ArrayList<>());
+                } else {
+                    // 如果是单个 URL 或逗号分隔的字符串
+                    List<String> imgList = new ArrayList<>();
+                    String[] urls = vo.getImgUrl().split(",");
+                    for (String url : urls) {
+                        if (StringUtils.hasText(url.trim())) {
+                            imgList.add(url.trim());
+                        }
+                    }
+                    vo.setImgList(imgList);
+                }
+            } catch (Exception e) {
+                log.warn("解析申诉图片失败,imgUrl={}", vo.getImgUrl(), e);
+                List<String> imgList = new ArrayList<>();
+                imgList.add(vo.getImgUrl());
+                vo.setImgList(imgList);
+            }
+        } else {
+            vo.setImgList(new ArrayList<>());
+        }
+
+        // 处理评价图片列表
+        if (StringUtils.hasText(vo.getReviewImages())) {
+            try {
+                // 如果是 JSON 数组格式,解析为列表
+                if (vo.getReviewImages().trim().startsWith("[")) {
+                    List<String> reviewImgList = JSON.parseArray(vo.getReviewImages(), String.class);
+                    vo.setReviewImgList(reviewImgList != null ? reviewImgList : new ArrayList<>());
+                } else {
+                    // 如果是单个 URL 或逗号分隔的字符串
+                    List<String> reviewImgList = new ArrayList<>();
+                    String[] urls = vo.getReviewImages().split(",");
+                    for (String url : urls) {
+                        if (StringUtils.hasText(url.trim())) {
+                            reviewImgList.add(url.trim());
+                        }
+                    }
+                    vo.setReviewImgList(reviewImgList);
+                }
+            } catch (Exception e) {
+                log.warn("解析评价图片失败,reviewImages={}", vo.getReviewImages(), e);
+                vo.setReviewImgList(new ArrayList<>());
+            }
+        } else {
+            vo.setReviewImgList(new ArrayList<>());
+        }
+    }
+
+    /**
+     * 删除评价及该评价下的所有评论和回复
+     *
+     * @param reviewId 评价ID
+     */
+    private void deleteReviewAndRelatedData(Integer reviewId) {
+        log.info("删除评价及关联数据,reviewId={}, userId={}", reviewId);
+
+        if (reviewId == null) {
+            log.warn("评价ID为空,无法删除");
+            return;
+        }
+
+        try {
+            // 1. 查询该评价下的所有评论
+            LambdaQueryWrapper<ReviewComment> commentWrapper = new LambdaQueryWrapper<>();
+            commentWrapper.eq(ReviewComment::getReviewId, reviewId)
+                    .eq(ReviewComment::getDeleteFlag, 0);
+            List<ReviewComment> comments = reviewCommentService.list(commentWrapper);
+
+            // 2. 删除每个评论下的所有回复
+            for (ReviewComment comment : comments) {
+                // 删除评论下的所有回复(逻辑删除)
+                commentReplyMapper.deleteRepliesByCommentId(comment.getId());
+                log.info("删除评论下的回复,commentId={}, 回复数已删除", comment.getId());
+            }
+
+            // 3. 删除所有评论(逻辑删除)
+            for (ReviewComment comment : comments) {
+                comment.setDeleteFlag(1);
+                comment.setUpdatedTime(new Date());
+                reviewCommentService.updateById(comment);
+            }
+            log.info("删除评价下的评论,reviewId={}, 评论数={}", reviewId, comments.size());
+
+            // 4. 删除评价本身(逻辑删除)
+            OrderReview review = orderReviewService.getById(reviewId);
+            if (review != null && review.getDeleteFlag() == 0) {
+                review.setDeleteFlag(1);
+                review.setUpdatedTime(new Date());
+                orderReviewService.updateById(review);
+                log.info("删除评价成功,reviewId={}", reviewId);
+            } else {
+                log.warn("评价不存在或已删除,reviewId={}", reviewId);
+            }
+        } catch (Exception e) {
+            log.error("删除评价及关联数据失败,reviewId={}, error={}", reviewId, e.getMessage(), e);
+            throw new RuntimeException("删除评价及关联数据失败:" + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public List<CommentAppealVo> getAppealHistory(Integer pageNum, Integer pageSize, Integer status, Integer userId) {
+        log.info("CommentAppealServiceImpl.getAppealHistory?pageNum={}, pageSize={}, status={}, userId={}",
+                pageNum, pageSize, status, userId);
+        List<CommentAppealVo> appealList = new ArrayList<>();
+        //status 3 查全部
+        if(status ==3){
+            appealList  = baseMapper.getAppealHistoryList(null, userId);
+        }else{
+            // 查询申诉历史列表
+            appealList  = baseMapper.getAppealHistoryList(status, userId);
+        }
+
+        ListToPage.setPage(appealList, pageNum, pageSize);
+        
+        return appealList;
     }
 }
 
+

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

@@ -27,3 +27,4 @@ public class LawFirmPaymentServiceImpl extends ServiceImpl<LawFirmPaymentMapper,
 
 
 
+