From c58ad923c5daa5fef8f3e188767b7af75c9fe195 Mon Sep 17 00:00:00 2001 From: merlin Date: Sat, 4 Apr 2026 17:41:28 +0800 Subject: [PATCH] feat: add PlayroomWebSocketHandler --- .gitignore | 2 +- .../websocket/PlayroomWebSocketHandler.java | 47 ++++++++- .../websocket/WebSocketMessageConsumer.java | 2 + .../websocket/command/BaseCommandHandler.java | 2 + .../command/CommandDispatcherVideo.java | 5 + .../websocket/command/impl/GroupMessage.java | 4 + .../websocket/command/impl/Heartbeat.java | 4 + .../utils/websocket/command/impl/Link.java | 32 +++++++ .../utils/websocket/command/impl/Message.java | 4 + .../command/impl/PersonalNotify.java | 4 + .../utils/websocket/command/impl/Ping.java | 4 + .../websocket/command/impl/SystemNotify.java | 4 + .../utils/websocket/command/impl/Typing.java | 4 + src/main/resources/application_template.yaml | 96 +++++++++++++++++++ 14 files changed, 208 insertions(+), 6 deletions(-) create mode 100644 src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Link.java create mode 100644 src/main/resources/application_template.yaml diff --git a/.gitignore b/.gitignore index abe3b4b..3d1e9b3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ target/ .mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ !**/src/test/**/target/ -src/main/resources +src/main/resources/application.yaml ### STS ### .apt_generated diff --git a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/PlayroomWebSocketHandler.java b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/PlayroomWebSocketHandler.java index 64e6c5d..744d36c 100644 --- a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/PlayroomWebSocketHandler.java +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/PlayroomWebSocketHandler.java @@ -14,7 +14,9 @@ import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; import xin.merlin.myplayerbackend.config.RabbitMQConfig; +import java.io.IOException; import java.time.LocalDateTime; +import java.util.Set; @Slf4j @Component @@ -32,7 +34,7 @@ public class PlayroomWebSocketHandler extends TextWebSocketHandler { @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { Integer userId = (Integer) session.getAttributes().get("id"); - String payload = "{\"cmd\": \"PING\", \"to\": "+ userId +", \"time\":\""+ LocalDateTime.now() +"\"}"; + String payload = "{\"cmd\": \"PING\", \"to\": "+ userId +", \"time\":\""+ LocalDateTime.now() +"\", \"playroom\":"+ session.getAttributes().get("r_id")+ "}"; sessionManager.addSession(userId, session); rabbitTemplate.convertAndSend(RabbitMQConfig.WS_VIDEO_QUEUE,payload); } @@ -42,18 +44,53 @@ public class PlayroomWebSocketHandler extends TextWebSocketHandler { JSONObject payload = JSONObject.parseObject(message.getPayload()); Integer userId = (Integer) session.getAttributes().get("id"); if(payload.get("cmd").equals("PONG")) { - if(Boolean.TRUE.equals(redis.opsForSet().isMember(IN_ROOM_BASE_KEY + session.getAttributes().get("r_id") + ":", String.valueOf(userId)))){ + if(Boolean.TRUE.equals(redis.opsForSet().isMember(IN_ROOM_BASE_KEY + session.getAttributes().get("r_id"), String.valueOf(userId)))){ return; } - redis.opsForSet().add(IN_ROOM_BASE_KEY+session.getAttributes().get("r_id")+":", String.valueOf(userId)); + redis.opsForSet().add(IN_ROOM_BASE_KEY+session.getAttributes().get("r_id"), String.valueOf(userId)); + } + if (payload.get("cmd").equals("VIDEO_SYNC")){ + String url = payload.getString("url"); + String timestamp = payload.getString("timestamp"); +// Integer playroom = (Integer) session.getAttributes().get("r_id"); + String sending = "{\"cmd\": \"VIDEO_SYNC\", \"from\": "+ + userId + +", \"time\":\""+ + LocalDateTime.now() + +"\", \"url\":\""+ + url + +"\", \"timestamp\":\""+ + timestamp + +"\", \"playroom\":"+ + session.getAttributes().get("r_id") + +"}"; + Set members = redis.opsForSet().members(IN_ROOM_BASE_KEY+session.getAttributes().get("r_id")); + try { + for (String member : members) { + sessionManager.sendToUser(Integer.valueOf(member),sending); + } + } catch (IOException | NumberFormatException e) { + log.error(e.getMessage()); + throw new RuntimeException(e); + } + } + if(payload.get("cmd").equals("VIDEO_PLAY") || payload.get("cmd").equals("VIDEO_PAUSE")){ + Set members = redis.opsForSet().members(IN_ROOM_BASE_KEY+session.getAttributes().get("r_id")); + try { + for (String member : members) { + sessionManager.sendToUser(Integer.valueOf(member),message.getPayload()); + } + } catch (IOException | NumberFormatException e) { + log.error(e.getMessage()); + throw new RuntimeException(e); + } } - } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { Integer userId = (Integer) session.getAttributes().get("id"); - redis.opsForSet().remove(IN_ROOM_BASE_KEY+session.getAttributes().get("r_id")+":", userId.toString()); + redis.opsForSet().remove(IN_ROOM_BASE_KEY+session.getAttributes().get("r_id"), userId.toString()); sessionManager.removeSession(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 8da1e9a..215e730 100644 --- a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/WebSocketMessageConsumer.java +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/WebSocketMessageConsumer.java @@ -10,6 +10,8 @@ import xin.merlin.myplayerbackend.config.RabbitMQConfig; import xin.merlin.myplayerbackend.utils.websocket.command.CommandDispatcher; import xin.merlin.myplayerbackend.utils.websocket.command.CommandDispatcherVideo; +import java.io.IOException; + @Slf4j @Component @RequiredArgsConstructor diff --git a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/BaseCommandHandler.java b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/BaseCommandHandler.java index f22ce6a..77eca37 100644 --- a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/BaseCommandHandler.java +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/BaseCommandHandler.java @@ -8,5 +8,7 @@ public interface BaseCommandHandler { void handle(JSONObject msg) throws IOException; void handle(String msg, Integer to) throws IOException; + + void handle(String msg, Integer from, Integer playroom); } diff --git a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/CommandDispatcherVideo.java b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/CommandDispatcherVideo.java index 27ed7e1..a4d15ee 100644 --- a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/CommandDispatcherVideo.java +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/CommandDispatcherVideo.java @@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSONObject; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import xin.merlin.myplayerbackend.utils.websocket.PlayroomWebSocketSessionManager; +import xin.merlin.myplayerbackend.utils.websocket.command.impl.Link; import xin.merlin.myplayerbackend.utils.websocket.command.impl.Ping; import java.io.IOException; @@ -14,14 +15,18 @@ public class CommandDispatcherVideo { private final Ping pingCommand; + private final Link linkCommand; + public void dispatch(JSONObject msg) throws IOException { String cmd = msg.getString("cmd"); Integer to = Integer.valueOf(msg.getString("to")); + Integer playroom = Integer.valueOf(msg.getString("playroom")); String jsonString = msg.toJSONString(); switch (cmd){ case "PING" -> pingCommand.handle(jsonString,to); + case "LINK" -> linkCommand.handle(jsonString,to,playroom); default -> { // System.err.println("Unknown command: " + cmd); // TODO: 这里应该使用default的一个类统一封装 diff --git a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/GroupMessage.java b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/GroupMessage.java index f1e2a4d..43ee78b 100644 --- a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/GroupMessage.java +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/GroupMessage.java @@ -49,4 +49,8 @@ public class GroupMessage implements BaseCommandHandler { } + @Override + public void handle(String msg, Integer from, Integer playroom) { + + } } diff --git a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Heartbeat.java b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Heartbeat.java index 727ce0f..64e5f55 100644 --- a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Heartbeat.java +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Heartbeat.java @@ -24,5 +24,9 @@ public class Heartbeat implements BaseCommandHandler { @Override public void handle(String msg, Integer to) throws IOException { + } + @Override + public void handle(String msg, Integer from, Integer playroom) { + } } diff --git a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Link.java b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Link.java new file mode 100644 index 0000000..97c398b --- /dev/null +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Link.java @@ -0,0 +1,32 @@ +package xin.merlin.myplayerbackend.utils.websocket.command.impl; + +import com.alibaba.fastjson2.JSONObject; +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import xin.merlin.myplayerbackend.utils.websocket.command.BaseCommandHandler; + +import java.io.IOException; + +@Component +@RequiredArgsConstructor +public class Link implements BaseCommandHandler { + + private final RedisTemplate redis; + + + @Override + public void handle(JSONObject msg) throws IOException { + + } + + @Override + public void handle(String msg, Integer from, Integer playroom) { + + } + + @Override + public void handle(String msg, Integer to) throws IOException { + + } +} 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 2f58994..876d54e 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 @@ -26,6 +26,10 @@ TODO: 这里需要处理如果目标用户不在线,如何去完成消息的 @Override public void handle(String msg, Integer to) throws IOException { + } + @Override + public void handle(String msg, Integer from, Integer playroom) { + } } 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 index 9f1eb3a..de36da2 100644 --- 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 @@ -23,5 +23,9 @@ public class PersonalNotify implements BaseCommandHandler { @Override public void handle(String msg, Integer to) throws IOException { + } + @Override + public void handle(String msg, Integer from, Integer playroom) { + } } diff --git a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Ping.java b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Ping.java index a55152d..9b5e982 100644 --- a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Ping.java +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Ping.java @@ -29,5 +29,9 @@ public class Ping implements BaseCommandHandler { System.out.println(to+":"+msg); playroomWebSocketSessionManager.sendToUser(to, msg); } + @Override + public void handle(String msg, Integer from, Integer playroom) { + + } } diff --git a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/SystemNotify.java b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/SystemNotify.java index 69bef3f..7e1e6e1 100644 --- a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/SystemNotify.java +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/SystemNotify.java @@ -22,6 +22,10 @@ public class SystemNotify implements BaseCommandHandler { @Override public void handle(String msg, Integer to) throws IOException { + } + @Override + public void handle(String msg, Integer from, Integer playroom) { + } } diff --git a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Typing.java b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Typing.java index a0a994c..57435dc 100644 --- a/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Typing.java +++ b/src/main/java/xin/merlin/myplayerbackend/utils/websocket/command/impl/Typing.java @@ -23,5 +23,9 @@ public class Typing implements BaseCommandHandler { @Override public void handle(String msg, Integer to) throws IOException { + } + @Override + public void handle(String msg, Integer from, Integer playroom) { + } } diff --git a/src/main/resources/application_template.yaml b/src/main/resources/application_template.yaml new file mode 100644 index 0000000..e24ca91 --- /dev/null +++ b/src/main/resources/application_template.yaml @@ -0,0 +1,96 @@ +server: + port: 8080 + + +jwt: + secret: + issuer: + subject: + expire: 604800 + +aes: +# aes16/32/48位key + key: + +resources: + public: C://resources/public + user: + avatar: C://resources/user/avatar + group: + avatar: C://resources/group/avatar + playroom: + avatar: C://resources/group/avatar + + +spring: + application: + name: myplayer-backend + servlet: + multipart: + max-file-size: 50MB + max-request-size: 10MB + datasource: + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://localhost:5432/myplayer + username: + password: + + mail: + protocol: smtps + port: 465 + default-encoding: utf-8 + host: smtp.163.com + username: + password: + properties: + mail: + smtp: + auth: true + ssl: + enable: false + required: false + protocols: TLSv1.2 + connectiontimeout: 10000 + timeout: 15000 + writetimeout: 10000 + debug: true + data: + redis: + host: localhost + port: 6379 + password: dctf8nUrn3 +# database: 0 + timeout: 2000 + lettuce: + pool: + max-active: 8 + max-idle: 8 + min-idle: 0 + rabbitmq: + host: localhost + port: 5672 + username: user + password: Aqe5U57kvj50k7rG + virtual-host: / + +mybatis-plus: + global-config: + db-config: + table-prefix: "" # 可选:表前缀(没有可以留空) + id-type: auto # 主键策略 + configuration: + map-underscore-to-camel-case: false # 禁用驼峰命名自动映射 + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印 SQL(调试用) +logging: + level: + org: + springframework: + security: DEBUG + +springdoc: + api-docs: + enabled: true + path: /v3/api-docs + swagger-ui: + enabled: true + path: /v3/api-docs/swagger-ui.html \ No newline at end of file