Browse Source

Merge remote-tracking branch 'origin/sit-shenzhen' into sit-shenzhen

# Conflicts:
#	alien-entity/src/main/java/shop/alien/entity/store/LifeFeedback.java
#	alien-store/src/main/java/shop/alien/store/controller/LifeFeedbackController.java
#	alien-store/src/main/java/shop/alien/store/service/LifeFeedbackService.java
#	alien-store/src/main/java/shop/alien/store/service/impl/LifeFeedbackServiceImpl.java
panzhilin 1 week ago
parent
commit
2a036f2110

+ 9 - 24
alien-entity/src/main/java/shop/alien/entity/store/LifeFeedback.java

@@ -28,15 +28,13 @@ public class LifeFeedback implements Serializable {
     @TableField("user_id")
     private Integer userId;
 
-    @ApiModelProperty(value = "反馈来源:1-用户端,2-商家端")
+    @ApiModelProperty(value = "反馈来源:0-用户端,1-商家端")
     @TableField("feedback_source")
     private Integer feedbackSource;
 
-    @ApiModelProperty(value = "反馈方式:1-主动反馈,2-平台回复")
     @TableField("feedback_way")
     private Integer feedbackWay;
 
-    @ApiModelProperty(value = "反馈类型:1-优化建议,2-问题")
     @TableField("feedback_type")
     private Integer feedbackType;
 
@@ -53,35 +51,22 @@ public class LifeFeedback implements Serializable {
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private Date feedbackTime;
 
-    @ApiModelProperty(value = "跟进工作人员ID(关联life_sys)")
-    @TableField("follow_up_staff")
-    private Integer followUpStaff;
+    @ApiModelProperty(value = "工作人员ID(关联life_sys)")
+    @TableField("staff_id")
+    private Integer staffId;
 
-    @ApiModelProperty(value = "处理状态:0-待处理,1-处理中,2-已完成")
+    @ApiModelProperty(value = "处理状态:0-处理中,1-已解决,2-未分配,3-无需解决")
     @TableField("handle_status")
     private Integer handleStatus;
 
-    @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
-    @TableField("delete_flag")
-    @TableLogic
-    private Integer deleteFlag;
-
     @ApiModelProperty(value = "创建时间")
-    @TableField(value = "created_time", fill = FieldFill.INSERT)
+    @TableField(value = "create_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;
+    private Date createTime;
 
     @ApiModelProperty(value = "修改时间")
-    @TableField(value = "updated_time", fill = FieldFill.UPDATE)
+    @TableField(value = "update_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;
+    private Date updateTime;
 }
 

+ 50 - 0
alien-entity/src/main/java/shop/alien/entity/store/LifeFeedbackReply.java

@@ -0,0 +1,50 @@
+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.io.Serializable;
+import java.util.Date;
+
+/**
+ * 反馈回复表
+ */
+@Data
+@JsonInclude
+@TableName("life_feedback_reply")
+@ApiModel(value = "LifeFeedbackReply对象", description = "反馈回复")
+public class LifeFeedbackReply implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "回复ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "关联的反馈ID")
+    @TableField("feedback_id")
+    private Integer feedbackId;
+
+    @ApiModelProperty(value = "回复类型:0-平台回复,1-我的回复")
+    @TableField("reply_type")
+    private Integer replyType;
+
+    @ApiModelProperty(value = "回复内容")
+    @TableField("reply_content")
+    private String replyContent;
+
+    @ApiModelProperty(value = "创建时间")
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    @ApiModelProperty(value = "更新时间")
+    @TableField(value = "update_time", fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+}
+
+

+ 8 - 0
alien-entity/src/main/java/shop/alien/entity/store/LifeLog.java

@@ -19,8 +19,16 @@ public class LifeLog {
     @TableId(value = "id", type = IdType.AUTO)
     private String id;
 
+    @ApiModelProperty(value = "反馈ID(关联life_feedback)")
+    @TableField("feedback_id")
+    private Integer feedbackId;
+
     private String context;
 
+    @ApiModelProperty(value = "操作类型:0-创建反馈工单,1-分配跟踪人员,2-回复用户,3-问题解决状态")
+    @TableField("type")
+    private String type;
+
     @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
     @TableField("delete_flag")
     @TableLogic

+ 33 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/UserReplyDto.java

@@ -0,0 +1,33 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 用户回复DTO
+ */
+@Data
+@ApiModel(value = "UserReplyDto对象", description = "用户回复DTO")
+public class UserReplyDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "用户ID", required = true)
+    private Integer userId;
+
+    @ApiModelProperty(value = "反馈来源:0-用户端,1-商家端", required = true)
+    private Integer feedbackSource;
+
+    @ApiModelProperty(value = "原始反馈ID(反馈详情页的反馈ID)", required = true)
+    private Integer feedbackId;
+
+    @ApiModelProperty(value = "回复内容", required = true)
+    private String content;
+
+    @ApiModelProperty(value = "文件URL列表(图片和视频,系统会自动识别类型。视频会自动匹配封面图)")
+    private List<String> fileUrlList;
+}
+

+ 23 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/FeedbackTypeVo.java

@@ -0,0 +1,23 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 反馈类型VO
+ */
+@Data
+@ApiModel(value = "FeedbackTypeVo对象", description = "反馈类型VO")
+public class FeedbackTypeVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "反馈类型值:0-bug反馈,1-优化反馈,2-新增功能反馈")
+    private Integer value;
+
+    @ApiModelProperty(value = "反馈类型名称")
+    private String label;
+}
+

