|
|
|
|
package com.ruoyi.websocket.controller;
|
|
|
|
|
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.UUID;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
import org.springframework.messaging.handler.annotation.DestinationVariable;
|
|
|
|
|
import org.springframework.messaging.handler.annotation.MessageMapping;
|
|
|
|
|
import org.springframework.messaging.handler.annotation.Payload;
|
|
|
|
|
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
|
|
|
|
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
|
|
|
|
import org.springframework.stereotype.Controller;
|
|
|
|
|
import com.alibaba.fastjson2.JSON;
|
|
|
|
|
import com.ruoyi.common.core.domain.model.LoginUser;
|
|
|
|
|
import com.ruoyi.websocket.config.LoginUserPrincipal;
|
|
|
|
|
import com.ruoyi.common.utils.StringUtils;
|
|
|
|
|
import com.ruoyi.system.domain.Rooms;
|
|
|
|
|
import com.ruoyi.system.service.IRoomsService;
|
|
|
|
|
import com.ruoyi.websocket.dto.RoomMemberDTO;
|
|
|
|
|
import com.ruoyi.websocket.service.RoomChatService;
|
|
|
|
|
import com.ruoyi.websocket.service.RoomWebSocketService;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* WebSocket 房间消息控制器
|
|
|
|
|
*
|
|
|
|
|
* @author ruoyi
|
|
|
|
|
*/
|
|
|
|
|
@Controller
|
|
|
|
|
public class RoomWebSocketController {
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private SimpMessagingTemplate messagingTemplate;
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private RoomWebSocketService roomWebSocketService;
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private RoomChatService roomChatService;
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
private IRoomsService roomsService;
|
|
|
|
|
|
|
|
|
|
private static final String TYPE_JOIN = "JOIN";
|
|
|
|
|
private static final String TYPE_LEAVE = "LEAVE";
|
|
|
|
|
private static final String TYPE_PING = "PING";
|
|
|
|
|
private static final String TYPE_CHAT = "CHAT";
|
|
|
|
|
private static final String TYPE_PRIVATE_CHAT = "PRIVATE_CHAT";
|
|
|
|
|
private static final String TYPE_CHAT_HISTORY = "CHAT_HISTORY";
|
|
|
|
|
private static final String TYPE_PRIVATE_CHAT_HISTORY = "PRIVATE_CHAT_HISTORY";
|
|
|
|
|
private static final String TYPE_PRIVATE_CHAT_HISTORY_REQUEST = "PRIVATE_CHAT_HISTORY_REQUEST";
|
|
|
|
|
private static final String TYPE_MEMBER_JOINED = "MEMBER_JOINED";
|
|
|
|
|
private static final String TYPE_MEMBER_LEFT = "MEMBER_LEFT";
|
|
|
|
|
private static final String TYPE_MEMBER_LIST = "MEMBER_LIST";
|
|
|
|
|
private static final String TYPE_PONG = "PONG";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 处理房间消息:JOIN、LEAVE、PING、CHAT、PRIVATE_CHAT
|
|
|
|
|
*/
|
|
|
|
|
@MessageMapping("/room/{roomId}")
|
|
|
|
|
public void handleRoomMessage(@DestinationVariable Long roomId, @Payload String payload,
|
|
|
|
|
SimpMessageHeaderAccessor accessor) {
|
|
|
|
|
LoginUser loginUser = null;
|
|
|
|
|
if (accessor.getUser() instanceof LoginUserPrincipal) {
|
|
|
|
|
loginUser = ((LoginUserPrincipal) accessor.getUser()).getLoginUser();
|
|
|
|
|
}
|
|
|
|
|
if (loginUser == null) return;
|
|
|
|
|
|
|
|
|
|
Map<String, Object> body = parsePayload(payload);
|
|
|
|
|
if (body == null) return;
|
|
|
|
|
|
|
|
|
|
String type = (String) body.get("type");
|
|
|
|
|
String sessionId = accessor.getSessionId();
|
|
|
|
|
if (sessionId == null) sessionId = UUID.randomUUID().toString();
|
|
|
|
|
|
|
|
|
|
if (TYPE_JOIN.equals(type)) {
|
|
|
|
|
handleJoin(roomId, sessionId, loginUser, body);
|
|
|
|
|
} else if (TYPE_LEAVE.equals(type)) {
|
|
|
|
|
handleLeave(roomId, sessionId, loginUser);
|
|
|
|
|
} else if (TYPE_PING.equals(type)) {
|
|
|
|
|
handlePing(roomId, sessionId, loginUser);
|
|
|
|
|
} else if (TYPE_CHAT.equals(type)) {
|
|
|
|
|
handleChat(roomId, sessionId, loginUser, body);
|
|
|
|
|
} else if (TYPE_PRIVATE_CHAT.equals(type)) {
|
|
|
|
|
handlePrivateChat(roomId, sessionId, loginUser, body);
|
|
|
|
|
} else if (TYPE_PRIVATE_CHAT_HISTORY_REQUEST.equals(type)) {
|
|
|
|
|
handlePrivateChatHistoryRequest(roomId, loginUser, body);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
private Map<String, Object> parsePayload(String payload) {
|
|
|
|
|
try {
|
|
|
|
|
return JSON.parseObject(payload, Map.class);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void handleJoin(Long roomId, String sessionId, LoginUser loginUser, Map<String, Object> body) {
|
|
|
|
|
Rooms room = roomsService.selectRoomsById(roomId);
|
|
|
|
|
if (room == null) return;
|
|
|
|
|
|
|
|
|
|
RoomMemberDTO member = buildMember(loginUser, sessionId, roomId, body);
|
|
|
|
|
roomWebSocketService.joinRoom(roomId, sessionId, member);
|
|
|
|
|
|
|
|
|
|
List<RoomMemberDTO> members = roomWebSocketService.getRoomMembers(roomId);
|
|
|
|
|
Map<String, Object> memberListMsg = new HashMap<>();
|
|
|
|
|
memberListMsg.put("type", TYPE_MEMBER_LIST);
|
|
|
|
|
memberListMsg.put("members", members);
|
|
|
|
|
|
|
|
|
|
Map<String, Object> joinedMsg = new HashMap<>();
|
|
|
|
|
joinedMsg.put("type", TYPE_MEMBER_JOINED);
|
|
|
|
|
joinedMsg.put("member", member);
|
|
|
|
|
|
|
|
|
|
String topic = "/topic/room/" + roomId;
|
|
|
|
|
messagingTemplate.convertAndSend(topic, memberListMsg);
|
|
|
|
|
|
|
|
|
|
List<Object> chatHistory = roomChatService.getGroupChatHistory(roomId);
|
|
|
|
|
Map<String, Object> chatHistoryMsg = new HashMap<>();
|
|
|
|
|
chatHistoryMsg.put("type", TYPE_CHAT_HISTORY);
|
|
|
|
|
chatHistoryMsg.put("messages", chatHistory);
|
|
|
|
|
messagingTemplate.convertAndSendToUser(loginUser.getUsername(), "/queue/private", chatHistoryMsg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void handleLeave(Long roomId, String sessionId, LoginUser loginUser) {
|
|
|
|
|
RoomMemberDTO member = buildMember(loginUser, sessionId, roomId, null);
|
|
|
|
|
roomWebSocketService.leaveRoom(roomId, sessionId, loginUser.getUserId());
|
|
|
|
|
|
|
|
|
|
Map<String, Object> msg = new HashMap<>();
|
|
|
|
|
msg.put("type", TYPE_MEMBER_LEFT);
|
|
|
|
|
msg.put("member", member);
|
|
|
|
|
msg.put("sessionId", sessionId);
|
|
|
|
|
|
|
|
|
|
messagingTemplate.convertAndSend("/topic/room/" + roomId, msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void handlePing(Long roomId, String sessionId, LoginUser loginUser) {
|
|
|
|
|
roomWebSocketService.refreshSessionHeartbeat(sessionId);
|
|
|
|
|
|
|
|
|
|
Map<String, Object> msg = new HashMap<>();
|
|
|
|
|
msg.put("type", TYPE_PONG);
|
|
|
|
|
|
|
|
|
|
messagingTemplate.convertAndSendToUser(loginUser.getUsername(), "/queue/private", msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 群聊:广播给房间内所有人 */
|
|
|
|
|
private void handleChat(Long roomId, String sessionId, LoginUser loginUser, Map<String, Object> body) {
|
|
|
|
|
String content = body != null && body.containsKey("content") ? String.valueOf(body.get("content")) : "";
|
|
|
|
|
if (content.isEmpty()) return;
|
|
|
|
|
|
|
|
|
|
Map<String, Object> sender = new HashMap<>();
|
|
|
|
|
sender.put("userId", loginUser.getUserId());
|
|
|
|
|
sender.put("userName", loginUser.getUsername());
|
|
|
|
|
sender.put("nickName", loginUser.getUser().getNickName());
|
|
|
|
|
sender.put("avatar", loginUser.getUser().getAvatar());
|
|
|
|
|
sender.put("sessionId", sessionId);
|
|
|
|
|
|
|
|
|
|
Map<String, Object> msg = new HashMap<>();
|
|
|
|
|
msg.put("type", TYPE_CHAT);
|
|
|
|
|
msg.put("sender", sender);
|
|
|
|
|
msg.put("content", content);
|
|
|
|
|
msg.put("timestamp", System.currentTimeMillis());
|
|
|
|
|
|
|
|
|
|
roomChatService.saveGroupChat(roomId, loginUser.getUserId(), msg);
|
|
|
|
|
messagingTemplate.convertAndSend("/topic/room/" + roomId, msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 私聊:发送给指定用户(仅限同房间成员) */
|
|
|
|
|
private void handlePrivateChat(Long roomId, String sessionId, LoginUser loginUser, Map<String, Object> body) {
|
|
|
|
|
Object targetUserNameObj = body != null ? body.get("targetUserName") : null;
|
|
|
|
|
Object targetUserIdObj = body != null ? body.get("targetUserId") : null;
|
|
|
|
|
String content = body != null && body.containsKey("content") ? String.valueOf(body.get("content")) : "";
|
|
|
|
|
if (targetUserNameObj == null || content.isEmpty()) return;
|
|
|
|
|
|
|
|
|
|
String targetUserName = String.valueOf(targetUserNameObj);
|
|
|
|
|
if (targetUserName.equals(loginUser.getUsername())) return;
|
|
|
|
|
|
|
|
|
|
List<RoomMemberDTO> members = roomWebSocketService.getRoomMembers(roomId);
|
|
|
|
|
boolean targetInRoom = members.stream().anyMatch(m -> targetUserName.equals(m.getUserName()));
|
|
|
|
|
if (!targetInRoom) return;
|
|
|
|
|
|
|
|
|
|
Map<String, Object> sender = new HashMap<>();
|
|
|
|
|
sender.put("userId", loginUser.getUserId());
|
|
|
|
|
sender.put("userName", loginUser.getUsername());
|
|
|
|
|
sender.put("nickName", loginUser.getUser().getNickName());
|
|
|
|
|
sender.put("avatar", loginUser.getUser().getAvatar());
|
|
|
|
|
sender.put("sessionId", sessionId);
|
|
|
|
|
|
|
|
|
|
Map<String, Object> msg = new HashMap<>();
|
|
|
|
|
msg.put("type", TYPE_PRIVATE_CHAT);
|
|
|
|
|
msg.put("sender", sender);
|
|
|
|
|
msg.put("targetUserId", targetUserIdObj);
|
|
|
|
|
msg.put("targetUserName", targetUserName);
|
|
|
|
|
msg.put("content", content);
|
|
|
|
|
msg.put("timestamp", System.currentTimeMillis());
|
|
|
|
|
|
|
|
|
|
Long targetUserId = targetUserIdObj instanceof Number ? ((Number) targetUserIdObj).longValue() : null;
|
|
|
|
|
if (targetUserId != null) {
|
|
|
|
|
roomChatService.savePrivateChat(loginUser.getUserId(), targetUserId, msg);
|
|
|
|
|
}
|
|
|
|
|
messagingTemplate.convertAndSendToUser(targetUserName, "/queue/private", msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 私聊历史请求:返回与指定用户的聊天记录 */
|
|
|
|
|
private void handlePrivateChatHistoryRequest(Long roomId, LoginUser loginUser, Map<String, Object> body) {
|
|
|
|
|
Object targetUserIdObj = body != null ? body.get("targetUserId") : null;
|
|
|
|
|
if (targetUserIdObj == null) return;
|
|
|
|
|
|
|
|
|
|
Long targetUserId = targetUserIdObj instanceof Number ? ((Number) targetUserIdObj).longValue() : null;
|
|
|
|
|
if (targetUserId == null) return;
|
|
|
|
|
|
|
|
|
|
List<RoomMemberDTO> members = roomWebSocketService.getRoomMembers(roomId);
|
|
|
|
|
boolean targetInRoom = members.stream().anyMatch(m -> targetUserId.equals(m.getUserId()));
|
|
|
|
|
if (!targetInRoom) return;
|
|
|
|
|
|
|
|
|
|
List<Object> history = roomChatService.getPrivateChatHistory(loginUser.getUserId(), targetUserId);
|
|
|
|
|
Map<String, Object> resp = new HashMap<>();
|
|
|
|
|
resp.put("type", TYPE_PRIVATE_CHAT_HISTORY);
|
|
|
|
|
resp.put("targetUserId", targetUserId);
|
|
|
|
|
resp.put("messages", history);
|
|
|
|
|
messagingTemplate.convertAndSendToUser(loginUser.getUsername(), "/queue/private", resp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private RoomMemberDTO buildMember(LoginUser loginUser, String sessionId, Long roomId, Map<String, Object> body) {
|
|
|
|
|
RoomMemberDTO dto = new RoomMemberDTO();
|
|
|
|
|
dto.setUserId(loginUser.getUserId());
|
|
|
|
|
dto.setUserName(loginUser.getUsername());
|
|
|
|
|
dto.setNickName(loginUser.getUser().getNickName());
|
|
|
|
|
dto.setAvatar(loginUser.getUser().getAvatar());
|
|
|
|
|
dto.setSessionId(sessionId);
|
|
|
|
|
dto.setDeviceId(body != null && body.containsKey("deviceId") ? String.valueOf(body.get("deviceId")) : "default");
|
|
|
|
|
dto.setJoinedAt(System.currentTimeMillis());
|
|
|
|
|
|
|
|
|
|
Rooms room = roomsService.selectRoomsById(roomId);
|
|
|
|
|
if (room != null && loginUser.getUserId().equals(room.getOwnerId())) {
|
|
|
|
|
dto.setRole("owner");
|
|
|
|
|
} else if (StringUtils.isNotEmpty(loginUser.getUser().getUserLevel())) {
|
|
|
|
|
dto.setRole(loginUser.getUser().getUserLevel());
|
|
|
|
|
} else {
|
|
|
|
|
dto.setRole("member");
|
|
|
|
|
}
|
|
|
|
|
return dto;
|
|
|
|
|
}
|
|
|
|
|
}
|