package com.wok.supportbot.controller; import com.wok.supportbot.app.AssistantApp; import com.wok.supportbot.entity.ApiKey; import com.wok.supportbot.entity.SearchResult; import com.wok.supportbot.rag.HybridSearchService; import com.wok.supportbot.rag.SearchMode; import com.wok.supportbot.service.ApiKeyService; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 开放 API 接口 * 提供第三方系统调用的对话和检索能力,鉴权由 ApiKeyAuthFilter 处理。 */ @Slf4j @RestController @RequestMapping("/open-api") public class OpenApiController { @Autowired private AssistantApp assistantApp; @Autowired private HybridSearchService hybridSearchService; @Autowired private ApiKeyService apiKeyService; /** * 同步对话接口 * * @param message 用户消息 * @param roleId 客服角色ID(可选) * @param chatId 会话ID(可选,不传则自动生成) * @param request HTTP 请求(含已鉴权的 API Key 信息) * @return AI 回答 */ @PostMapping("/chat") public ResponseEntity> chat( @RequestParam String message, @RequestParam(required = false) String roleId, @RequestParam(required = false) String chatId, HttpServletRequest request) { try { ApiKey apiKey = getApiKeyFromRequest(request); String resolvedChatId = (chatId != null && !chatId.isBlank()) ? chatId : "openapi-" + apiKey.getId() + "-" + System.currentTimeMillis(); String reply = assistantApp.doChat(message, resolvedChatId); Map result = new HashMap<>(); result.put("success", true); result.put("data", Map.of( "reply", reply, "chatId", resolvedChatId )); return ResponseEntity.ok(result); } catch (Exception e) { log.error("开放 API 对话失败", e); return ResponseEntity.status(500).body(Map.of( "success", false, "message", "对话失败:" + e.getMessage() )); } } /** * SSE 流式对话接口 * * @param message 用户消息 * @param chatId 会话ID(可选) * @param request HTTP 请求 * @return SSE 流式回答 */ @GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux chatStream( @RequestParam String message, @RequestParam(required = false) String roleId, @RequestParam(required = false) String chatId, HttpServletRequest request) { ApiKey apiKey = getApiKeyFromRequest(request); String resolvedChatId = (chatId != null && !chatId.isBlank()) ? chatId : "openapi-stream-" + apiKey.getId() + "-" + System.currentTimeMillis(); return assistantApp.doChatByStream(message, resolvedChatId); } /** * 知识库检索接口 * * @param query 查询文本 * @param topK 返回条数(默认 5) * @param searchMode 检索模式:VECTOR / KEYWORD / HYBRID(默认 VECTOR) * @return 检索结果列表 */ @PostMapping("/rag/search") public ResponseEntity> ragSearch( @RequestParam String query, @RequestParam(defaultValue = "5") int topK, @RequestParam(defaultValue = "VECTOR") String searchMode) { try { SearchMode mode; try { mode = SearchMode.valueOf(searchMode.toUpperCase()); } catch (IllegalArgumentException e) { mode = SearchMode.VECTOR; } List results = hybridSearchService.search( query, mode, topK, 0.0, Collections.emptyList()); Map result = new HashMap<>(); result.put("success", true); result.put("data", results); result.put("total", results.size()); return ResponseEntity.ok(result); } catch (Exception e) { log.error("开放 API 检索失败", e); return ResponseEntity.status(500).body(Map.of( "success", false, "message", "检索失败:" + e.getMessage() )); } } /** * 从 request attribute 获取已鉴权的 API Key 信息 */ private ApiKey getApiKeyFromRequest(HttpServletRequest request) { Object attr = request.getAttribute("apiKey"); if (attr instanceof ApiKey apiKey) { return apiKey; } throw new IllegalStateException("API Key 鉴权信息缺失"); } }