浏览代码

开发人员评价评论回复功能,点赞

lutong 3 月之前
父节点
当前提交
220919ca4c
共有 18 个文件被更改,包括 2334 次插入0 次删除
  1. 96 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreStaffComment.java
  2. 100 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreStaffReview.java
  3. 23 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/StoreStaffCommentRequestDto.java
  4. 35 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/StoreStaffReplyDto.java
  5. 47 0
      alien-entity/src/main/java/shop/alien/entity/store/dto/StoreStaffReviewDto.java
  6. 76 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreStaffCommentVo.java
  7. 25 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreStaffReviewDetailVo.java
  8. 87 0
      alien-entity/src/main/java/shop/alien/entity/store/vo/StoreStaffReviewVo.java
  9. 54 0
      alien-entity/src/main/java/shop/alien/mapper/StoreStaffCommentMapper.java
  10. 44 0
      alien-entity/src/main/java/shop/alien/mapper/StoreStaffReviewMapper.java
  11. 184 0
      alien-entity/src/main/resources/mapper/StoreStaffCommentMapper.xml
  12. 122 0
      alien-entity/src/main/resources/mapper/StoreStaffReviewMapper.xml
  13. 186 0
      alien-store/src/main/java/shop/alien/store/controller/StoreStaffCommentController.java
  14. 151 0
      alien-store/src/main/java/shop/alien/store/controller/StoreStaffReviewController.java
  15. 88 0
      alien-store/src/main/java/shop/alien/store/service/StoreStaffCommentService.java
  16. 83 0
      alien-store/src/main/java/shop/alien/store/service/StoreStaffReviewService.java
  17. 519 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffCommentServiceImpl.java
  18. 414 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffReviewServiceImpl.java

+ 96 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreStaffComment.java

@@ -0,0 +1,96 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 员工评论回复表
+ * 其他用户对员工评价的评论
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("store_staff_comment")
+@ApiModel(value = "StoreStaffComment对象", description = "员工评论回复")
+public class StoreStaffComment {
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "评价ID")
+    @TableField("review_id")
+    private Integer reviewId;
+
+    @ApiModelProperty(value = "发送用户类型:1-普通用户,2-员工")
+    @TableField("send_user_type")
+    private Integer sendUserType;
+
+    @ApiModelProperty(value = "评论用户ID")
+    @TableField("send_user_id")
+    private Integer sendUserId;
+
+    @ApiModelProperty(value = "接收用户类型:1-普通用户,2-员工")
+    @TableField("receive_user_type")
+    private Integer receiveUserType;
+
+    @ApiModelProperty(value = "接收用户ID")
+    @TableField("receive_user_id")
+    private Integer receiveUserId;
+
+    @ApiModelProperty(value = "评论内容")
+    @TableField("comment_content")
+    private String commentContent;
+
+    @ApiModelProperty(value = "点赞数")
+    @TableField("like_count")
+    private Integer likeCount;
+
+    @ApiModelProperty(value = "回复数")
+    @TableField("reply_count")
+    private Integer replyCount;
+
+    @ApiModelProperty(value = "首评标记, 0:是, 1:否")
+    @TableField("head_type")
+    private Integer headType;
+
+    @ApiModelProperty(value = "首评ID(仅子评论存在该属性)")
+    @TableField("head_id")
+    private Integer headId;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "登陆人")
+    @TableField(exist = false)
+    private Integer userId;
+}
+

+ 100 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreStaffReview.java

@@ -0,0 +1,100 @@
+package shop.alien.entity.store;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 员工评价表
+ * 用户对员工的评价和评分
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude
+@TableName("store_staff_review")
+@ApiModel(value = "StoreStaffReview对象", description = "员工评价")
+public class StoreStaffReview {
+
+    @ApiModelProperty(value = "主键")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "评价用户ID(订单用户)")
+    @TableField("user_id")
+    private Integer userId;
+
+    @ApiModelProperty(value = "员工用户ID")
+    @TableField("staff_user_id")
+    private Integer staffUserId;
+
+    @ApiModelProperty(value = "总体评分(1-5星,支持0.5星)")
+    @TableField("overall_rating")
+    private java.math.BigDecimal overallRating;
+
+    @ApiModelProperty(value = "服务态度评分(1-5星,支持0.5星)")
+    @TableField("service_attitude_rating")
+    private java.math.BigDecimal serviceAttitudeRating;
+
+    @ApiModelProperty(value = "响应时间评分(1-5星,支持0.5星)")
+    @TableField("response_time_rating")
+    private java.math.BigDecimal responseTimeRating;
+
+    @ApiModelProperty(value = "专业能力评分(1-5星,支持0.5星)")
+    @TableField("professional_ability_rating")
+    private java.math.BigDecimal professionalAbilityRating;
+
+    @ApiModelProperty(value = "评价内容")
+    @TableField("review_content")
+    private String reviewContent;
+
+    @ApiModelProperty(value = "评价图片(JSON数组格式存储图片URL)")
+    @TableField("review_images")
+    private String reviewImages;
+
+    @ApiModelProperty(value = "是否匿名评价,0:否,1:是")
+    @TableField("is_anonymous")
+    private Integer isAnonymous;
+
+    @ApiModelProperty(value = "点赞数")
+    @TableField("like_count")
+    private Integer likeCount;
+
+    @ApiModelProperty(value = "评论数")
+    @TableField("comment_count")
+    private Integer commentCount;
+
+    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
+    @TableField("delete_flag")
+    @TableLogic
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "创建人ID")
+    @TableField("created_user_id")
+    private Integer createdUserId;
+
+    @ApiModelProperty(value = "修改时间")
+    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedTime;
+
+    @ApiModelProperty(value = "修改人ID")
+    @TableField("updated_user_id")
+    private Integer updatedUserId;
+
+    @ApiModelProperty(value = "申诉id (如果该评价被申诉 申诉id会有值)")
+    @TableField("appeal_id")
+    private String appealId;
+}
+

+ 23 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreStaffCommentRequestDto.java

@@ -0,0 +1,23 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 员工评论请求DTO
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@ApiModel(value = "StoreStaffCommentRequestDto对象", description = "员工评论请求DTO")
+public class StoreStaffCommentRequestDto {
+
+    @ApiModelProperty(value = "评论ID")
+    private Integer commentId;
+
+    @ApiModelProperty(value = "用户ID")
+    private Integer userId;
+}
+

+ 35 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreStaffReplyDto.java

@@ -0,0 +1,35 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 员工评论回复DTO
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@ApiModel(value = "StoreStaffReplyDto对象", description = "员工评论回复DTO")
+public class StoreStaffReplyDto {
+
+    @ApiModelProperty(value = "首评ID")
+    private Integer commentId;
+
+    @ApiModelProperty(value = "被回复用户ID")
+    private Integer replyToUserId;
+
+    @ApiModelProperty(value = "回复内容")
+    private String replyContent;
+
+    @ApiModelProperty(value = "用户ID")
+    private Integer userId;
+
+    @ApiModelProperty(value = "发送用户类型:1-普通用户,2-员工(可选,不填时使用首评的接收用户类型)")
+    private Integer sendUserType;
+
+    @ApiModelProperty(value = "接收用户类型:1-普通用户,2-员工(可选,不填时使用首评的发送用户类型)")
+    private Integer receiveUserType;
+}
+

+ 47 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreStaffReviewDto.java

@@ -0,0 +1,47 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 员工评价DTO
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@ApiModel(value = "StoreStaffReviewDto对象", description = "员工评价DTO")
+public class StoreStaffReviewDto {
+
+    @ApiModelProperty(value = "员工用户ID")
+    private Integer staffUserId;
+
+    @ApiModelProperty(value = "总体评分(1-5星,支持0.5星)")
+    private BigDecimal overallRating;
+
+    @ApiModelProperty(value = "服务态度评分(1-5星,支持0.5星)")
+    private BigDecimal serviceAttitudeRating;
+
+    @ApiModelProperty(value = "响应时间评分(1-5星,支持0.5星)")
+    private BigDecimal responseTimeRating;
+
+    @ApiModelProperty(value = "专业能力评分(1-5星,支持0.5星)")
+    private BigDecimal professionalAbilityRating;
+
+    @ApiModelProperty(value = "评价内容")
+    private String reviewContent;
+
+    @ApiModelProperty(value = "评价图片列表")
+    private List<String> reviewImages;
+
+    @ApiModelProperty(value = "是否匿名评价,0:否,1:是")
+    private Integer isAnonymous;
+
+    @ApiModelProperty(value = "用户id")
+    private Integer userId;
+}
+

+ 76 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreStaffCommentVo.java

