fix: 暂存

This commit is contained in:
2026-06-23 14:32:19 +08:00
parent 9be461539c
commit 2a311ca25f
4 changed files with 40 additions and 4 deletions

View File

@@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.context.request.async.AsyncRequestNotUsableException;
import org.springframework.web.servlet.resource.NoResourceFoundException;
/**
@@ -49,6 +50,16 @@ public class GlobalExceptionHandler {
log.warn("SaToken 上下文未初始化(异步分派场景): {}", ex.getMessage());
}
/**
* 异步请求响应已不可用SSE 连接断开后心跳试图写入)
*
* 返回 void 避免 Spring 尝试以 JSON 写入已损坏的 text/event-stream 响应。
*/
@ExceptionHandler(AsyncRequestNotUsableException.class)
public void handleAsyncRequestNotUsable(AsyncRequestNotUsableException ex) {
// SSE 连接已断开后心跳的残留异常,无需处理
}
// ==================== 业务异常 ====================
/**

View File

@@ -57,6 +57,9 @@ public class SseConnectionManager {
/**
* 移除连接
*
* 先移除映射再尝试关闭,避免回调递归。
* 响应已损坏时 complete() 可能抛出 RuntimeException静默忽略。
*
* @param userId 用户 accountId
*/
public void remove(String userId) {
@@ -64,7 +67,8 @@ public class SseConnectionManager {
if (removed != null) {
try {
removed.complete();
} catch (Exception ignored) {
} catch (Throwable ignored) {
// 响应已损坏AsyncRequestNotUsableException 等),忽略
}
log.info("[SSE] 用户连接已移除, userId={}, 当前在线={}", userId, emitters.size());
}

View File

@@ -69,8 +69,8 @@ public class SseController {
emitter.send(SseEmitter.event()
.name("heartbeat")
.data(""));
} catch (IOException e) {
// 客户端已断开,清理
} catch (Exception e) {
// 客户端已断开IOException或响应已不可用AsyncRequestNotUsableException
connectionManager.remove(userId);
heartbeat.shutdown();
}

View File

@@ -158,14 +158,35 @@ public class RoomWebSocketHandler extends TextWebSocketHandler {
return null;
}
/** 聊天消息最大长度 */
private static final int CHAT_MAX_LENGTH = 500;
/**
* 聊天消息处理(暂不持久化,直接广播)
*
* 校验规则:
* - content 不能为 null 或空字符串
* - content 长度不能超过 CHAT_MAX_LENGTH500 字符)
* - 移除 HTML 标签防止 XSS
*/
private void handleChat(String roomId, String userId, String content) {
// 内容为空或超长时直接丢弃
if (content == null || content.isBlank()) {
return;
}
if (content.length() > CHAT_MAX_LENGTH) {
content = content.substring(0, CHAT_MAX_LENGTH);
}
// 简易 XSS 防护:移除 HTML 标签
String sanitized = content
.replace("<", "")
.replace(">", "");
// TODO: 后续可增加消息持久化
broadcastToRoom(roomId, new WsMessage(WsOutboundMessage.CHAT_BROADCAST, Map.of(
"userId", userId,
"content", content,
"content", sanitized,
"timestamp", System.currentTimeMillis()
)));
}