Browse Source

Merge remote-tracking branch 'origin/wyc' into test

# Conflicts:
#	src/main/java/com/zhentao/groups/dto/AddGroupsDto.java
#	src/main/java/com/zhentao/groups/service/GroupsService.java
#	src/main/java/com/zhentao/information/controller/MessageController.java
#	src/main/java/com/zhentao/information/handler/WebSocketHandler.java
#	src/main/resources/application.yml
lzy 2 weeks ago
parent
commit
ea4794a80a
56 changed files with 1701 additions and 212 deletions
  1. 6 0
      pom.xml
  2. 0 29
      src/main/java/com/zhentao/config/NettyConfig.java
  3. 2 0
      src/main/java/com/zhentao/groups/controller/groupsController.java
  4. 3 2
      src/main/java/com/zhentao/groups/dto/AddGroupsDto.java
  5. 2 2
      src/main/java/com/zhentao/groups/pojo/GroupMembers.java
  6. 1 2
      src/main/java/com/zhentao/groups/service/GroupsService.java
  7. 70 0
      src/main/java/com/zhentao/information/cache/ChannelCache.java
  8. 99 0
      src/main/java/com/zhentao/information/config/NettyConfig.java
  9. 42 0
      src/main/java/com/zhentao/information/config/WebSocketHandshakeInterceptor.java
  10. 9 7
      src/main/java/com/zhentao/information/controller/MessageController.java
  11. 1 1
      src/main/java/com/zhentao/information/entity/ChatMessage.java
  12. 14 10
      src/main/java/com/zhentao/information/entity/Message.java
  13. 40 0
      src/main/java/com/zhentao/information/handler/HeartbeatHandler.java
  14. 90 28
      src/main/java/com/zhentao/information/handler/WebSocketHandler.java
  15. 2 2
      src/main/java/com/zhentao/information/repository/ChatMessageRepository.java
  16. 112 0
      src/main/java/com/zhentao/information/service/WebSocketService.java
  17. 32 0
      src/main/java/com/zhentao/moment/controller/MonmentController.java
  18. 109 0
      src/main/java/com/zhentao/moment/domain/MomentComments.java
  19. 85 0
      src/main/java/com/zhentao/moment/domain/MomentLikes.java
  20. 92 0
      src/main/java/com/zhentao/moment/domain/MomentMedias.java
  21. 117 0
      src/main/java/com/zhentao/moment/domain/UserMoments.java
  22. 14 0
      src/main/java/com/zhentao/moment/dto/MonmentDto.java
  23. 34 0
      src/main/java/com/zhentao/moment/enums/ContentTypeEnum.java
  24. 18 0
      src/main/java/com/zhentao/moment/mapper/MomentCommentsMapper.java
  25. 18 0
      src/main/java/com/zhentao/moment/mapper/MomentLikesMapper.java
  26. 18 0
      src/main/java/com/zhentao/moment/mapper/MomentMediasMapper.java
  27. 18 0
      src/main/java/com/zhentao/moment/mapper/UserMomentsMapper.java
  28. 13 0
      src/main/java/com/zhentao/moment/service/MomentCommentsService.java
  29. 13 0
      src/main/java/com/zhentao/moment/service/MomentLikesService.java
  30. 13 0
      src/main/java/com/zhentao/moment/service/MomentMediasService.java
  31. 18 0
      src/main/java/com/zhentao/moment/service/UserMomentsService.java
  32. 22 0
      src/main/java/com/zhentao/moment/service/impl/MomentCommentsServiceImpl.java
  33. 22 0
      src/main/java/com/zhentao/moment/service/impl/MomentLikesServiceImpl.java
  34. 22 0
      src/main/java/com/zhentao/moment/service/impl/MomentMediasServiceImpl.java
  35. 95 0
      src/main/java/com/zhentao/moment/service/impl/UserMomentsServiceImpl.java
  36. 0 110
      src/main/java/com/zhentao/oss/controller/ossController.java
  37. 17 0
      src/main/java/com/zhentao/osspicture/OssConfig.java
  38. 61 0
      src/main/java/com/zhentao/osspicture/OssUtil.java
  39. 10 7
      src/main/java/com/zhentao/shouye/controller/UserShouyeController.java
  40. 62 4
      src/main/java/com/zhentao/user/controller/UserController.java
  41. 3 3
      src/main/java/com/zhentao/user/domain/UserLogin.java
  42. 53 0
      src/main/java/com/zhentao/user/dto/FriendDto.java
  43. 13 0
      src/main/java/com/zhentao/user/dto/UserInfo.java
  44. 1 0
      src/main/java/com/zhentao/user/dto/UserPassDto.java
  45. 6 0
      src/main/java/com/zhentao/user/service/UserLoginService.java
  46. 19 2
      src/main/java/com/zhentao/user/service/impl/UserLoginServiceImpl.java
  47. 18 0
      src/main/java/com/zhentao/utils/SensitiveWordFilter.java
  48. 78 0
      src/main/java/com/zhentao/utils/SnowflakeUtil.java
  49. 7 0
      src/main/java/com/zhentao/vo/Result.java
  50. 2 0
      src/main/resources/application.yml
  51. 2 2
      src/main/resources/mapper/GroupsMapper.xml
  52. 22 0
      src/main/resources/mapper/MomentCommentsMapper.xml
  53. 18 0
      src/main/resources/mapper/MomentLikesMapper.xml
  54. 19 0
      src/main/resources/mapper/MomentMediasMapper.xml
  55. 23 0
      src/main/resources/mapper/UserMomentsMapper.xml
  56. 1 1
      src/main/resources/mapper/UserShouyeMapper.xml

+ 6 - 0
pom.xml

@@ -118,6 +118,12 @@
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <version>3.15.1</version> <!-- 使用最新稳定版本 -->
+        </dependency>
+
     </dependencies>
     <dependencyManagement>
         <dependencies>

+ 0 - 29
src/main/java/com/zhentao/config/NettyConfig.java

@@ -1,29 +0,0 @@
-package com.zhentao.config;
-
-import io.netty.bootstrap.ServerBootstrap;
-import io.netty.channel.ChannelOption;
-import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.socket.nio.NioServerSocketChannel;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * Netty服务器配置类
- */
-@Configuration
-public class NettyConfig {
-
-    @Value("${netty.port:8888}")
-    private int port;
-
-    @Bean
-    public ServerBootstrap serverBootstrap() {
-        ServerBootstrap bootstrap = new ServerBootstrap();
-        bootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup())
-                .channel(NioServerSocketChannel.class)
-                .option(ChannelOption.SO_BACKLOG, 128)
-                .childOption(ChannelOption.SO_KEEPALIVE, true);
-        return bootstrap;
-    }
-}

+ 2 - 0
src/main/java/com/zhentao/groups/controller/groupsController.java

@@ -66,4 +66,6 @@ public class groupsController {
 
 
 
+
+
 }

+ 3 - 2
src/main/java/com/zhentao/groups/dto/AddGroupsDto.java

@@ -5,8 +5,8 @@ import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
-import lombok.ToString;
-import org.springframework.web.multipart.MultipartFile;
+
+
 
 @Data
 @AllArgsConstructor
@@ -28,6 +28,7 @@ public class AddGroupsDto {
      * 群头像
      */
     private String avatar;
+
     /**
      * 群公告
      */

+ 2 - 2
src/main/java/com/zhentao/groups/pojo/GroupMembers.java

@@ -19,7 +19,7 @@ import lombok.Data;
 @Data
 public class GroupMembers implements Serializable {
     /**
-     * 
+     *
      */
     @JsonFormat(shape = JsonFormat.Shape.STRING)
     @TableId
@@ -113,4 +113,4 @@ public class GroupMembers implements Serializable {
         sb.append("]");
         return sb.toString();
     }
-}
+}

+ 1 - 2
src/main/java/com/zhentao/groups/service/GroupsService.java

@@ -1,9 +1,8 @@
 package com.zhentao.groups.service;
 
-
 import com.zhentao.groups.dto.AddGroupMembers;
+import com.zhentao.groups.dto.AddGroupsDto;
 import com.zhentao.groups.dto.DelGroupMembers;
-import com.zhentao.groups.dto.OutGroupsDto;
 import com.zhentao.groups.pojo.Groupss;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.zhentao.vo.Result;

+ 70 - 0
src/main/java/com/zhentao/information/cache/ChannelCache.java