+ 24 - 0
alien-entity/src/main/java/shop/alien/mapper/LifeFeedbackReplyMapper.java

@@ -0,0 +1,24 @@
+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.LifeFeedbackReply;
+
+import java.util.List;
+
+/**
+ * 反馈回复 Mapper 接口
+ */
+@Mapper
+public interface LifeFeedbackReplyMapper extends BaseMapper<LifeFeedbackReply> {
+
+    /**
+     * 根据反馈ID查询回复列表
+     * @param feedbackId 反馈ID
+     * @return 回复列表
+     */
+    List<LifeFeedbackReply> selectByFeedbackId(@Param("feedbackId") Integer feedbackId);
+}
+
+

+ 119 - 0
alien-entity/src/main/resources/mapper/LifeFeedbackMapper.xml

@@ -0,0 +1,119 @@
+<?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.LifeFeedbackMapper">
+
+    <!-- 通用结果映射 -->
+    <resultMap id="BaseResultMap" type="shop.alien.entity.store.LifeFeedback">
+        <id column="id" property="id" />
+        <result column="user_id" property="userId" />
+        <result column="feedback_source" property="feedbackSource" />
+        <result column="feedback_way" property="feedbackWay" />
+        <result column="feedback_type" property="feedbackType" />
+        <result column="content" property="content" />
+        <result column="contact_way" property="contactWay" />
+        <result column="feedback_time" property="feedbackTime" />
+        <result column="follow_up_staff" property="followUpStaff" />
+        <result column="handle_status" property="handleStatus" />
+        <result column="delete_flag" property="deleteFlag" />
+        <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" />
+    </resultMap>
+
+    <!-- 基础字段 -->
+    <sql id="Base_Column_List">
+        id, user_id, feedback_source, feedback_way, feedback_type, 
+        content, contact_way, feedback_time, follow_up_staff, handle_status,
+        delete_flag, created_time, created_user_id, updated_time, updated_user_id
+    </sql>
+
+    <!-- 查询用户反馈列表(带工作人员名称) -->
+    <select id="selectFeedbackListWithStaff" resultType="shop.alien.entity.store.vo.LifeFeedbackVo">
+        SELECT 
+            f.id,
+            f.user_id AS userId,
+            f.feedback_source AS feedbackSource,
+            f.feedback_way AS feedbackWay,
+            f.feedback_type AS feedbackType,
+            f.content,
+            f.contact_way AS contactWay,
+            f.feedback_time AS feedbackTime,
+            f.follow_up_staff AS followUpStaff,
+            f.handle_status AS handleStatus,
+            s.user_name AS staffName
+        FROM life_feedback f
+        LEFT JOIN life_sys s ON f.follow_up_staff = s.id
+        WHERE f.delete_flag = 0
+        <if test="userId != null">
+            AND f.user_id = #{userId}
+        </if>
+        <if test="feedbackSource != null">
+            AND f.feedback_source = #{feedbackSource}
+        </if>
+        <if test="feedbackWay != null">
+            AND f.feedback_way = #{feedbackWay}
+        </if>
+        <if test="handleStatus != null">
+            AND f.handle_status = #{handleStatus}
+        </if>
+        ORDER BY f.feedback_time DESC
+    </select>
+
+    <!-- 查询反馈详情(带工作人员名称和图片) -->
+    <select id="selectFeedbackDetail" resultType="shop.alien.entity.store.vo.LifeFeedbackVo">
+        SELECT 
+            f.id,
+            f.user_id AS userId,
+            f.feedback_source AS feedbackSource,
+            f.feedback_way AS feedbackWay,
+            f.feedback_type AS feedbackType,
+            f.content,
+            f.contact_way AS contactWay,
+            f.feedback_time AS feedbackTime,
+            f.follow_up_staff AS followUpStaff,
+            f.handle_status AS handleStatus,
+            s.user_name AS staffName
+        FROM life_feedback f
+        LEFT JOIN life_sys s ON f.follow_up_staff = s.id
+        WHERE f.id = #{feedbackId} AND f.delete_flag = 0
+    </select>
+
+    <!-- 统计待处理反馈数量 -->
+    <select id="countPendingFeedback" resultType="java.lang.Integer">
+        SELECT COUNT(1)
+        FROM life_feedback
+        WHERE delete_flag = 0
+        AND handle_status = 0
+        <if test="feedbackSource != null">
+            AND feedback_source = #{feedbackSource}
+        </if>
+    </select>
+
+    <!-- 查询平台回复列表 -->
+    <select id="selectPlatformReplies" resultType="shop.alien.entity.store.vo.LifeFeedbackVo">
+        SELECT 
+            f.id,
+            f.user_id AS userId,
+            f.feedback_source AS feedbackSource,
+            f.feedback_way AS feedbackWay,
+            f.feedback_type AS feedbackType,
+            f.content,
+            f.feedback_time AS feedbackTime,
+            f.follow_up_staff AS followUpStaff,
+            f.handle_status AS handleStatus,
+            s.user_name AS staffName
+        FROM life_feedback f
+        LEFT JOIN life_sys s ON f.follow_up_staff = s.id
+        WHERE f.user_id = #{userId}
+        AND f.feedback_source = #{feedbackSource}
+        AND f.feedback_way = 2
+        AND f.feedback_time >= #{startTime}
+        AND f.delete_flag = 0
+        ORDER BY f.feedback_time ASC
+    </select>
+
+</mapper>
+