@@ -0,0 +1,76 @@
+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.util.Date;
+import java.util.List;
+
+/**
+ * 员工评论VO
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@ApiModel(value = "StoreStaffCommentVo对象", description = "员工评论VO")
+public class StoreStaffCommentVo {
+
+    @ApiModelProperty(value = "主键")
+    private Integer id;
+
+    @ApiModelProperty(value = "评价ID")
+    private Integer reviewId;
+
+    @ApiModelProperty(value = "发送用户类型:1-普通用户,2-员工")
+    private Integer sendUserType;
+
+    @ApiModelProperty(value = "发送用户ID")
+    private Integer sendUserId;
+
+    @ApiModelProperty(value = "发送用户名称")
+    private String sendUserName;
+
+    @ApiModelProperty(value = "发送用户头像")
+    private String sendUserAvatar;
+
+    @ApiModelProperty(value = "接收用户类型:1-普通用户,2-员工")
+    private Integer receiveUserType;
+
+    @ApiModelProperty(value = "接收用户ID")
+    private Integer receiveUserId;
+
+    @ApiModelProperty(value = "接收用户名称")
+    private String receiveUserName;
+
+    @ApiModelProperty(value = "接收用户头像")
+    private String receiveUserAvatar;
+
+    @ApiModelProperty(value = "评论内容")
+    private String commentContent;
+
+    @ApiModelProperty(value = "点赞数")
+    private Integer likeCount;
+
+    @ApiModelProperty(value = "回复数")
+    private Integer replyCount;
+
+    @ApiModelProperty(value = "首评标记, 0:是, 1:否")
+    private Integer headType;
+
+    @ApiModelProperty(value = "首评ID(仅子评论存在该属性)")
+    private Integer headId;
+
+    @ApiModelProperty(value = "当前用户是否已点赞,0:未点赞,1:已点赞")
+    private Integer isLiked;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "回复列表(仅首评存在)")
+    private List<StoreStaffCommentVo> replyList;
+}
+

+ 25 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreStaffReviewDetailVo.java

@@ -0,0 +1,25 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 员工评价详情VO(包含评论和回复)
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@ApiModel(value = "StoreStaffReviewDetailVo对象", description = "员工评价详情VO")
+public class StoreStaffReviewDetailVo {
+
+    @ApiModelProperty(value = "评价信息")
+    private StoreStaffReviewVo review;
+
+    @ApiModelProperty(value = "评论列表(包含回复)")
+    private List<StoreStaffCommentVo> commentList;
+}
+

+ 87 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreStaffReviewVo.java

@@ -0,0 +1,87 @@
+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.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 员工评价VO
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@ApiModel(value = "StoreStaffReviewVo对象", description = "员工评价VO")
+public class StoreStaffReviewVo {
+
+    @ApiModelProperty(value = "主键")
+    private Integer id;
+
+    @ApiModelProperty(value = "评价用户ID")
+    private Integer userId;
+
+    @ApiModelProperty(value = "评价用户名称")
+    private String userName;
+
+    @ApiModelProperty(value = "评价用户头像")
+    private String userAvatar;
+
+    @ApiModelProperty(value = "员工用户ID")
+    private Integer staffUserId;
+
+    @ApiModelProperty(value = "员工名称")
+    private String staffName;
+
+    @ApiModelProperty(value = "员工头像")
+    private String staffAvatar;
+
+    @ApiModelProperty(value = "总体评分")
+    private BigDecimal overallRating;
+
+    @ApiModelProperty(value = "服务态度评分")
+    private BigDecimal serviceAttitudeRating;
+
+    @ApiModelProperty(value = "响应时间评分")
+    private BigDecimal responseTimeRating;
+
+    @ApiModelProperty(value = "专业能力评分")
+    private BigDecimal professionalAbilityRating;
+
+    @ApiModelProperty(value = "评价内容")
+    private String reviewContent;
+
+    @ApiModelProperty(value = "评价图片列表")
+    private List<String> reviewImages;
+
+    @ApiModelProperty(value = "评价图片JSON字符串(内部使用,不对外暴露)")
+    @com.fasterxml.jackson.annotation.JsonIgnore
+    private String reviewImagesJson;
+
+    @ApiModelProperty(value = "是否匿名评价,0:否,1:是")
+    private Integer isAnonymous;
+
+    @ApiModelProperty(value = "点赞数")
+    private Integer likeCount;
+
+    @ApiModelProperty(value = "评论数")
+    private Integer commentCount;
+
+    @ApiModelProperty(value = "当前用户是否已点赞,0:未点赞,1:已点赞")
+    private Integer isLiked;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "逻辑删除")
+    private Integer deleteFlag;
+
+    @ApiModelProperty(value = "申诉id (如果该评价被申诉 申诉id会有值)")
+    private String appealId;
+}
+

+ 54 - 0
alien-entity/src/main/java/shop/alien/mapper/StoreStaffCommentMapper.java

@@ -0,0 +1,54 @@
+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.StoreStaffComment;
+import shop.alien.entity.store.vo.StoreStaffCommentVo;
+
+import java.util.List;
+
+/**
+ * 员工评论 Mapper 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface StoreStaffCommentMapper extends BaseMapper<StoreStaffComment> {
+
+    /**
+     * 根据评价ID查询评论列表(包含用户信息)
+     *
+     * @param reviewId 评价ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return 评论列表
+     */
+    List<StoreStaffCommentVo> getCommentListByReviewId(@Param("reviewId") Integer reviewId, @Param("currentUserId") Integer currentUserId);
+
+    /**
+     * 根据评价ID查询评论数量(只统计首评论)
+     *
+     * @param reviewId 评价ID
+     * @return 评论数量
+     */
+    Integer getCommentCountByReviewId(@Param("reviewId") Integer reviewId);
+
+    /**
+     * 根据评价ID查询总评论数量(包括首评论和子评论)
+     *
+     * @param reviewId 评价ID
+     * @return 总评论数量
+     */
+    Integer getTotalCommentCountByReviewId(@Param("reviewId") Integer reviewId);
+
+    /**
+     * 根据首评ID查询回复列表(包含用户信息)
+     *
+     * @param headId 首评ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return 回复列表
+     */
+    List<StoreStaffCommentVo> getReplyListByHeadId(@Param("headId") Integer headId, @Param("currentUserId") Integer currentUserId);
+}
+

+ 44 - 0
alien-entity/src/main/java/shop/alien/mapper/StoreStaffReviewMapper.java

@@ -0,0 +1,44 @@
+package shop.alien.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import shop.alien.entity.store.StoreStaffReview;
+import shop.alien.entity.store.vo.StoreStaffReviewVo;
+
+/**
+ * 员工评价 Mapper 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Mapper
+public interface StoreStaffReviewMapper extends BaseMapper<StoreStaffReview> {
+
+    /**
+     * 分页查询评价列表(包含用户和员工信息)
+     *
+     * @param page 分页对象
+     * @param staffUserId 员工用户ID
+     * @param userId 评价用户ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return 分页结果
+     */
+    IPage<StoreStaffReviewVo> getReviewListWithUser(
+            IPage<StoreStaffReviewVo> page,
+            @Param("staffUserId") Integer staffUserId,
+            @Param("userId") Integer userId,
+            @Param("currentUserId") Integer currentUserId
+    );
+
+    /**
+     * 根据评价ID查询评价详情(包含用户和员工信息)
+     *
+     * @param reviewId 评价ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return 评价详情
+     */
+    StoreStaffReviewVo getReviewDetailById(@Param("reviewId") Integer reviewId, @Param("currentUserId") Integer currentUserId);
+}
+

+ 184 - 0
alien-entity/src/main/resources/mapper/StoreStaffCommentMapper.xml