@@ -0,0 +1,70 @@
+package com.zhentao.information.cache;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Channel缓存管理类
+ * 用于管理用户Channel
+ */
+@Slf4j
+@Component
+public class ChannelCache {
+    
+    /**
+     * 用户ID和Channel的映射关系
+     * key: 用户ID
+     * value: Channel上下文
+     */
+    private static final Map<String, ChannelHandlerContext> USER_CHANNEL_MAP = new ConcurrentHashMap<>();
+
+    /**
+     * 添加用户Channel映射
+     * @param userId 用户ID
+     * @param ctx Channel上下文
+     */
+    public void addCache(String userId, ChannelHandlerContext ctx) {
+        USER_CHANNEL_MAP.put(userId, ctx);
+        log.info("用户 {} 的Channel已添加到缓存", userId);
+    }
+
+    /**
+     * 获取用户的Channel
+     * @param userId 用户ID
+     * @return Channel上下文
+     */
+    public ChannelHandlerContext getCache(String userId) {
+        return USER_CHANNEL_MAP.get(userId);
+    }
+
+    /**
+     * 移除用户Channel映射
+     * @param userId 用户ID
+     */
+    public void removeCache(String userId) {
+        USER_CHANNEL_MAP.remove(userId);
+        log.info("用户 {} 的Channel已从缓存移除", userId);
+    }
+
+    /**
+     * 获取所有用户Channel映射
+     * @return 用户Channel映射Map
+     */
+    public Map<String, ChannelHandlerContext> getAllCache() {
+        return USER_CHANNEL_MAP;
+    }
+
+    /**
+     * 判断用户是否在线
+     * @param userId 用户ID
+     * @return 是否在线
+     */
+    public boolean isOnline(String userId) {
+        return USER_CHANNEL_MAP.containsKey(userId);
+    }
+} 

+ 99 - 0
src/main/java/com/zhentao/information/config/NettyConfig.java

@@ -0,0 +1,99 @@
+package com.zhentao.information.config;
+
+import com.zhentao.information.handler.WebSocketHandler;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpServerCodec;
+import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
+import io.netty.handler.stream.ChunkedWriteHandler;
+import io.netty.handler.timeout.IdleStateHandler;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.Resource;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Netty服务器配置类
+ * 配置WebSocket服务器的启动参数和处理器链
+ */
+@Slf4j
+@Configuration
+public class NettyConfig {
+
+    /**
+     * WebSocket服务器端口
+     */
+    @Value("${netty.websocket.port}")
+    private int port;
+
+    /**
+     * WebSocket消息处理器
+     */
+    @Resource
+    private WebSocketHandler webSocketHandler;
+
+    /**
+     * 配置并启动Netty服务器
+     * @return ServerBootstrap实例
+     */
+    @Bean
+    public ServerBootstrap serverBootstrap() {
+        // 创建主从线程组
+        // bossGroup用于接收客户端连接
+        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
+        // workerGroup用于处理客户端数据
+        EventLoopGroup workerGroup = new NioEventLoopGroup();
+        
+        // 创建服务器启动对象
+        ServerBootstrap bootstrap = new ServerBootstrap();
+        bootstrap.group(bossGroup, workerGroup)
+                // 设置服务器通道实现
+                .channel(NioServerSocketChannel.class)
+                // 设置线程队列等待连接个数
+                .option(ChannelOption.SO_BACKLOG, 128)
+                // 设置保持活动连接状态
+                .childOption(ChannelOption.SO_KEEPALIVE, true)
+                // 禁用Nagle算法,减少延迟
+                .childOption(ChannelOption.TCP_NODELAY, true)
+                // 设置处理器
+                .childHandler(new ChannelInitializer<SocketChannel>() {
+                    @Override
+                    protected void initChannel(SocketChannel ch) {
+                        // 获取管道
+                        ch.pipeline()
+                                // HTTP编解码器
+                                .addLast(new HttpServerCodec())
+                                // 支持大数据流
+                                .addLast(new ChunkedWriteHandler())
+                                // HTTP消息聚合器
+                                .addLast(new HttpObjectAggregator(65536))
+                                // 心跳检测,60秒没有收到消息就触发
+                                .addLast(new IdleStateHandler(60, 0, 0, TimeUnit.SECONDS))
+                                // WebSocket协议处理器
+                                .addLast(new WebSocketServerProtocolHandler("/ws", null, true))
+                                // 自定义消息处理器
+                                .addLast(webSocketHandler);
+                    }
+                });
+        
+        try {
+            // 绑定端口并启动服务器
+            bootstrap.bind(port).sync();
+            log.info("Netty WebSocket服务器启动成功,端口:{}", port);
+        } catch (InterruptedException e) {
+            log.error("Netty WebSocket服务器启动失败", e);
+            Thread.currentThread().interrupt();
+        }
+        
+        return bootstrap;
+    }
+} 

+ 42 - 0
src/main/java/com/zhentao/information/config/WebSocketHandshakeInterceptor.java

@@ -0,0 +1,42 @@
+package com.zhentao.information.config;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.util.AttributeKey;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+
+public class WebSocketHandshakeInterceptor extends ChannelInboundHandlerAdapter {
+    public static final AttributeKey<String> PEER_ID_KEY = AttributeKey.valueOf("peerId");
+    public static final AttributeKey<String> TOKEN_KEY = AttributeKey.valueOf("token");
+
+    @Override
+    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+        if (msg instanceof FullHttpRequest) {
+            FullHttpRequest req = (FullHttpRequest) msg;
+            URI uri = new URI(req.uri());
+            String query = uri.getQuery();
+            if (query != null) {
+                Map<String, String> params = new HashMap<>();
+                for (String param : query.split("&")) {
+                    String[] pair = param.split("=");
+                    if (pair.length == 2) {
+                        params.put(URLDecoder.decode(pair[0], String.valueOf(StandardCharsets.UTF_8)),
+                                   URLDecoder.decode(pair[1], String.valueOf(StandardCharsets.UTF_8)));
+                    }
+                }
+                if (params.containsKey("peerId")) {
+                    ctx.channel().attr(PEER_ID_KEY).set(params.get("peerId"));
+                }
+                if (params.containsKey("token")) {
+                    ctx.channel().attr(TOKEN_KEY).set(params.get("token"));
+                }
+            }
+        }
+        super.channelRead(ctx, msg);
+    }
+}

+ 9 - 7
src/main/java/com/zhentao/information/controller/MessageController.java

@@ -3,10 +3,12 @@ package com.zhentao.information.controller;
 
 import com.alibaba.fastjson2.JSON;
 import com.zhentao.config.NullLogin;
+import com.alibaba.fastjson.JSON;
+import com.zhentao.information.cache.ChannelCache;
 import com.zhentao.information.entity.ChatMessage;
 import com.zhentao.information.entity.Message;
-import com.zhentao.information.handler.WebSocketHandler;
 import com.zhentao.information.repository.ChatMessageRepository;
+import com.zhentao.tool.TokenUtils;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
 import lombok.extern.slf4j.Slf4j;