+ 33 - 0
alien-entity/src/main/resources/mapper/LifeFeedbackReplyMapper.xml

@@ -0,0 +1,33 @@
+<?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.LifeFeedbackReplyMapper">
+
+    <!-- 通用结果映射 -->
+    <resultMap id="BaseResultMap" type="shop.alien.entity.store.LifeFeedbackReply">
+        <id column="id" property="id" />
+        <result column="feedback_id" property="feedbackId" />
+        <result column="reply_type" property="replyType" />
+        <result column="reply_content" property="replyContent" />
+        <result column="create_time" property="createTime" />
+        <result column="update_time" property="updateTime" />
+    </resultMap>
+
+    <!-- 基础字段 -->
+    <sql id="Base_Column_List">
+        id, feedback_id, reply_type, reply_content, create_time, update_time
+    </sql>
+
+    <!-- 根据反馈ID查询回复列表 -->
+    <select id="selectByFeedbackId" resultMap="BaseResultMap">
+        SELECT 
+            <include refid="Base_Column_List" />
+        FROM life_feedback_reply
+        WHERE feedback_id = #{feedbackId}
+        ORDER BY create_time ASC
+    </select>
+
+</mapper>
+
+

+ 71 - 0
alien-entity/src/main/resources/mapper/LifeImgMapper.xml

@@ -0,0 +1,71 @@
+<?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.LifeImgMapper">
+
+    <!-- 通用结果映射 -->
+    <resultMap id="BaseResultMap" type="shop.alien.entity.store.LifeImg">
+        <id column="id" property="id" />
+        <result column="feedback_id" property="feedbackId" />
+        <result column="img_url" property="imgUrl" />
+        <result column="img_sort" property="imgSort" />
+        <result column="delete_flag" property="deleteFlag" />
+        <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" />
+    </resultMap>
+
+    <!-- 基础字段 -->
+    <sql id="Base_Column_List">
+        id, feedback_id, img_url, img_sort, 
+        delete_flag, created_time, created_user_id, updated_time, updated_user_id
+    </sql>
+
+    <!-- 根据反馈ID查询图片列表 -->
+    <select id="selectByFeedbackId" resultMap="BaseResultMap">
+        SELECT 
+            <include refid="Base_Column_List" />
+        FROM life_img
+        WHERE feedback_id = #{feedbackId}
+        AND delete_flag = 0
+        ORDER BY img_sort ASC
+    </select>
+
+    <!-- 批量插入图片 -->
+    <insert id="batchInsert" parameterType="java.util.List">
+        INSERT INTO life_img (
+            feedback_id, img_url, img_sort, 
+            delete_flag, created_time, created_user_id
+        ) VALUES
+        <foreach collection="list" item="item" separator=",">
+            (
+                #{item.feedbackId},
+                #{item.imgUrl},
+                #{item.imgSort},
+                0,
+                NOW(),
+                #{item.createdUserId}
+            )
+        </foreach>
+    </insert>
+
+    <!-- 根据反馈ID删除图片 -->
+    <delete id="deleteByFeedbackId">
+        UPDATE life_img 
+        SET delete_flag = 1
+        WHERE feedback_id = #{feedbackId}
+    </delete>
+
+    <!-- 查询反馈的图片URL列表 -->
+    <select id="selectImgUrlsByFeedbackId" resultType="java.lang.String">
+        SELECT img_url
+        FROM life_img
+        WHERE feedback_id = #{feedbackId}
+        AND delete_flag = 0
+        ORDER BY img_sort ASC
+    </select>
+
+</mapper>
+

