/** * 智能文献检索 Controller * * SSE 流式 + PubMed 链接提取 */ import { FastifyRequest, FastifyReply } from 'fastify'; import { researchService } from '../services/researchService.js'; import { logger } from '../../../common/logging/index.js'; interface SearchBody { projectId: string; query: string; } /** * POST /api/v1/asl/research/stream * SSE 实时流式检索 */ export async function streamSearch( request: FastifyRequest<{ Body: SearchBody }>, reply: FastifyReply ) { const { projectId, query } = request.body; const userId = request.user?.userId; if (!userId) { return reply.code(401).send({ success: false, error: '用户未认证' }); } if (!query?.trim()) { return reply.code(400).send({ success: false, error: '请输入检索问题' }); } logger.info('[ResearchController] Starting SSE stream', { userId, queryLength: query.length }); // 设置 SSE 响应头 reply.raw.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Access-Control-Allow-Origin': '*', 'X-Accel-Buffering': 'no', }); try { const task = await researchService.createTaskRecord(projectId, userId, query); reply.raw.write(`data: ${JSON.stringify({ type: 'task_created', taskId: task.id })}\n\n`); await researchService.executeStreamSearch( task.id, query, // 思考过程(统一追加) (reasoning: string) => { reply.raw.write(`data: ${JSON.stringify({ type: 'reasoning', content: reasoning })}\n\n`); }, // 结果内容(统一追加) (content: string) => { reply.raw.write(`data: ${JSON.stringify({ type: 'content', content })}\n\n`); }, // 完成(返回链接列表) (result: { links: string[] }) => { reply.raw.write(`data: ${JSON.stringify({ type: 'completed', links: result.links })}\n\n`); reply.raw.end(); }, // 错误 (error: string) => { reply.raw.write(`data: ${JSON.stringify({ type: 'error', error })}\n\n`); reply.raw.end(); } ); } catch (error: any) { logger.error('[ResearchController] Stream search failed', { error: error.message }); reply.raw.write(`data: ${JSON.stringify({ type: 'error', error: error.message })}\n\n`); reply.raw.end(); } } /** * POST /api/v1/asl/research/tasks * 创建检索任务(异步模式) */ export async function createTask( request: FastifyRequest<{ Body: SearchBody }>, reply: FastifyReply ) { try { const { projectId, query } = request.body; const userId = request.user?.userId; if (!userId) { return reply.code(401).send({ success: false, error: '用户未认证' }); } if (!projectId) { return reply.code(400).send({ success: false, error: '缺少 projectId' }); } if (!query?.trim()) { return reply.code(400).send({ success: false, error: '请输入检索问题' }); } const task = await researchService.createTask({ projectId, userId, query: query.trim() }); return reply.send({ success: true, data: task }); } catch (error: any) { logger.error('[ResearchController] Create task failed', { error: error.message }); return reply.code(500).send({ success: false, error: error.message }); } } /** * GET /api/v1/asl/research/tasks/:taskId/status * 获取任务状态 */ export async function getTaskStatus( request: FastifyRequest<{ Params: { taskId: string } }>, reply: FastifyReply ) { try { const { taskId } = request.params; if (!taskId) { return reply.code(400).send({ success: false, error: '缺少 taskId' }); } const status = await researchService.getTaskStatus(taskId); if (!status) { return reply.code(404).send({ success: false, error: '任务不存在' }); } return reply.send({ success: true, data: status }); } catch (error: any) { logger.error('[ResearchController] Get task status failed', { error: error.message }); return reply.code(500).send({ success: false, error: error.message }); } }