@@ -25,7 +27,7 @@ import java.util.Map;
 public class MessageController {
 
     @Resource
-    private WebSocketHandler webSocketHandler;
+    private ChannelCache channelCache;
 
     @Resource
     private ChatMessageRepository chatMessageRepository;
@@ -49,7 +51,7 @@ public class MessageController {
         chatMessage.setFromUserId(message.getFromUserId());
         chatMessage.setToUserId(message.getToUserId());
         chatMessage.setContent(message.getContent());
-        chatMessage.setType(message.getType());
+        chatMessage.setType(String.valueOf(message.getType()));
         chatMessage.setTimestamp(System.currentTimeMillis());
         chatMessage.setIsRead(false);
         chatMessage.setChatId(chatId);
@@ -58,8 +60,7 @@ public class MessageController {
         chatMessageRepository.save(chatMessage);
 
         // 获取接收者的Channel
-        Map<String, ChannelHandlerContext> userChannelMap = webSocketHandler.getUserChannelMap();
-        ChannelHandlerContext toUserCtx = userChannelMap.get(message.getToUserId());
+        ChannelHandlerContext toUserCtx = channelCache.getCache(message.getToUserId());
 
         if (toUserCtx != null) {
             // 发送消息给接收者
@@ -74,8 +75,9 @@ public class MessageController {
      * 获取两个用户之间的聊天记录
      */
     @GetMapping("/history")
-    public List<ChatMessage> getChatHistory(@RequestParam String userId1, @RequestParam String userId2) {
-        String chatId = generateChatId(userId1, userId2);
+    public List<ChatMessage> getChatHistory(@RequestHeader("token") String token, @RequestParam String userId2) {
+        String userIdFromToken = TokenUtils.getUserIdFromToken(token);
+        String chatId = generateChatId(userIdFromToken, userId2);
         return chatMessageRepository.findByChatId(chatId);
     }
 

+ 1 - 1
src/main/java/com/zhentao/information/entity/ChatMessage.java

@@ -23,7 +23,7 @@ public class ChatMessage {
 
     private String content;
 
-    private Integer type;
+    private String type;
 
     private Long timestamp;
 

+ 14 - 10
src/main/java/com/zhentao/information/entity/Message.java

@@ -3,32 +3,36 @@ package com.zhentao.information.entity;
 import lombok.Data;
 
 /**
- * 消息实体类
+ * WebSocket消息实体类
  */
 @Data
 public class Message {
     /**
+     * 消息类型
+     * connect: 连接消息
+     * text: 文本消息
+     * image: 图片消息
+     * voice: 语音消息
+     */
+    private String type;
+    
+    /**
      * 发送者ID
      */
     private String fromUserId;
-
+    
     /**
      * 接收者ID
      */
     private String toUserId;
-
+    
     /**
      * 消息内容
      */
     private String content;
-
-    /**
-     * 消息类型
-     */
-    private Integer type;
-
+    
     /**
-     * 发送时间
+     * 消息时间戳
      */
     private Long timestamp;
 }

+ 40 - 0
src/main/java/com/zhentao/information/handler/HeartbeatHandler.java

@@ -0,0 +1,40 @@
+package com.zhentao.information.handler;
+
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.handler.timeout.IdleState;
+import io.netty.handler.timeout.IdleStateEvent;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * 心跳处理器
+ * 处理客户端的心跳检测
+ */
+@Slf4j
+@Component
+@ChannelHandler.Sharable
+public class HeartbeatHandler extends ChannelInboundHandlerAdapter {
+
+    /**
+     * 处理用户事件
+     * 当触发IdleStateEvent时调用
+     * @param ctx Channel上下文
+     * @param evt 事件对象
+     */
+    @Override
+    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+        if (evt instanceof IdleStateEvent) {
+            IdleStateEvent event = (IdleStateEvent) evt;
+            
+            // 如果是读空闲事件
+            if (event.state() == IdleState.READER_IDLE) {
+                log.info("读空闲,关闭连接:{}", ctx.channel().id().asLongText());
+                ctx.close();
+            }
+        } else {
+            super.userEventTriggered(ctx, evt);
+        }
+    }
+} 

+ 90 - 28
src/main/java/com/zhentao/information/handler/WebSocketHandler.java

@@ -1,57 +1,119 @@
 package com.zhentao.information.handler;
 
-
-import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson.JSON;
+import com.zhentao.information.entity.ChatMessage;
 import com.zhentao.information.entity.Message;
+import com.zhentao.information.repository.ChatMessageRepository;
+import com.zhentao.information.service.WebSocketService;
+import io.netty.channel.ChannelHandler;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.SimpleChannelInboundHandler;
 import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import javax.annotation.Resource;
 
 /**
  * WebSocket消息处理器
+ * 处理WebSocket连接、消息接收和发送
  */
 @Slf4j
 @Component
+@ChannelHandler.Sharable
 public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
 
-    // 用户ID和Channel的映射关系
-    private static final Map<String, ChannelHandlerContext> USER_CHANNEL_MAP = new ConcurrentHashMap<>();
+    @Resource
+    private ChatMessageRepository chatMessageRepository;
+
+    @Resource
+    private WebSocketService webSocketService;
 
     /**
-     * 获取用户Channel映射
-     * @return 用户Channel映射
+     * 处理接收到的WebSocket消息
      */
-    public Map<String, ChannelHandlerContext> getUserChannelMap() {
-        return USER_CHANNEL_MAP;
+    @Override
+    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
+        String text = msg.text();
+        log.info("收到消息:{}", text);
+        try {
+            Message message = JSON.parseObject(text, Message.class);
+
+            // 如果是连接消息,处理token
+            if ("connect".equals(message.getType())) {
+                webSocketService.handleUserLogin(message.getContent(), ctx);
+                return;
+            }
+
+            // 处理普通消息
+            handleMessage(message);
+
+        } catch (Exception e) {
+            log.error("处理消息失败", e);
+        }
     }
 
-    @Override
-    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
-        String message = msg.text();
-        Message messageObj = JSON.parseObject(message, Message.class);
-
-        // 存储用户连接
-        USER_CHANNEL_MAP.put(messageObj.getFromUserId(), ctx);
-
-        // 获取接收者的Channel
-        ChannelHandlerContext toUserCtx = USER_CHANNEL_MAP.get(messageObj.getToUserId());
-        if (toUserCtx != null) {
-            // 发送消息给接收者
-            toUserCtx.channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(messageObj)));
-            log.info("消息已发送给用户: {}, 内容: {}", messageObj.getToUserId(), messageObj.getContent());
+    /**
+     * 处理普通消息
+     */
+    private void handleMessage(Message message) {
+        // 生成聊天ID
+        String chatId = generateChatId(message.getFromUserId(), message.getToUserId());
+
+        // 创建MongoDB消息对象
+        ChatMessage chatMessage = new ChatMessage();
+        chatMessage.setFromUserId(message.getFromUserId());
+        chatMessage.setToUserId(message.getToUserId());
+        chatMessage.setContent(message.getContent());
+        chatMessage.setType(String.valueOf(message.getType()));
+        chatMessage.setTimestamp(System.currentTimeMillis());
+        chatMessage.setIsRead(false);
+        chatMessage.setChatId(chatId);
+
+        // 保存消息到MongoDB
+        chatMessageRepository.save(chatMessage);
+
+        // 发送消息给接收者
+        boolean sent = webSocketService.sendMessageToUser(message.getToUserId(), message);
+        System.err.println("判断对方用户是否在线"+sent);
+        if (sent) {
+            log.info("消息已发送给用户: {}, 内容: {}", message.getToUserId(), message.getContent());
         } else {
-            log.info("用户 {} 不在线", messageObj.getToUserId());
+            log.info("用户 {} 不在线,消息已保存到MongoDB", message.getToUserId());
         }
     }
 
+    /**
+     * 当新的WebSocket连接建立时调用
+     */
     @Override
-    public void handlerRemoved(ChannelHandlerContext ctx) {
-        // 用户断开连接时,移除映射关系
-        USER_CHANNEL_MAP.entrySet().removeIf(entry -> entry.getValue().equals(ctx));
+    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+        log.info("新的连接:{}", ctx.channel().id().asLongText());
+    }
+
+    /**
+     * 当WebSocket连接断开时调用
+     */
+    @Override
+    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+        log.info("连接断开:{}", ctx.channel().id().asLongText());
+    }
+
+    /**
+     * 处理异常情况
+     */
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+        log.error("WebSocket异常", cause);
+        ctx.close();
+    }
+
+    /**
+     * 生成聊天ID
+     */
+    private String generateChatId(String userId1, String userId2) {
+        return userId1.compareTo(userId2) < 0 ?
+                userId1 + "_" + userId2 :
+                userId2 + "_" + userId1;
     }
 }

+ 2 - 2
src/main/java/com/zhentao/information/repository/ChatMessageRepository.java

@@ -2,19 +2,19 @@ package com.zhentao.information.repository;
 
 import com.zhentao.information.entity.ChatMessage;
 import org.springframework.data.mongodb.repository.MongoRepository;
-import org.springframework.data.mongodb.repository.Query;
+import org.springframework.stereotype.Repository;
 
 import java.util.List;
 
 /**
  * 聊天消息仓库
  */
+@Repository
 public interface ChatMessageRepository extends MongoRepository<ChatMessage, String> {
 
     /**
      * 查询两个用户之间的聊天记录
      */
-    @Query("{'chatId': ?0}")
     List<ChatMessage> findByChatId(String chatId);
 
     /**

+ 112 - 0
src/main/java/com/zhentao/information/service/WebSocketService.java

@@ -0,0 +1,112 @@
+package com.zhentao.information.service;
+
+import com.alibaba.fastjson.JSON;
+import com.zhentao.information.cache.ChannelCache;
+import com.zhentao.information.entity.Message;
+import com.zhentao.tool.TokenUtils;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * WebSocket服务类
+ * 处理WebSocket连接、消息发送等业务逻辑
+ */
+@Slf4j
+@Service
+public class WebSocketService {
+
+    @Resource
+    private ChannelCache channelCache;
+
+    // 存储用户token的Map
+    private final Map<String, String> userTokenMap = new ConcurrentHashMap<>();
+
+    /**
+     * 存储用户token
+     * @param userId 用户ID
+     * @param token 用户token
+     */
+    public void storeUserToken(String userId, String token) {
+        userTokenMap.put(userId, token);
+        log.info("用户 {} 的token已存储", userId);
+    }
+
+    /**
+     * 获取用户token
+     * @param userId 用户ID
+     * @return 用户token
+     */
+    public String getUserToken(String userId) {
+        return userTokenMap.get(userId);
+    }
+
+    /**
+     * 处理用户登录
+     * @param token 用户token
+     * @param ctx Channel上下文
+     */
+    public void handleUserLogin(String token, ChannelHandlerContext ctx) {
+        String userId = TokenUtils.getUserIdFromToken(token);
+        if (userId != null) {
+            // 验证token是否与存储的token匹配
+            String storedToken = userTokenMap.get(userId);
+            if (storedToken != null && storedToken.equals(token)) {
+                // 将用户ID和Channel绑定
+                channelCache.addCache(userId, ctx);
+                log.info("用户 {} 连接成功", userId);
+                
+                // 发送连接成功消息
+                Message response = new Message();
+                response.setType("connect_success");
+                response.setContent("连接成功");
+                ctx.channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(response)));
+            } else {
+                log.error("用户 {} 的token验证失败", userId);
+                ctx.close();
+            }
+        } else {
+            log.error("无效的token");
+            ctx.close();
+        }
+    }
+
+    /**
+     * 发送消息给指定用户
+     * @param userId 接收者用户ID
+     * @param message 消息内容
+     * @return 是否发送成功
+     */
+    public boolean sendMessageToUser(String userId, Message message) {
+        ChannelHandlerContext ctx = channelCache.getCache(userId);
+        if (ctx != null) {
+            ctx.channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(message)));
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 广播消息给所有在线用户
+     * @param message 消息内容
+     */
+    public void broadcastMessage(Message message) {
+        channelCache.getAllCache().forEach((userId, ctx) -> {
+            ctx.channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(message)));
+        });
+    }
+
+    /**
+     * 检查用户是否在线
+     * @param userId 用户ID
+     * @return 是否在线
+     */
+    public boolean isUserOnline(String userId) {
+        return channelCache.getCache(userId) != null;
+    }
+} 