+ 6 - 22
alien-store/src/main/java/shop/alien/store/controller/LifeFeedbackController.java

@@ -6,8 +6,8 @@ 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.dto.FeedbackReplyDto;
 import shop.alien.entity.store.dto.LifeFeedbackDto;
+import shop.alien.entity.store.dto.UserReplyDto;
 import shop.alien.entity.store.vo.LifeFeedbackVo;
 import shop.alien.store.service.LifeFeedbackService;
 
@@ -31,13 +31,6 @@ public class LifeFeedbackController {
         return lifeFeedbackService.submitFeedback(dto);
     }
 
-    @ApiOperation(value = "平台回复反馈", httpMethod = "POST")
-    @PostMapping("/reply")
-    public R<String> replyFeedback(@RequestBody FeedbackReplyDto dto) {
-        log.info("LifeFeedbackController.replyFeedback, dto={}", dto);
-        return lifeFeedbackService.replyFeedback(dto);
-    }
-
     @ApiOperation(value = "查询用户历史反馈列表", httpMethod = "GET")
     @ApiImplicitParams({
             @ApiImplicitParam(name = "userId", value = "用户ID", dataType = "Integer", paramType = "query", required = true),
@@ -67,20 +60,11 @@ public class LifeFeedbackController {
         return lifeFeedbackService.getFeedbackDetail(feedbackId);
     }
 
-    @ApiOperation(value = "更新反馈处理状态", httpMethod = "POST")
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "feedbackId", value = "反馈ID", dataType = "Integer", paramType = "query", required = true),
-            @ApiImplicitParam(name = "handleStatus", value = "处理状态:0-待处理,1-处理中,2-已完成", dataType = "Integer", paramType = "query", required = true),
-            @ApiImplicitParam(name = "staffId", value = "跟进工作人员ID", dataType = "Integer", paramType = "query", required = false)
-    })
-    @PostMapping("/updateStatus")
-    public R<String> updateHandleStatus(
-            @RequestParam("feedbackId") Integer feedbackId,
-            @RequestParam("handleStatus") Integer handleStatus,
-            @RequestParam(value = "staffId", required = false) Integer staffId) {
-        log.info("LifeFeedbackController.updateHandleStatus, feedbackId={}, handleStatus={}, staffId={}", 
-                feedbackId, handleStatus, staffId);
-        return lifeFeedbackService.updateHandleStatus(feedbackId, handleStatus, staffId);
+    @ApiOperation(value = "用户回复反馈", httpMethod = "POST")
+    @PostMapping("/userReply")
+    public R<String> userReply(@RequestBody UserReplyDto dto) {
+        log.info("LifeFeedbackController.userReply, dto={}", dto);
+        return lifeFeedbackService.userReply(dto);
     }
 }
 

+ 21 - 0
alien-store/src/main/java/shop/alien/store/service/LifeFeedbackReplyService.java

@@ -0,0 +1,21 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.LifeFeedbackReply;
+
+import java.util.List;
+
+/**
+ * 反馈回复 Service
+ */
+public interface LifeFeedbackReplyService extends IService<LifeFeedbackReply> {
+
+    /**
+     * 根据反馈ID查询回复列表
+     * @param feedbackId 反馈ID
+     * @return 回复列表
+     */
+    List<LifeFeedbackReply> getByFeedbackId(Integer feedbackId);
+}
+
+

+ 5 - 14
alien-store/src/main/java/shop/alien/store/service/LifeFeedbackService.java

@@ -4,8 +4,8 @@ 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.LifeFeedback;
-import shop.alien.entity.store.dto.FeedbackReplyDto;
 import shop.alien.entity.store.dto.LifeFeedbackDto;
+import shop.alien.entity.store.dto.UserReplyDto;
 import shop.alien.entity.store.vo.LifeFeedbackVo;
 
 /**
@@ -21,13 +21,6 @@ public interface LifeFeedbackService extends IService<LifeFeedback> {
     R<String> submitFeedback(LifeFeedbackDto dto);
 
     /**
-     * 平台回复反馈
-     * @param dto 回复信息
-     * @return 回复结果
-     */
-    R<String> replyFeedback(FeedbackReplyDto dto);
-
-    /**
      * 查询用户历史反馈列表
      * @param userId 用户ID
      * @param feedbackSource 反馈来源
@@ -45,12 +38,10 @@ public interface LifeFeedbackService extends IService<LifeFeedback> {
     R<LifeFeedbackVo> getFeedbackDetail(Integer feedbackId);
 
     /**
-     * 更新反馈处理状态
-     * @param feedbackId 反馈ID
-     * @param handleStatus 处理状态
-     * @param staffId 跟进人员ID
-     * @return 更新结果
+     * 用户回复反馈
+     * @param dto 回复信息
+     * @return 回复结果
      */
