Skip to content
Merged

123 #48

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import io.github.timemachinelab.core.session.domain.entity.ConversationSession;
import io.github.timemachinelab.core.session.infrastructure.web.dto.*;
import io.github.timemachinelab.util.QaTreeSerializeUtil;
import io.github.timemachinelab.entity.req.RetryRequest;
import io.github.timemachinelab.entity.resp.ApiResult;
import io.github.timemachinelab.entity.resp.RetryResponse;
Expand Down Expand Up @@ -375,14 +376,24 @@ public ResponseEntity<ApiResult<ConversationHistoryResponse>> getConversationHis
.body(ApiResult.error("会话数据不完整"));
}

// 4. 组装返回数据
// 4. 序列化QaTree为前端期望的格式
String serializedQaTree;
try {
serializedQaTree = QaTreeSerializeUtil.serialize(qaTree);
} catch (Exception e) {
log.error("序列化QaTree失败 - sessionId: {}", sessionId, e);
return ResponseEntity.internalServerError()
.body(ApiResult.error("序列化会话数据失败: " + e.getMessage()));
}

// 5. 组装返回数据
ConversationHistoryResponse response = new ConversationHistoryResponse(
session.getSessionId(),
session.getUserId(),
session.getCurrentNode(),
session.getCreateTime(),
session.getUpdateTime(),
qaTree
serializedQaTree
);