@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.mapper.StoreStaffCommentMapper">
+
+    <!-- 评论列表查询结果映射 -->
+    <resultMap id="StoreStaffCommentVoResultMap" type="shop.alien.entity.store.vo.StoreStaffCommentVo">
+        <id column="id" property="id" />
+        <result column="review_id" property="reviewId" />
+        <result column="send_user_id" property="sendUserId" />
+        <result column="receive_user_id" property="receiveUserId" />
+        <result column="send_user_name" property="sendUserName" />
+        <result column="send_user_avatar" property="sendUserAvatar" />
+        <result column="receive_user_name" property="receiveUserName" />
+        <result column="receive_user_avatar" property="receiveUserAvatar" />
+        <result column="send_user_type" property="sendUserType" />
+        <result column="receive_user_type" property="receiveUserType" />
+        <result column="comment_content" property="commentContent" />
+        <result column="like_count" property="likeCount" />
+        <result column="reply_count" property="replyCount" />
+        <result column="head_type" property="headType" />
+        <result column="head_id" property="headId" />
+        <result column="is_liked" property="isLiked" />
+        <result column="created_time" property="createdTime" />
+    </resultMap>
+
+    <!-- 根据评价ID查询评论列表(包含用户信息) -->
+    <select id="getCommentListByReviewId" resultMap="StoreStaffCommentVoResultMap">
+        SELECT
+            ssc.id,
+            ssc.review_id,
+            ssc.send_user_id,
+            ssc.receive_user_id,
+            -- 发送用户名称:根据 sendUserType 分别查询普通用户表和员工表
+            CASE
+                WHEN ssc.send_user_type = 2 THEN sp_send.personnel_name
+                WHEN ssc.send_user_type = 1 OR ssc.send_user_type IS NULL THEN lu_send.user_name
+                ELSE lu_send.user_name
+            END AS send_user_name,
+            -- 发送用户头像:根据 sendUserType 分别查询普通用户表和员工表
+            CASE
+                WHEN ssc.send_user_type = 2 THEN si_send.img_url
+                WHEN ssc.send_user_type = 1 OR ssc.send_user_type IS NULL THEN lu_send.user_image
+                ELSE lu_send.user_image
+            END AS send_user_avatar,
+            -- 接收用户名称:根据 receiveUserType 分别查询普通用户表和员工表
+            CASE
+                WHEN ssc.receive_user_type = 2 THEN sp_receive.personnel_name
+                WHEN ssc.receive_user_type = 1 OR ssc.receive_user_type IS NULL THEN lu_receive.user_name
+                ELSE NULL
+            END AS receive_user_name,
+            -- 接收用户头像:根据 receiveUserType 分别查询普通用户表和员工表
+            CASE
+                WHEN ssc.receive_user_type = 2 THEN si_receive.img_url
+                WHEN ssc.receive_user_type = 1 OR ssc.receive_user_type IS NULL THEN lu_receive.user_image
+                ELSE NULL
+            END AS receive_user_avatar,
+            ssc.send_user_type,
+            ssc.receive_user_type,
+            ssc.comment_content,
+            ssc.like_count,
+            ssc.reply_count,
+            ssc.head_type,
+            ssc.head_id,
+            CASE 
+                WHEN #{currentUserId} IS NOT NULL AND llr.id IS NOT NULL THEN 1
+                ELSE 0
+            END AS is_liked,
+            ssc.created_time
+        FROM store_staff_comment ssc
+        -- 发送用户:普通用户表
+        LEFT JOIN life_user lu_send ON lu_send.id = ssc.send_user_id 
+            AND lu_send.delete_flag = 0
+        -- 发送用户:员工表
+        LEFT JOIN store_personnel sp_send ON sp_send.id = ssc.send_user_id 
+            AND sp_send.delete_flag = 0
+        LEFT JOIN store_img si_send ON si_send.id = sp_send.img_id 
+            AND si_send.delete_flag = 0
+        -- 接收用户:普通用户表
+        LEFT JOIN life_user lu_receive ON lu_receive.id = ssc.receive_user_id 
+            AND lu_receive.delete_flag = 0
+        -- 接收用户:员工表
+        LEFT JOIN store_personnel sp_receive ON sp_receive.id = ssc.receive_user_id 
+            AND sp_receive.delete_flag = 0
+        LEFT JOIN store_img si_receive ON si_receive.id = sp_receive.img_id 
+            AND si_receive.delete_flag = 0
+        LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(ssc.id, CHAR)
+            AND llr.type = '10' 
+            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.delete_flag = 0
+        WHERE ssc.delete_flag = 0
+        AND ssc.review_id = #{reviewId}
+        AND ssc.head_type = 0
+        ORDER BY ssc.created_time DESC
+    </select>
+
+    <!-- 根据首评ID查询回复列表(包含用户信息) -->
+    <select id="getReplyListByHeadId" resultMap="StoreStaffCommentVoResultMap">
+        SELECT
+            ssc.id,
+            ssc.review_id,
+            ssc.send_user_id,
+            ssc.receive_user_id,
+            -- 发送用户名称:根据 sendUserType 分别查询普通用户表和员工表
+            CASE
+                WHEN ssc.send_user_type = 2 THEN sp_send.personnel_name
+                WHEN ssc.send_user_type = 1 OR ssc.send_user_type IS NULL THEN lu_send.user_name
+                ELSE lu_send.user_name
+            END AS send_user_name,
+            -- 发送用户头像:根据 sendUserType 分别查询普通用户表和员工表
+            CASE
+                WHEN ssc.send_user_type = 2 THEN si_send.img_url
+                WHEN ssc.send_user_type = 1 OR ssc.send_user_type IS NULL THEN lu_send.user_image
+                ELSE lu_send.user_image
+            END AS send_user_avatar,
+            -- 接收用户名称:根据 receiveUserType 分别查询普通用户表和员工表
+            CASE
+                WHEN ssc.receive_user_type = 2 THEN sp_receive.personnel_name
+                WHEN ssc.receive_user_type = 1 OR ssc.receive_user_type IS NULL THEN lu_receive.user_name
+                ELSE NULL
+            END AS receive_user_name,
+            -- 接收用户头像:根据 receiveUserType 分别查询普通用户表和员工表
+            CASE
+                WHEN ssc.receive_user_type = 2 THEN si_receive.img_url
+                WHEN ssc.receive_user_type = 1 OR ssc.receive_user_type IS NULL THEN lu_receive.user_image
+                ELSE NULL
+            END AS receive_user_avatar,
+            ssc.send_user_type,
+            ssc.receive_user_type,
+            ssc.comment_content,
+            ssc.like_count,
+            ssc.reply_count,
+            ssc.head_type,
+            ssc.head_id,
+            CASE 
+                WHEN #{currentUserId} IS NOT NULL AND llr.id IS NOT NULL THEN 1
+                ELSE 0
+            END AS is_liked,
+            ssc.created_time
+        FROM store_staff_comment ssc
+        -- 发送用户:普通用户表
+        LEFT JOIN life_user lu_send ON lu_send.id = ssc.send_user_id 
+            AND lu_send.delete_flag = 0
+        -- 发送用户:员工表
+        LEFT JOIN store_personnel sp_send ON sp_send.id = ssc.send_user_id 
+            AND sp_send.delete_flag = 0
+        LEFT JOIN store_img si_send ON si_send.id = sp_send.img_id 
+            AND si_send.delete_flag = 0
+        -- 接收用户:普通用户表
+        LEFT JOIN life_user lu_receive ON lu_receive.id = ssc.receive_user_id 
+            AND lu_receive.delete_flag = 0
+        -- 接收用户:员工表
+        LEFT JOIN store_personnel sp_receive ON sp_receive.id = ssc.receive_user_id 
+            AND sp_receive.delete_flag = 0
+        LEFT JOIN store_img si_receive ON si_receive.id = sp_receive.img_id 
+            AND si_receive.delete_flag = 0
+        LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(ssc.id, CHAR)
+            AND llr.type = '10' 
+            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.delete_flag = 0
+        WHERE ssc.delete_flag = 0
+        AND ssc.head_id = #{headId}
+        AND ssc.head_type = 1
+        ORDER BY ssc.created_time ASC
+    </select>
+
+    <!-- 根据评价ID查询评论数量(只统计首评论) -->
+    <select id="getCommentCountByReviewId" resultType="java.lang.Integer">
+        SELECT COUNT(*)
+        FROM store_staff_comment
+        WHERE delete_flag = 0
+        AND review_id = #{reviewId}
+        AND head_type = 0
+    </select>
+
+    <!-- 根据评价ID查询总评论数量(包括首评论和子评论) -->
+    <select id="getTotalCommentCountByReviewId" resultType="java.lang.Integer">
+        SELECT COUNT(*)
+        FROM store_staff_comment
+        WHERE delete_flag = 0
+        AND review_id = #{reviewId}
+    </select>
+
+</mapper>
+

+ 122 - 0
alien-entity/src/main/resources/mapper/StoreStaffReviewMapper.xml

