diff --git a/src/main/java/xin/merlin/myplayerbackend/config/WebsocketConfig.java b/src/main/java/xin/merlin/myplayerbackend/config/WebsocketConfig.java index f376f90..252d384 100644 --- a/src/main/java/xin/merlin/myplayerbackend/config/WebsocketConfig.java +++ b/src/main/java/xin/merlin/myplayerbackend/config/WebsocketConfig.java @@ -21,6 +21,6 @@ public class WebsocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { - registry.addHandler(handler, "/online").addInterceptors(interceptor); + registry.addHandler(handler, "/ws/online").addInterceptors(interceptor).setAllowedOrigins("*"); } } diff --git a/src/main/java/xin/merlin/myplayerbackend/config/security/SecurityConfig.java b/src/main/java/xin/merlin/myplayerbackend/config/security/SecurityConfig.java index c17a949..f271d29 100644 --- a/src/main/java/xin/merlin/myplayerbackend/config/security/SecurityConfig.java +++ b/src/main/java/xin/merlin/myplayerbackend/config/security/SecurityConfig.java @@ -32,7 +32,8 @@ public class SecurityConfig { "/health", "/code/**", "/v3/api-docs/**", - "/account/mail/verify/**" + "/account/mail/verify/**", + "/ws/**" ).permitAll() .anyRequest().authenticated() ) diff --git a/src/main/java/xin/merlin/myplayerbackend/config/security/WebsocketInterceptor.java b/src/main/java/xin/merlin/myplayerbackend/config/security/WebsocketInterceptor.java index 5ef82af..0ce2cbe 100644 --- a/src/main/java/xin/merlin/myplayerbackend/config/security/WebsocketInterceptor.java +++ b/src/main/java/xin/merlin/myplayerbackend/config/security/WebsocketInterceptor.java @@ -5,11 +5,13 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; +import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.HandshakeInterceptor; import xin.merlin.myplayerbackend.utils.JwtUtil; +import java.util.List; import java.util.Map; @Slf4j @@ -23,21 +25,32 @@ public class WebsocketInterceptor implements HandshakeInterceptor { public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception { try { // 获取 token - String token = request.getHeaders().getFirst("Authorization"); - if (token != null && token.startsWith("Bearer ")) { - token = token.substring(7); - if (jwtUtil.isTokenExpired(token)){ - log.info("token expired"); - return false; - } - } else { + List protocols = request.getHeaders().get("Sec-WebSocket-Protocol"); + String token = null; + if (protocols == null){ + log.info("no token!"); + return false; + } + String tokenHeader = protocols.get(0); +// System.out.println(tokenHeader); + response.getHeaders().add("Sec-WebSocket-Protocol",tokenHeader); + token = tokenHeader.replace("token-", ""); + if (jwtUtil.isTokenExpired(token)){ + log.info("token expired"); return false; } - Integer id = jwtUtil.getId(token); - attributes.put("userId", id); - return true; + String account = jwtUtil.getAccount(token); + attributes.put("id", id); + attributes.put("account", account); + if (request instanceof ServletServerHttpRequest servletRequest) { + String userName = servletRequest.getServletRequest().getParameter("name"); + if (userName != null) { + attributes.put("name", userName); // 将u_name存储到Attributes中 + } + } + return true; }catch (Exception e){ log.error(e.getMessage()); return false; diff --git a/src/main/java/xin/merlin/myplayerbackend/entity/Account.java b/src/main/java/xin/merlin/myplayerbackend/entity/Account.java index ee3f4ab..d589f87 100644 --- a/src/main/java/xin/merlin/myplayerbackend/entity/Account.java +++ b/src/main/java/xin/merlin/myplayerbackend/entity/Account.java @@ -1,6 +1,7 @@ package xin.merlin.myplayerbackend.entity; +import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @@ -10,7 +11,7 @@ import lombok.NoArgsConstructor; @TableName("account") @NoArgsConstructor public class Account { - @TableId("id") + @TableId(value = "id",type = IdType.AUTO) private Integer id; private String account; diff --git a/src/main/java/xin/merlin/myplayerbackend/entity/Inviting.java b/src/main/java/xin/merlin/myplayerbackend/entity/Inviting.java index a20697e..bb78518 100644 --- a/src/main/java/xin/merlin/myplayerbackend/entity/Inviting.java +++ b/src/main/java/xin/merlin/myplayerbackend/entity/Inviting.java @@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; +import java.time.LocalDateTime; + @Data @TableName("inviting") public class Inviting { @@ -16,4 +18,5 @@ public class Inviting { // 0 表示未处理 1 表示同意 2 表示拒绝 private Integer status; private Integer room; + private LocalDateTime time; } diff --git a/src/main/java/xin/merlin/myplayerbackend/entity/http/InvitingDetails.java b/src/main/java/xin/merlin/myplayerbackend/entity/http/InvitingDetails.java index 42a375a..cb2ea60 100644 --- a/src/main/java/xin/merlin/myplayerbackend/entity/http/InvitingDetails.java +++ b/src/main/java/xin/merlin/myplayerbackend/entity/http/InvitingDetails.java @@ -5,6 +5,8 @@ import lombok.NoArgsConstructor; import xin.merlin.myplayerbackend.entity.Inviting; import xin.merlin.myplayerbackend.entity.UserInfo; +import java.time.LocalDateTime; + @Data @NoArgsConstructor public class InvitingDetails { @@ -18,6 +20,7 @@ public class InvitingDetails { // 0 表示未处理 1 表示已处理 private Integer status; private Integer room; + private LocalDateTime time; public InvitingDetails(Inviting inviting, UserInfo userInfo) { this.i_id = inviting.getI_id(); @@ -27,5 +30,6 @@ public class InvitingDetails { this.room = inviting.getRoom(); this.inviter_name = userInfo.getU_name(); this.inviter_avatar = userInfo.getU_avatar(); + this.time = inviting.getTime(); } } diff --git a/src/main/java/xin/merlin/myplayerbackend/service/LoginService.java b/src/main/java/xin/merlin/myplayerbackend/service/LoginService.java index 9b89b5c..1b9a4e9 100644 --- a/src/main/java/xin/merlin/myplayerbackend/service/LoginService.java +++ b/src/main/java/xin/merlin/myplayerbackend/service/LoginService.java @@ -67,9 +67,9 @@ public class LoginService{ u.setId(account.getId()); do{ u.setU_id(RandomCode.generateID()); - }while (userMapper.selectById(u.getU_id())!=null); + }while (userMapper.selectOne(Wrappers.lambdaQuery().eq(UserInfo::getU_id,u.getU_id()))!=null); userMapper.insert(u); - u = userMapper.selectById(u.getU_id()); + u = userMapper.selectById(u.getId()); codeService.getWaitingList().invalidate(code.getC_id()); return Response.success(ResultCode.SUCCESS,Map.of("token",jwtUtil.generateToken(account),"token_type","Bearer","userinfo",u)); } diff --git a/src/main/java/xin/merlin/myplayerbackend/service/impl/InvitingServiceImpl.java b/src/main/java/xin/merlin/myplayerbackend/service/impl/InvitingServiceImpl.java index 118603e..0a4f8cc 100644 --- a/src/main/java/xin/merlin/myplayerbackend/service/impl/InvitingServiceImpl.java +++ b/src/main/java/xin/merlin/myplayerbackend/service/impl/InvitingServiceImpl.java @@ -16,6 +16,7 @@ import xin.merlin.myplayerbackend.mapper.InvitingMapper; import xin.merlin.myplayerbackend.mapper.PlayroomsMapper; import xin.merlin.myplayerbackend.mapper.UserMapper; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -48,10 +49,11 @@ public class InvitingServiceImpl extends ServiceImpl { inviting.setInviter(inviter); inviting.setTarget(target); inviting.setStatus(0); + inviting.setTime(LocalDateTime.now()); if (room != null){ inviting.setRoom(room); } - if (invitingMapper.selectCount(Wrappers.lambdaQuery().eq(Inviting::getInviter,inviter).eq(Inviting::getTarget,target).eq(Inviting::getStatus,0).eq(Inviting::getRoom,room)) > 0) return false; + if (invitingMapper.selectCount(Wrappers.lambdaQuery().eq(Inviting::getInviter,inviter).eq(Inviting::getTarget,target).eq(Inviting::getStatus,0).isNull(Inviting::getRoom)) > 0) return false; return invitingMapper.insert(inviting) == 1; } @@ -81,12 +83,15 @@ public class InvitingServiceImpl extends ServiceImpl { public Boolean handleFriendInviting(Inviting inviting) { try { if (inviting.getStatus().equals(0)) return false; - else if (inviting.getStatus().equals(1)){ - friendsMapper.insert(new Friends(inviting.getInviter(), inviting.getTarget(), null)); - friendsMapper.insert(new Friends(inviting.getTarget(), inviting.getInviter(), null)); + else{ + if(inviting.getStatus().equals(1)){ + friendsMapper.insert(new Friends(inviting.getInviter(), inviting.getTarget(), null)); + friendsMapper.insert(new Friends(inviting.getTarget(), inviting.getInviter(), null)); + } + inviting.setTime(LocalDateTime.now()); + invitingMapper.updateById(inviting); return true; } - else return true; } catch (Exception e) { log.error(e.getMessage()); throw new RuntimeException(e); diff --git a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/CustomWebSocketHandler.java b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/CustomWebSocketHandler.java index 9a3c7ce..08b22dd 100644 --- a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/CustomWebSocketHandler.java +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/CustomWebSocketHandler.java @@ -1,8 +1,5 @@ package xin.merlin.myplayerbackend.utils.websocket; -import com.alibaba.fastjson2.JSON; -import com.alibaba.fastjson2.JSONObject; -import jakarta.annotation.PreDestroy; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.rabbit.core.RabbitTemplate; @@ -30,7 +27,14 @@ public class CustomWebSocketHandler extends TextWebSocketHandler { @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { - Integer userId = (Integer) session.getAttributes().get("userId"); + /* + TODO:这里需要处理在数据库中未发送的离线消息 + 初步想法: + 开多线程,分页获取所有相关信息,然后送入rabbitmq,送入之后删除对应的message + + 但是需要处理的是,用户一上线即下线的问题,如何终止这个多线程任务以及送入rabbitmq的message可能又会回到数据库中并出现重复数据 + */ + Integer userId = (Integer) session.getAttributes().get("id"); onlineStatusService.online(userId); @@ -64,7 +68,7 @@ public class CustomWebSocketHandler extends TextWebSocketHandler { @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { - Integer userId = (Integer) session.getAttributes().get("userId"); + Integer userId = (Integer) session.getAttributes().get("id"); onlineStatusService.offline(userId); diff --git a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/WebSocketMessageConsumer.java b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/WebSocketMessageConsumer.java index c99fb3b..2f1f4c7 100644 --- a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/WebSocketMessageConsumer.java +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/WebSocketMessageConsumer.java @@ -22,17 +22,7 @@ public class WebSocketMessageConsumer { public void onMessage(String json) { try { JSONObject msg = JSON.parseObject(json); - - String to = msg.getString("to"); - - if (to == null || to.isEmpty()) { - // 这是广播 / 系统消息 - sessionManager.broadcast(json); - } else { - // 单发消息 -// sessionManager.sendToUser(Integer.valueOf(to), json); - commandDispatcher.dispatch(msg); - } + commandDispatcher.dispatch(msg); } catch (Exception e) { log.info(e.getMessage()); diff --git a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/CommandDispatcher.java b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/CommandDispatcher.java index fa979cd..8292c0c 100644 --- a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/CommandDispatcher.java +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/CommandDispatcher.java @@ -15,6 +15,7 @@ public class CommandDispatcher { private final Typing typingCommand; private final Heartbeat heartbeatCommand; private final SystemNotify systemNotifyCommand; + private final PersonalNotify personalNotifyCommand; public void dispatch(JSONObject msg) throws IOException { String cmd = msg.getString("cmd"); @@ -25,6 +26,7 @@ public class CommandDispatcher { case "TYPING" -> typingCommand.handle(msg); case "HEARTBEAT" -> heartbeatCommand.handle(msg); case "SYSTEM_NOTIFY" -> systemNotifyCommand.handle(msg); + case "PERSONAL_NOTIFY" -> personalNotifyCommand.handle(msg); default -> { System.err.println("Unknown command: " + cmd); diff --git a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Message.java b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Message.java index 43fcc25..8787571 100644 --- a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Message.java +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Message.java @@ -16,6 +16,9 @@ public class Message implements BaseCommandHandler { @Override public void handle(JSONObject msg) throws IOException { +/* +TODO: 这里需要处理如果目标用户不在线,如何去完成消息的存储 + */ String to = msg.getString("to"); sessionManager.sendToUser(Integer.valueOf(to), msg.toJSONString()); } diff --git a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/PersonalNotify.java b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/PersonalNotify.java new file mode 100644 index 0000000..3df266f --- /dev/null +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/PersonalNotify.java @@ -0,0 +1,22 @@ +package xin.merlin.myplayerbackend.utils.websocket.command.impl; + +import com.alibaba.fastjson2.JSONObject; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import xin.merlin.myplayerbackend.utils.websocket.WebSocketSessionManager; +import xin.merlin.myplayerbackend.utils.websocket.command.BaseCommandHandler; + +import java.io.IOException; + +@Component +@RequiredArgsConstructor +public class PersonalNotify implements BaseCommandHandler { + + private final WebSocketSessionManager sessionManager; + + @Override + public void handle(JSONObject msg) throws IOException { + String to = msg.getString("to"); + sessionManager.sendToUser(Integer.valueOf(to), msg.toJSONString()); + } +}