Sprint 1-3 Completed (Backend + Frontend): Backend (Sprint 1-2): - Implement 5-layer Agent framework (Query->Planner->Executor->Tools->Reflection) - Create agent_schema with 6 tables (agent_definitions, stages, prompts, sessions, traces, reflexion_rules) - Create protocol_schema with 2 tables (protocol_contexts, protocol_generations) - Implement Protocol Agent core services (Orchestrator, ContextService, PromptBuilder) - Integrate LLM service adapter (DeepSeek/Qwen/GPT-5/Claude) - 6 API endpoints with full authentication - 10/10 API tests passed Frontend (Sprint 3): - Add Protocol Agent entry in AgentHub (indigo theme card) - Implement ProtocolAgentPage with 3-column layout - Collapsible sidebar (Gemini style, 48px <-> 280px) - StatePanel with 5 stage cards (scientific_question, pico, study_design, sample_size, endpoints) - ChatArea with sync button and action cards integration - 100% prototype design restoration (608 lines CSS) - Detailed endpoints structure: baseline, exposure, outcomes, confounders Features: - 5-stage dialogue flow for research protocol design - Conversation-driven interaction with sync-to-protocol button - Real-time context state management - One-click protocol generation button (UI ready, backend pending) Database: - agent_schema: 6 tables for reusable Agent framework - protocol_schema: 2 tables for Protocol Agent - Seed data: 1 agent + 5 stages + 9 prompts + 4 reflexion rules Code Stats: - Backend: 13 files, 4338 lines - Frontend: 14 files, 2071 lines - Total: 27 files, 6409 lines Status: MVP core functionality completed, pending frontend-backend integration testing Next: Sprint 4 - One-click protocol generation + Word export
103 lines
2.1 KiB
TypeScript
103 lines
2.1 KiB
TypeScript
/**
|
|
* AIA 智能问答模块 - 附件控制器
|
|
* @module aia/controllers/attachmentController
|
|
*
|
|
* API 端点:
|
|
* - POST /api/v1/aia/conversations/:id/attachments 上传附件
|
|
*/
|
|
|
|
import type { FastifyRequest, FastifyReply } from 'fastify';
|
|
import { logger } from '../../../common/logging/index.js';
|
|
import * as attachmentService from '../services/attachmentService.js';
|
|
|
|
/**
|
|
* 从 JWT Token 获取用户 ID
|
|
*/
|
|
function getUserId(request: FastifyRequest): string {
|
|
const userId = (request as any).user?.userId;
|
|
if (!userId) {
|
|
throw new Error('User not authenticated');
|
|
}
|
|
return userId;
|
|
}
|
|
|
|
/**
|
|
* 上传附件
|
|
* POST /api/v1/aia/conversations/:id/attachments
|
|
*/
|
|
export async function uploadAttachment(
|
|
request: FastifyRequest<{
|
|
Params: { id: string };
|
|
}>,
|
|
reply: FastifyReply
|
|
) {
|
|
try {
|
|
const userId = getUserId(request);
|
|
const { id: conversationId } = request.params;
|
|
|
|
// 获取上传的文件
|
|
const data = await request.file();
|
|
|
|
if (!data) {
|
|
return reply.status(400).send({
|
|
code: -1,
|
|
error: {
|
|
code: 'VALIDATION_ERROR',
|
|
message: '请上传文件',
|
|
},
|
|
});
|
|
}
|
|
|
|
const buffer = await data.toBuffer();
|
|
|
|
logger.info('[AIA:AttachmentController] 上传附件', {
|
|
userId,
|
|
conversationId,
|
|
filename: data.filename,
|
|
mimetype: data.mimetype,
|
|
size: buffer.length,
|
|
});
|
|
|
|
const attachment = await attachmentService.uploadAttachment(
|
|
userId,
|
|
conversationId,
|
|
{
|
|
filename: data.filename,
|
|
mimetype: data.mimetype,
|
|
buffer,
|
|
}
|
|
);
|
|
|
|
return reply.send({
|
|
code: 0,
|
|
data: attachment,
|
|
});
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
logger.error('[AIA:AttachmentController] 上传附件失败', {
|
|
error: errorMessage,
|
|
stack: error instanceof Error ? error.stack : undefined,
|
|
});
|
|
|
|
return reply.status(500).send({
|
|
code: -1,
|
|
error: {
|
|
code: 'INTERNAL_ERROR',
|
|
message: errorMessage,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|