|
|
@ -1,6 +1,9 @@
|
|
|
|
package com.example.venue_reservation_service.netty;
|
|
|
|
package com.example.venue_reservation_service.netty;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import com.example.venue_reservation_service.entity.ChatMessage;
|
|
|
|
|
|
|
|
import com.example.venue_reservation_service.repository.ChatMessageRepository;
|
|
|
|
import io.netty.channel.Channel;
|
|
|
|
import io.netty.channel.Channel;
|
|
|
|
|
|
|
|
import io.netty.channel.ChannelHandler;
|
|
|
|
import io.netty.channel.ChannelHandlerContext;
|
|
|
|
import io.netty.channel.ChannelHandlerContext;
|
|
|
|
import io.netty.channel.SimpleChannelInboundHandler;
|
|
|
|
import io.netty.channel.SimpleChannelInboundHandler;
|
|
|
|
import io.netty.channel.group.ChannelGroup;
|
|
|
|
import io.netty.channel.group.ChannelGroup;
|
|
|
@ -9,18 +12,28 @@ import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
|
|
|
import io.netty.util.concurrent.GlobalEventExecutor;
|
|
|
|
import io.netty.util.concurrent.GlobalEventExecutor;
|
|
|
|
import com.alibaba.fastjson.JSON;
|
|
|
|
import com.alibaba.fastjson.JSON;
|
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
|
|
|
import org.springframework.context.annotation.Scope;
|
|
|
|
|
|
|
|
import org.springframework.data.domain.Page;
|
|
|
|
|
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
|
|
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Component
|
|
|
|
|
|
|
|
@Scope("prototype")
|
|
|
|
|
|
|
|
@ChannelHandler.Sharable
|
|
|
|
public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
|
|
|
|
public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
|
|
|
|
|
|
|
|
|
|
|
|
private static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
|
|
|
|
private static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
|
|
|
|
private static final Map<String, Channel> userChannelMap = new HashMap<>();
|
|
|
|
private static final Map<String, Channel> userChannelMap = new HashMap<>();
|
|
|
|
private static final Map<Integer, String> roleMap = new HashMap<>();
|
|
|
|
private static final Map<Integer, String> roleMap = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
|
|
private ChatMessageRepository chatMessageRepository;
|
|
|
|
|
|
|
|
|
|
|
|
static {
|
|
|
|
static {
|
|
|
|
roleMap.put(0, "普通用户");
|
|
|
|
roleMap.put(0, "普通用户");
|
|
|
|
roleMap.put(1, "普通用户");
|
|
|
|
roleMap.put(1, "普通用户");
|
|
|
@ -41,7 +54,6 @@ public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocket
|
|
|
|
Channel incoming = ctx.channel();
|
|
|
|
Channel incoming = ctx.channel();
|
|
|
|
String username = null;
|
|
|
|
String username = null;
|
|
|
|
|
|
|
|
|
|
|
|
// 查找并移除用户
|
|
|
|
|
|
|
|
for (Map.Entry<String, Channel> entry : userChannelMap.entrySet()) {
|
|
|
|
for (Map.Entry<String, Channel> entry : userChannelMap.entrySet()) {
|
|
|
|
if (entry.getValue().equals(incoming)) {
|
|
|
|
if (entry.getValue().equals(incoming)) {
|
|
|
|
username = entry.getKey();
|
|
|
|
username = entry.getKey();
|
|
|
@ -71,6 +83,8 @@ public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocket
|
|
|
|
handleLogin(incoming, jsonObject);
|
|
|
|
handleLogin(incoming, jsonObject);
|
|
|
|
} else if ("message".equals(type)) {
|
|
|
|
} else if ("message".equals(type)) {
|
|
|
|
handleMessage(incoming, jsonObject);
|
|
|
|
handleMessage(incoming, jsonObject);
|
|
|
|
|
|
|
|
} else if ("loadHistory".equals(type)) {
|
|
|
|
|
|
|
|
handleLoadHistory(incoming, jsonObject);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
sendErrorMessage(incoming, "未知消息类型: " + type);
|
|
|
|
sendErrorMessage(incoming, "未知消息类型: " + type);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -115,6 +129,10 @@ public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocket
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 保存消息到MongoDB
|
|
|
|
|
|
|
|
ChatMessage chatMessage = new ChatMessage(username, userRole, content);
|
|
|
|
|
|
|
|
chatMessageRepository.save(chatMessage);
|
|
|
|
|
|
|
|
|
|
|
|
String roleName = roleMap.getOrDefault(userRole, "未知角色");
|
|
|
|
String roleName = roleMap.getOrDefault(userRole, "未知角色");
|
|
|
|
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
|
|
|
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
|
|
|
|
|
|
|
|
|
|
@ -129,6 +147,32 @@ public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocket
|
|
|
|
broadcastMessage(messageJson.toJSONString());
|
|
|
|
broadcastMessage(messageJson.toJSONString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void handleLoadHistory(Channel incoming, JSONObject jsonObject) {
|
|
|
|
|
|
|
|
String username = jsonObject.getString("username");
|
|
|
|
|
|
|
|
long lastTimestamp = jsonObject.getLongValue("lastTimestamp");
|
|
|
|
|
|
|
|
int pageSize = jsonObject.getIntValue("pageSize");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!userChannelMap.containsKey(username) || !userChannelMap.get(username).equals(incoming)) {
|
|
|
|
|
|
|
|
sendErrorMessage(incoming, "用户未登录");
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LocalDateTime beforeDate = lastTimestamp == 0 ? LocalDateTime.now()
|
|
|
|
|
|
|
|
: LocalDateTime.ofEpochSecond(lastTimestamp/1000, 0, java.time.ZoneOffset.UTC);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Page<ChatMessage> messages = chatMessageRepository.findByTimestampBeforeOrderByTimestampDesc(
|
|
|
|
|
|
|
|
beforeDate,
|
|
|
|
|
|
|
|
org.springframework.data.domain.PageRequest.of(0, pageSize)
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JSONObject response = new JSONObject();
|
|
|
|
|
|
|
|
response.put("type", "history");
|
|
|
|
|
|
|
|
response.put("messages", messages.getContent());
|
|
|
|
|
|
|
|
response.put("hasMore", !messages.isLast());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
incoming.writeAndFlush(new TextWebSocketFrame(response.toJSONString()));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void broadcastMessage(String message) {
|
|
|
|
private void broadcastMessage(String message) {
|
|
|
|
for (Channel channel : channels) {
|
|
|
|
for (Channel channel : channels) {
|
|
|
|
channel.writeAndFlush(new TextWebSocketFrame(message));
|
|
|
|
channel.writeAndFlush(new TextWebSocketFrame(message));
|
|
|
@ -158,7 +202,6 @@ public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocket
|
|
|
|
Channel channel = ctx.channel();
|
|
|
|
Channel channel = ctx.channel();
|
|
|
|
String username = null;
|
|
|
|
String username = null;
|
|
|
|
|
|
|
|
|
|
|
|
// 查找并移除用户
|
|
|
|
|
|
|
|
for (Map.Entry<String, Channel> entry : userChannelMap.entrySet()) {
|
|
|
|
for (Map.Entry<String, Channel> entry : userChannelMap.entrySet()) {
|
|
|
|
if (entry.getValue().equals(channel)) {
|
|
|
|
if (entry.getValue().equals(channel)) {
|
|
|
|
username = entry.getKey();
|
|
|
|
username = entry.getKey();
|
|
|
|