feat: add PlayroomWebSocketHandler
All checks were successful
dev middleware install / deploy (push) Successful in 52s

This commit is contained in:
2026-04-04 17:41:28 +08:00
parent 9f22f33a9a
commit c58ad923c5
14 changed files with 208 additions and 6 deletions

2
.gitignore vendored
View File

@@ -3,7 +3,7 @@ target/
.mvn/wrapper/maven-wrapper.jar .mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/ !**/src/main/**/target/
!**/src/test/**/target/ !**/src/test/**/target/
src/main/resources src/main/resources/application.yaml
### STS ### ### STS ###
.apt_generated .apt_generated

View File

@@ -14,7 +14,9 @@ import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler; import org.springframework.web.socket.handler.TextWebSocketHandler;
import xin.merlin.myplayerbackend.config.RabbitMQConfig; import xin.merlin.myplayerbackend.config.RabbitMQConfig;
import java.io.IOException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Set;
@Slf4j @Slf4j
@Component @Component
@@ -32,7 +34,7 @@ public class PlayroomWebSocketHandler extends TextWebSocketHandler {
@Override @Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception { public void afterConnectionEstablished(WebSocketSession session) throws Exception {
Integer userId = (Integer) session.getAttributes().get("id"); 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); sessionManager.addSession(userId, session);
rabbitTemplate.convertAndSend(RabbitMQConfig.WS_VIDEO_QUEUE,payload); rabbitTemplate.convertAndSend(RabbitMQConfig.WS_VIDEO_QUEUE,payload);
} }
@@ -42,18 +44,53 @@ public class PlayroomWebSocketHandler extends TextWebSocketHandler {
JSONObject payload = JSONObject.parseObject(message.getPayload()); JSONObject payload = JSONObject.parseObject(message.getPayload());
Integer userId = (Integer) session.getAttributes().get("id"); Integer userId = (Integer) session.getAttributes().get("id");
if(payload.get("cmd").equals("PONG")) { 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; 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<String> 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<String> 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 @Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
Integer userId = (Integer) session.getAttributes().get("id"); 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); sessionManager.removeSession(userId);
} }

View File

@@ -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.CommandDispatcher;
import xin.merlin.myplayerbackend.utils.websocket.command.CommandDispatcherVideo; import xin.merlin.myplayerbackend.utils.websocket.command.CommandDispatcherVideo;
import java.io.IOException;
@Slf4j @Slf4j
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor

View File

@@ -8,5 +8,7 @@ public interface BaseCommandHandler {
void handle(JSONObject msg) throws IOException; void handle(JSONObject msg) throws IOException;
void handle(String msg, Integer to) throws IOException; void handle(String msg, Integer to) throws IOException;
void handle(String msg, Integer from, Integer playroom);
} }

View File

@@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSONObject;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import xin.merlin.myplayerbackend.utils.websocket.PlayroomWebSocketSessionManager; 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 xin.merlin.myplayerbackend.utils.websocket.command.impl.Ping;
import java.io.IOException; import java.io.IOException;
@@ -14,14 +15,18 @@ public class CommandDispatcherVideo {
private final Ping pingCommand; private final Ping pingCommand;
private final Link linkCommand;
public void dispatch(JSONObject msg) throws IOException { public void dispatch(JSONObject msg) throws IOException {
String cmd = msg.getString("cmd"); String cmd = msg.getString("cmd");
Integer to = Integer.valueOf(msg.getString("to")); Integer to = Integer.valueOf(msg.getString("to"));
Integer playroom = Integer.valueOf(msg.getString("playroom"));
String jsonString = msg.toJSONString(); String jsonString = msg.toJSONString();
switch (cmd){ switch (cmd){
case "PING" -> pingCommand.handle(jsonString,to); case "PING" -> pingCommand.handle(jsonString,to);
case "LINK" -> linkCommand.handle(jsonString,to,playroom);
default -> { default -> {
// System.err.println("Unknown command: " + cmd); // System.err.println("Unknown command: " + cmd);
// TODO: 这里应该使用default的一个类统一封装 // TODO: 这里应该使用default的一个类统一封装

View File

@@ -49,4 +49,8 @@ public class GroupMessage implements BaseCommandHandler {
} }
@Override
public void handle(String msg, Integer from, Integer playroom) {
}
} }

View File

@@ -24,5 +24,9 @@ public class Heartbeat implements BaseCommandHandler {
@Override @Override
public void handle(String msg, Integer to) throws IOException { public void handle(String msg, Integer to) throws IOException {
}
@Override
public void handle(String msg, Integer from, Integer playroom) {
} }
} }

View File

@@ -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<String, String> 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 {
}
}

View File

@@ -26,6 +26,10 @@ TODO: 这里需要处理如果目标用户不在线,如何去完成消息的
@Override @Override
public void handle(String msg, Integer to) throws IOException { public void handle(String msg, Integer to) throws IOException {
}
@Override
public void handle(String msg, Integer from, Integer playroom) {
} }
} }

View File

@@ -23,5 +23,9 @@ public class PersonalNotify implements BaseCommandHandler {
@Override @Override
public void handle(String msg, Integer to) throws IOException { public void handle(String msg, Integer to) throws IOException {
}
@Override
public void handle(String msg, Integer from, Integer playroom) {
} }
} }

View File

@@ -29,5 +29,9 @@ public class Ping implements BaseCommandHandler {
System.out.println(to+":"+msg); System.out.println(to+":"+msg);
playroomWebSocketSessionManager.sendToUser(to, msg); playroomWebSocketSessionManager.sendToUser(to, msg);
} }
@Override
public void handle(String msg, Integer from, Integer playroom) {
}
} }

View File

@@ -22,6 +22,10 @@ public class SystemNotify implements BaseCommandHandler {
@Override @Override
public void handle(String msg, Integer to) throws IOException { public void handle(String msg, Integer to) throws IOException {
}
@Override
public void handle(String msg, Integer from, Integer playroom) {
} }
} }

View File

@@ -23,5 +23,9 @@ public class Typing implements BaseCommandHandler {
@Override @Override
public void handle(String msg, Integer to) throws IOException { public void handle(String msg, Integer to) throws IOException {
}
@Override
public void handle(String msg, Integer from, Integer playroom) {
} }
} }

View File

@@ -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