@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="shop.alien.mapper.StoreStaffReviewMapper">
+
+    <!-- 评价列表查询结果映射 -->
+    <resultMap id="StoreStaffReviewVoResultMap" type="shop.alien.entity.store.vo.StoreStaffReviewVo">
+        <id column="id" property="id" />
+        <result column="user_id" property="userId" />
+        <result column="user_name" property="userName" />
+        <result column="user_avatar" property="userAvatar" />
+        <result column="staff_user_id" property="staffUserId" />
+        <result column="staff_name" property="staffName" />
+        <result column="staff_avatar" property="staffAvatar" />
+        <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_images" property="reviewImagesJson" />
+        <result column="is_anonymous" property="isAnonymous" />
+        <result column="like_count" property="likeCount" />
+        <result column="comment_count" property="commentCount" />
+        <result column="is_liked" property="isLiked" />
+        <result column="created_time" property="createdTime" />
+        <result column="appeal_id" property="appealId" />
+    </resultMap>
+
+    <!-- 分页查询评价列表(包含用户和员工信息) -->
+    <select id="getReviewListWithUser" resultMap="StoreStaffReviewVoResultMap">
+        SELECT
+            ssr.id,
+            ssr.user_id,
+            CASE 
+                WHEN ssr.is_anonymous = 1 THEN '匿名用户'
+                ELSE lu.user_name
+            END AS user_name,
+            CASE 
+                WHEN ssr.is_anonymous = 1 THEN NULL
+                ELSE lu.user_image
+            END AS user_avatar,
+            ssr.staff_user_id,
+            sp.personnel_name AS staff_name,
+            si.img_url AS staff_avatar,
+            ssr.overall_rating,
+            ssr.service_attitude_rating,
+            ssr.response_time_rating,
+            ssr.professional_ability_rating,
+            ssr.review_content,
+            ssr.is_anonymous,
+            ssr.like_count,
+            ssr.comment_count,
+            ssr.review_images,
+            CASE 
+                WHEN #{currentUserId} IS NOT NULL AND llr.id IS NOT NULL THEN 1
+                ELSE 0
+            END AS is_liked,
+            ssr.created_time,
+            ssr.appeal_id
+        FROM store_staff_review ssr
+        LEFT JOIN life_user lu ON lu.id = ssr.user_id AND lu.delete_flag = 0
+        LEFT JOIN store_personnel sp ON sp.id = ssr.staff_user_id AND sp.delete_flag = 0
+        LEFT JOIN store_img si ON si.id = sp.img_id AND si.delete_flag = 0
+        LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(ssr.id, CHAR)
+            AND llr.type = '9' 
+            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.delete_flag = 0
+        WHERE ssr.delete_flag = 0
+        <if test="staffUserId != null">
+            AND ssr.staff_user_id = #{staffUserId}
+        </if>
+        <if test="userId != null">
+            AND ssr.user_id = #{userId}
+        </if>
+        ORDER BY ssr.created_time DESC
+    </select>
+
+    <!-- 根据评价ID查询评价详情(包含用户和员工信息) -->
+    <select id="getReviewDetailById" resultMap="StoreStaffReviewVoResultMap">
+        SELECT
+            ssr.id,
+            ssr.user_id,
+            CASE 
+                WHEN ssr.is_anonymous = 1 THEN '匿名用户'
+                ELSE lu.user_name
+            END AS user_name,
+            CASE 
+                WHEN ssr.is_anonymous = 1 THEN NULL
+                ELSE lu.user_image
+            END AS user_avatar,
+            ssr.staff_user_id,
+            sp.personnel_name AS staff_name,
+            si.img_url AS staff_avatar,
+            ssr.overall_rating,
+            ssr.service_attitude_rating,
+            ssr.response_time_rating,
+            ssr.professional_ability_rating,
+            ssr.review_content,
+            ssr.is_anonymous,
+            ssr.like_count,
+            ssr.comment_count,
+            ssr.review_images,
+            CASE 
+                WHEN #{currentUserId} IS NOT NULL AND llr.id IS NOT NULL THEN 1
+                ELSE 0
+            END AS is_liked,
+            ssr.created_time,
+            ssr.appeal_id
+        FROM store_staff_review ssr
+        LEFT JOIN life_user lu ON lu.id = ssr.user_id AND lu.delete_flag = 0
+        LEFT JOIN store_personnel sp ON sp.id = ssr.staff_user_id AND sp.delete_flag = 0
+        LEFT JOIN store_img si ON si.id = sp.img_id AND si.delete_flag = 0
+        LEFT JOIN life_like_record llr ON CONVERT(llr.huifu_id, CHAR) = CONVERT(ssr.id, CHAR)
+            AND llr.type = '9' 
+            AND CONVERT(llr.dianzan_id, CHAR) = CONVERT(#{currentUserId}, CHAR)
+            AND llr.delete_flag = 0
+        WHERE ssr.delete_flag = 0
+        AND ssr.id = #{reviewId}
+        LIMIT 1
+    </select>
+
+</mapper>
+

+ 186 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreStaffCommentController.java

@@ -0,0 +1,186 @@
+package shop.alien.store.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreStaffComment;
+import shop.alien.entity.store.dto.StoreStaffCommentRequestDto;
+import shop.alien.entity.store.dto.StoreStaffReplyDto;
+import shop.alien.entity.store.vo.StoreStaffCommentVo;
+import shop.alien.store.service.StoreStaffCommentService;
+
+import java.util.List;
+
+/**
+ * 员工评论 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"员工评论"})
+@ApiSort(21)
+@CrossOrigin
+@RestController
+@RequestMapping("/store/staffComment")
+@RequiredArgsConstructor
+public class StoreStaffCommentController {
+
+    private final StoreStaffCommentService storeStaffCommentService;
+
+    @ApiOperation("创建评论(用户对评价的评论,支持普通用户和员工)")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/create")
+    public R<StoreStaffComment> createComment(@RequestBody StoreStaffComment comment) {
+        log.info("创建评论, comment={}", comment);
+        
+        // 参数校验:优先使用sendUserId,如果不存在则使用userId
+        if (comment.getSendUserId() == null) {
+            if (comment.getUserId() == null) {
+                return R.fail("用户ID不能为空");
+            }
+            comment.setSendUserId(comment.getUserId());
+        }
+        
+        // 校验用户类型字段
+        if (comment.getSendUserType() == null) {
+            return R.fail("发送用户类型不能为空");
+        }
+        if (comment.getReceiveUserType() == null) {
+            return R.fail("接收用户类型不能为空");
+        }
+        
+        return storeStaffCommentService.createComment(comment);
+    }
+
+    @ApiOperation("根据评价ID查询评论列表")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/list/reviewId")
+    public R<List<StoreStaffCommentVo>> getCommentListByReviewId(
+            @RequestParam Integer reviewId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("根据评价ID查询评论列表, reviewId={}, currentUserId={}", reviewId, currentUserId);
+        if (reviewId == null) {
+            return R.fail("评价ID不能为空");
+        }
+        return storeStaffCommentService.getCommentListByReviewId(reviewId, currentUserId);
+    }
+
+    @ApiOperation("点赞评论")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "commentId", value = "评论ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/like")
+    public R<Boolean> likeComment(@RequestParam Integer commentId,
+                                   @RequestParam Integer userId) {
+        log.info("点赞评论, commentId={}, userId={}", commentId, userId);
+        if (commentId == null) {
+            return R.fail("评论ID不能为空");
+        }
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        StoreStaffCommentRequestDto requestDto = new StoreStaffCommentRequestDto();
+        requestDto.setCommentId(commentId);
+        requestDto.setUserId(userId);
+        return storeStaffCommentService.likeComment(requestDto);
+    }
+
+    @ApiOperation("取消点赞评论")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "commentId", value = "评论ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/cancelLike")
+    public R<Boolean> cancelLikeComment(@RequestParam Integer commentId,
+                                         @RequestParam Integer userId) {
+        log.info("取消点赞评论, commentId={}, userId={}", commentId, userId);
+        if (commentId == null) {
+            return R.fail("评论ID不能为空");
+        }
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        StoreStaffCommentRequestDto requestDto = new StoreStaffCommentRequestDto();
+        requestDto.setCommentId(commentId);
+        requestDto.setUserId(userId);
+        return storeStaffCommentService.cancelLikeComment(requestDto);
+    }
+
+    @ApiOperation("创建回复(用户对评论的回复)")
+    @ApiOperationSupport(order = 5)
+    @PostMapping("/reply/create")
+    public R<StoreStaffComment> createReply(@RequestBody StoreStaffReplyDto replyDto) {
+        log.info("创建回复, replyDto={}", replyDto);
+        
+        if (replyDto == null || replyDto.getUserId() == null) {
+            return R.fail("用户ID不能为空");
+        }
+        
+        // 校验用户类型字段
+        if (replyDto.getSendUserType() == null) {
+            return R.fail("发送用户类型不能为空");
+        }
+        if (replyDto.getReceiveUserType() == null) {
+            return R.fail("接收用户类型不能为空");
+        }
+        
+        return storeStaffCommentService.createReply(replyDto);
+    }
+
+    @ApiOperation("根据首评ID查询回复列表")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "headId", value = "首评ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/reply/list/headId")
+    public R<List<StoreStaffCommentVo>> getReplyListByHeadId(
+            @RequestParam Integer headId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("根据首评ID查询回复列表, headId={}, currentUserId={}", headId, currentUserId);
+        if (headId == null) {
+            return R.fail("首评ID不能为空");
+        }
+        return storeStaffCommentService.getReplyListByHeadId(headId, currentUserId);
+    }
+
+    @ApiOperation("删除回复")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "replyId", value = "回复ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/reply/delete")
+    public R<Boolean> deleteReply(@RequestParam Integer replyId,
+                                   @RequestParam Integer userId) {
+        log.info("删除回复, replyId={}, userId={}", replyId, userId);
+        if (replyId == null) {
+            return R.fail("回复ID不能为空");
+        }
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        return storeStaffCommentService.deleteReply(replyId, userId);
+    }
+
+    @ApiOperation(value = "删除评论(根据ID)", notes = "根据评论ID删除评论,userId有值时只能删除自己发布的评论,userId为空时允许删除任何评论(管理员删除)")
+    @ApiOperationSupport(order = 8)
+    @PostMapping("/deleteReviewComment")
+    public R<Boolean> deleteReviewComment(@RequestBody StoreStaffComment storeStaffComment) {
+        log.info("删除评论, storeStaffComment={}", storeStaffComment);
+        if (storeStaffComment == null || storeStaffComment.getId() == null) {
+            return R.fail("评论ID不能为空");
+        }
+        return storeStaffCommentService.deleteReviewComment(storeStaffComment);
+    }
+}

+ 151 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreStaffReviewController.java

@@ -0,0 +1,151 @@
+package shop.alien.store.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.StoreStaffReview;
+import shop.alien.entity.store.dto.StoreStaffReviewDto;
+import shop.alien.entity.store.vo.StoreStaffReviewDetailVo;
+import shop.alien.entity.store.vo.StoreStaffReviewVo;
+import shop.alien.store.service.StoreStaffReviewService;
+
+/**
+ * 员工评价 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"员工评价"})
+@ApiSort(20)
+@CrossOrigin
+@RestController
+@RequestMapping("/store/staffReview")
+@RequiredArgsConstructor
+public class StoreStaffReviewController {
+
+    private final StoreStaffReviewService storeStaffReviewService;
+
+    @ApiOperation("创建员工评价")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/create")
+    public R<StoreStaffReview> createReview(@RequestBody StoreStaffReviewDto reviewDto) {
+        log.info("创建员工评价, reviewDto={}", reviewDto);
+        if (reviewDto == null || reviewDto.getUserId() == null) {
+            return R.fail("用户未登录");
+        }
+        return storeStaffReviewService.createReview(reviewDto);
+    }
+
+    @ApiOperation("获取评价详情(包含评论和回复)")
+    @ApiOperationSupport(order = 2)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/detail/reviewId")
+    public R<StoreStaffReviewDetailVo> getReviewDetail(
+            @RequestParam Integer reviewId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("获取评价详情, reviewId={}, currentUserId={}", reviewId, currentUserId);
+        if (reviewId == null) {
+            return R.fail("评价ID不能为空");
+        }
+        return storeStaffReviewService.getReviewDetail(reviewId, currentUserId);
+    }
+
+    @ApiOperation("点赞评价")
+    @ApiOperationSupport(order = 3)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/like")
+    public R<Boolean> likeReview(@RequestParam Integer reviewId,
+                                  @RequestParam Integer userId) {
+        log.info("点赞评价, reviewId={}, userId={}", reviewId, userId);
+        if (reviewId == null) {
+            return R.fail("评价ID不能为空");
+        }
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        return storeStaffReviewService.likeReview(reviewId, userId);
+    }
+
+    @ApiOperation("取消点赞评价")
+    @ApiOperationSupport(order = 4)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/cancelLike")
+    public R<Boolean> cancelLikeReview(@RequestParam Integer reviewId,
+                                       @RequestParam Integer userId) {
+        log.info("取消点赞评价, reviewId={}, userId={}", reviewId, userId);
+        if (reviewId == null) {
+            return R.fail("评价ID不能为空");
+        }
+        if (userId == null) {
+            return R.fail("用户未登录");
+        }
+        return storeStaffReviewService.cancelLikeReview(reviewId, userId);
+    }
+
+    @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 = "staffUserId", value = "员工用户ID", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "userId", value = "评价用户ID", dataType = "int", paramType = "query"),
+            @ApiImplicitParam(name = "currentUserId", value = "当前用户ID(用于判断是否已点赞)", dataType = "int", paramType = "query")
+    })
+    @GetMapping("/list")
+    public R<IPage<StoreStaffReviewVo>> getReviewList(
+            @RequestParam(defaultValue = "1") int page,
+            @RequestParam(defaultValue = "10") int size,
+            @RequestParam(required = false) Integer staffUserId,
+            @RequestParam(required = false) Integer userId,
+            @RequestParam(required = false) Integer currentUserId) {
+        log.info("分页查询评价列表, page={}, size={}, staffUserId={}, userId={}, currentUserId={}",
+                page, size, staffUserId, userId, currentUserId);
+        return storeStaffReviewService.getReviewList(page, size, staffUserId, userId, currentUserId);
+    }
+
+    @ApiOperation("用户删除评价(只能删除自己的评价,删除评价时,会级联删除该评价下的所有评论和回复)")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true),
+            @ApiImplicitParam(name = "userId", value = "用户ID(必填,只能删除自己的评价)", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/delete/reviewId")
+    public R<Boolean> deleteReview(@RequestParam Integer reviewId,
+                                   @RequestParam Integer userId) {
+        log.info("用户删除评价, reviewId={}, userId={}", reviewId, userId);
+        if (reviewId == null) {
+            return R.fail("评价ID不能为空");
+        }
+        if (userId == null) {
+            return R.fail("用户ID不能为空");
+        }
+        return storeStaffReviewService.deleteReview(reviewId, userId);
+    }
+
+    @ApiOperation("管理员删除评价(可以删除任何评价,删除评价时,会级联删除该评价下的所有评论和回复)")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reviewId", value = "评价ID", dataType = "int", paramType = "query", required = true)
+    })
+    @PostMapping("/admin/delete/reviewId")
+    public R<Boolean> deleteReviewByAdmin(@RequestParam Integer reviewId) {
+        log.info("管理员删除评价, reviewId={}", reviewId);
+        if (reviewId == null) {
+            return R.fail("评价ID不能为空");
+        }
+        return storeStaffReviewService.deleteReviewByAdmin(reviewId);
+    }
+}

+ 88 - 0
alien-store/src/main/java/shop/alien/store/service/StoreStaffCommentService.java

@@ -0,0 +1,88 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.StoreStaffComment;
+import shop.alien.entity.store.dto.StoreStaffCommentRequestDto;
+import shop.alien.entity.store.dto.StoreStaffReplyDto;
+import shop.alien.entity.store.vo.StoreStaffCommentVo;
+
+import java.util.List;
+
+/**
+ * 员工评论 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StoreStaffCommentService extends IService<StoreStaffComment> {
+
+    /**
+     * 创建评论(其他用户对评价的评论)
+     *
+     * @param comment 评论实体
+     * @return R<StoreStaffComment>
+     */
+    R<StoreStaffComment> createComment(StoreStaffComment comment);
+
+    /**
+     * 根据评价ID查询评论列表
+     *
+     * @param reviewId 评价ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<List<StoreStaffCommentVo>>
+     */
+    R<List<StoreStaffCommentVo>> getCommentListByReviewId(Integer reviewId, Integer currentUserId);
+
+    /**
+     * 点赞评论
+     *
+     * @param requestDto 请求DTO
+     * @return R<Boolean>
+     */
+    R<Boolean> likeComment(StoreStaffCommentRequestDto requestDto);
+
+    /**
+     * 取消点赞评论
+     *
+     * @param requestDto 请求DTO
+     * @return R<Boolean>
+     */
+    R<Boolean> cancelLikeComment(StoreStaffCommentRequestDto requestDto);
+
+    /**
+     * 创建回复(用户对评论的回复)
+     *
+     * @param replyDto 回复DTO
+     * @return R<StoreStaffComment>
+     */
+    R<StoreStaffComment> createReply(StoreStaffReplyDto replyDto);
+
+    /**
+     * 根据首评ID查询回复列表
+     *
+     * @param headId 首评ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<List<StoreStaffCommentVo>>
+     */
+    R<List<StoreStaffCommentVo>> getReplyListByHeadId(Integer headId, Integer currentUserId);
+
+    /**
+     * 删除回复
+     *
+     * @param replyId 回复ID
+     * @param userId 用户ID
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteReply(Integer replyId, Integer userId);
+
+    /**
+     * 删除评论(根据ID,物理删除)
+     * userId有值时只能删除自己发布的评论,userId为空时允许删除任何评论(管理员删除)
+     *
+     * @param comment 评论对象(包含id和userId,userId可选)
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteReviewComment(StoreStaffComment comment);
+}
+

+ 83 - 0
alien-store/src/main/java/shop/alien/store/service/StoreStaffReviewService.java

@@ -0,0 +1,83 @@
+package shop.alien.store.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.StoreStaffReview;
+import shop.alien.entity.store.dto.StoreStaffReviewDto;
+import shop.alien.entity.store.vo.StoreStaffReviewDetailVo;
+import shop.alien.entity.store.vo.StoreStaffReviewVo;
+
+/**
+ * 员工评价 服务类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+public interface StoreStaffReviewService extends IService<StoreStaffReview> {
+
+    /**
+     * 创建员工评价
+     *
+     * @param reviewDto 评价DTO
+     * @return R<StoreStaffReview>
+     */
+    R<StoreStaffReview> createReview(StoreStaffReviewDto reviewDto);
+
+    /**
+     * 获取评价详情(包含评论和回复)
+     *
+     * @param reviewId 评价ID
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<StoreStaffReviewDetailVo>
+     */
+    R<StoreStaffReviewDetailVo> getReviewDetail(Integer reviewId, Integer currentUserId);
+
+    /**
+     * 点赞评价
+     *
+     * @param reviewId 评价ID
+     * @param userId 用户ID
+     * @return R<Boolean>
+     */
+    R<Boolean> likeReview(Integer reviewId, Integer userId);
+
+    /**
+     * 取消点赞评价
+     *
+     * @param reviewId 评价ID
+     * @param userId 用户ID
+     * @return R<Boolean>
+     */
+    R<Boolean> cancelLikeReview(Integer reviewId, Integer userId);
+
+    /**
+     * 分页查询评价列表
+     *
+     * @param pageNum 页码
+     * @param pageSize 页大小
+     * @param staffUserId 员工用户ID(可选)
+     * @param userId 评价用户ID(可选)
+     * @param currentUserId 当前用户ID(用于判断是否已点赞,可为null)
+     * @return R<IPage<StoreStaffReviewVo>>
+     */
+    R<IPage<StoreStaffReviewVo>> getReviewList(int pageNum, int pageSize, Integer staffUserId, Integer userId, Integer currentUserId);
+
+    /**
+     * 用户删除评价(只能删除自己的评价,删除评价时,会级联删除该评价下的所有评论和回复)
+     *
+     * @param reviewId 评价ID
+     * @param userId 用户ID(必填,只能删除自己的评价)
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteReview(Integer reviewId, Integer userId);
+
+    /**
+     * 管理员删除评价(可以删除任何评价,删除评价时,会级联删除该评价下的所有评论和回复)
+     *
+     * @param reviewId 评价ID
+     * @return R<Boolean>
+     */
+    R<Boolean> deleteReviewByAdmin(Integer reviewId);
+}
+

