Skip to content

Commit 73dec92

Browse files
authored
Merge pull request #48 from welsir/master
123
2 parents db89cba + cee186a commit 73dec92

File tree

10 files changed

+1489
-410
lines changed

10 files changed

+1489
-410
lines changed

prompto-lab-app/src/main/java/io/github/timemachinelab/controller/UserInteractionController.java

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

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

378-
// 4. 组装返回数据
379+
// 4. 序列化QaTree为前端期望的格式
380+
String serializedQaTree;
381+
try {
382+
serializedQaTree = QaTreeSerializeUtil.serialize(qaTree);
383+
} catch (Exception e) {
384+
log.error("序列化QaTree失败 - sessionId: {}", sessionId, e);
385+
return ResponseEntity.internalServerError()
386+
.body(ApiResult.error("序列化会话数据失败: " + e.getMessage()));
387+
}
388+
389+
// 5. 组装返回数据
379390
ConversationHistoryResponse response = new ConversationHistoryResponse(
380391
session.getSessionId(),
381392
session.getUserId(),
382393
session.getCurrentNode(),
383394
session.getCreateTime(),
384395
session.getUpdateTime(),
385-
qaTree
396+
serializedQaTree
386397
);
387398

388399
log.info("成功获取对话历史 - sessionId: {}, 节点数量: {}", sessionId, qaTree.getNodeCount());
@@ -411,4 +422,59 @@ public ResponseEntity<Map<String, Object>> getSseStatus() {
411422
public ResponseEntity<Boolean> setUserProfile(@RequestBody SetUserProfileRequest request) {
412423
return ResponseEntity.ok(sessionManagementService.setUserProfile(request.getSessionId(), request.getUserId(), request.getUserProfile()));
413424
}
425+
426+
/**
427+
* 验证指纹一致性
428+
* 检查客户端提供的指纹是否与服务端生成的指纹一致
429+
* 如果不一致,生成新的指纹替代客户端指纹,并返回空的sessionIdList避免水平越权
430+
*
431+
* @param request HTTP请求对象
432+
* @return 验证结果,包含是否一致和正确的指纹
433+
*/
434+
@PostMapping("/validate-fingerprint")
435+
public ResponseEntity<ApiResult<Map<String, Object>>> validateFingerprint(
436+
@RequestBody Map<String, String> requestBody,
437+
HttpServletRequest request) {
438+
439+
try {
440+
// 从请求体中获取客户端指纹
441+
String clientFingerprint = requestBody.get("fingerprint");
442+
443+
// 生成服务端指纹
444+
UserFingerprint serverFingerprint = fingerprintService.getOrCreateUserFingerprint(request);
445+
String serverFingerprintId = serverFingerprint.getFingerprint();
446+
447+
// 比较指纹
448+
boolean isConsistent = serverFingerprintId.equals(clientFingerprint);
449+
450+
Map<String, Object> result = new ConcurrentHashMap<>();
451+
result.put("isConsistent", isConsistent);
452+
result.put("serverFingerprint", serverFingerprintId);
453+
result.put("clientFingerprint", clientFingerprint);
454+
result.put("timestamp", System.currentTimeMillis());
455+
456+
if (!isConsistent) {
457+
log.warn("指纹不一致 - 客户端: {}, 服务端: {}, 生成新指纹替代", clientFingerprint, serverFingerprintId);
458+
result.put("message", "指纹不一致,已生成新指纹");
459+
result.put("newFingerprint", serverFingerprintId);
460+
// 指纹不匹配时返回空的sessionIdList,避免水平越权
461+
result.put("sessionIdList", List.of());
462+
result.put("requireUpdate", true);
463+
} else {
464+
log.debug("指纹验证通过 - 指纹: {}", serverFingerprintId);
465+
result.put("message", "指纹验证通过");
466+
// 指纹匹配时返回对应的sessionIdList
467+
List<String> sessionIds = sessionManagementService.getUserSessionIds(serverFingerprintId);
468+
result.put("sessionIdList", sessionIds);
469+
result.put("requireUpdate", false);
470+
}
471+
472+
return ResponseEntity.ok(ApiResult.success(result));
473+
474+
} catch (Exception e) {
475+
log.error("指纹验证失败: {}", e.getMessage(), e);
476+
return ResponseEntity.internalServerError()
477+
.body(ApiResult.error("指纹验证失败: " + e.getMessage()));
478+
}
479+
}
414480
}

