|
|
@@ -8,6 +8,7 @@ import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.BeanUtils;
|
|
|
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.LifeFeedback;
|
|
|
import shop.alien.entity.store.LifeFeedbackReply;
|
|
|
@@ -15,12 +16,21 @@ import shop.alien.entity.store.LifeImg;
|
|
|
import shop.alien.entity.store.LifeLog;
|
|
|
import shop.alien.entity.store.dto.*;
|
|
|
import shop.alien.entity.store.vo.*;
|
|
|
+import shop.alien.entity.store.vo.FeedbackReplyVo;
|
|
|
import shop.alien.mapper.LifeFeedbackMapper;
|
|
|
import shop.alien.mapper.LifeLogMapper;
|
|
|
+import shop.alien.mapper.LifeNoticeMapper;
|
|
|
+import shop.alien.mapper.LifeUserMapper;
|
|
|
+import shop.alien.mapper.StoreUserMapper;
|
|
|
+import shop.alien.entity.store.LifeNotice;
|
|
|
+import shop.alien.entity.store.LifeUser;
|
|
|
+import shop.alien.entity.store.StoreUser;
|
|
|
+import shop.alien.entity.store.vo.WebSocketVo;
|
|
|
+import shop.alien.store.config.WebSocketProcess;
|
|
|
import shop.alien.store.service.LifeFeedbackService;
|
|
|
import shop.alien.store.service.LifeFeedbackReplyService;
|
|
|
import shop.alien.store.service.LifeImgService;
|
|
|
-import org.springframework.util.CollectionUtils;
|
|
|
+import com.alibaba.fastjson2.JSONObject;
|
|
|
|
|
|
import java.util.Date;
|
|
|
import java.util.List;
|
|
|
@@ -39,6 +49,10 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
|
|
|
private final LifeImgService lifeImgService;
|
|
|
private final LifeLogMapper lifeLogMapper;
|
|
|
private final LifeFeedbackReplyService lifeFeedbackReplyService;
|
|
|
+ private final LifeNoticeMapper lifeNoticeMapper;
|
|
|
+ private final LifeUserMapper lifeUserMapper;
|
|
|
+ private final StoreUserMapper storeUserMapper;
|
|
|
+ private final WebSocketProcess webSocketProcess;
|
|
|
|
|
|
@Override
|
|
|
public R<String> submitFeedback(LifeFeedbackDto dto) {
|
|
|
@@ -464,6 +478,48 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
|
|
|
}
|
|
|
detail.setAttachments(attachments);
|
|
|
|
|
|
+ // 3. 查询回复列表(平台回复和用户回复)
|
|
|
+ List<LifeFeedbackReply> replyList = lifeFeedbackReplyService.getByFeedbackId(feedbackId);
|
|
|
+ List<FeedbackReplyVo> replyVoList = new ArrayList<>();
|
|
|
+ for (LifeFeedbackReply reply : replyList) {
|
|
|
+ FeedbackReplyVo replyVo = new FeedbackReplyVo();
|
|
|
+ replyVo.setId(reply.getId());
|
|
|
+ replyVo.setFeedbackId(reply.getFeedbackId());
|
|
|
+ replyVo.setReplyType(reply.getReplyType());
|
|
|
+ replyVo.setReplyTypeName(reply.getReplyType() == 0 ? "平台回复" : "用户回复");
|
|
|
+ replyVo.setReplyContent(reply.getReplyContent());
|
|
|
+ replyVo.setCreateTime(reply.getCreateTime());
|
|
|
+
|
|
|
+ // 查询回复的附件(通过时间判断:上传时间在回复创建时间前后5分钟内)
|
|
|
+ List<String> replyImgUrls = new ArrayList<>();
|
|
|
+ List<String> replyVideoUrls = new ArrayList<>();
|
|
|
+ Date replyTime = reply.getCreateTime();
|
|
|
+ if (replyTime != null && !CollectionUtils.isEmpty(imgList)) {
|
|
|
+ long replyTimeMs = replyTime.getTime();
|
|
|
+ for (LifeImg img : imgList) {
|
|
|
+ if (img.getUploadTime() != null) {
|
|
|
+ long imgTimeMs = img.getUploadTime().getTime();
|
|
|
+ // 判断附件是否属于该回复(时间差在5分钟内)
|
|
|
+ long timeDiff = Math.abs(imgTimeMs - replyTimeMs);
|
|
|
+ if (timeDiff <= 5 * 60 * 1000) { // 5分钟
|
|
|
+ if (img.getFileType() == 1) {
|
|
|
+ replyImgUrls.add(img.getImgUrl());
|
|
|
+ } else if (img.getFileType() == 2) {
|
|
|
+ replyVideoUrls.add(img.getImgUrl());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ replyVo.setImgUrlList(replyImgUrls);
|
|
|
+ replyVo.setVideoUrlList(replyVideoUrls);
|
|
|
+ replyVoList.add(replyVo);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按时间升序排序回复
|
|
|
+ replyVoList.sort((a, b) -> a.getCreateTime().compareTo(b.getCreateTime()));
|
|
|
+ detail.setReplies(replyVoList);
|
|
|
+
|
|
|
return R.data(detail);
|
|
|
} catch (Exception e) {
|
|
|
log.error("中台-查询反馈详情失败", e);
|
|
|
@@ -489,13 +545,29 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
|
|
|
return R.fail("反馈记录不存在");
|
|
|
}
|
|
|
|
|
|
- // 3. 记录回复日志(类型3-回复用户)
|
|
|
+ // 3. 保存平台回复到life_feedback_reply表
|
|
|
+ LifeFeedbackReply platformReply = new LifeFeedbackReply();
|
|
|
+ platformReply.setFeedbackId(replyDto.getFeedbackId());
|
|
|
+ platformReply.setReplyType(0); // 0-平台回复
|
|
|
+ platformReply.setReplyContent(replyDto.getContent());
|
|
|
+ platformReply.setCreateTime(new Date());
|
|
|
+ platformReply.setUpdateTime(new Date());
|
|
|
+
|
|
|
+ boolean saveResult = lifeFeedbackReplyService.save(platformReply);
|
|
|
+ if (!saveResult) {
|
|
|
+ return R.fail("保存回复失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 记录回复日志(类型3-回复用户)
|
|
|
String logContent = replyDto.getContent();
|
|
|
if (replyDto.getUserReply() != null && !replyDto.getUserReply().trim().isEmpty()) {
|
|
|
logContent = replyDto.getContent() + "||用户回复:" + replyDto.getUserReply();
|
|
|
}
|
|
|
saveFeedbackLog(replyDto.getFeedbackId(), 3, logContent);
|
|
|
|
|
|
+ // 5. 发送通知给用户
|
|
|
+ sendFeedbackReplyNotice(feedback, replyDto.getContent());
|
|
|
+
|
|
|
return R.success("回复成功");
|
|
|
} catch (Exception e) {
|
|
|
log.error("中台-回复用户失败", e);
|
|
|
@@ -553,5 +625,89 @@ public class LifeFeedbackServiceImpl extends ServiceImpl<LifeFeedbackMapper, Lif
|
|
|
log.error("保存反馈日志失败", e);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发送平台回复通知给用户
|
|
|
+ * @param feedback 反馈记录
|
|
|
+ * @param replyContent 回复内容
|
|
|
+ */
|
|
|
+ private void sendFeedbackReplyNotice(LifeFeedback feedback, String replyContent) {
|
|
|
+ try {
|
|
|
+ String receiverId = null;
|
|
|
+
|
|
|
+ // 根据反馈来源判断是用户端还是商家端
|
|
|
+ if (feedback.getFeedbackSource() == null) {
|
|
|
+ log.warn("反馈来源为空,无法发送通知,feedbackId={}", feedback.getId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (feedback.getFeedbackSource() == 0) {
|
|
|
+ // 用户端 - 使用user_手机号格式
|
|
|
+ if (feedback.getUserId() == null) {
|
|
|
+ log.warn("用户ID为空,无法发送通知,feedbackId={}", feedback.getId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ LifeUser lifeUser = lifeUserMapper.selectById(feedback.getUserId());
|
|
|
+ if (lifeUser == null || lifeUser.getUserPhone() == null || lifeUser.getUserPhone().trim().isEmpty()) {
|
|
|
+ log.warn("未找到用户信息或手机号为空,无法发送通知,userId={}", feedback.getUserId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ receiverId = "user_" + lifeUser.getUserPhone();
|
|
|
+ } else if (feedback.getFeedbackSource() == 1) {
|
|
|
+ // 商家端 - 使用store_手机号格式
|
|
|
+ if (feedback.getUserId() == null) {
|
|
|
+ log.warn("商家用户ID为空,无法发送通知,feedbackId={}", feedback.getId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ StoreUser storeUser = storeUserMapper.selectById(feedback.getUserId());
|
|
|
+ if (storeUser == null || storeUser.getPhone() == null || storeUser.getPhone().trim().isEmpty()) {
|
|
|
+ log.warn("未找到商家用户信息或手机号为空,无法发送通知,userId={}", feedback.getUserId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ receiverId = "store_" + storeUser.getPhone();
|
|
|
+ } else {
|
|
|
+ log.warn("未知的反馈来源,feedbackSource={}, feedbackId={}", feedback.getFeedbackSource(), feedback.getId());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建通知消息
|
|
|
+ JSONObject messageJson = new JSONObject();
|
|
|
+ messageJson.put("feedbackId", feedback.getId()); // 添加反馈ID用于区分
|
|
|
+ messageJson.put("message", "平台已回复您的意见反馈:" + replyContent);
|
|
|
+
|
|
|
+ // 创建通知记录
|
|
|
+ LifeNotice lifeNotice = new LifeNotice();
|
|
|
+ lifeNotice.setReceiverId(receiverId);
|
|
|
+ lifeNotice.setContext(messageJson.toJSONString());
|
|
|
+ lifeNotice.setTitle("意见反馈回复通知");
|
|
|
+ lifeNotice.setSenderId("system");
|
|
|
+ lifeNotice.setIsRead(0);
|
|
|
+ lifeNotice.setNoticeType(1); // 1-系统通知
|
|
|
+ lifeNotice.setBusinessId(feedback.getId());
|
|
|
+
|
|
|
+ // 保存通知
|
|
|
+ lifeNoticeMapper.insert(lifeNotice);
|
|
|
+
|
|
|
+ // 通过WebSocket发送实时通知
|
|
|
+ WebSocketVo webSocketVo = new WebSocketVo();
|
|
|
+ webSocketVo.setSenderId("system");
|
|
|
+ webSocketVo.setReceiverId(receiverId);
|
|
|
+ webSocketVo.setCategory("notice");
|
|
|
+ webSocketVo.setNoticeType("1");
|
|
|
+ webSocketVo.setIsRead(0);
|
|
|
+ webSocketVo.setText(JSONObject.toJSONString(lifeNotice));
|
|
|
+
|
|
|
+ try {
|
|
|
+ webSocketProcess.sendMessage(receiverId, JSONObject.toJSONString(webSocketVo));
|
|
|
+ log.info("平台回复通知发送成功,feedbackId={}, receiverId={}", feedback.getId(), receiverId);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("发送WebSocket通知失败,feedbackId={}, receiverId={}, error={}",
|
|
|
+ feedback.getId(), receiverId, e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("发送平台回复通知异常,feedbackId={}, error={}", feedback.getId(), e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|