-    R<String> updateHandleStatus(Integer feedbackId, Integer handleStatus, Integer staffId);
+    R<String> userReply(UserReplyDto dto);
 }
 

+ 41 - 0
alien-store/src/main/java/shop/alien/store/service/LifeImgService.java

@@ -0,0 +1,41 @@
+package shop.alien.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import shop.alien.entity.store.LifeImg;
+
+import java.util.List;
+
+/**
+ * 反馈图片 Service
+ */
+public interface LifeImgService extends IService<LifeImg> {
+
+    /**
+     * 根据反馈ID查询图片列表
+     * @param feedbackId 反馈ID
+     * @return 图片列表
+     */
+    List<LifeImg> getByFeedbackId(Integer feedbackId);
+
+    /**
+     * 批量保存图片
+     * @param imgList 图片列表
+     * @return 是否成功
+     */
+    boolean batchSave(List<LifeImg> imgList);
+
+    /**
+     * 根据反馈ID删除图片
+     * @param feedbackId 反馈ID
+     * @return 是否成功
+     */
+    boolean removeByFeedbackId(Integer feedbackId);
+
+    /**
+     * 查询反馈的图片URL列表
+     * @param feedbackId 反馈ID
+     * @return 图片URL列表
+     */
+    List<String> getImgUrlsByFeedbackId(Integer feedbackId);
+}
+

+ 29 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LifeFeedbackReplyServiceImpl.java

