From 718f84b8a49bfa6bf1773e3cb3b0670d2f0c0c5b Mon Sep 17 00:00:00 2001 From: GoCo Date: Sat, 27 Sep 2025 10:50:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20=E8=AF=84=E8=AE=BA?= =?UTF-8?q?=E7=82=B9=E8=B5=9E=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/mybatis/MybatisInterceptor.java | 1 + .../controller/AiolCommentController.java | 269 +++++++++++++----- .../controller/AiolMessageController.java | 101 ++++--- .../aiol/utils/MessageNotificationUtil.java | 191 +++++++++++++ .../service/ISysAnnouncementService.java | 1 + .../impl/SysAnnouncementServiceImpl.java | 1 + 6 files changed, 448 insertions(+), 116 deletions(-) create mode 100644 jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/utils/MessageNotificationUtil.java diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisInterceptor.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisInterceptor.java index aef22667..1f903398 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisInterceptor.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisInterceptor.java @@ -56,6 +56,7 @@ public class MybatisInterceptor implements Interceptor { if (sysUser != null) { // 登录人账号 field.setAccessible(true); + // field.set(parameter, sysUser.getId()); field.set(parameter, sysUser.getUsername()); field.setAccessible(false); } diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolCommentController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolCommentController.java index ee400b30..e8d3e98c 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolCommentController.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/controller/AiolCommentController.java @@ -1,51 +1,41 @@ package org.jeecg.modules.aiol.controller; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.jeecg.common.api.vo.Result; import org.jeecg.common.system.query.QueryGenerator; -import org.jeecg.common.system.query.QueryRuleEnum; import org.jeecg.common.system.util.JwtUtil; import org.jeecg.common.system.vo.LoginUser; -import org.jeecg.common.util.oConvertUtils; import org.jeecg.config.shiro.IgnoreAuth; import org.jeecg.modules.aiol.dto.CommentWithUserInfo; import org.jeecg.modules.aiol.entity.AiolComment; import org.jeecg.modules.aiol.service.IAiolCommentService; +import org.jeecg.modules.aiol.entity.AiolCourse; +import org.jeecg.modules.aiol.entity.AiolDiscussion; +import org.jeecg.modules.aiol.service.IAiolCourseService; +import org.jeecg.modules.aiol.service.IAiolDiscussionService; +import org.jeecg.modules.aiol.utils.MessageNotificationUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.extern.slf4j.Slf4j; -import org.jeecgframework.poi.excel.ExcelImportUtil; -import org.jeecgframework.poi.excel.def.NormalExcelConstants; -import org.jeecgframework.poi.excel.entity.ExportParams; -import org.jeecgframework.poi.excel.entity.ImportParams; -import org.jeecgframework.poi.excel.view.JeecgEntityExcelView; import org.jeecg.common.system.api.ISysBaseAPI; import org.jeecg.common.system.base.controller.JeecgController; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.servlet.ModelAndView; -import com.alibaba.fastjson.JSON; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; import org.jeecg.common.aspect.annotation.AutoLog; import org.jeecg.common.constant.CommonConstant; import org.apache.shiro.authz.annotation.RequiresPermissions; -import java.util.Date; /** * @Description: 评论 @@ -60,6 +50,15 @@ import java.util.Date; public class AiolCommentController extends JeecgController { @Autowired private IAiolCommentService aiolCommentService; + + @Autowired + private MessageNotificationUtil messageNotificationUtil; + + @Autowired + private IAiolCourseService aiolCourseService; + + @Autowired + private IAiolDiscussionService aiolDiscussionService; /** * 分页列表查询 @@ -95,21 +94,35 @@ public class AiolCommentController extends JeecgController add(@RequestBody AiolComment aiolComment, HttpServletRequest request) { - // 1. 获取当前登录用户信息 - String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); - String username = JwtUtil.getUsername(token); - LoginUser sysUser = sysBaseApi.getUserByName(username); + try { + // 1. 获取当前登录用户信息 + String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); + String username = JwtUtil.getUsername(token); + LoginUser sysUser = sysBaseApi.getUserByName(username); - if (sysUser == null) { - return Result.error("用户未登录或登录已过期"); + if (sysUser == null) { + return Result.error("用户未登录或登录已过期"); + } + + aiolComment.setUserId(sysUser.getId()); // 用户ID + aiolComment.setLikeCount(0); + + // 2. 保存评论 + aiolCommentService.save(aiolComment); + + // 3. 发送评论通知 + sendCommentNotification(aiolComment, sysUser); + + log.info("用户 {} 成功添加评论,评论ID: {}, 目标类型: {}, 目标ID: {}", + username, aiolComment.getId(), aiolComment.getTargetType(), aiolComment.getTargetId()); + + return Result.OK(aiolComment.getId()); + + } catch (Exception e) { + log.error("添加评论失败: targetType={}, targetId={}, error={}", + aiolComment.getTargetType(), aiolComment.getTargetId(), e.getMessage(), e); + return Result.error("添加评论失败: " + e.getMessage()); } - - aiolComment.setUserId(sysUser.getId()); // 用户ID - aiolComment.setLikeCount(0); - - aiolCommentService.save(aiolComment); - - return Result.OK("添加成功!"); } /** @@ -299,14 +312,47 @@ public class AiolCommentController extends JeecgController addCourseComment( - @PathVariable("courseId") String courseId, - @RequestBody AiolComment aiolComment, - HttpServletRequest request) { + // @AutoLog(value = "评论-新增课程评论") + // @Operation(summary = "新增课程评论", description = "新增课程评论,target_type默认为course,user_id通过token自动获取") + // @PostMapping(value = "/course/{courseId}/add") + // public Result addCourseComment( + // @PathVariable("courseId") String courseId, + // @RequestBody AiolComment aiolComment, + // HttpServletRequest request) { + // try { + // // 1. 获取当前登录用户信息 + // String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); + // String username = JwtUtil.getUsername(token); + // LoginUser sysUser = sysBaseApi.getUserByName(username); + + // if (sysUser == null) { + // return Result.error("用户未登录或登录已过期"); + // } + + // // 2. 设置评论基本信息 + // aiolComment.setTargetType("course"); + // aiolComment.setTargetId(courseId); // 课程ID + // aiolComment.setUserId(sysUser.getId()); // 用户ID + + // // 3. 保存评论 + // aiolCommentService.save(aiolComment); + + // log.info("用户 {} 成功添加课程评论,课程ID: {}, 评论ID: {}", + // username, courseId, aiolComment.getId()); + + // return Result.OK("评论添加成功!"); + + // } catch (Exception e) { + // log.error("添加课程评论失败: courseId={}, error={}", courseId, e.getMessage(), e); + // return Result.error("评论添加失败: " + e.getMessage()); + // } + // } + + @AutoLog(value = "评论-点赞") + @Operation(summary = "点赞评论", description = "为指定评论点赞,增加like_count字段值并发送通知") + @GetMapping(value = "/like/{commentId}") + public Result likeComment(@PathVariable("commentId") String commentId, HttpServletRequest request) { try { // 1. 获取当前登录用户信息 String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); @@ -317,38 +363,13 @@ public class AiolCommentController extends JeecgController likeComment(@PathVariable("commentId") String commentId) { - try { - // 1. 查询评论是否存在 + // 2. 查询评论是否存在 AiolComment comment = aiolCommentService.getById(commentId); if (comment == null) { return Result.error("评论不存在"); } - // 2. 更新点赞数 + // 3. 更新点赞数 Integer currentLikeCount = comment.getLikeCount() != null ? comment.getLikeCount() : 0; comment.setLikeCount(currentLikeCount + 1); @@ -357,7 +378,10 @@ public class AiolCommentController extends JeecgController result = new HashMap<>(); - + // 1. 先查询用户所有未读消息的annt_id QueryWrapper unreadWrapper = new QueryWrapper<>(); unreadWrapper.eq("user_id", userId) - .eq("read_flag", 0); + .or().eq("user_id", sysUser.getUsername()) + .eq("read_flag", 0); List unreadMessages = sysAnnouncementSendService.list(unreadWrapper); - + if (unreadMessages.isEmpty()) { // 如果没有未读消息,返回0 result.put("commentsUnreadCount", 0); @@ -182,22 +181,22 @@ public class AiolMessageController { result.put("totalUnreadCount", 0); return Result.OK(result); } - + // 2. 提取所有annt_id List anntIds = unreadMessages.stream() .map(SysAnnouncementSend::getAnntId) .collect(java.util.stream.Collectors.toList()); - + // 3. 根据annt_id查询对应的消息详情,获取msg_category QueryWrapper announcementWrapper = new QueryWrapper<>(); announcementWrapper.in("id", anntIds); List announcements = sysAnnouncementService.list(announcementWrapper); - + // 4. 按msg_category分类统计 - long commentsUnreadCount = 0; // msg_category=3 - long likesUnreadCount = 0; // msg_category=4 - long systemUnreadCount = 0; // msg_category=2 - + long commentsUnreadCount = 0; // msg_category=3 + long likesUnreadCount = 0; // msg_category=4 + long systemUnreadCount = 0; // msg_category=2 + for (SysAnnouncement announcement : announcements) { String msgCategory = announcement.getMsgCategory(); if ("3".equals(msgCategory)) { @@ -208,15 +207,15 @@ public class AiolMessageController { systemUnreadCount++; } } - + // 5. 计算总未读数量 long totalUnreadCount = commentsUnreadCount + likesUnreadCount + systemUnreadCount; - + result.put("commentsUnreadCount", commentsUnreadCount); result.put("likesUnreadCount", likesUnreadCount); result.put("systemUnreadCount", systemUnreadCount); result.put("totalUnreadCount", totalUnreadCount); - + return Result.OK(result); } catch (Exception e) { return Result.error("查询未读消息数量失败: " + e.getMessage()); @@ -236,23 +235,23 @@ public class AiolMessageController { result.error500("消息ID不能为空"); return result; } - + // 构建更新条件:根据sendId和userId更新单条消息 LambdaUpdateWrapper updateWrapper = new UpdateWrapper().lambda(); updateWrapper.set(SysAnnouncementSend::getReadFlag, CommonConstant.HAS_READ_FLAG); updateWrapper.set(SysAnnouncementSend::getReadTime, new Date()); updateWrapper.eq(SysAnnouncementSend::getId, sendId); - + SysAnnouncementSend announcementSend = new SysAnnouncementSend(); boolean updateResult = sysAnnouncementSendService.update(announcementSend, updateWrapper); - + if (updateResult) { result.setSuccess(true); result.setMessage("消息已标记为已读"); } else { result.error500("标记已读失败,可能消息不存在或已被标记"); } - + return result; } catch (Exception e) { log.error("标记单条消息已读失败: " + e.getMessage(), e); diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/utils/MessageNotificationUtil.java b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/utils/MessageNotificationUtil.java new file mode 100644 index 00000000..ddb76e11 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-aiol/src/main/java/org/jeecg/modules/aiol/utils/MessageNotificationUtil.java @@ -0,0 +1,191 @@ +package org.jeecg.modules.aiol.utils; + +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.constant.CommonConstant; +import org.jeecg.modules.message.websocket.WebSocket; +import org.jeecg.modules.system.entity.SysAnnouncement; +import org.jeecg.modules.system.entity.SysAnnouncementSend; +import org.jeecg.modules.system.mapper.SysAnnouncementMapper; +import org.jeecg.modules.system.mapper.SysAnnouncementSendMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Date; + +/** + * 消息通知工具类 + * 用于发送评论通知和点赞通知,避免循环依赖问题 + * + * @Author: jeecg-boot + * @Date: 2025-01-17 + * @Version: V1.0 + */ +@Component +@Slf4j +public class MessageNotificationUtil { + + @Autowired + private SysAnnouncementMapper sysAnnouncementMapper; + + @Autowired + private SysAnnouncementSendMapper sysAnnouncementSendMapper; + + @Autowired + private WebSocket webSocket; + + /** + * 发送评论通知 + * + * @param toUser 接收用户ID + * @param fromUser 发送用户ID + * @param senderId 发送者ID + * @param senderUsername 发送者用户名 + * @param commentId 评论ID + * @param commentContent 评论内容 + * @param entityType 实体类型 + * @param entityId 实体ID + * @param entityTitle 实体标题 + * @param actionTime 操作时间 + */ + public void sendCommentNotification(String toUser, String fromUser, String senderId, String senderUsername, + Long commentId, String commentContent, String entityType, String entityId, + String entityTitle, String actionTime) { + try { + log.info("发送评论通知 - 接收者: {}, 发送者: {}, 评论ID: {}", toUser, fromUser, commentId); + + // 创建评论和@消息的JSON内容 + JSONObject contentJson = new JSONObject(); + + // 发送者信息 + JSONObject sender = new JSONObject(); + sender.put("id", senderId); + sender.put("username", senderUsername); + contentJson.put("sender", sender); + + // 评论信息 + JSONObject comment = new JSONObject(); + comment.put("id", commentId); + comment.put("content", commentContent); + contentJson.put("comment", comment); + + // 实体信息 + JSONObject entity = new JSONObject(); + entity.put("type", entityType); + entity.put("id", entityId); + entity.put("title", entityTitle); + contentJson.put("entity", entity); + + // 操作时间 + contentJson.put("actionTime", actionTime); + + // 1. 创建系统公告 + SysAnnouncement announcement = new SysAnnouncement(); + announcement.setTitile("评论和@"); + announcement.setMsgContent(contentJson.toJSONString()); + announcement.setSender(senderUsername); + announcement.setPriority(CommonConstant.PRIORITY_L); + announcement.setMsgType(CommonConstant.MSG_TYPE_UESR); + announcement.setMsgCategory("3"); + announcement.setSendStatus(CommonConstant.HAS_SEND); + announcement.setSendTime(new Date()); + announcement.setDelFlag(CommonConstant.DEL_FLAG_0.toString()); + announcement.setUserIds(toUser + ","); + + // 保存公告 + sysAnnouncementMapper.insert(announcement); + + // 2. 创建用户通告阅读标记记录 + SysAnnouncementSend announcementSend = new SysAnnouncementSend(); + announcementSend.setAnntId(announcement.getId()); + announcementSend.setUserId(toUser); + announcementSend.setReadFlag(CommonConstant.NO_READ_FLAG); + announcementSend.setReadTime(new Date()); + sysAnnouncementSendMapper.insert(announcementSend); + + log.info("评论通知发送成功 - 接收者: {}, 内容: {}", toUser, contentJson.toJSONString()); + } catch (Exception e) { + log.error("发送评论通知失败 - 接收者: {}, 错误: {}", toUser, e.getMessage(), e); + } + } + + /** + * 发送点赞通知 + * + * @param toUser 接收用户ID + * @param fromUser 发送用户ID + * @param senderId 发送者ID + * @param senderUsername 发送者用户名 + * @param entityType 实体类型 + * @param entityId 实体ID + * @param entityTitle 实体标题 + * @param action 操作类型(like/collect等) + * @param actionTime 操作时间 + */ + public void sendLikeNotification(String toUser, String fromUser, String senderId, String senderUsername, + String entityType, String entityId, String entityTitle, String action, String actionTime) { + try { + log.info("发送点赞通知 - 接收者: {}, 发送者: {}, 操作: {}", toUser, fromUser, action); + + // 创建赞和收藏消息的JSON内容 + JSONObject contentJson = new JSONObject(); + + // 发送者信息 + JSONObject sender = new JSONObject(); + sender.put("id", senderId); + sender.put("username", senderUsername); + contentJson.put("sender", sender); + + // 实体信息 + JSONObject entity = new JSONObject(); + entity.put("type", entityType); + entity.put("id", entityId); + entity.put("title", entityTitle); + contentJson.put("entity", entity); + + // 操作类型和时间 + contentJson.put("action", action); + contentJson.put("actionTime", actionTime); + + // 1. 创建系统公告 + SysAnnouncement announcement = new SysAnnouncement(); + announcement.setTitile("赞和收藏"); + announcement.setMsgContent(contentJson.toJSONString()); + announcement.setSender(senderUsername); + announcement.setPriority(CommonConstant.PRIORITY_L); + announcement.setMsgType(CommonConstant.MSG_TYPE_UESR); + announcement.setMsgCategory("4"); + announcement.setSendStatus(CommonConstant.HAS_SEND); + announcement.setSendTime(new Date()); + announcement.setDelFlag(CommonConstant.DEL_FLAG_0.toString()); + announcement.setUserIds(toUser + ","); + + // 保存公告 + sysAnnouncementMapper.insert(announcement); + + // 2. 创建用户通告阅读标记记录 + SysAnnouncementSend announcementSend = new SysAnnouncementSend(); + announcementSend.setAnntId(announcement.getId()); + announcementSend.setUserId(toUser); + announcementSend.setReadFlag(CommonConstant.NO_READ_FLAG); + announcementSend.setReadTime(new Date()); + sysAnnouncementSendMapper.insert(announcementSend); + + // 3. 发送WebSocket通知 + try { + JSONObject obj = new JSONObject(); + obj.put("cmd", "user"); + obj.put("userId", toUser); + obj.put("msgId", announcement.getId()); + obj.put("msgTxt", announcement.getTitile()); + webSocket.pushMessage(toUser, obj.toJSONString()); + } catch (Exception we) { + log.warn("WebSocket通知发送失败: {}", we.getMessage()); + } + + log.info("点赞通知发送成功 - 接收者: {}, 内容: {}", toUser, contentJson.toJSONString()); + } catch (Exception e) { + log.error("发送点赞通知失败 - 接收者: {}, 错误: {}", toUser, e.getMessage(), e); + } + } +} diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/ISysAnnouncementService.java b/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/ISysAnnouncementService.java index 7aed2aa0..67965c13 100644 --- a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/ISysAnnouncementService.java +++ b/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/ISysAnnouncementService.java @@ -98,4 +98,5 @@ public interface ISysAnnouncementService extends IService { * @param count */ void updateVisitsNum(String id, int count); + } diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysAnnouncementServiceImpl.java b/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysAnnouncementServiceImpl.java index 8e7da070..44ec07af 100644 --- a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysAnnouncementServiceImpl.java +++ b/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysAnnouncementServiceImpl.java @@ -251,4 +251,5 @@ public class SysAnnouncementServiceImpl extends ServiceImpl