+ 32 - 0
src/main/java/com/zhentao/moment/controller/MonmentController.java

@@ -0,0 +1,32 @@
+package com.zhentao.moment.controller;
+
+import com.zhentao.moment.dto.MonmentDto;
+import com.zhentao.moment.service.UserMomentsService;
+import com.zhentao.tool.TokenUtils;
+import com.zhentao.vo.Result;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("monment")
+public class MonmentController {
+    @Autowired
+    private UserMomentsService userMomentsService;
+//    发布朋友圈
+    @RequestMapping("sendMonment")
+    public Result sendMonment(@RequestHeader String  token,@RequestBody MonmentDto monmentDto){
+        return userMomentsService.sendMonment(token,monmentDto);
+    }
+////    查看所有朋友圈
+//    @RequestMapping("getAllMonment")
+//    删除朋友圈
+//    @RequestMapping("deleteMonment")
+//    public Result deleteMonment(@RequestHeader String token,@RequestBody MonmentDto monmentDto){
+//        String uid = TokenUtils.getUserIdFromToken(token);
+//        monmentDto.setUid(Long.valueOf(uid));
+//        return userMomentsService.deleteMonment(monmentDto);
+//    }
+}

+ 109 - 0
src/main/java/com/zhentao/moment/domain/MomentComments.java

@@ -0,0 +1,109 @@
+package com.zhentao.moment.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.util.Date;
+import lombok.Data;
+
+/**
+ * 朋友圈评论表
+ * @TableName moment_comments
+ */
+@TableName(value ="moment_comments")
+@Data
+public class MomentComments implements Serializable {
+    /**
+     * 
+     */
+    @TableId(type = IdType.AUTO)
+    private Long commentId;
+
+    /**
+     * 动态ID
+     */
+    private Long momentId;
+
+    /**
+     * 评论用户ID
+     */
+    private Long userId;
+
+    /**
+     * 评论内容
+     */
+    private String content;
+
+    /**
+     * 回复的评论ID
+     */
+    private Long replyTo;
+
+    /**
+     * 回复的用户ID
+     */
+    private Long replyToUser;
+
+    /**
+     * 
+     */
+    private Date createdAt;
+
+    @TableField(exist = false)
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        }
+        if (that == null) {
+            return false;
+        }
+        if (getClass() != that.getClass()) {
+            return false;
+        }
+        MomentComments other = (MomentComments) that;
+        return (this.getCommentId() == null ? other.getCommentId() == null : this.getCommentId().equals(other.getCommentId()))
+            && (this.getMomentId() == null ? other.getMomentId() == null : this.getMomentId().equals(other.getMomentId()))
+            && (this.getUserId() == null ? other.getUserId() == null : this.getUserId().equals(other.getUserId()))
+            && (this.getContent() == null ? other.getContent() == null : this.getContent().equals(other.getContent()))
+            && (this.getReplyTo() == null ? other.getReplyTo() == null : this.getReplyTo().equals(other.getReplyTo()))
+            && (this.getReplyToUser() == null ? other.getReplyToUser() == null : this.getReplyToUser().equals(other.getReplyToUser()))
+            && (this.getCreatedAt() == null ? other.getCreatedAt() == null : this.getCreatedAt().equals(other.getCreatedAt()));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((getCommentId() == null) ? 0 : getCommentId().hashCode());
+        result = prime * result + ((getMomentId() == null) ? 0 : getMomentId().hashCode());
+        result = prime * result + ((getUserId() == null) ? 0 : getUserId().hashCode());
+        result = prime * result + ((getContent() == null) ? 0 : getContent().hashCode());
+        result = prime * result + ((getReplyTo() == null) ? 0 : getReplyTo().hashCode());
+        result = prime * result + ((getReplyToUser() == null) ? 0 : getReplyToUser().hashCode());
+        result = prime * result + ((getCreatedAt() == null) ? 0 : getCreatedAt().hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", commentId=").append(commentId);
+        sb.append(", momentId=").append(momentId);
+        sb.append(", userId=").append(userId);
+        sb.append(", content=").append(content);
+        sb.append(", replyTo=").append(replyTo);
+        sb.append(", replyToUser=").append(replyToUser);
+        sb.append(", createdAt=").append(createdAt);
+        sb.append(", serialVersionUID=").append(serialVersionUID);
+        sb.append("]");
+        return sb.toString();
+    }
+}

+ 85 - 0
src/main/java/com/zhentao/moment/domain/MomentLikes.java

@@ -0,0 +1,85 @@
+package com.zhentao.moment.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.util.Date;
+import lombok.Data;
+
+/**
+ * 朋友圈点赞表
+ * @TableName moment_likes
+ */
+@TableName(value ="moment_likes")
+@Data
+public class MomentLikes implements Serializable {
+    /**
+     * 
+     */
+    @TableId(type = IdType.AUTO)
+    private Long likeId;
+
+    /**
+     * 动态ID
+     */
+    private Long momentId;
+
+    /**
+     * 点赞用户ID
+     */
+    private Long userId;
+
+    /**
+     * 
+     */
+    private Date createdAt;
+
+    @TableField(exist = false)
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        }
+        if (that == null) {
+            return false;
+        }
+        if (getClass() != that.getClass()) {
+            return false;
+        }
+        MomentLikes other = (MomentLikes) that;
+        return (this.getLikeId() == null ? other.getLikeId() == null : this.getLikeId().equals(other.getLikeId()))
+            && (this.getMomentId() == null ? other.getMomentId() == null : this.getMomentId().equals(other.getMomentId()))
+            && (this.getUserId() == null ? other.getUserId() == null : this.getUserId().equals(other.getUserId()))
+            && (this.getCreatedAt() == null ? other.getCreatedAt() == null : this.getCreatedAt().equals(other.getCreatedAt()));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((getLikeId() == null) ? 0 : getLikeId().hashCode());
+        result = prime * result + ((getMomentId() == null) ? 0 : getMomentId().hashCode());
+        result = prime * result + ((getUserId() == null) ? 0 : getUserId().hashCode());
+        result = prime * result + ((getCreatedAt() == null) ? 0 : getCreatedAt().hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", likeId=").append(likeId);
+        sb.append(", momentId=").append(momentId);
+        sb.append(", userId=").append(userId);
+        sb.append(", createdAt=").append(createdAt);
+        sb.append(", serialVersionUID=").append(serialVersionUID);
+        sb.append("]");
+        return sb.toString();
+    }
+}

+ 92 - 0
src/main/java/com/zhentao/moment/domain/MomentMedias.java

@@ -0,0 +1,92 @@
+package com.zhentao.moment.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import lombok.Data;
+
+/**
+ * 朋友圈媒体表
+ * @TableName moment_medias
+ */
+@TableName(value ="moment_medias")
+@Data
+public class MomentMedias implements Serializable {
+    /**
+     * 
+     */
+    @TableId(type = IdType.AUTO)
+    private Long mediaId;
+
+    /**
+     * 动态ID
+     */
+    private Long momentId;
+
+    /**
+     * 资源URL
+     */
+    private String mediaUrl;
+
+    /**
+     * 类型(1-图片,2-视频)
+     */
+    private Integer mediaType;
+
+    /**
+     * 排序
+     */
+    private Integer sortOrder;
+
+    @TableField(exist = false)
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        }
+        if (that == null) {
+            return false;
+        }
+        if (getClass() != that.getClass()) {
+            return false;
+        }
+        MomentMedias other = (MomentMedias) that;
+        return (this.getMediaId() == null ? other.getMediaId() == null : this.getMediaId().equals(other.getMediaId()))
+            && (this.getMomentId() == null ? other.getMomentId() == null : this.getMomentId().equals(other.getMomentId()))
+            && (this.getMediaUrl() == null ? other.getMediaUrl() == null : this.getMediaUrl().equals(other.getMediaUrl()))
+            && (this.getMediaType() == null ? other.getMediaType() == null : this.getMediaType().equals(other.getMediaType()))
+            && (this.getSortOrder() == null ? other.getSortOrder() == null : this.getSortOrder().equals(other.getSortOrder()));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((getMediaId() == null) ? 0 : getMediaId().hashCode());
+        result = prime * result + ((getMomentId() == null) ? 0 : getMomentId().hashCode());
+        result = prime * result + ((getMediaUrl() == null) ? 0 : getMediaUrl().hashCode());
+        result = prime * result + ((getMediaType() == null) ? 0 : getMediaType().hashCode());
+        result = prime * result + ((getSortOrder() == null) ? 0 : getSortOrder().hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", mediaId=").append(mediaId);
+        sb.append(", momentId=").append(momentId);
+        sb.append(", mediaUrl=").append(mediaUrl);
+        sb.append(", mediaType=").append(mediaType);
+        sb.append(", sortOrder=").append(sortOrder);
+        sb.append(", serialVersionUID=").append(serialVersionUID);
+        sb.append("]");
+        return sb.toString();
+    }
+}

+ 117 - 0
src/main/java/com/zhentao/moment/domain/UserMoments.java