@@ -0,0 +1,29 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.LifeFeedbackReply;
+import shop.alien.mapper.LifeFeedbackReplyMapper;
+import shop.alien.store.service.LifeFeedbackReplyService;
+
+import java.util.List;
+
+/**
+ * 反馈回复 Service实现类
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class LifeFeedbackReplyServiceImpl extends ServiceImpl<LifeFeedbackReplyMapper, LifeFeedbackReply> implements LifeFeedbackReplyService {
+
+    private final LifeFeedbackReplyMapper lifeFeedbackReplyMapper;
+
+    @Override
+    public List<LifeFeedbackReply> getByFeedbackId(Integer feedbackId) {
+        return lifeFeedbackReplyMapper.selectByFeedbackId(feedbackId);
+    }
+}
+
+

+ 135 - 102
alien-store/src/main/java/shop/alien/store/service/impl/LifeFeedbackServiceImpl.java

@@ -11,11 +11,13 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.LifeFeedback;
+import shop.alien.entity.store.LifeFeedbackReply;
 import shop.alien.entity.store.LifeImg;
 import shop.alien.entity.store.LifeLog;
-import shop.alien.entity.store.dto.FeedbackReplyDto;
 import shop.alien.entity.store.dto.LifeFeedbackDto;
+import shop.alien.entity.store.dto.UserReplyDto;
 import shop.alien.entity.store.vo.LifeFeedbackVo;
+import shop.alien.mapper.LifeFeedbackReplyMapper;
 import shop.alien.mapper.LifeFeedbackMapper;
 import shop.alien.mapper.LifeLogMapper;
 import shop.alien.store.service.LifeFeedbackService;
@@ -36,6 +38,7 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
     private final LifeFeedbackMapper lifeFeedbackMapper;
     private final LifeImgService lifeImgService;
     private final LifeLogMapper lifeLogMapper;
+    private final LifeFeedbackReplyMapper lifeFeedbackReplyMapper;
 
     @Override
     public R<String> submitFeedback(LifeFeedbackDto dto) {
@@ -57,34 +60,37 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
             // 2. 创建反馈记录(使用MyBatis Plus的save方法)
             LifeFeedback feedback = new LifeFeedback();
             BeanUtils.copyProperties(dto, feedback);
+            // 如果feedbackWay为空,默认为用户主动反馈(0)
+            if (feedback.getFeedbackWay() == null) {
+                feedback.setFeedbackWay(0);
+            }
             feedback.setFeedbackTime(new Date());
             feedback.setHandleStatus(0); // 待处理
-            feedback.setCreatedTime(new Date());
-            feedback.setCreatedUserId(dto.getUserId());
+            // createTime 和 updateTime 由 MyBatis Plus 自动填充,无需手动设置
 
             boolean saveResult = this.save(feedback);
             if (!saveResult) {
                 return R.fail("提交反馈失败");
             }
 
-            // 3. 保存附件图片(使用批量插入)
+            // 3. 保存附件(图片和视频,使用批量插入)
             if (!CollectionUtils.isEmpty(dto.getImgUrlList())) {
                 List<LifeImg> imgList = new java.util.ArrayList<>();
                 int sort = 1;
-                for (String imgUrl : dto.getImgUrlList()) {
+                for (String fileUrl : dto.getImgUrlList()) {
                     LifeImg img = new LifeImg();
                     img.setFeedbackId(feedback.getId());
-                    img.setImgUrl(imgUrl);
+                    img.setImgUrl(fileUrl);
                     img.setImgSort(sort++);
-                    img.setCreatedTime(new Date());
                     img.setCreatedUserId(dto.getUserId());
+                    // createdTime 由 MyBatis Plus 自动填充
                     imgList.add(img);
                 }
                 lifeImgService.batchSave(imgList);
             }
 
-            // 4. 记录日志
-            saveLog("用户提交反馈,ID:" + feedback.getId());
+            // 4. 记录日志(只记录内容)
+            saveLog(feedback.getId(), feedback.getContent(), "0");
 
             return R.success("提交成功");
         } catch (Exception e) {
@@ -94,78 +100,32 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
     }
 
     @Override
-    public R<String> replyFeedback(FeedbackReplyDto dto) {
-        try {
-            // 1. 参数校验
-            if (dto.getFeedbackId() == null) {
-                return R.fail("反馈ID不能为空");
-            }
-            if (dto.getStaffId() == null) {
-                return R.fail("工作人员ID不能为空");
-            }
-            if (dto.getContent() == null || dto.getContent().trim().isEmpty()) {
-                return R.fail("回复内容不能为空");
-            }
-
-            // 2. 查询原始反馈
-            LifeFeedback originalFeedback = lifeFeedbackMapper.selectById(dto.getFeedbackId());
-            if (originalFeedback == null) {
-                return R.fail("反馈记录不存在");
-            }
-
-            // 3. 创建平台回复记录(使用MyBatis Plus的save方法)
-            LifeFeedback reply = new LifeFeedback();
-            reply.setUserId(originalFeedback.getUserId());
-            reply.setFeedbackSource(originalFeedback.getFeedbackSource());
-            reply.setFeedbackWay(2); // 平台回复
-            reply.setFeedbackType(originalFeedback.getFeedbackType());
-            reply.setContent(dto.getContent());
-            reply.setFeedbackTime(new Date());
-            reply.setFollowUpStaff(dto.getStaffId());
-            reply.setHandleStatus(2); // 已完成
-            reply.setCreatedTime(new Date());
-            reply.setCreatedUserId(dto.getStaffId());
-
-            boolean saveResult = this.save(reply);
-            if (!saveResult) {
-                return R.fail("回复失败");
-            }
-
-            // 4. 更新原始反馈的处理状态和跟进人员(使用MyBatis Plus的updateById方法)
-            LifeFeedback updateFeedback = new LifeFeedback();
-            updateFeedback.setId(dto.getFeedbackId());
-            updateFeedback.setHandleStatus(1); // 处理中
-            updateFeedback.setFollowUpStaff(dto.getStaffId());
-            updateFeedback.setUpdatedTime(new Date());
-            updateFeedback.setUpdatedUserId(dto.getStaffId());
-            this.updateById(updateFeedback);
-
-            // 5. 记录日志
-            saveLog("平台回复反馈,原始反馈ID:" + dto.getFeedbackId() + ",回复ID:" + reply.getId());
-
-            return R.success("回复成功");
-        } catch (Exception e) {
-            log.error("回复反馈失败", e);
-            return R.fail("回复反馈失败:" + e.getMessage());
-        }
-    }
-
-    @Override
     public IPage<LifeFeedbackVo> getFeedbackList(Integer userId, Integer feedbackSource, int page, int size) {
         try {
             // 使用自定义SQL查询(已包含工作人员名称)
+            // 只返回原始反馈(feedback_way=0或1),不包括回复
             Page<LifeFeedbackVo> pageParam = new Page<>(page, size);
             IPage<LifeFeedbackVo> voPage = lifeFeedbackMapper.selectFeedbackListWithStaff(
-                    pageParam, userId, feedbackSource, 1, null
+                    pageParam, userId, feedbackSource, null, null
             );
-
+            
+            // 过滤出原始反馈(feedback_way=0或1)
+            List<LifeFeedbackVo> originalFeedbacks = voPage.getRecords().stream()
+                    .filter(vo -> vo.getFeedbackWay() == 0 || vo.getFeedbackWay() == 1)
+                    .collect(java.util.stream.Collectors.toList());
+            
             // 为每条记录查询附件图片
-            voPage.getRecords().forEach(vo -> {
+            originalFeedbacks.forEach(vo -> {
                 List<String> imgUrls = lifeImgService.getImgUrlsByFeedbackId(vo.getId());
                 vo.setImgUrlList(imgUrls);
             });
+            
+            // 重新设置records
+            Page<LifeFeedbackVo> resultPage = new Page<>(page, size);
+            resultPage.setRecords(originalFeedbacks);
+            resultPage.setTotal(originalFeedbacks.size());
 
-            return voPage;
+            return resultPage;
         } catch (Exception e) {
             log.error("查询反馈列表失败", e);
             return new Page<>(page, size);
@@ -181,17 +141,70 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
                 return R.fail("反馈记录不存在");
             }
 
-            // 2. 查询附件图片
-            List<String> imgUrls = lifeImgService.getImgUrlsByFeedbackId(feedbackId);
-            vo.setImgUrlList(imgUrls);
-
-            // 3. 查询平台回复列表(如果是主动反馈)
-            if (vo.getFeedbackWay() == 1) {
-                List<LifeFeedbackVo> replyList = lifeFeedbackMapper.selectPlatformReplies(
-                        vo.getUserId(), vo.getFeedbackSource(), vo.getFeedbackTime()
-                );
-                vo.setPlatformReplies(replyList);
+            // 2. 查询所有附件(包括原始反馈和回复的附件)
+            List<LifeImg> allImgs = lifeImgService.getByFeedbackId(feedbackId);
+            
+            // 3. 查询回复列表(从life_feedback_reply表查询)
+            List<LifeFeedbackReply> replyList = lifeFeedbackReplyMapper.selectByFeedbackId(feedbackId);
+            
+            // 4. 获取最早回复时间(用于区分原始反馈附件和回复附件)
+            Date earliestReplyTime = null;
+            if (!replyList.isEmpty()) {
+                earliestReplyTime = replyList.stream()
+                        .map(LifeFeedbackReply::getCreateTime)
+                        .min(Date::compareTo)
+                        .orElse(null);
             }
+            
+            // 5. 通过时间逻辑区分附件归属
+            Date feedbackTime = vo.getFeedbackTime();
+            List<String> originalImgUrls = new java.util.ArrayList<>();
+            List<LifeFeedbackVo> platformReplies = new java.util.ArrayList<>();
+            
+            // 5.1 处理回复列表,为每个回复匹配附件
+            for (LifeFeedbackReply reply : replyList) {
+                LifeFeedbackVo replyVo = new LifeFeedbackVo();
+                replyVo.setId(reply.getId());
+                replyVo.setContent(reply.getReplyContent());
+                replyVo.setFeedbackTime(reply.getCreateTime());
+                replyVo.setFeedbackWay(reply.getReplyType() == 0 ? 2 : 1); // 0-平台回复,1-用户回复
+                // 平台回复通过staffId区分,用户回复staffId为null
+                if (reply.getReplyType() == 0) {
+                    replyVo.setStaffName("平台客服"); // 平台回复显示工作人员名称
+                }
+                
+                // 为回复匹配附件(时间差在5分钟内)
+                List<String> replyImgUrls = new java.util.ArrayList<>();
+                for (LifeImg img : allImgs) {
+                    if (img.getCreatedTime() != null && reply.getCreateTime() != null) {
+                        long timeDiff = Math.abs(img.getCreatedTime().getTime() - reply.getCreateTime().getTime());
+                        if (timeDiff <= 5 * 60 * 1000) { // 5分钟内
+                            // 如果附件时间晚于最早回复时间,则属于回复
+                            if (earliestReplyTime != null && img.getCreatedTime().after(earliestReplyTime)) {
+                                replyImgUrls.add(img.getImgUrl());
+                            }
+                        }
+                    }
+                }
+                replyVo.setImgUrlList(replyImgUrls);
+                platformReplies.add(replyVo);
+            }
+            
+            // 5.2 处理原始反馈附件(时间差在反馈时间5分钟内,且早于最早回复时间)
+            for (LifeImg img : allImgs) {
+                if (img.getCreatedTime() != null && feedbackTime != null) {
+                    long timeDiff = Math.abs(img.getCreatedTime().getTime() - feedbackTime.getTime());
+                    if (timeDiff <= 5 * 60 * 1000) { // 5分钟内
+                        // 如果早于最早回复时间,或没有回复,则属于原始反馈
+                        if (earliestReplyTime == null || img.getCreatedTime().before(earliestReplyTime)) {
+                            originalImgUrls.add(img.getImgUrl());
+                        }
+                    }
+                }
+            }
+            
+            vo.setImgUrlList(originalImgUrls);
+            vo.setPlatformReplies(platformReplies);
 
             return R.data(vo);
         } catch (Exception e) {
@@ -201,37 +214,57 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
     }
 
     @Override
-    public R<String> updateHandleStatus(Integer feedbackId, Integer handleStatus, Integer staffId) {
+    public R<String> userReply(UserReplyDto dto) {
         try {
-            // 使用MyBatis Plus的updateById方法
-            LifeFeedback feedback = new LifeFeedback();
-            feedback.setId(feedbackId);
-            feedback.setHandleStatus(handleStatus);
-            feedback.setFollowUpStaff(staffId);
-            feedback.setUpdatedTime(new Date());
-            feedback.setUpdatedUserId(staffId);
-
-            boolean result = this.updateById(feedback);
-            if (result) {
-                saveLog("更新反馈处理状态,反馈ID:" + feedbackId + ",状态:" + handleStatus);
-                return R.success("更新成功");
-            }
-            return R.fail("更新失败");
+            // 1. 参数校验
+            if (dto.getUserId() == null) {
+                return R.fail("用户ID不能为空");
+            }
+            if (dto.getFeedbackId() == null) {
+                return R.fail("反馈ID不能为空");
+            }
+            if (dto.getContent() == null || dto.getContent().trim().isEmpty()) {
+                return R.fail("回复内容不能为空");
+            }
+
+            // 2. 查询原始反馈
+            LifeFeedback originalFeedback = lifeFeedbackMapper.selectById(dto.getFeedbackId());
+            if (originalFeedback == null) {
+                return R.fail("反馈记录不存在");
+            }
+
+            // 3. 创建用户回复记录(保存到life_feedback_reply表,reply_type=1)
+            LifeFeedbackReply reply = new LifeFeedbackReply();
+            reply.setFeedbackId(dto.getFeedbackId());
+            reply.setReplyType(1); // 1-用户回复
+            reply.setReplyContent(dto.getContent());
+            // createTime 和 updateTime 由 MyBatis Plus 自动填充
+
+            boolean saveResult = lifeFeedbackReplyMapper.insert(reply) > 0;
+            if (!saveResult) {
+                return R.fail("回复失败");
+            }
+
+            // 注意:根据文档,用户回复不保存附件、不更新反馈状态、不记录操作日志
+
+            return R.success("回复成功");
         } catch (Exception e) {
-            log.error("更新反馈处理状态失败", e);
-            return R.fail("更新失败:" + e.getMessage());
+            log.error("用户回复失败", e);
+            return R.fail("用户回复失败:" + e.getMessage());
         }
     }
 
     /**
      * 保存操作日志
      */