+ 519 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffCommentServiceImpl.java

@@ -0,0 +1,519 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeLikeRecord;
+import shop.alien.entity.store.StoreStaffComment;
+import shop.alien.entity.store.StoreStaffReview;
+import shop.alien.entity.store.dto.StoreStaffCommentRequestDto;
+import shop.alien.entity.store.dto.StoreStaffReplyDto;
+import shop.alien.entity.store.vo.StoreStaffCommentVo;
+import shop.alien.mapper.LifeLikeRecordMapper;
+import shop.alien.mapper.StoreStaffCommentMapper;
+import shop.alien.store.service.StoreStaffCommentService;
+import shop.alien.store.service.StoreStaffReviewService;
+
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 员工评论 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+public class StoreStaffCommentServiceImpl extends ServiceImpl<StoreStaffCommentMapper, StoreStaffComment> implements StoreStaffCommentService {
+
+    private static final String LIKE_TYPE_COMMENT = "10"; // 员工评论点赞类型
+    private static final int HEAD_TYPE_COMMENT = 0; // 首评类型
+    private static final int HEAD_TYPE_REPLY = 1; // 回复类型
+
+    private final StoreStaffCommentMapper storeStaffCommentMapper;
+    private final StoreStaffReviewService storeStaffReviewService;
+    private final LifeLikeRecordMapper lifeLikeRecordMapper;
+
+    public StoreStaffCommentServiceImpl(StoreStaffCommentMapper storeStaffCommentMapper,
+                                        @Lazy StoreStaffReviewService storeStaffReviewService,
+                                        LifeLikeRecordMapper lifeLikeRecordMapper) {
+        this.storeStaffCommentMapper = storeStaffCommentMapper;
+        this.storeStaffReviewService = storeStaffReviewService;
+        this.lifeLikeRecordMapper = lifeLikeRecordMapper;
+    }
+
+    @Override
+    public R<StoreStaffComment> createComment(StoreStaffComment comment) {
+        log.info("创建评论, comment={}", comment);
+        
+        // 参数校验
+        R<String> validateResult = validateComment(comment);
+        if (!validateResult.isSuccess()) {
+            return R.fail(validateResult.getMsg());
+        }
+
+        // 验证评价是否存在
+        StoreStaffReview review = storeStaffReviewService.getById(comment.getReviewId());
+        if (review == null || review.getDeleteFlag() == 1) {
+            return R.fail("评价不存在或已删除");
+        }
+
+        // 设置评论属性
+        setCommentDefaults(comment, review);
+
+        boolean success = this.save(comment);
+        if (success) {
+            // 更新评价的评论数
+            updateReviewCommentCount(review.getId(), 1);
+            log.info("创建评论成功, 评论ID={}", comment.getId());
+            return R.data(comment, "评论成功");
+        } else {
+            log.error("创建评论失败");
+            return R.fail("创建评论失败");
+        }
+    }
+
+    @Override
+    public R<List<StoreStaffCommentVo>> getCommentListByReviewId(Integer reviewId, Integer currentUserId) {
+        log.info("根据评价ID查询评论列表, reviewId={}, currentUserId={}", reviewId, currentUserId);
+
+        if (reviewId == null) {
+            return R.fail("评价ID不能为空");
+        }
+
+        List<StoreStaffCommentVo> comments = storeStaffCommentMapper.getCommentListByReviewId(reviewId, currentUserId);
+
+        // 为每个首评加载回复列表
+        if (!CollectionUtils.isEmpty(comments)) {
+            comments.forEach(comment -> {
+                List<StoreStaffCommentVo> replies = storeStaffCommentMapper.getReplyListByHeadId(comment.getId(), currentUserId);
+                comment.setReplyList(replies != null ? replies : new java.util.ArrayList<>());
+            });
+        }
+
+        return R.data(comments);
+    }
+
+    @Override
+    public R<Boolean> likeComment(StoreStaffCommentRequestDto requestDto) {
+        log.info("点赞评论, requestDto={}", requestDto);
+
+        Integer commentId = requestDto.getCommentId();
+        Integer userId = requestDto.getUserId();
+
+        if (commentId == null) {
+            return R.fail("评论ID不能为空");
+        }
+        if (userId == null) {
+            return R.fail("用户ID不能为空");
+        }
+
+        // 验证评论是否存在
+        StoreStaffComment comment = this.getById(commentId);
+        if (comment == null || comment.getDeleteFlag() == 1) {
+            return R.fail("评论不存在或已删除");
+        }
+
+        // 检查是否已点赞
+        if (isLiked(commentId, userId, LIKE_TYPE_COMMENT)) {
+            return R.data(true, "已点赞");
+        }
+
+        // 执行点赞
+        return doLikeComment(commentId, userId);
+    }
+
+    @Override
+    public R<Boolean> cancelLikeComment(StoreStaffCommentRequestDto requestDto) {
+        log.info("取消点赞评论, requestDto={}", requestDto);
+
+        Integer commentId = requestDto.getCommentId();
+        Integer userId = requestDto.getUserId();
+        
+        if (commentId == null) {
+            return R.fail("评论ID不能为空");
+        }
+        if (userId == null) {
+            return R.fail("用户ID不能为空");
+        }
+
+        // 执行取消点赞
+        return doCancelLikeComment(commentId, userId);
+    }
+
+    @Override
+    public R<StoreStaffComment> createReply(StoreStaffReplyDto replyDto) {
+        log.info("创建回复, replyDto={}", replyDto);
+
+        // 参数校验
+        R<String> validateResult = validateReplyDto(replyDto);
+        if (!validateResult.isSuccess()) {
+            return R.fail(validateResult.getMsg());
+        }
+
+        // 验证首评是否存在
+        StoreStaffComment headComment = this.getById(replyDto.getCommentId());
+        if (headComment == null || headComment.getDeleteFlag() == 1) {
+            return R.fail("评论不存在或已删除");
+        }
+        if (headComment.getHeadType() != HEAD_TYPE_COMMENT) {
+            return R.fail("只能回复首评");
+        }
+
+        // 构建回复对象
+        StoreStaffComment reply = buildReplyFromDto(replyDto, headComment);
+
+        boolean success = this.save(reply);
+        if (success) {
+            // 更新首评的回复数
+            updateHeadCommentReplyCount(headComment.getId(), 1);
+            // 更新评价的评论数
+            updateReviewCommentCount(reply.getReviewId(), 1);
+            log.info("创建回复成功, 回复ID={}", reply.getId());
+            return R.data(reply, "回复成功");
+        } else {
+            log.error("创建回复失败");
+            return R.fail("创建回复失败");
+        }
+    }
+
+    @Override
+    public R<List<StoreStaffCommentVo>> getReplyListByHeadId(Integer headId, Integer currentUserId) {
+        log.info("根据首评ID查询回复列表, headId={}, currentUserId={}", headId, currentUserId);
+
+        if (headId == null) {
+            return R.fail("首评ID不能为空");
+        }
+
+        List<StoreStaffCommentVo> replies = storeStaffCommentMapper.getReplyListByHeadId(headId, currentUserId);
+        return R.data(replies);
+    }
+
+    @Override
+    public R<Boolean> deleteReply(Integer replyId, Integer userId) {
+        log.info("删除回复, replyId={}, userId={}", replyId, userId);
+
+        if (replyId == null) {
+            return R.fail("回复ID不能为空");
+        }
+        if (userId == null) {
+            return R.fail("用户ID不能为空");
+        }
+
+        // 查询回复
+        StoreStaffComment reply = this.getById(replyId);
+        if (reply == null) {
+            return R.fail("回复不存在");
+        }
+        if (reply.getHeadType() != HEAD_TYPE_REPLY) {
+            return R.fail("该记录不是回复");
+        }
+
+        // 验证是否为回复用户
+        if (!reply.getSendUserId().equals(userId)) {
+            return R.fail("只能删除自己的回复");
+        }
+
+        // 删除回复(逻辑删除)
+        boolean success = this.removeById(reply.getId());
+
+        if (success) {
+            // 更新首评的回复数
+            updateHeadCommentReplyCount(reply.getHeadId(), -1);
+            // 更新评价的评论数
+            updateReviewCommentCount(reply.getReviewId(), -1);
+            log.info("删除回复成功, replyId={}", replyId);
+            return R.data(true, "删除成功");
+        } else {
+            log.error("删除回复失败, replyId={}", replyId);
+            return R.fail("删除回复失败");
+        }
+    }
+
+    @Override
+    public R<Boolean> deleteReviewComment(StoreStaffComment comment) {
+        Integer id = comment.getId();
+        Integer userId = comment.getUserId();
+        log.info("删除评论, id={}, userId={}", id, userId);
+        
+        if (id == null) {
+            return R.fail("评论ID不能为空");
+        }
+
+        // 查询评论
+        StoreStaffComment commentEntity = this.getById(id);
+        if (commentEntity == null) {
+            return R.fail("评论不存在");
+        }
+
+        // 当userId有值时,验证是否为评论用户
+        if (userId != null && !commentEntity.getSendUserId().equals(userId)) {
+            return R.fail("只能删除自己的评论");
+        }
+
+        // 计算需要减少的评论数
+        int commentCountToReduce = 1;
+
+        // 判断是否为首评论
+        if (commentEntity.getHeadType() != null && commentEntity.getHeadType() == HEAD_TYPE_COMMENT) {
+            // 如果是首评论,需要先删除所有子评论
+            LambdaQueryWrapper<StoreStaffComment> childQueryWrapper = new LambdaQueryWrapper<>();
+            childQueryWrapper.eq(StoreStaffComment::getHeadId, id)
+                    .eq(StoreStaffComment::getHeadType, HEAD_TYPE_REPLY)
+                    .eq(StoreStaffComment::getDeleteFlag, 0);
+            List<StoreStaffComment> childComments = this.list(childQueryWrapper);
+
+            if (!CollectionUtils.isEmpty(childComments)) {
+                List<Integer> childIds = childComments.stream()
+                        .map(StoreStaffComment::getId)
+                        .collect(Collectors.toList());
+                boolean deleteChildrenResult = this.removeByIds(childIds);
+                if (!deleteChildrenResult) {
+                    log.warn("删除子评论失败, 首评论ID={}, 子评论数量={}", id, childIds.size());
+                    return R.fail("删除子评论失败");
+                }
+                commentCountToReduce += childComments.size();
+            }
+        }
+
+        // 删除评论本身
+        boolean result = this.removeById(id);
+        if (result) {
+            // 更新评价的评论数
+            updateReviewCommentCount(commentEntity.getReviewId(), -commentCountToReduce);
+            
+            if (userId != null) {
+                log.info("用户删除评论成功, 评论ID={}, userId={}", id, userId);
+            } else {
+                log.info("管理员删除评论成功, 评论ID={}", id);
+            }
+            return R.success("删除成功");
+        }
+        return R.fail("删除失败");
+    }
+
+    /**
+     * 校验评论参数
+     */
+    private R<String> validateComment(StoreStaffComment comment) {
+        if (comment == null) {
+            return R.fail("评论信息不能为空");
+        }
+        if (comment.getReviewId() == null) {
+            return R.fail("评价ID不能为空");
+        }
+        if (!StringUtils.hasText(comment.getCommentContent())) {
+            return R.fail("评论内容不能为空");
+        }
+        if (comment.getSendUserId() == null) {
+            return R.fail("用户ID不能为空");
+        }
+        if (comment.getSendUserType() == null) {
+            return R.fail("发送用户类型不能为空");
+        }
+        if (comment.getReceiveUserType() == null) {
+            return R.fail("接收用户类型不能为空");
+        }
+        return R.success("校验评论参数成功");
+    }
+
+    /**
+     * 校验回复DTO参数
+     */
+    private R<String> validateReplyDto(StoreStaffReplyDto replyDto) {
+        if (replyDto == null) {
+            return R.fail("回复信息不能为空");
+        }
+        if (replyDto.getCommentId() == null) {
+            return R.fail("评论ID不能为空");
+        }
+        if (!StringUtils.hasText(replyDto.getReplyContent())) {
+            return R.fail("回复内容不能为空");
+        }
+        if (replyDto.getUserId() == null) {
+            return R.fail("用户ID不能为空");
+        }
+        return R.success("校验评论参数成功");
+    }
+
+    /**
+     * 设置评论默认值
+     */
+    private void setCommentDefaults(StoreStaffComment comment, StoreStaffReview review) {
+        if (comment.getReceiveUserId() == null) {
+            comment.setReceiveUserId(review.getUserId());
+        }
+        if (comment.getLikeCount() == null) {
+            comment.setLikeCount(0);
+        }
+        if (comment.getReplyCount() == null) {
+            comment.setReplyCount(0);
+        }
+        if (comment.getHeadType() == null) {
+            comment.setHeadType(HEAD_TYPE_COMMENT);
+        }
+        comment.setCreatedUserId(comment.getSendUserId());
+        comment.setCreatedTime(new Date());
+    }
+
+    /**
+     * 构建回复对象
+     */
+    private StoreStaffComment buildReplyFromDto(StoreStaffReplyDto replyDto, StoreStaffComment headComment) {
+        StoreStaffComment reply = new StoreStaffComment();
+        reply.setReviewId(headComment.getReviewId());
+        reply.setSendUserId(replyDto.getUserId());
+        
+        Integer receiveUserId = replyDto.getReplyToUserId() != null 
+                ? replyDto.getReplyToUserId() 
+                : headComment.getSendUserId();
+        reply.setReceiveUserId(receiveUserId);
+        
+        // 处理用户类型
+        Integer sendUserType = replyDto.getSendUserType();
+        if (sendUserType == null) {
+            sendUserType = headComment.getReceiveUserType();
+        }
+        if (sendUserType == null) {
+            throw new IllegalArgumentException("发送用户类型不能为空");
+        }
+        
+        Integer receiveUserType = replyDto.getReceiveUserType();
+        if (receiveUserType == null) {
+            receiveUserType = headComment.getSendUserType();
+        }
+        if (receiveUserType == null) {
+            throw new IllegalArgumentException("接收用户类型不能为空");
+        }
+        
+        reply.setSendUserType(sendUserType);
+        reply.setReceiveUserType(receiveUserType);
+        reply.setCommentContent(replyDto.getReplyContent());
+        reply.setLikeCount(0);
+        reply.setReplyCount(0);
+        reply.setHeadType(HEAD_TYPE_REPLY);
+        reply.setHeadId(replyDto.getCommentId());
+        reply.setCreatedUserId(replyDto.getUserId());
+        reply.setCreatedTime(new Date());
+        
+        return reply;
+    }
+
+    /**
+     * 检查是否已点赞
+     */
+    private boolean isLiked(Integer commentId, Integer userId, String type) {
+        LambdaQueryWrapper<LifeLikeRecord> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LifeLikeRecord::getType, type)
+                .eq(LifeLikeRecord::getDianzanId, String.valueOf(userId))
+                .eq(LifeLikeRecord::getHuifuId, String.valueOf(commentId))
+                .eq(LifeLikeRecord::getDeleteFlag, 0);
+        List<LifeLikeRecord> records = lifeLikeRecordMapper.selectList(queryWrapper);
+        return !CollectionUtils.isEmpty(records);
+    }
+
+    /**
+     * 执行点赞评论
+     */
+    private R<Boolean> doLikeComment(Integer commentId, Integer userId) {
+        try {
+            // 插入点赞记录
+            LifeLikeRecord likeRecord = new LifeLikeRecord();
+            likeRecord.setDianzanId(String.valueOf(userId));
+            likeRecord.setHuifuId(String.valueOf(commentId));
+            likeRecord.setType(LIKE_TYPE_COMMENT);
+            likeRecord.setCreatedTime(new Date());
+            likeRecord.setCreatedUserId(userId);
+            lifeLikeRecordMapper.insert(likeRecord);
+
+            // 更新评论点赞数
+            LambdaUpdateWrapper<StoreStaffComment> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.eq(StoreStaffComment::getId, commentId);
+            updateWrapper.setSql("like_count = like_count + 1");
+            int result = storeStaffCommentMapper.update(null, updateWrapper);
+
+            if (result > 0) {
+                log.info("点赞评论成功, commentId={}", commentId);
+                return R.data(true, "点赞成功");
+            } else {
+                return R.fail("点赞失败");
+            }
+        } catch (Exception e) {
+            log.error("点赞评论异常, commentId={}, userId={}, error={}", commentId, userId, e.getMessage(), e);
+            return R.fail("点赞失败");
+        }
+    }
+
+    /**
+     * 执行取消点赞评论
+     */
+    private R<Boolean> doCancelLikeComment(Integer commentId, Integer userId) {
+        try {
+            // 查询点赞记录
+            LambdaQueryWrapper<LifeLikeRecord> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(LifeLikeRecord::getType, LIKE_TYPE_COMMENT)
+                    .eq(LifeLikeRecord::getDianzanId, String.valueOf(userId))
+                    .eq(LifeLikeRecord::getHuifuId, String.valueOf(commentId))
+                    .eq(LifeLikeRecord::getDeleteFlag, 0);
+            List<LifeLikeRecord> records = lifeLikeRecordMapper.selectList(queryWrapper);
+
+            if (CollectionUtils.isEmpty(records)) {
+                return R.data(true, "未点赞");
+            }
+
+            // 删除点赞记录(逻辑删除)
+            records.forEach(record -> lifeLikeRecordMapper.deleteById(record.getId()));
+
+            // 更新评论点赞数
+            LambdaUpdateWrapper<StoreStaffComment> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.eq(StoreStaffComment::getId, commentId);
+            updateWrapper.setSql("like_count = GREATEST(0, like_count - 1)");
+            int result = storeStaffCommentMapper.update(null, updateWrapper);
+
+            if (result > 0) {
+                log.info("取消点赞评论成功, commentId={}", commentId);
+                return R.data(true, "取消点赞成功");
+            } else {
+                return R.fail("取消点赞失败");
+            }
+        } catch (Exception e) {
+            log.error("取消点赞评论异常, commentId={}, userId={}, error={}", commentId, userId, e.getMessage(), e);
+            return R.fail("取消点赞失败");
+        }
+    }
+
+    /**
+     * 更新首评的回复数
+     */
+    private void updateHeadCommentReplyCount(Integer headId, int delta) {
+        StoreStaffComment headComment = this.getById(headId);
+        if (headComment != null) {
+            Integer currentCount = headComment.getReplyCount() == null ? 0 : headComment.getReplyCount();
+            headComment.setReplyCount(Math.max(0, currentCount + delta));
+            this.updateById(headComment);
+        }
+    }
+
+    /**
+     * 更新评价的评论数
+     */
+    private void updateReviewCommentCount(Integer reviewId, int delta) {
+        StoreStaffReview review = storeStaffReviewService.getById(reviewId);
+        if (review != null) {
+            Integer currentCount = review.getCommentCount() == null ? 0 : review.getCommentCount();
+            review.setCommentCount(Math.max(0, currentCount + delta));
+            storeStaffReviewService.updateById(review);
+            log.debug("更新评价评论数, reviewId={}, delta={}, 当前评论数={}", reviewId, delta, review.getCommentCount());
+        }
+    }
+}