prompto-lab-app/src/main/java/io/github/timemachinelab/core/session/application/SseNotificationService.java

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,19 @@ public void registerSseConnection(String fingerprint, SseEmitter emitter) {
4343
// 如果用户已有连接,先移除旧连接
4444
SseEmitter oldEmitter = sseEmitters.get(fingerprint);
4545
if (oldEmitter != null) {
46-
oldEmitter.complete(); // 完成旧连接
46+
// 静默关闭旧连接,避免向已断开的连接发送消息导致 Broken pipe 错误
47+
try {
48+
oldEmitter.complete(); // 直接完成旧连接,不发送关闭通知
49+
log.info("旧SSE连接已静默关闭 - 用户指纹: {}", fingerprint);
50+
} catch (Exception e) {
51+
log.warn("关闭旧连接时出错 - 用户指纹: {}, 错误: {}", fingerprint, e.getMessage());
52+
}
4753
}
4854

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

52-
// 添加连接完成和超时的回调
53-
emitter.onCompletion(() -> {
54-
sseEmitters.remove(fingerprint);
55-
log.info("SSE连接已完成 - 用户指纹: {}", fingerprint);
56-
});
57-
58-
emitter.onTimeout(() -> {
59-
sseEmitters.remove(fingerprint);
60-
log.info("SSE连接已超时 - 用户指纹: {}", fingerprint);
61-
});
58+
// 注意:回调处理由Controller层统一管理,避免重复设置
6259
}
6360

6461
/**
@@ -67,8 +64,22 @@ public void registerSseConnection(String fingerprint, SseEmitter emitter) {
6764
* @param fingerprint 用户指纹
6865
*/
6966
public void removeSseConnection(String fingerprint) {
70-
SseEmitter emitter = sseEmitters.remove(fingerprint);
67+
SseEmitter emitter = sseEmitters.get(fingerprint);
7168
if (emitter != null) {
69+
try {
70+
// 在关闭连接前通知客户端
71+
emitter.send(SseEmitter.event()
72+
.name("close")
73+
.data("服务器主动关闭连接"));
74+
log.info("已发送连接关闭通知 - 用户指纹: {}", fingerprint);
75+
} catch (IOException e) {
76+
log.warn("发送连接关闭通知失败 - 用户指纹: {}, 错误: {}", fingerprint, e.getMessage());
77+
} catch (IllegalStateException e) {
78+
log.warn("连接已关闭,无法发送关闭通知 - 用户指纹: {}", fingerprint);
79+
}
80+
81+
// 移除并完成连接
82+
sseEmitters.remove(fingerprint);
7283
emitter.complete();
7384
}
7485
log.info("SSE连接已移除 - 用户指纹: {}", fingerprint);

prompto-lab-app/src/main/java/io/github/timemachinelab/core/session/application/SseValidationService.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,18 @@ public String getFingerprint() {
7171
*/
7272
public String validateAndGetFingerprint(HttpServletRequest request) throws SessionException {
7373
try {
74-
// 1. 获取已存在的用户指纹(不创建新指纹)
74+
// 1. 优先获取已存在的用户指纹
7575
UserFingerprint userFingerprint = fingerprintService.getExistingUserFingerprint(request);
76+
77+
// 如果指纹不存在,尝试创建新指纹(可能是页面刷新后的情况)
7678
if (userFingerprint == null) {
77-
log.warn("用户指纹不存在,请先建立SSE连接");
78-
throw new SessionException("SSE连接验证失败: 用户指纹不存在,请先建立SSE连接");
79+
log.info("用户指纹不存在,尝试创建新指纹(可能是页面刷新)");
80+
userFingerprint = fingerprintService.getOrCreateUserFingerprint(request);
81+
82+
if (userFingerprint == null) {
83+
log.error("无法创建用户指纹");
84+
throw new SessionException("SSE连接验证失败: 无法生成用户指纹,请重试");
85+
}
7986
}
8087

8188
String fingerprint = userFingerprint.getFingerprint();

prompto-lab-app/src/main/java/io/github/timemachinelab/core/session/infrastructure/web/dto/ConversationHistoryResponse.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.github.timemachinelab.core.session.infrastructure.web.dto;
22

3-
import io.github.timemachinelab.core.qatree.QaTree;
43
import lombok.AllArgsConstructor;
54
import lombok.Data;
65
import lombok.NoArgsConstructor;
@@ -17,5 +16,5 @@ public class ConversationHistoryResponse {
1716
private String currentNode;
1817
private LocalDateTime createTime;
1918
private LocalDateTime updateTime;
20-
private QaTree qaTree;
19+
private String qaTree;
2120
}

0 commit comments

Comments
 (0)