@@ -0,0 +1,117 @@
+package com.zhentao.moment.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.util.Date;
+import lombok.Data;
+
+/**
+ * 朋友圈动态表
+ * @TableName user_moments
+ */
+@TableName(value ="user_moments")
+@Data
+public class UserMoments implements Serializable {
+    /**
+     * 
+     */
+    @TableId(type = IdType.AUTO)
+    private Long momentId;
+
+    /**
+     * 发布者ID
+     */
+    private Long userId;
+
+    /**
+     * 动态内容
+     */
+    private String content;
+
+    /**
+     * 内容类型(1-文本,2-图片,3-视频)
+     */
+    private Integer contentType;
+
+    /**
+     * 可见性(0-公开,1-仅好友,2-私密)
+     */
+    private Integer visibility;
+
+    /**
+     * 位置信息
+     */
+    private String location;
+
+    /**
+     * 
+     */
+    private Date createdAt;
+
+    /**
+     * 
+     */
+    private Date updatedAt;
+
+    @TableField(exist = false)
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        }
+        if (that == null) {
+            return false;
+        }
+        if (getClass() != that.getClass()) {
+            return false;
+        }
+        UserMoments other = (UserMoments) that;
+        return (this.getMomentId() == null ? other.getMomentId() == null : this.getMomentId().equals(other.getMomentId()))
+            && (this.getUserId() == null ? other.getUserId() == null : this.getUserId().equals(other.getUserId()))
+            && (this.getContent() == null ? other.getContent() == null : this.getContent().equals(other.getContent()))
+            && (this.getContentType() == null ? other.getContentType() == null : this.getContentType().equals(other.getContentType()))
+            && (this.getVisibility() == null ? other.getVisibility() == null : this.getVisibility().equals(other.getVisibility()))
+            && (this.getLocation() == null ? other.getLocation() == null : this.getLocation().equals(other.getLocation()))
+            && (this.getCreatedAt() == null ? other.getCreatedAt() == null : this.getCreatedAt().equals(other.getCreatedAt()))
+            && (this.getUpdatedAt() == null ? other.getUpdatedAt() == null : this.getUpdatedAt().equals(other.getUpdatedAt()));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((getMomentId() == null) ? 0 : getMomentId().hashCode());
+        result = prime * result + ((getUserId() == null) ? 0 : getUserId().hashCode());
+        result = prime * result + ((getContent() == null) ? 0 : getContent().hashCode());
+        result = prime * result + ((getContentType() == null) ? 0 : getContentType().hashCode());
+        result = prime * result + ((getVisibility() == null) ? 0 : getVisibility().hashCode());
+        result = prime * result + ((getLocation() == null) ? 0 : getLocation().hashCode());
+        result = prime * result + ((getCreatedAt() == null) ? 0 : getCreatedAt().hashCode());
+        result = prime * result + ((getUpdatedAt() == null) ? 0 : getUpdatedAt().hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", momentId=").append(momentId);
+        sb.append(", userId=").append(userId);
+        sb.append(", content=").append(content);
+        sb.append(", contentType=").append(contentType);
+        sb.append(", visibility=").append(visibility);
+        sb.append(", location=").append(location);
+        sb.append(", createdAt=").append(createdAt);
+        sb.append(", updatedAt=").append(updatedAt);
+        sb.append(", serialVersionUID=").append(serialVersionUID);
+        sb.append("]");
+        return sb.toString();
+    }
+}

+ 14 - 0
src/main/java/com/zhentao/moment/dto/MonmentDto.java

@@ -0,0 +1,14 @@
+package com.zhentao.moment.dto;
+
+import lombok.Data;
+import org.springframework.web.multipart.MultipartFile;
+
+@Data
+public class MonmentDto {
+    private Long uid;
+    private Integer visible;//是否可见  1公开  2好友 3自己
+    private String content;//发布内容
+//    private MultipartFile urlImage;
+    private Integer contentType;//文章类型
+    private String location;
+}

+ 34 - 0
src/main/java/com/zhentao/moment/enums/ContentTypeEnum.java