+ 414 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreStaffReviewServiceImpl.java

@@ -0,0 +1,414 @@
+package shop.alien.store.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.LifeLikeRecord;
+import shop.alien.entity.store.StoreStaffComment;
+import shop.alien.entity.store.StoreStaffReview;
+import shop.alien.entity.store.dto.StoreStaffReviewDto;
+import shop.alien.entity.store.vo.StoreStaffCommentVo;
+import shop.alien.entity.store.vo.StoreStaffReviewDetailVo;
+import shop.alien.entity.store.vo.StoreStaffReviewVo;
+import shop.alien.mapper.LifeLikeRecordMapper;
+import shop.alien.mapper.StoreStaffCommentMapper;
+import shop.alien.mapper.StoreStaffReviewMapper;
+import shop.alien.store.service.StoreStaffCommentService;
+import shop.alien.store.service.StoreStaffReviewService;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 员工评价 服务实现类
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class StoreStaffReviewServiceImpl extends ServiceImpl<StoreStaffReviewMapper, StoreStaffReview> implements StoreStaffReviewService {
+
+    private static final String LIKE_TYPE_REVIEW = "9"; // 员工评价点赞类型
+
+    private final StoreStaffReviewMapper storeStaffReviewMapper;
+    private final StoreStaffCommentService storeStaffCommentService;
+    private final StoreStaffCommentMapper storeStaffCommentMapper;
+    private final LifeLikeRecordMapper lifeLikeRecordMapper;
+
+    @Override
+    public R<StoreStaffReview> createReview(StoreStaffReviewDto reviewDto) {
+        log.info("创建员工评价, reviewDto={}", reviewDto);
+
+        // 参数校验
+        R<String> validateResult = validateReviewDto(reviewDto);
+        if (!validateResult.isSuccess()) {
+            return R.fail(validateResult.getMsg());
+        }
+
+        // 创建评价
+        StoreStaffReview review = buildReviewFromDto(reviewDto);
+        boolean success = this.save(review);
+        
+        if (success) {
+            log.info("创建评价成功, 评价ID={}", review.getId());
+            return R.data(review, "提交成功");
+        } else {
+            log.error("创建评价失败");
+            return R.fail("创建评价失败");
+        }
+    }
+
+    @Override
+    public R<IPage<StoreStaffReviewVo>> getReviewList(int pageNum, int pageSize, Integer staffUserId, Integer userId, Integer currentUserId) {
+        log.info("分页查询评价列表, pageNum={}, pageSize={}, staffUserId={}, userId={}, currentUserId={}",
+                pageNum, pageSize, staffUserId, userId, currentUserId);
+
+        Page<StoreStaffReviewVo> page = new Page<>(pageNum, pageSize);
+        IPage<StoreStaffReviewVo> result = storeStaffReviewMapper.getReviewListWithUser(page, staffUserId, userId, currentUserId);
+
+        // 处理评价图片JSON字符串转换为列表
+        result.getRecords().forEach(this::parseReviewImages);
+
+        return R.data(result);
+    }
+
+    @Override
+    public R<StoreStaffReviewDetailVo> getReviewDetail(Integer reviewId, Integer currentUserId) {
+        log.info("获取评价详情, reviewId={}, currentUserId={}", reviewId, currentUserId);
+
+        if (reviewId == null) {
+            return R.fail("评价ID不能为空");
+        }
+
+        // 查询评价详情
+        StoreStaffReviewVo reviewVo = storeStaffReviewMapper.getReviewDetailById(reviewId, currentUserId);
+        if (reviewVo == null) {
+            return R.fail("评价不存在");
+        }
+
+        // 处理评价图片
+        parseReviewImages(reviewVo);
+
+        // 查询评论列表
+        List<StoreStaffCommentVo> comments = storeStaffCommentMapper.getCommentListByReviewId(reviewId, currentUserId);
+
+        // 为每个评论查询回复列表
+        if (!CollectionUtils.isEmpty(comments)) {
+            comments.forEach(comment -> {
+                List<StoreStaffCommentVo> replies = storeStaffCommentMapper.getReplyListByHeadId(comment.getId(), currentUserId);
+                comment.setReplyList(replies != null ? replies : new ArrayList<>());
+            });
+        }
+
+        // 构建返回结果
+        StoreStaffReviewDetailVo detailVo = new StoreStaffReviewDetailVo();
+        detailVo.setReview(reviewVo);
+        detailVo.setCommentList(comments != null ? comments : new ArrayList<>());
+
+        return R.data(detailVo);
+    }
+
+    @Override
+    public R<Boolean> likeReview(Integer reviewId, Integer userId) {
+        log.info("点赞评价, reviewId={}, userId={}", reviewId, userId);
+
+        // 参数校验
+        R<String> validateResult = validateLikeParams(reviewId, userId);
+        if (!validateResult.isSuccess()) {
+            return R.fail(validateResult.getMsg());
+        }
+
+        // 验证评价是否存在
+        StoreStaffReview review = this.getById(reviewId);
+        if (review == null || review.getDeleteFlag() == 1) {
+            return R.fail("评价不存在或已删除");
+        }
+
+        // 检查是否已点赞
+        if (isLiked(reviewId, userId, LIKE_TYPE_REVIEW)) {
+            return R.data(true, "已点赞");
+        }
+
+        // 执行点赞
+        return doLike(reviewId, userId, LIKE_TYPE_REVIEW, review);
+    }
+
+    @Override
+    public R<Boolean> cancelLikeReview(Integer reviewId, Integer userId) {
+        log.info("取消点赞评价, reviewId={}, userId={}", reviewId, userId);
+
+        // 参数校验
+        R<String> validateResult = validateLikeParams(reviewId, userId);
+        if (!validateResult.isSuccess()) {
+            return R.fail(validateResult.getMsg());
+        }
+
+        // 执行取消点赞
+        return doCancelLike(reviewId, userId, LIKE_TYPE_REVIEW, StoreStaffReview.class);
+    }
+
+    @Override
+    public R<Boolean> deleteReview(Integer reviewId, Integer userId) {
+        log.info("用户删除评价, reviewId={}, userId={}", reviewId, userId);
+
+        if (reviewId == null) {
+            return R.fail("评价ID不能为空");
+        }
+        if (userId == null) {
+            return R.fail("用户ID不能为空");
+        }
+
+        // 查询评价
+        StoreStaffReview review = this.getById(reviewId);
+        if (review == null) {
+            return R.fail("评价不存在");
+        }
+
+        // 验证是否为评价用户
+        if (!review.getUserId().equals(userId)) {
+            return R.fail("只能删除自己的评价");
+        }
+
+        return deleteReviewInternal(reviewId, userId, review);
+    }
+
+    @Override
+    public R<Boolean> deleteReviewByAdmin(Integer reviewId) {
+        log.info("管理员删除评价, reviewId={}", reviewId);
+
+        if (reviewId == null) {
+            return R.fail("评价ID不能为空");
+        }
+
+        // 查询评价
+        StoreStaffReview review = this.getById(reviewId);
+        if (review == null) {
+            return R.fail("评价不存在");
+        }
+
+        return deleteReviewInternal(reviewId, null, review);
+    }
+
+    /**
+     * 校验评价DTO参数
+     */
+    private R<String> validateReviewDto(StoreStaffReviewDto reviewDto) {
+        if (reviewDto == null) {
+            return R.fail("评价信息不能为空");
+        }
+        if (reviewDto.getUserId() == null) {
+            return R.fail("用户ID不能为空");
+        }
+        if (reviewDto.getStaffUserId() == null) {
+            return R.fail("员工用户ID不能为空");
+        }
+        return R.success("校验评论参数成功");
+    }
+
+    /**
+     * 构建评价对象
+     */
+    private StoreStaffReview buildReviewFromDto(StoreStaffReviewDto reviewDto) {
+        StoreStaffReview review = new StoreStaffReview();
+        review.setUserId(reviewDto.getUserId());
+        review.setStaffUserId(reviewDto.getStaffUserId());
+        review.setOverallRating(reviewDto.getOverallRating());
+        review.setServiceAttitudeRating(reviewDto.getServiceAttitudeRating());
+        review.setResponseTimeRating(reviewDto.getResponseTimeRating());
+        review.setProfessionalAbilityRating(reviewDto.getProfessionalAbilityRating());
+        review.setReviewContent(reviewDto.getReviewContent());
+        review.setIsAnonymous(reviewDto.getIsAnonymous() != null ? reviewDto.getIsAnonymous() : 0);
+        review.setLikeCount(0);
+        review.setCommentCount(0);
+        review.setCreatedUserId(reviewDto.getUserId());
+        review.setCreatedTime(new Date());
+
+        // 处理评价图片
+        if (reviewDto.getReviewImages() != null && !reviewDto.getReviewImages().isEmpty()) {
+            JSONArray jsonArray = new JSONArray();
+            jsonArray.addAll(reviewDto.getReviewImages());
+            review.setReviewImages(jsonArray.toString());
+        }
+
+        return review;
+    }
+
+    /**
+     * 解析评价图片JSON字符串为列表
+     */
+    private void parseReviewImages(StoreStaffReviewVo vo) {
+        if (vo.getReviewImagesJson() != null && !vo.getReviewImagesJson().trim().isEmpty()) {
+            try {
+                List<String> images = JSON.parseArray(vo.getReviewImagesJson(), String.class);
+                vo.setReviewImages(images != null ? images : new ArrayList<>());
+            } catch (Exception e) {
+                log.warn("解析评价图片失败, reviewId={}, error={}", vo.getId(), e.getMessage());
+                vo.setReviewImages(new ArrayList<>());
+            }
+        } else {
+            vo.setReviewImages(new ArrayList<>());
+        }
+    }
+
+    /**
+     * 校验点赞参数
+     */
+    private R<String> validateLikeParams(Integer id, Integer userId) {
+        if (id == null) {
+            return R.fail("ID不能为空");
+        }
+        if (userId == null) {
+            return R.fail("用户ID不能为空");
+        }
+        return R.success("校验评论参数成功");
+    }
+
+    /**
+     * 检查是否已点赞
+     */
+    private boolean isLiked(Integer targetId, Integer userId, String type) {
+        LambdaQueryWrapper<LifeLikeRecord> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(LifeLikeRecord::getType, type)
+                .eq(LifeLikeRecord::getDianzanId, String.valueOf(userId))
+                .eq(LifeLikeRecord::getHuifuId, String.valueOf(targetId))
+                .eq(LifeLikeRecord::getDeleteFlag, 0);
+        List<LifeLikeRecord> records = lifeLikeRecordMapper.selectList(queryWrapper);
+        return !CollectionUtils.isEmpty(records);
+    }
+
+    /**
+     * 执行点赞操作
+     */
+    private R<Boolean> doLike(Integer reviewId, Integer userId, String type, StoreStaffReview review) {
+        try {
+            // 插入点赞记录
+            LifeLikeRecord likeRecord = new LifeLikeRecord();
+            likeRecord.setDianzanId(String.valueOf(userId));
+            likeRecord.setHuifuId(String.valueOf(reviewId));
+            likeRecord.setType(type);
+            likeRecord.setCreatedTime(new Date());
+            likeRecord.setCreatedUserId(userId);
+            lifeLikeRecordMapper.insert(likeRecord);
+
+            // 更新点赞数
+            LambdaUpdateWrapper<StoreStaffReview> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.eq(StoreStaffReview::getId, reviewId);
+            updateWrapper.setSql("like_count = like_count + 1");
+            int result = storeStaffReviewMapper.update(null, updateWrapper);
+
+            if (result > 0) {
+                log.info("点赞成功, reviewId={}", reviewId);
+                return R.data(true, "点赞成功");
+            } else {
+                return R.fail("点赞失败");
+            }
+        } catch (Exception e) {
+            log.error("点赞操作异常, reviewId={}, userId={}, error={}", reviewId, userId, e.getMessage(), e);
+            return R.fail("点赞失败");
+        }
+    }
+
+    /**
+     * 执行取消点赞操作
+     */
+    private <T> R<Boolean> doCancelLike(Integer targetId, Integer userId, String type, Class<T> entityClass) {
+        try {
+            // 查询点赞记录
+            LambdaQueryWrapper<LifeLikeRecord> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(LifeLikeRecord::getType, type)
+                    .eq(LifeLikeRecord::getDianzanId, String.valueOf(userId))
+                    .eq(LifeLikeRecord::getHuifuId, String.valueOf(targetId))
+                    .eq(LifeLikeRecord::getDeleteFlag, 0);
+            List<LifeLikeRecord> records = lifeLikeRecordMapper.selectList(queryWrapper);
+
+            if (CollectionUtils.isEmpty(records)) {
+                return R.data(true, "未点赞");
+            }
+
+            // 删除点赞记录(逻辑删除)
+            records.forEach(record -> lifeLikeRecordMapper.deleteById(record.getId()));
+
+            // 更新点赞数
+            if (entityClass == StoreStaffReview.class) {
+                LambdaUpdateWrapper<StoreStaffReview> updateWrapper = new LambdaUpdateWrapper<>();
+                updateWrapper.eq(StoreStaffReview::getId, targetId);
+                updateWrapper.setSql("like_count = GREATEST(0, like_count - 1)");
+                storeStaffReviewMapper.update(null, updateWrapper);
+            }
+
+            log.info("取消点赞成功, targetId={}", targetId);
+            return R.data(true, "取消点赞成功");
+        } catch (Exception e) {
+            log.error("取消点赞操作异常, targetId={}, userId={}, error={}", targetId, userId, e.getMessage(), e);
+            return R.fail("取消点赞失败");
+        }
+    }
+
+    /**
+     * 内部删除评价方法
+     */
+    private R<Boolean> deleteReviewInternal(Integer reviewId, Integer userId, StoreStaffReview review) {
+        // 查询该评价下的所有评论
+        LambdaQueryWrapper<StoreStaffComment> commentWrapper = new LambdaQueryWrapper<>();
+        commentWrapper.eq(StoreStaffComment::getReviewId, reviewId)
+                .eq(StoreStaffComment::getDeleteFlag, 0);
+        List<StoreStaffComment> comments = storeStaffCommentService.list(commentWrapper);
+
+        // 删除评价(逻辑删除)
+        int num = storeStaffReviewMapper.deleteById(reviewId);
+
+        if (num > 0) {
+            // 级联删除该评价下的所有评论和回复
+            comments.forEach(comment -> deleteCommentAndReplies(comment, userId));
+
+            if (userId != null) {
+                log.info("用户删除评价成功, reviewId={}, userId={}", reviewId, userId);
+            } else {
+                log.info("管理员删除评价成功, reviewId={}", reviewId);
+            }
+            return R.data(true, "删除成功");
+        } else {
+            log.error("删除评价失败, reviewId={}", reviewId);
+            return R.fail("删除评价失败");
+        }
+    }
+
+    /**
+     * 删除评论及其回复
+     */
+    private void deleteCommentAndReplies(StoreStaffComment comment, Integer userId) {
+        // 删除评论下的所有回复(逻辑删除)
+        LambdaUpdateWrapper<StoreStaffComment> replyUpdateWrapper = new LambdaUpdateWrapper<>();
+        replyUpdateWrapper.eq(StoreStaffComment::getHeadId, comment.getId())
+                .eq(StoreStaffComment::getHeadType, 1) // 1表示回复类型
+                .eq(StoreStaffComment::getDeleteFlag, 0);
+        replyUpdateWrapper.set(StoreStaffComment::getDeleteFlag, 1);
+        if (userId != null) {
+            replyUpdateWrapper.set(StoreStaffComment::getUpdatedUserId, userId);
+        }
+        replyUpdateWrapper.set(StoreStaffComment::getUpdatedTime, new Date());
+        storeStaffCommentService.update(replyUpdateWrapper);
+
+        // 删除评论
+        comment.setDeleteFlag(1);
+        if (userId != null) {
+            comment.setUpdatedUserId(userId);
+        }
+        comment.setUpdatedTime(new Date());
+        storeStaffCommentService.updateById(comment);
+    }
+}