diff --git a/prompto-lab-app/pom.xml b/prompto-lab-app/pom.xml
index 7581912..6942066 100644
--- a/prompto-lab-app/pom.xml
+++ b/prompto-lab-app/pom.xml
@@ -15,6 +15,8 @@
提示词工程
17
+ 17
+ 17
diff --git a/prompto-lab-app/src/main/java/io/github/timemachinelab/config/CorsConfig.java b/prompto-lab-app/src/main/java/io/github/timemachinelab/config/CorsConfig.java
index 6f93d68..f179e39 100644
--- a/prompto-lab-app/src/main/java/io/github/timemachinelab/config/CorsConfig.java
+++ b/prompto-lab-app/src/main/java/io/github/timemachinelab/config/CorsConfig.java
@@ -27,8 +27,13 @@ public void addCorsMappings(CorsRegistry registry) {
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
- // 允许的源
- configuration.setAllowedOriginPatterns(Arrays.asList("http://localhost:*", "http://127.0.0.1:*"));
+ // 允许的源 - 使用具体的域名模式而不是通配符
+ configuration.setAllowedOriginPatterns(Arrays.asList(
+ "http://localhost:*",
+ "http://127.0.0.1:*",
+ "https://localhost:*",
+ "https://127.0.0.1:*"
+ ));
// 允许的HTTP方法
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
diff --git a/prompto-lab-app/src/main/java/io/github/timemachinelab/controller/UserInteractionController.java b/prompto-lab-app/src/main/java/io/github/timemachinelab/controller/UserInteractionController.java
index a3b2955..0417800 100644
--- a/prompto-lab-app/src/main/java/io/github/timemachinelab/controller/UserInteractionController.java
+++ b/prompto-lab-app/src/main/java/io/github/timemachinelab/controller/UserInteractionController.java
@@ -1,14 +1,28 @@
package io.github.timemachinelab.controller;
+import io.github.timemachinelab.core.session.application.ConversationService;
+import io.github.timemachinelab.core.session.application.MessageProcessingService;
+import io.github.timemachinelab.core.session.application.SessionManagementService;
+import io.github.timemachinelab.core.session.domain.entity.ConversationSession;
+import io.github.timemachinelab.core.session.infrastructure.ai.QuestionGenerationOperation;
+import io.github.timemachinelab.core.session.infrastructure.web.dto.UnifiedAnswerRequest;
+import io.github.timemachinelab.core.session.infrastructure.web.dto.MessageResponse;
import io.github.timemachinelab.entity.req.RetryRequest;
import io.github.timemachinelab.entity.resp.ApiResult;
import io.github.timemachinelab.entity.resp.RetryResponse;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+import javax.annotation.Resource;
import javax.validation.Valid;
+import java.io.IOException;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
/**
* 用户交互控制器
@@ -23,6 +37,55 @@
@Validated
public class UserInteractionController {
+ @Resource
+ private ConversationService conversationService;
+ @Resource
+ private MessageProcessingService messageProcessingService;
+ @Resource
+ private SessionManagementService sessionManagementService;
+ private final Map sseEmitters = new ConcurrentHashMap<>();
+
+ /**
+ * 建立SSE连接
+ */
+ @GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+ public SseEmitter streamConversation(@RequestParam(required = false) String sessionId) {
+ log.info("建立SSE连接 - 会话ID: {}", sessionId);
+
+ if(sessionId == null || sessionId.isEmpty()) {
+ sessionId = UUID.randomUUID().toString();
+ }
+ SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
+ sseEmitters.put(sessionId, emitter);
+
+ // 连接建立时发送欢迎消息
+ try {
+ emitter.send(SseEmitter.event()
+ .name("connected")
+ .data("SSE连接已建立,会话ID: " + sessionId));
+ } catch (IOException e) {
+ log.error("发送欢迎消息失败: {}", e.getMessage());
+ }
+
+ // 设置连接事件处理
+ String finalSessionId = sessionId;
+ emitter.onCompletion(() -> {
+ log.info("SSE连接完成: {}", finalSessionId);
+ });
+
+ emitter.onTimeout(() -> {
+ log.info("SSE连接超时: {}", finalSessionId);
+ sseEmitters.remove(finalSessionId);
+ });
+
+ emitter.onError((ex) -> {
+ log.error("SSE连接错误: {} - {}", finalSessionId, ex.getMessage());
+ sseEmitters.remove(finalSessionId);
+ });
+
+ return emitter;
+ }
+
/**
* 重试接口
*
@@ -34,7 +97,9 @@ public ResponseEntity> retry(@Valid @RequestBody RetryR
try {
log.info("收到重试请求 - nodeId: {}, sessionId: {}, whyretry: {}",
request.getNodeId(), request.getSessionId(), request.getWhyretry());
-
+
+
+
// 构建响应数据
RetryResponse response = RetryResponse.builder()
.nodeId(request.getNodeId())
@@ -53,4 +118,90 @@ public ResponseEntity> retry(@Valid @RequestBody RetryR
return ResponseEntity.badRequest().body(ApiResult.serverError("重试请求处理失败: " + e.getMessage()));
}
}
+
+ /**
+ * 处理统一答案请求
+ * 支持单选、多选、输入框、表单等多种问题类型的回答
+ */
+ @PostMapping("/message")
+ public ResponseEntity processAnswer(@Validated @RequestBody UnifiedAnswerRequest request) {
+ try {
+ log.info("接收到答案请求 - 会话ID: {}, 节点ID: {}, 问题类型: {}",
+ request.getSessionId(),
+ request.getNodeId(),
+ request.getQuestionType());
+
+ // 1. 会话管理和验证
+ String userId = request.getUserId();
+
+ ConversationSession session = sessionManagementService.getOrCreateSession(userId, request.getSessionId());
+
+ // 2. 验证nodeId是否属于该会话
+ if (request.getNodeId() != null && !sessionManagementService.validateNodeId(session.getSessionId(), request.getNodeId())) {
+ log.warn("无效的节点ID - 会话: {}, 节点: {}", session.getSessionId(), request.getNodeId());
+ return ResponseEntity.badRequest().body("无效的节点ID");
+ }
+
+ // 3. 验证答案格式
+ if (!messageProcessingService.validateAnswer(request)) {
+ log.warn("答案格式验证失败: {}", request);
+ return ResponseEntity.badRequest().body("答案格式不正确");
+ }
+
+ // 4. 处理答案并转换为消息
+ String processedMessage = messageProcessingService.preprocessMessage(
+ null, // 没有额外的原始消息
+ request,
+ session
+ );
+
+ // 5. 发送处理后的消息给AI服务
+ conversationService.processUserMessage(
+ session.getUserId(),
+ processedMessage,
+ response -> sendSseMessage(session.getSessionId(), response)
+ );
+
+ return ResponseEntity.ok("答案处理成功");
+
+ } catch (Exception e) {
+ log.error("处理答案失败 - 会话ID: {}, 错误: {}", request.getSessionId(), e.getMessage(), e);
+ return ResponseEntity.internalServerError().body("答案处理失败: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 通过SSE发送消息给客户端
+ *
+ * @param sessionId 会话ID
+ * @param response 消息响应对象
+ */
+ private void sendSseMessage(String sessionId, QuestionGenerationOperation.QuestionGenerationResponse response) {
+ SseEmitter emitter = sseEmitters.get(sessionId);
+ if (emitter != null) {
+ try {
+ emitter.send(SseEmitter.event()
+ .name("message")
+ .data(response));
+ log.info("SSE消息发送成功 - 会话: {}, 消息: {}", sessionId, response);
+ } catch (IOException e) {
+ log.error("SSE消息发送失败 - 会话: {}, 错误: {}", sessionId, e.getMessage());
+ sseEmitters.remove(sessionId);
+ }
+ } else {
+ log.warn("SSE连接不存在 - 会话: {}", sessionId);
+ }
+ }
+
+ /**
+ * 获取SSE连接状态
+ */
+ @GetMapping("/sse-status")
+ public ResponseEntity