@@ -0,0 +1,34 @@
+package com.zhentao.moment.enums;
+
+public enum ContentTypeEnum {
+    TEXT(1, "文本"),
+    IMAGE(2, "图片"),
+    VIDEO(3, "视频");
+    private final Integer code;
+    private final String desc;
+
+    ContentTypeEnum(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    // 根据 code 获取枚举
+    public static ContentTypeEnum getByCode(Integer code) {
+        for (ContentTypeEnum type : values()) {
+            if (type.code.equals(code)) {
+                return type;
+            }
+        }
+        // 不存在时可抛异常或返回默认值,根据业务决定
+        throw new IllegalArgumentException("无效的 content_type: " + code);
+    }
+
+    // getter
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+}

+ 18 - 0
src/main/java/com/zhentao/moment/mapper/MomentCommentsMapper.java

@@ -0,0 +1,18 @@
+package com.zhentao.moment.mapper;
+
+import com.zhentao.moment.domain.MomentComments;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+* @author 86159
+* @description 针对表【moment_comments(朋友圈评论表)】的数据库操作Mapper
+* @createDate 2025-06-04 11:56:59
+* @Entity com.zhentao.moment.domain.MomentComments
+*/
+public interface MomentCommentsMapper extends BaseMapper<MomentComments> {
+
+}
+
+
+
+

+ 18 - 0
src/main/java/com/zhentao/moment/mapper/MomentLikesMapper.java

@@ -0,0 +1,18 @@
+package com.zhentao.moment.mapper;
+
+import com.zhentao.moment.domain.MomentLikes;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+* @author 86159
+* @description 针对表【moment_likes(朋友圈点赞表)】的数据库操作Mapper
+* @createDate 2025-06-04 11:56:59
+* @Entity com.zhentao.moment.domain.MomentLikes
+*/
+public interface MomentLikesMapper extends BaseMapper<MomentLikes> {
+
+}
+
+
+
+

+ 18 - 0
src/main/java/com/zhentao/moment/mapper/MomentMediasMapper.java

@@ -0,0 +1,18 @@
+package com.zhentao.moment.mapper;
+
+import com.zhentao.moment.domain.MomentMedias;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+* @author 86159
+* @description 针对表【moment_medias(朋友圈媒体表)】的数据库操作Mapper
+* @createDate 2025-06-04 11:56:59
+* @Entity com.zhentao.moment.domain.MomentMedias
+*/
+public interface MomentMediasMapper extends BaseMapper<MomentMedias> {
+
+}
+
+
+
+

+ 18 - 0
src/main/java/com/zhentao/moment/mapper/UserMomentsMapper.java

@@ -0,0 +1,18 @@
+package com.zhentao.moment.mapper;
+
+import com.zhentao.moment.domain.UserMoments;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+* @author 86159
+* @description 针对表【user_moments(朋友圈动态表)】的数据库操作Mapper
+* @createDate 2025-06-04 11:56:59
+* @Entity com.zhentao.moment.domain.UserMoments
+*/
+public interface UserMomentsMapper extends BaseMapper<UserMoments> {
+
+}
+
+
+
+

+ 13 - 0
src/main/java/com/zhentao/moment/service/MomentCommentsService.java

@@ -0,0 +1,13 @@
+package com.zhentao.moment.service;
+
+import com.zhentao.moment.domain.MomentComments;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+* @author 86159
+* @description 针对表【moment_comments(朋友圈评论表)】的数据库操作Service
+* @createDate 2025-06-04 11:56:59
+*/
+public interface MomentCommentsService extends IService<MomentComments> {
+
+}

+ 13 - 0
src/main/java/com/zhentao/moment/service/MomentLikesService.java

@@ -0,0 +1,13 @@
+package com.zhentao.moment.service;
+
+import com.zhentao.moment.domain.MomentLikes;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+* @author 86159
+* @description 针对表【moment_likes(朋友圈点赞表)】的数据库操作Service
+* @createDate 2025-06-04 11:56:59
+*/
+public interface MomentLikesService extends IService<MomentLikes> {
+
+}

+ 13 - 0
src/main/java/com/zhentao/moment/service/MomentMediasService.java

@@ -0,0 +1,13 @@
+package com.zhentao.moment.service;
+
+import com.zhentao.moment.domain.MomentMedias;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+* @author 86159
+* @description 针对表【moment_medias(朋友圈媒体表)】的数据库操作Service
+* @createDate 2025-06-04 11:56:59
+*/
+public interface MomentMediasService extends IService<MomentMedias> {
+
+}

+ 18 - 0
src/main/java/com/zhentao/moment/service/UserMomentsService.java

@@ -0,0 +1,18 @@
+package com.zhentao.moment.service;
+
+import com.zhentao.moment.domain.UserMoments;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zhentao.moment.dto.MonmentDto;
+import com.zhentao.vo.Result;
+
+/**
+* @author 86159
+* @description 针对表【user_moments(朋友圈动态表)】的数据库操作Service
+* @createDate 2025-06-04 11:56:59
+*/
+public interface UserMomentsService extends IService<UserMoments> {
+//  发布朋友圈
+    Result sendMonment(String  token,MonmentDto monmentDto);
+//  删除朋友圈
+//    Result deleteMonment(MonmentDto monmentDto);
+}

+ 22 - 0
src/main/java/com/zhentao/moment/service/impl/MomentCommentsServiceImpl.java

@@ -0,0 +1,22 @@
+package com.zhentao.moment.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhentao.moment.domain.MomentComments;
+import com.zhentao.moment.service.MomentCommentsService;
+import com.zhentao.moment.mapper.MomentCommentsMapper;
+import org.springframework.stereotype.Service;
+
+/**
+* @author 86159
+* @description 针对表【moment_comments(朋友圈评论表)】的数据库操作Service实现
+* @createDate 2025-06-04 11:56:59
+*/
+@Service
+public class MomentCommentsServiceImpl extends ServiceImpl<MomentCommentsMapper, MomentComments>
+    implements MomentCommentsService{
+
+}
+
+
+
+

+ 22 - 0
src/main/java/com/zhentao/moment/service/impl/MomentLikesServiceImpl.java

@@ -0,0 +1,22 @@
+package com.zhentao.moment.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhentao.moment.domain.MomentLikes;
+import com.zhentao.moment.service.MomentLikesService;
+import com.zhentao.moment.mapper.MomentLikesMapper;
+import org.springframework.stereotype.Service;
+
+/**
+* @author 86159
+* @description 针对表【moment_likes(朋友圈点赞表)】的数据库操作Service实现
+* @createDate 2025-06-04 11:56:59
+*/
+@Service
+public class MomentLikesServiceImpl extends ServiceImpl<MomentLikesMapper, MomentLikes>
+    implements MomentLikesService{
+
+}
+
+
+
+

+ 22 - 0
src/main/java/com/zhentao/moment/service/impl/MomentMediasServiceImpl.java

@@ -0,0 +1,22 @@
+package com.zhentao.moment.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhentao.moment.domain.MomentMedias;
+import com.zhentao.moment.service.MomentMediasService;
+import com.zhentao.moment.mapper.MomentMediasMapper;
+import org.springframework.stereotype.Service;
+
+/**
+* @author 86159
+* @description 针对表【moment_medias(朋友圈媒体表)】的数据库操作Service实现
+* @createDate 2025-06-04 11:56:59
+*/
+@Service
+public class MomentMediasServiceImpl extends ServiceImpl<MomentMediasMapper, MomentMedias>
+    implements MomentMediasService{
+
+}
+
+
+
+

+ 95 - 0
src/main/java/com/zhentao/moment/service/impl/UserMomentsServiceImpl.java

@@ -0,0 +1,95 @@
+package com.zhentao.moment.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zhentao.moment.domain.UserMoments;
+import com.zhentao.moment.dto.MonmentDto;
+import com.zhentao.moment.enums.ContentTypeEnum;
+import com.zhentao.moment.service.UserMomentsService;
+import com.zhentao.moment.mapper.UserMomentsMapper;
+import com.zhentao.tool.TokenUtils;
+import com.zhentao.utils.SensitiveWordFilter;
+import com.zhentao.utils.SnowflakeUtil;
+import com.zhentao.vo.Result;
+import io.jsonwebtoken.Claims;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+* @author 86159
+* @description 针对表【user_moments(朋友圈动态表)】的数据库操作Service实现
+* @createDate 2025-06-04 11:56:59
+*/
+@Service
+public class UserMomentsServiceImpl extends ServiceImpl<UserMomentsMapper, UserMoments>
+    implements UserMomentsService{
+    @Autowired
+    private UserMomentsMapper userMomentsMapper;
+    @Override
+    public Result sendMonment(String token,MonmentDto monmentDto) {
+        if (monmentDto.getContent()==null){
+            return Result.error(400,"请输入内容");
+        }
+        if (monmentDto.getContentType()==null){
+            return Result.error(400,"内容类型不能为空");
+        }
+        try {
+            UserMoments userMoments=new UserMoments();
+            userMoments.setMomentId(SnowflakeUtil.nextId());
+            String userId = TokenUtils.getUserIdFromToken(token);
+            userMoments.setUserId(Long.valueOf(userId));
+            Integer typeCode = monmentDto.getContentType();
+            ContentTypeEnum type = ContentTypeEnum.getByCode(typeCode);
+            switch (type){
+                case TEXT:
+                    String filter = SensitiveWordFilter.filter(monmentDto.getContent());
+                    userMoments.setContent(filter);
+                    break;
+                case IMAGE:
+                    validateImageContent(monmentDto.getContent());
+//                    userMoments.setContentType(ContentTypeEnum.IMAGE.getCode());
+                    break;
+                case VIDEO:
+                    validateVideoContent(monmentDto.getContent());
+//                    userMoments.setContentType(ContentTypeEnum.VIDEO.getCode());
+                    break;
+                default:
+                    return Result.error(400,"内容类型错误");
+            }
+////            userMoments.setContentType();
+//            userMoments.setContent(monmentDto.getContent());
+            userMomentsMapper.insert(userMoments);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return Result.OK(null,"发送成功");
+    }
+
+//    @Override
+//    public Result deleteMonment(MonmentDto monmentDto) {
+//
+//
+//    }
+
+    // 图片内容校验
+    private void validateImageContent(String content) {
+        // content 存图片 URL 列表,用逗号分隔
+        String[] imageUrls = content.split(",");
+        for (String url : imageUrls) {
+            if (!url.matches("^https?://.*\\.(png|jpg|jpeg)$")) {
+                throw new IllegalArgumentException("图片 URL 格式错误: " + url);
+            }
+        }
+    }
+
+    // 视频内容校验
+    private void validateVideoContent(String content) {
+        //  content 存视频 URL
+        if (!content.matches("^https?://.*\\.(mp4|avi)$")) {
+            throw new IllegalArgumentException("视频 URL 格式错误: " + content);
+        }
+    }
+}
+
+
+
+

+ 0 - 110
src/main/java/com/zhentao/oss/controller/ossController.java

@@ -1,110 +0,0 @@
-package com.zhentao.oss.controller;
-
-import com.aliyun.oss.OSS;
-import com.aliyun.oss.OSSClientBuilder;
-import com.aliyun.oss.model.PutObjectRequest;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-
-@RestController
-@RequestMapping("/api/upload")
-public class ossController {
-
-    @Value("${aliyun.oss.endpoint}")
-    private String endpoint;
-
-    @Value("${aliyun.oss.accessKeyId}")
-    private String accessKeyId;
-
-    @Value("${aliyun.oss.accessKeySecret}")
-    private String accessKeySecret;
-
-    @Value("${aliyun.oss.bucketName}")
-    private String bucketName;
-
-    // 允许的图片格式
-    private static final String[] ALLOWED_TYPES = {
-            "image/jpeg", "image/png", "image/gif"
-    };
-
-    @PostMapping("/simple-image")
-    public Map<String, Object> uploadSimpleImage(@RequestParam("file") MultipartFile file) {
-        Map<String, Object> result = new HashMap<>();
-
-        // 基础验证
-        if (file == null || file.isEmpty()) {
-            result.put("code", 400);
-            result.put("message", "上传图片不能为空");
-            return result;
-        }
-
-        // 验证图片格式
-        String contentType = file.getContentType();
-        if (!isValidImageType(contentType)) {
-            result.put("code", 400);
-            result.put("message", "仅支持JPG、PNG、GIF格式的图片");
-            return result;
-        }
-
-        OSS ossClient = null;
-        try {
-            ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
-
-            // 生成唯一文件名
-            String originalFilename = file.getOriginalFilename();
-            String extension = getFileExtension(originalFilename);
-            String fileName = UUID.randomUUID().toString() + "." + extension;
-
-            // 上传图片
-            PutObjectRequest putObjectRequest = new PutObjectRequest(
-                    bucketName, fileName, file.getInputStream()
-            );
-            ossClient.putObject(putObjectRequest);
-
-            // 构建访问URL
-            String endpointWithoutProtocol = endpoint.replaceFirst("^https?://", "");
-            String imageUrl = "https://" + bucketName + "." + endpointWithoutProtocol + "/" + fileName;
-
-            // 返回结果
-            result.put("code", 200);
-            result.put("message", "图片上传成功");
-            result.put("imageUrl", imageUrl);
-
-        } catch (IOException e) {
-            result.put("code", 500);
-            result.put("message", "上传失败:" + e.getMessage());
-        } finally {
-            if (ossClient != null) {
-                ossClient.shutdown();
-            }
-        }
-
-        return result;
-    }
-
-    private boolean isValidImageType(String contentType) {
-        if (contentType == null) return false;
-        for (String type : ALLOWED_TYPES) {
-            if (contentType.equalsIgnoreCase(type)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private String getFileExtension(String fileName) {
-        if (fileName == null || !fileName.contains(".")) {
-            return "jpg";
-        }
-        return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
-    }
-}

+ 17 - 0
src/main/java/com/zhentao/osspicture/OssConfig.java

@@ -0,0 +1,17 @@
+package com.zhentao.osspicture;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "aliyun.oss")
+public class OssConfig {
+    private String endpoint;
+    private String accessKeyId;
+    private String accessKeySecret;
+    private String bucketName;
+    private String fileHost;
+    private Integer urlExpireTime;
+}

+ 61 - 0
src/main/java/com/zhentao/osspicture/OssUtil.java

@@ -0,0 +1,61 @@
+package com.zhentao.osspicture;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.model.PutObjectRequest;
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.UUID;
+
+@Component
+public class OssUtil {
+
+    // 从配置文件中读取OSS相关信息
+    private static final String endpoint = "https://oss-cn-beijing.aliyuncs.com";
+    private static final String accessKeyId = "LTAI5tH9VHPZwGJu4UX3hrL5";
+    private static final String accessKeySecret = "mbsutFJYLkzosvvKNr0DD28XSg4mqA";
+    private static final String bucketName = "fjj1";
+
+    public String uploadFile(MultipartFile file) throws IOException {
+        // 创建OSSClient实例
+        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+
+        try {
+            // 生成唯一的文件名
+//            String fileName = UUID.randomUUID().toString() + "-" + file.getOriginalFilename();
+            String originalFilename = file.getOriginalFilename();
+            String extension = getFileExtension(originalFilename);
+            String fileName = UUID.randomUUID().toString() + "." + extension;
+            //上传图片
+            PutObjectRequest putObjectRequest = new PutObjectRequest(
+                    bucketName, fileName, file.getInputStream()
+            );
+            ossClient.putObject(putObjectRequest);
+
+            // 拼接文件访问URL
+            String endpointWithoutProtocol = endpoint.replaceFirst("^https?://", "");
+            String imageUrl = "https://" + bucketName + "." + endpointWithoutProtocol + "/" + fileName;
+
+            System.err.println(imageUrl);
+            // 返回文件访问URL
+            return imageUrl;
+
+
+        }catch (IOException e){
+            e.printStackTrace();
+        } finally {
+            // 关闭OSSClient
+            ossClient.shutdown();
+        }
+        return null;
+    }
+    private String getFileExtension(String fileName) {
+        if (fileName == null || !fileName.contains(".")) {
+            return "jpg";
+        }
+        return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
+    }
+}

+ 10 - 7
src/main/java/com/zhentao/shouye/controller/UserShouyeController.java

@@ -3,12 +3,10 @@ package com.zhentao.shouye.controller;
 import com.zhentao.config.NullLogin;
 import com.zhentao.shouye.dto.UserShouyeDto;
 import com.zhentao.shouye.service.UserShouyeService;
+import com.zhentao.tool.TokenUtils;
 import com.zhentao.vo.Result;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 @RestController
 @RequestMapping("shouye")
@@ -16,9 +14,14 @@ public class UserShouyeController {
     @Autowired
     private UserShouyeService userShouyeService;
 
-    @PostMapping("findAll")
+    @GetMapping("findAll")
     @NullLogin
-    public Result findAll(@RequestBody UserShouyeDto dto){
-        return userShouyeService.findAll(dto);
+    public Result findAll(@RequestHeader("token") String token){
+        System.err.println(token);
+        String userIdFromToken = TokenUtils.getUserIdFromToken(token);
+        UserShouyeDto userShouyeDto = new UserShouyeDto();
+        userShouyeDto.setUid1(Long.valueOf(userIdFromToken));
+        System.err.println(userShouyeDto);
+        return userShouyeService.findAll(userShouyeDto);
     }
 }

+ 62 - 4
src/main/java/com/zhentao/user/controller/UserController.java

@@ -1,25 +1,44 @@
 package com.zhentao.user.controller;
 
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.model.PutObjectRequest;
 import com.zhentao.config.NullLogin;
+import com.zhentao.information.service.WebSocketService;
+import com.zhentao.osspicture.OssUtil;
+import com.zhentao.tool.TokenUtils;
+import com.zhentao.user.domain.UserLogin;
 import com.zhentao.user.dto.*;
 import com.zhentao.user.service.UserLoginService;
 import com.zhentao.vo.Result;
+import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
 
 
 @RestController
 @RequestMapping("user")
+@RequiredArgsConstructor
+@Validated
 public class UserController {
 
     @Autowired
     public UserLoginService userLoginService;
 
+    @Autowired
+    public OssUtil ossUtil;
+
+
     //注册
     @PostMapping("/register")
     @NullLogin
@@ -56,4 +75,43 @@ public class UserController {
     public Result ForgetPass(@RequestBody @Valid ForgetPassDto forgetPassDto) {
         return userLoginService.ForgetPass(forgetPassDto);
     }
+
+
+    /**
+     * 上传头像
+     */
+    @PostMapping("/upload-avatar")
+    @NullLogin
+    public Map<String, Object> uploadAvatar(@NotNull(message = "请选择上传文件")
+                                            @RequestParam("file") MultipartFile file) {
+        Map<String, Object> result = new HashMap<>();
+
+        try {
+            // 上传到OSS
+            String url = ossUtil.uploadFile(file);
+
+            // TODO: 保存URL到数据库
+            // userService.updateAvatar(userId, url);
+
+            result.put("success", true);
+            result.put("message", "上传成功");
+            result.put("url", url);
+        } catch (IOException e) {
+            result.put("success", false);
+            result.put("message", "上传失败: " + e.getMessage());
+        }
+
+        return result;
+    }
+
+
+    //根据ID查询用户的信息
+    @GetMapping("getUserById")
+    @NullLogin
+    public Result getUserById(@RequestHeader("token") String token) {
+        String userIdFromToken = TokenUtils.getUserIdFromToken(token);
+        UserLogin userById = userLoginService.getUserById(Long.valueOf(userIdFromToken));
+        System.err.println(userById);
+        return Result.OK(userById, "查询成功");
+    }
 }

+ 3 - 3
src/main/java/com/zhentao/user/domain/UserLogin.java

@@ -125,17 +125,17 @@ public class UserLogin implements Serializable {
     private Date updatedTime;
 
     /**
-     *
+
      */
     private String openId;
 
     /**
-     *
+
      */
     private String sessionKey;
 
     /**
-     *
+
      */
     private String uniId;
 

+ 53 - 0
src/main/java/com/zhentao/user/dto/FriendDto.java

@@ -0,0 +1,53 @@
+package com.zhentao.user.dto;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class FriendDto {
+    /**
+     * 用户ID
+     */
+    @TableId
+    private Long id;
+
+    /**
+     * 账号
+     */
+    private String userUsername;
+
+
+
+    /**
+     * 用户昵称
+     */
+    private String nickName;
+
+    /**
+     * 头像图片
+     */
+    private String avatar;
+
+    /**
+     * 用户名称
+     */
+    private String userName;
+
+    /**
+     * 性别1男2女3未知
+     */
+    private Integer gender;
+
+    /**
+     * 个性签名
+     */
+    private String userIntro;
+
+
+    @TableField(exist = false)
+    private static final long serialVersionUID = 1L;
+
+}

+ 13 - 0
src/main/java/com/zhentao/user/dto/UserInfo.java

@@ -0,0 +1,13 @@
+package com.zhentao.user.dto;
+
+import lombok.Data;
+
+@Data
+public class UserInfo {
+    private Integer userId;
+
+    private String UserUsername;
+
+    private String NickName;
+
+}

+ 1 - 0
src/main/java/com/zhentao/user/dto/UserPassDto.java

@@ -3,6 +3,7 @@ package com.zhentao.user.dto;
 import lombok.Data;
 import lombok.ToString;
 
+
 import javax.validation.constraints.NotBlank;
 
 @Data

+ 6 - 0
src/main/java/com/zhentao/user/service/UserLoginService.java

@@ -23,4 +23,10 @@ public interface UserLoginService extends IService<UserLogin> {
     Result UserPassLogin(UserPassDto userPassDto);
     //    忘记密码
     Result ForgetPass(ForgetPassDto forgetPassDto);
+
+
+    //根据id查询用户的信息
+    UserLogin getUserById(Long id);
+
+
 }

+ 19 - 2
src/main/java/com/zhentao/user/service/impl/UserLoginServiceImpl.java

@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zhentao.enums.ApiServerException;
 import com.zhentao.exception.AsynException;
+
+import com.zhentao.information.service.WebSocketService;
 import com.zhentao.tool.TokenUtils;
 import com.zhentao.user.domain.UserLogin;
 import com.zhentao.user.dto.*;
@@ -17,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.util.DigestUtils;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
@@ -35,6 +38,8 @@ public class UserLoginServiceImpl extends ServiceImpl<UserLoginMapper, UserLogin
     private RedissonClient redissonClient;
     @Autowired
     private StringRedisTemplate stringRedisTemplate;
+    @Autowired
+    public WebSocketService webSocketService;
 
     //注册
     @Override
@@ -185,7 +190,7 @@ public class UserLoginServiceImpl extends ServiceImpl<UserLoginMapper, UserLogin
      */
     @Override
     public Result UserPassLogin(UserPassDto userPassDto) {
-        System.err.println(userPassDto);
+//        System.err.println(userPassDto);
         // 获取Redisson客户端的锁对象,用于处理并发登录请求
         RLock lock = redissonClient.getLock("user"+userPassDto.getUsername());
         try {
@@ -210,9 +215,13 @@ public class UserLoginServiceImpl extends ServiceImpl<UserLoginMapper, UserLogin
                 // 生成JWT令牌
                 String jwtToken = TokenUtils.generateToken(one.getId()+"");
                 stringRedisTemplate.opsForValue().set(one.getId().toString(),jwtToken);
-                System.err.println(jwtToken);
                 System.err.println(stringRedisTemplate.opsForValue().get(one.getId().toString()));
                 // 返回登录成功结果和JWT令牌
+
+                // 将用户ID和token存储到WebSocketService中
+                webSocketService.storeUserToken(one.getId()+"", jwtToken);
+
+
                 return Result.OK("登录成功",jwtToken);
             }else {
                 // 如果获取锁超时,返回错误信息
@@ -264,6 +273,14 @@ public class UserLoginServiceImpl extends ServiceImpl<UserLoginMapper, UserLogin
         return null;
     }
 
+    //获取用户信息
+
+    @Override
+    public UserLogin getUserById(Long id) {
+        UserLogin userLogin = userLoginMapper.selectById(id);
+        return userLogin;
+    }
+
 }
 
 

+ 18 - 0
src/main/java/com/zhentao/utils/SensitiveWordFilter.java

@@ -0,0 +1,18 @@
+package com.zhentao.utils;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class SensitiveWordFilter {
+    // 敏感词列表(可从数据库/配置文件加载)
+    private static final Set<String> SENSITIVE_WORDS = new HashSet<>(Arrays.asList("暴力", "抢劫","杀人"));
+
+    public static String filter(String content) {
+        for (String word : SENSITIVE_WORDS) {
+            // 简单替换,也可扩展为正则匹配、全角半角转换等
+            content = content.replace(word, "***");
+        }
+        return content;
+    }
+}

+ 78 - 0
src/main/java/com/zhentao/utils/SnowflakeUtil.java

@@ -0,0 +1,78 @@
+package com.zhentao.utils;
+
+/**
+ * @Date 2025/4/21 19:25
+ * @Author gln
+ **/
+
+public class SnowflakeUtil {
+    // 起始时间戳,可自定义,这里以 2020-01-01 00:00:00 为例
+    private static final long START_TIMESTAMP = 1577836800000L;
+
+    // 数据中心 ID 所占位数
+    private static final long DATA_CENTER_ID_BITS = 5L;
+    // 机器 ID 所占位数
+    private static final long WORKER_ID_BITS = 5L;
+    // 序列号所占位数
+    private static final long SEQUENCE_BITS = 12L;
+
+    // 数据中心 ID 最大值
+    private static final long MAX_DATA_CENTER_ID = -1L ^ (-1L << DATA_CENTER_ID_BITS);
+    // 机器 ID 最大值
+    private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);
+    // 序列号最大值
+    private static final long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS);
+
+    // 机器 ID 向左移位数
+    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
+    // 数据中心 ID 向左移位数
+    private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
+    // 时间戳向左移位数
+    private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;
+
+    // 固定数据中心 ID 和机器 ID
+    private static final long DATA_CENTER_ID = 1;
+    private static final long WORKER_ID = 1;
+
+    private static long sequence = 0L;
+    private static long lastTimestamp = -1L;
+
+    public static synchronized long nextId() {
+        long timestamp = System.currentTimeMillis();
+
+        if (timestamp < lastTimestamp) {
+            throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
+        }
+
+        if (timestamp == lastTimestamp) {
+            sequence = (sequence + 1) & SEQUENCE_MASK;
+            if (sequence == 0) {
+                timestamp = waitForNextMillis(lastTimestamp);
+            }
+        } else {
+            sequence = 0L;
+        }
+
+        lastTimestamp = timestamp;
+
+        return ((timestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT) |
+                (DATA_CENTER_ID << DATA_CENTER_ID_SHIFT) |
+                (WORKER_ID << WORKER_ID_SHIFT) |
+                sequence;
+    }
+
+    private static long waitForNextMillis(long lastTimestamp) {
+        long timestamp = System.currentTimeMillis();
+        while (timestamp <= lastTimestamp) {
+            timestamp = System.currentTimeMillis();
+        }
+        return timestamp;
+    }
+
+    public static void main(String[] args) {
+        for (int i = 0; i < 10; i++) {
+            System.out.println(nextId());
+        }
+    }
+}
+

+ 7 - 0
src/main/java/com/zhentao/vo/Result.java

@@ -34,5 +34,12 @@ public class Result {
     }
 
 
+    public static Result success(String msg)
+    {
+        return Result.success("操作成功");
+    }
+
+    public void put(String url, String imageUrl) {
 
+    }
 }

+ 2 - 0
src/main/resources/application.yml

@@ -2,6 +2,8 @@ server:
   port: 8081
 netty:
   port: 8888
+  websocket:
+    port: 9099
 spring:
   application:
     name: im-server

+ 2 - 2
src/main/resources/mapper/GroupsMapper.xml

@@ -2,9 +2,9 @@
 <!DOCTYPE mapper
         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.zhentao.groups.mapper.GroupsMapper">
+<mapper namespace="com.zhentao.shouye.mapper.GroupsMapper">
 
-    <resultMap id="BaseResultMap" type="com.zhentao.groups.pojo.Groupss">
+    <resultMap id="BaseResultMap" type="com.zhentao.shouye.domain.Groups">
             <id property="groupId" column="group_id" jdbcType="BIGINT"/>
             <result property="name" column="name" jdbcType="VARCHAR"/>
             <result property="creatorId" column="creator_id" jdbcType="BIGINT"/>

+ 22 - 0
src/main/resources/mapper/MomentCommentsMapper.xml

@@ -0,0 +1,22 @@
+<?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="com.zhentao.moment.mapper.MomentCommentsMapper">
+
+    <resultMap id="BaseResultMap" type="com.zhentao.moment.domain.MomentComments">
+            <id property="commentId" column="comment_id" jdbcType="BIGINT"/>
+            <result property="momentId" column="moment_id" jdbcType="BIGINT"/>
+            <result property="userId" column="user_id" jdbcType="BIGINT"/>
+            <result property="content" column="content" jdbcType="VARCHAR"/>
+            <result property="replyTo" column="reply_to" jdbcType="BIGINT"/>
+            <result property="replyToUser" column="reply_to_user" jdbcType="BIGINT"/>
+            <result property="createdAt" column="created_at" jdbcType="TIMESTAMP"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        comment_id,moment_id,user_id,
+        content,reply_to,reply_to_user,
+        created_at
+    </sql>
+</mapper>

+ 18 - 0
src/main/resources/mapper/MomentLikesMapper.xml

@@ -0,0 +1,18 @@
+<?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="com.zhentao.moment.mapper.MomentLikesMapper">
+
+    <resultMap id="BaseResultMap" type="com.zhentao.moment.domain.MomentLikes">
+            <id property="likeId" column="like_id" jdbcType="BIGINT"/>
+            <result property="momentId" column="moment_id" jdbcType="BIGINT"/>
+            <result property="userId" column="user_id" jdbcType="BIGINT"/>
+            <result property="createdAt" column="created_at" jdbcType="TIMESTAMP"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        like_id,moment_id,user_id,
+        created_at
+    </sql>
+</mapper>

+ 19 - 0
src/main/resources/mapper/MomentMediasMapper.xml

@@ -0,0 +1,19 @@
+<?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="com.zhentao.moment.mapper.MomentMediasMapper">
+
+    <resultMap id="BaseResultMap" type="com.zhentao.moment.domain.MomentMedias">
+            <id property="mediaId" column="media_id" jdbcType="BIGINT"/>
+            <result property="momentId" column="moment_id" jdbcType="BIGINT"/>
+            <result property="mediaUrl" column="media_url" jdbcType="VARCHAR"/>
+            <result property="mediaType" column="media_type" jdbcType="TINYINT"/>
+            <result property="sortOrder" column="sort_order" jdbcType="INTEGER"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        media_id,moment_id,media_url,
+        media_type,sort_order
+    </sql>
+</mapper>

+ 23 - 0
src/main/resources/mapper/UserMomentsMapper.xml

@@ -0,0 +1,23 @@
+<?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="com.zhentao.moment.mapper.UserMomentsMapper">
+
+    <resultMap id="BaseResultMap" type="com.zhentao.moment.domain.UserMoments">
+            <id property="momentId" column="moment_id" jdbcType="BIGINT"/>
+            <result property="userId" column="user_id" jdbcType="BIGINT"/>
+            <result property="content" column="content" jdbcType="VARCHAR"/>
+            <result property="contentType" column="content_type" jdbcType="TINYINT"/>
+            <result property="visibility" column="visibility" jdbcType="TINYINT"/>
+            <result property="location" column="location" jdbcType="VARCHAR"/>
+            <result property="createdAt" column="created_at" jdbcType="TIMESTAMP"/>
+            <result property="updatedAt" column="updated_at" jdbcType="TIMESTAMP"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        moment_id,user_id,content,
+        content_type,visibility,location,
+        created_at,updated_at
+    </sql>
+</mapper>

+ 1 - 1
src/main/resources/mapper/UserShouyeMapper.xml

@@ -2,7 +2,7 @@
 <!DOCTYPE mapper
         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.zhentao.shouye.mapper.UserShouyeMapper">
+<mapper namespace="com.zhentao.user.mapper.UserShouyeMapper">
 
     <resultMap id="BaseResultMap" type="com.zhentao.shouye.domain.UserShouye">
             <id property="id" column="id" jdbcType="BIGINT"/>