log.info("成功获取对话历史 - sessionId: {}, 节点数量: {}", sessionId, qaTree.getNodeCount());
Expand Down Expand Up @@ -411,4 +422,59 @@ public ResponseEntity<Map<String, Object>> getSseStatus() {
public ResponseEntity<Boolean> setUserProfile(@RequestBody SetUserProfileRequest request) {
return ResponseEntity.ok(sessionManagementService.setUserProfile(request.getSessionId(), request.getUserId(), request.getUserProfile()));
}

/**
* 验证指纹一致性
* 检查客户端提供的指纹是否与服务端生成的指纹一致
* 如果不一致,生成新的指纹替代客户端指纹,并返回空的sessionIdList避免水平越权
*
* @param request HTTP请求对象
* @return 验证结果,包含是否一致和正确的指纹
*/
@PostMapping("/validate-fingerprint")
public ResponseEntity<ApiResult<Map<String, Object>>> validateFingerprint(
@RequestBody Map<String, String> requestBody,
HttpServletRequest request) {

try {
// 从请求体中获取客户端指纹
String clientFingerprint = requestBody.get("fingerprint");

// 生成服务端指纹
UserFingerprint serverFingerprint = fingerprintService.getOrCreateUserFingerprint(request);
String serverFingerprintId = serverFingerprint.getFingerprint();

// 比较指纹
boolean isConsistent = serverFingerprintId.equals(clientFingerprint);

Map<String, Object> result = new ConcurrentHashMap<>();
result.put("isConsistent", isConsistent);
result.put("serverFingerprint", serverFingerprintId);
result.put("clientFingerprint", clientFingerprint);
result.put("timestamp", System.currentTimeMillis());

if (!isConsistent) {
log.warn("指纹不一致 - 客户端: {}, 服务端: {}, 生成新指纹替代", clientFingerprint, serverFingerprintId);
result.put("message", "指纹不一致,已生成新指纹");
result.put("newFingerprint", serverFingerprintId);
// 指纹不匹配时返回空的sessionIdList,避免水平越权
result.put("sessionIdList", List.of());
result.put("requireUpdate", true);
} else {
log.debug("指纹验证通过 - 指纹: {}", serverFingerprintId);
result.put("message", "指纹验证通过");
// 指纹匹配时返回对应的sessionIdList
List<String> sessionIds = sessionManagementService.getUserSessionIds(serverFingerprintId);
result.put("sessionIdList", sessionIds);
result.put("requireUpdate", false);
}

return ResponseEntity.ok(ApiResult.success(result));

} catch (Exception e) {
log.error("指纹验证失败: {}", e.getMessage(), e);
return ResponseEntity.internalServerError()
.body(ApiResult.error("指纹验证失败: " + e.getMessage()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,19 @@ public void registerSseConnection(String fingerprint, SseEmitter emitter) {
// 如果用户已有连接,先移除旧连接
SseEmitter oldEmitter = sseEmitters.get(fingerprint);
if (oldEmitter != null) {
oldEmitter.complete(); // 完成旧连接
// 静默关闭旧连接,避免向已断开的连接发送消息导致 Broken pipe 错误
try {
oldEmitter.complete(); // 直接完成旧连接,不发送关闭通知
log.info("旧SSE连接已静默关闭 - 用户指纹: {}", fingerprint);
} catch (Exception e) {
log.warn("关闭旧连接时出错 - 用户指纹: {}, 错误: {}", fingerprint, e.getMessage());
}
}

sseEmitters.put(fingerprint, emitter);
log.info("SSE连接已注册 - 用户指纹: {}", fingerprint);

// 添加连接完成和超时的回调
emitter.onCompletion(() -> {
sseEmitters.remove(fingerprint);
log.info("SSE连接已完成 - 用户指纹: {}", fingerprint);
});

emitter.onTimeout(() -> {
sseEmitters.remove(fingerprint);
log.info("SSE连接已超时 - 用户指纹: {}", fingerprint);
});
// 注意:回调处理由Controller层统一管理,避免重复设置
}

/**
Expand All @@ -67,8 +64,22 @@ public void registerSseConnection(String fingerprint, SseEmitter emitter) {
* @param fingerprint 用户指纹
*/
public void removeSseConnection(String fingerprint) {
SseEmitter emitter = sseEmitters.remove(fingerprint);
SseEmitter emitter = sseEmitters.get(fingerprint);
if (emitter != null) {
try {
// 在关闭连接前通知客户端
emitter.send(SseEmitter.event()
.name("close")
.data("服务器主动关闭连接"));
log.info("已发送连接关闭通知 - 用户指纹: {}", fingerprint);
} catch (IOException e) {
log.warn("发送连接关闭通知失败 - 用户指纹: {}, 错误: {}", fingerprint, e.getMessage());
} catch (IllegalStateException e) {
log.warn("连接已关闭,无法发送关闭通知 - 用户指纹: {}", fingerprint);
}

// 移除并完成连接
sseEmitters.remove(fingerprint);
emitter.complete();
}
log.info("SSE连接已移除 - 用户指纹: {}", fingerprint);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,18 @@ public String getFingerprint() {
*/
public String validateAndGetFingerprint(HttpServletRequest request) throws SessionException {
try {
// 1. 获取已存在的用户指纹(不创建新指纹)
// 1. 优先获取已存在的用户指纹
UserFingerprint userFingerprint = fingerprintService.getExistingUserFingerprint(request);

// 如果指纹不存在,尝试创建新指纹(可能是页面刷新后的情况)
if (userFingerprint == null) {
log.warn("用户指纹不存在,请先建立SSE连接");
throw new SessionException("SSE连接验证失败: 用户指纹不存在,请先建立SSE连接");
log.info("用户指纹不存在,尝试创建新指纹(可能是页面刷新)");
userFingerprint = fingerprintService.getOrCreateUserFingerprint(request);

if (userFingerprint == null) {
log.error("无法创建用户指纹");
throw new SessionException("SSE连接验证失败: 无法生成用户指纹,请重试");
}
}

String fingerprint = userFingerprint.getFingerprint();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.github.timemachinelab.core.session.infrastructure.web.dto;

import io.github.timemachinelab.core.qatree.QaTree;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
Expand All @@ -17,5 +16,5 @@ public class ConversationHistoryResponse {
private String currentNode;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private QaTree qaTree;
private String qaTree;
}
Loading
Loading