-    private void saveLog(String context) {
+    private void saveLog(Integer feedbackId, String context, String type) {
         try {
-            LifeLog log = new LifeLog();
-            log.setContext(context);
-            log.setCreatedTime(new Date());
-            lifeLogMapper.insert(log);
+            LifeLog lifeLog = new LifeLog();
+            lifeLog.setFeedbackId(feedbackId);
+            lifeLog.setContext(context);
+            lifeLog.setType(type);
+            // createdTime 由 MyBatis Plus 自动填充
+            lifeLogMapper.insert(lifeLog);
         } catch (Exception e) {
             log.error("保存日志失败", e);
         }

+ 46 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LifeImgServiceImpl.java

@@ -0,0 +1,46 @@
+package shop.alien.store.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.LifeImg;
+import shop.alien.mapper.LifeImgMapper;
+import shop.alien.store.service.LifeImgService;
+
+import java.util.List;
+
+/**
+ * 反馈图片 Service实现类
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class LifeImgServiceImpl extends ServiceImpl<LifeImgMapper, LifeImg> implements LifeImgService {
+
+    private final LifeImgMapper lifeImgMapper;
+
+    @Override
+    public List<LifeImg> getByFeedbackId(Integer feedbackId) {
+        return lifeImgMapper.selectByFeedbackId(feedbackId);
+    }
+
+    @Override
+    public boolean batchSave(List<LifeImg> imgList) {
+        if (imgList == null || imgList.isEmpty()) {
+            return false;
+        }
+        return lifeImgMapper.batchInsert(imgList) > 0;
+    }
+
+    @Override
+    public boolean removeByFeedbackId(Integer feedbackId) {
+        return lifeImgMapper.deleteByFeedbackId(feedbackId) > 0;
+    }
+
+    @Override
+    public List<String> getImgUrlsByFeedbackId(Integer feedbackId) {
+        return lifeImgMapper.selectImgUrlsByFeedbackId(feedbackId);
+    }
+}
+