diff --git a/珞珈岛-项目相关文件/luojia-island/common/src/main/java/com/luojia_channel/common/utils/JWTUtil.java b/珞珈岛-项目相关文件/luojia-island/common/src/main/java/com/luojia_channel/common/utils/JWTUtil.java index 30330fb..a21f67d 100644 --- a/珞珈岛-项目相关文件/luojia-island/common/src/main/java/com/luojia_channel/common/utils/JWTUtil.java +++ b/珞珈岛-项目相关文件/luojia-island/common/src/main/java/com/luojia_channel/common/utils/JWTUtil.java @@ -30,6 +30,7 @@ public final class JWTUtil { private static final long NEED_REFRESH_TTL = 60 * 60 * 24 * 7 * 1000; //7天 private static final String USER_ID_KEY = "userId"; private static final String USER_NAME_KEY = "username"; + private static final String USER_AVATAR_KEY = "avatar"; public static final String TOKEN_PREFIX = "Bearer "; public static final String ISS = "luojiachannel"; public static final String SECRET = "SecretKey5464Created2435By54377Forely02345239354893543157956476525685754352976546564766315468763584576"; @@ -45,6 +46,7 @@ public final class JWTUtil { Map customerUserMap = new HashMap<>(); customerUserMap.put(USER_ID_KEY, userInfo.getUserId()); customerUserMap.put(USER_NAME_KEY, userInfo.getUsername()); + customerUserMap.put(USER_AVATAR_KEY, userInfo.getAvatar()); String jwtToken = Jwts.builder() .signWith(SignatureAlgorithm.HS512, SECRET) .setIssuedAt(new Date()) @@ -64,6 +66,7 @@ public final class JWTUtil { Map customerUserMap = new HashMap<>(); customerUserMap.put(USER_ID_KEY, userInfo.getUserId()); customerUserMap.put(USER_NAME_KEY, userInfo.getUsername()); + customerUserMap.put(USER_AVATAR_KEY, userInfo.getAvatar()); String jwtToken = Jwts.builder() .signWith(SignatureAlgorithm.HS512, SECRET) .setIssuedAt(new Date()) diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/interact/controller/FollowController.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/interact/controller/FollowController.java new file mode 100644 index 0000000..c6a6860 --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/interact/controller/FollowController.java @@ -0,0 +1,31 @@ +package com.luojia_channel.modules.interact.controller; + +import com.luojia_channel.common.domain.Result; +import com.luojia_channel.common.domain.UserDTO; +import com.luojia_channel.modules.interact.service.FollowService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/follow") +@RequiredArgsConstructor +public class FollowController { + + private final FollowService followService; + @PutMapping("/{id}/{isFollow}") + public Result follow(@PathVariable("id") Long followUserId, @PathVariable("isFollow") Boolean isFollow){ + followService.follow(followUserId, isFollow); + return Result.success(); + } + @GetMapping("/or/not/{id}") + public Result isFollow(@PathVariable("id") Long followUserId){ + return Result.success(followService.isFollow(followUserId)); + } + @GetMapping("/common/{id}") + public Result> followCommons(@PathVariable("id") Long id){ + return Result.success(followService.followCommons(id)); + } +} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/interact/mapper/FollowMapper.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/interact/mapper/FollowMapper.java new file mode 100644 index 0000000..da5680c --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/interact/mapper/FollowMapper.java @@ -0,0 +1,18 @@ +package com.luojia_channel.modules.interact.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.luojia_channel.modules.interact.entity.Follow; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + + +@Mapper +public interface FollowMapper extends BaseMapper { + @Delete("delete from follow where user_id = #{userId} and follow_user_id = #{followUserId}") + boolean delete(Long userId, Long followUserId); + + @Select("select count(*) from follow where user_id = #{userId} and follow_user_id = #{followUserId}") + Integer queryCount(Long userId, Long followUserId); +} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/interact/service/FollowService.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/interact/service/FollowService.java new file mode 100644 index 0000000..76ff904 --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/interact/service/FollowService.java @@ -0,0 +1,17 @@ +package com.luojia_channel.modules.interact.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.luojia_channel.common.domain.UserDTO; +import com.luojia_channel.modules.interact.entity.Follow; + +import java.util.List; + + +public interface FollowService extends IService { + + void follow(Long followUserId, Boolean isFollow); + + boolean isFollow(Long followUserId); + + List followCommons(Long id); +} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/config/WebSocketConfig.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/config/WebSocketConfig.java index e039eb2..8f96c47 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/config/WebSocketConfig.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/config/WebSocketConfig.java @@ -1,17 +1,27 @@ package com.luojia_channel.modules.message.config; +import jakarta.servlet.ServletContext; +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; -import org.springframework.web.socket.config.annotation.WebSocketConfigurer; -import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; +import org.springframework.web.util.WebAppRootListener; @Configuration @EnableWebSocket -public class WebSocketConfig implements WebSocketConfigurer { +public class WebSocketConfig implements ServletContextInitializer { + + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } + @Override - public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { - registry.addHandler(new MyWebSocketHandler(), "/ws") - .setAllowedOrigins("*"); // 允许跨域 + public void onStartup(ServletContext servletContext) { + servletContext.addListener(WebAppRootListener.class); + servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize", (1024 * 200) + ""); + servletContext.setInitParameter("org.apache.tomcat.websocket.binaryBufferSize", (1024 * 200) + ""); } } diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/dto/MessageRequest.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/dto/MessageRequest.java new file mode 100644 index 0000000..697a737 --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/dto/MessageRequest.java @@ -0,0 +1,18 @@ +package com.luojia_channel.modules.message.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MessageRequest { + private Integer messageType; // 私信0,系统通知1 + private String content; // 消息内容 + private Long receiverId; // 接收者ID + private String senderName; // 用户名 + private String senderAvatar; // 用户头像 +} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/dto/MessageResponse.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/dto/MessageResponse.java new file mode 100644 index 0000000..a899139 --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/dto/MessageResponse.java @@ -0,0 +1,22 @@ +package com.luojia_channel.modules.message.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MessageResponse { + private Integer messageType; // 私信0,系统通知1 + private String content; // 消息内容 + private Long senderId; + private Long receiverId; // 接收者ID + private String senderName; // 用户名 + private String senderAvatar; // 用户头像 + private LocalDateTime createTime; +} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/entity/MessageDO.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/entity/MessageDO.java new file mode 100644 index 0000000..bbb1d2c --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/entity/MessageDO.java @@ -0,0 +1,20 @@ +package com.luojia_channel.modules.message.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import java.time.LocalDateTime; + +/** + * 消息实体类 + */ +@Data +@TableName("message") +public class MessageDO { + @TableId(type = IdType.AUTO) + private Long id; + private Integer messageType; // 0-私聊, 1-系统消息 + private String content; + private Long senderId; + private Long receiverId; + private LocalDateTime createTime; +} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mapper/MessageMapper.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mapper/MessageMapper.java new file mode 100644 index 0000000..d33f5f7 --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mapper/MessageMapper.java @@ -0,0 +1,10 @@ +package com.luojia_channel.modules.message.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.luojia_channel.modules.message.entity.MessageDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface MessageMapper extends BaseMapper { + +} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/AbstractSendProduceTemplate.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/AbstractSendProduceTemplate.java index 343a078..cd8b44d 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/AbstractSendProduceTemplate.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/AbstractSendProduceTemplate.java @@ -5,8 +5,10 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.messaging.MessageHeaders; +import org.springframework.stereotype.Component; @Slf4j +@Component @RequiredArgsConstructor // 模板方法类,提供消息发送的模板方法 public abstract class AbstractSendProduceTemplate { diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/consumer/NotificationListener.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/consumer/NotificationListener.java new file mode 100644 index 0000000..f3c7f78 --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/consumer/NotificationListener.java @@ -0,0 +1,36 @@ +package com.luojia_channel.modules.message.mq.consumer; + +import cn.hutool.core.bean.BeanUtil; +import com.luojia_channel.modules.message.dto.MessageRequest; +import com.luojia_channel.modules.message.mq.domain.NotificationMessage; +import com.luojia_channel.modules.message.server.WebSocketServer; +import lombok.RequiredArgsConstructor; +import org.springframework.amqp.rabbit.annotation.Exchange; +import org.springframework.amqp.rabbit.annotation.Queue; +import org.springframework.amqp.rabbit.annotation.QueueBinding; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class NotificationListener { + public static final String EXCHANGE_NAME = "notify.exchange"; + public static final String QUEUE_NAME = "notify.queue"; + public static final String ROUTING_KEY = "notify.routing.key"; + + private final WebSocketServer webSocketServer; + @RabbitListener(bindings = @QueueBinding( + value = @Queue(name = QUEUE_NAME), + exchange = @Exchange(name = EXCHANGE_NAME, type = "direct"), + key = ROUTING_KEY + )) + + public void handleNotification(NotificationMessage message) { + MessageRequest request = BeanUtil.copyProperties(message, MessageRequest.class); + if (message.getMessageType() == 0) { + webSocketServer.sendPrivateMessage(message.getSenderId(), request); + } else { + webSocketServer.sendSystemNotification(request); + } + } +} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/domain/NotificationMessage.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/domain/NotificationMessage.java new file mode 100644 index 0000000..7d96dc8 --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/mq/domain/NotificationMessage.java @@ -0,0 +1,19 @@ +package com.luojia_channel.modules.message.mq.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class NotificationMessage { + private Long senderId; + private Long receiverId; + private String content; + private String senderName; // 用户名 + private String senderAvatar; // 用户头像 + private Integer messageType; // 0-私信 1-系统 +} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/server/WebSocketServer.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/server/WebSocketServer.java new file mode 100644 index 0000000..4b9d87c --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/message/server/WebSocketServer.java @@ -0,0 +1,141 @@ +package com.luojia_channel.modules.message.server; + +import cn.hutool.core.bean.BeanUtil; +import com.alibaba.fastjson.JSON; +import com.luojia_channel.modules.message.dto.MessageRequest; +import com.luojia_channel.modules.message.dto.MessageResponse; +import com.luojia_channel.modules.message.entity.MessageDO; +import com.luojia_channel.modules.message.mapper.MessageMapper; +import jakarta.websocket.*; +import jakarta.websocket.server.PathParam; +import jakarta.websocket.server.ServerEndpoint; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@ServerEndpoint(value = "/connect/{userId}") +@Slf4j +@Component +@RequiredArgsConstructor +public class WebSocketServer { + // 存储在线用户会话 + private final static Map CLIENTS = new ConcurrentHashMap<>(); + private final MessageMapper messageMapper; + + @OnOpen + public void onOpen(@PathParam("userId") String userId, + Session session) { + // 将新连接加入客户端列表 + CLIENTS.put(userId, session); + log.info("用户 [{}] 已连接,当前在线人数:{}", userId, CLIENTS.size()); + } + + @OnClose + public void onClose(@PathParam("userId") String userId, + Session session) { + // 移除断开连接的用户 + CLIENTS.remove(userId); + log.info("用户 [{}] 已断开,当前在线人数:{}", userId, CLIENTS.size()); + } + + @OnError + public void onError(@PathParam("userId") String userId, + Session session, Throwable e) { + log.error("用户 [{}] 发生错误: {}", userId, e.getMessage(), e); + try { + session.close(); + } catch (IOException ex) { + log.error("关闭会话失败: {}", ex.getMessage()); + } + } + + @OnMessage + public void onMessage(@PathParam("userId") String senderId, + String message, Session session) { + try { + // 解析客户端发送的 JSON 消息 + MessageRequest request = JSON.parseObject(message, MessageRequest.class); + + switch (request.getMessageType()) { + case 0: + sendPrivateMessage(Long.parseLong(senderId), request); + break; + case 1: + sendSystemNotification(request); + break; + default: + log.warn("未知消息类型: {}", request.getMessageType()); + } + } catch (Exception e) { + log.error("消息处理失败: {}", e.getMessage()); + sendErrorResponse(session, "消息处理失败,请稍后重试"); + } + } + + // 发送一对一私信 + public void sendPrivateMessage(Long senderId, MessageRequest request) { + Long receiverId = request.getReceiverId(); + Session receiverSession = CLIENTS.get(receiverId.toString()); + // 构建私信响应 + MessageResponse response = MessageResponse.builder() + .messageType(0) + .content(request.getContent()) + .senderId(senderId) + .senderName(request.getSenderName()) + .senderAvatar(request.getSenderAvatar()) + .receiverId(receiverId) + .createTime(LocalDateTime.now()) + .build(); + + // 发送给接收方 + if (receiverSession != null && receiverSession.isOpen()) { + sendMessage(receiverSession, JSON.toJSONString(response)); + } else { + log.warn("接收方 [{}] 不在线,消息无法即时送达", receiverId); + } + MessageDO message = BeanUtil.copyProperties(response, MessageDO.class); + messageMapper.insert(message); + sendMessage(CLIENTS.get(senderId.toString()), JSON.toJSONString(response)); + } + + // 发送系统通知 + public void sendSystemNotification(MessageRequest request) { + MessageResponse response = MessageResponse.builder() + .messageType(1) + .content(request.getContent()) + .createTime(LocalDateTime.now()) + .build(); + // 广播给所有在线用户 + for (Session session : CLIENTS.values()) { + sendMessage(session, JSON.toJSONString(response)); + } + MessageDO message = BeanUtil.copyProperties(response, MessageDO.class); + messageMapper.insert(message); + } + + // 安全消息 + private void sendMessage(Session session, String message) { + try { + if (session != null && session.isOpen()) { + session.getBasicRemote().sendText(message); + } + } catch (IOException e) { + log.error("发送消息失败: {}", e.getMessage()); + } + } + + // 发送错误响应 + private void sendErrorResponse(Session session, String errorMessage) { + MessageResponse errorResponse = MessageResponse.builder() + .messageType(-1) // 错误消息类型 + .content(errorMessage) + .createTime(LocalDateTime.now()) + .build(); + sendMessage(session, JSON.toJSONString(errorResponse)); + } +} \ No newline at end of file diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/mq/producer/NotificationProducer.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/mq/producer/NotificationProducer.java new file mode 100644 index 0000000..a8beeb0 --- /dev/null +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/mq/producer/NotificationProducer.java @@ -0,0 +1,27 @@ +package com.luojia_channel.modules.post.mq.producer; + +import com.luojia_channel.modules.message.mq.AbstractSendProduceTemplate; +import com.luojia_channel.modules.message.mq.BaseSendExtendDTO; +import com.luojia_channel.modules.message.mq.domain.NotificationMessage; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class NotificationProducer extends AbstractSendProduceTemplate { + + public NotificationProducer(@Autowired RabbitTemplate rabbitTemplate){ + super(rabbitTemplate); + } + + @Override + protected BaseSendExtendDTO buildBaseSendParam(NotificationMessage messageSendEvent) { + return BaseSendExtendDTO.builder() + .eventName("NotificationMessageEvent") + .exchange("notify.exchange") + .routingKey("notify.routing.key") + .keys(messageSendEvent.getSenderId().toString()) + .delay(null) + .build(); + } +} diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/CommentServiceImpl.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/CommentServiceImpl.java index 794da2b..9278ddb 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/CommentServiceImpl.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/CommentServiceImpl.java @@ -10,6 +10,7 @@ import com.luojia_channel.common.domain.page.PageResponse; import com.luojia_channel.common.exception.PostException; import com.luojia_channel.common.utils.PageUtil; import com.luojia_channel.common.utils.UserContext; +import com.luojia_channel.modules.message.mq.domain.NotificationMessage; import com.luojia_channel.modules.post.dto.req.CommentPageQueryDTO; import com.luojia_channel.modules.post.dto.req.CommentSaveDTO; import com.luojia_channel.modules.post.dto.resp.CommentInfoDTO; @@ -17,11 +18,13 @@ import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO; import com.luojia_channel.modules.post.entity.Comment; import com.luojia_channel.modules.post.entity.Post; import com.luojia_channel.modules.post.mapper.CommentMapper; +import com.luojia_channel.modules.post.mq.producer.NotificationProducer; import com.luojia_channel.modules.post.service.CommentService; import com.luojia_channel.modules.post.utils.ValidatePostUtil; import com.luojia_channel.modules.user.entity.User; import com.luojia_channel.modules.user.mapper.UserMapper; import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -41,28 +44,30 @@ public class CommentServiceImpl extends ServiceImpl impl private final ValidatePostUtil validatePostUtil; private final CommentMapper commentMapper; private final UserMapper userMapper; + private final NotificationProducer notificationProducer; @Override @Transactional(rollbackFor = Exception.class) public void saveComment(CommentSaveDTO commentSaveDTO) { validatePostUtil.validateComment(commentSaveDTO); Comment comment = BeanUtil.copyProperties(commentSaveDTO, Comment.class); + comment.setCreateTime(LocalDateTime.now()); + comment.setUpdateTime(LocalDateTime.now()); if(!save(comment)){ throw new PostException("创建评论失败"); } Long parentCommentId = commentSaveDTO.getParentCommentId(); if(parentCommentId != null){ - LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(Comment.class) - .eq(Comment::getId, parentCommentId) - .setSql("reply_count = reply_count + 1"); - int update = commentMapper.update(null, updateWrapper); + Comment partentComment = commentMapper.selectById(parentCommentId); + partentComment.setReplyCount(partentComment.getReplyCount() + 1); + int update = commentMapper.updateById(partentComment); if(update <= 0) { throw new PostException("回复评论失败"); } Long topId = commentSaveDTO.getTopId(); // 更新顶级评论回复数 if(!parentCommentId.equals(topId)){ - updateWrapper = Wrappers.lambdaUpdate(Comment.class) + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(Comment.class) .eq(Comment::getId, topId) .setSql("reply_count = reply_count + 1"); update = commentMapper.update(null, updateWrapper); @@ -70,11 +75,19 @@ public class CommentServiceImpl extends ServiceImpl impl throw new PostException("回复顶级评论失败"); } } - - // TODO 消息通知 - + // 消息通知 + String content = String.format("%s 回复了你的评论: %s", + UserContext.getUsername(), + StringUtils.abbreviate(commentSaveDTO.getContent(), 20)); + NotificationMessage notificationMessage = NotificationMessage.builder() + .senderId(UserContext.getUserId()) + .senderName(UserContext.getUsername()) + .senderAvatar(UserContext.getAvatar()) + .receiverId(partentComment.getUserId()) + .content(content) + .build(); + notificationProducer.sendMessage(notificationMessage); } - } @Override diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/PostServiceImpl.java b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/PostServiceImpl.java index 7c80b53..d89f44d 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/PostServiceImpl.java +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/java/com/luojia_channel/modules/post/service/impl/PostServiceImpl.java @@ -48,6 +48,8 @@ public class PostServiceImpl extends ServiceImpl implements Po public void createPost(PostSaveDTO postSaveDTO) { validatePostUtil.validatePost(postSaveDTO); Post post = BeanUtil.copyProperties(postSaveDTO, Post.class); + post.setCreateTime(LocalDateTime.now()); + post.setUpdateTime(LocalDateTime.now()); if(!save(post)){ throw new PostException("创建帖子失败"); } diff --git a/珞珈岛-项目相关文件/luojia-island/service/src/main/resources/db/luojia_channel.sql b/珞珈岛-项目相关文件/luojia-island/service/src/main/resources/db/luojia_channel.sql index d3da488..ac2a900 100644 --- a/珞珈岛-项目相关文件/luojia-island/service/src/main/resources/db/luojia_channel.sql +++ b/珞珈岛-项目相关文件/luojia-island/service/src/main/resources/db/luojia_channel.sql @@ -152,3 +152,27 @@ CREATE TABLE `view_record` ( INDEX idx_post_type (post_type), INDEX idx_post_id (post_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='浏览记录表'; + +## 私信消息表 +DROP TABLE IF EXISTS `message`; +CREATE TABLE message ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + message_type TINYINT NOT NULL COMMENT '0-私聊, 1-系统消息', + content TEXT NOT NULL, + sender_id BIGINT NOT NULL, + receiver_id BIGINT NOT NULL, + create_time DATETIME NOT NULL, + INDEX idx_sender_id (sender_id), + INDEX idx_receiver_id (receiver_id), + INDEX idx_create_time (create_time) +); + +## 关注表 +DROP TABLE IF EXISTS `follow`; +CREATE TABLE `follow` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', + `user_id` bigint(20) UNSIGNED NOT NULL COMMENT '用户id', + `follow_user_id` bigint(20) UNSIGNED NOT NULL COMMENT '关联的用户id', + `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='关注表';