Files
AIclinicalresearch/backend/src/modules/admin/iit-projects/iitQcCockpitRoutes.ts
HaHafeng 203846968c feat(iit): Complete CRA Agent V3.0 P0 milestone - autonomous QC pipeline
P0-1: Variable list sync from REDCap metadata
P0-2: QC rule configuration with JSON Logic + AI suggestion
P0-3: Scheduled QC + report generation + eQuery closed loop
P0-4: Unified dashboard + AI stream timeline + critical events

Backend:
- Add IitEquery, IitCriticalEvent Prisma models + migration
- Add cronEnabled/cronExpression to IitProject
- Implement eQuery service/controller/routes (CRUD + respond/review/close)
- Implement DailyQcOrchestrator (report -> eQuery -> critical events -> notify)
- Add AI rule suggestion service
- Register daily QC cron worker and eQuery auto-review worker
- Extend QC cockpit with timeline, trend, critical events APIs
- Fix timeline issues field compat (object vs array format)

Frontend:
- Create IIT business module with 6 pages (Dashboard, AI Stream, eQuery,
  Reports, Variable List + project config pages)
- Migrate IIT config from admin panel to business module
- Implement health score, risk heatmap, trend chart, critical event alerts
- Register IIT module in App router and top navigation

Testing:
- Add E2E API test script covering 7 modules (46 assertions, all passing)

Tested: E2E API tests 46/46 passed, backend and frontend verified
Made-with: Cursor
2026-02-26 13:28:08 +08:00

255 lines
8.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* IIT 质控驾驶舱路由
*
* API:
* - GET /api/v1/admin/iit-projects/:projectId/qc-cockpit 获取驾驶舱数据
* - GET /api/v1/admin/iit-projects/:projectId/qc-cockpit/records/:recordId 获取记录详情
* - GET /api/v1/admin/iit-projects/:projectId/qc-cockpit/report 获取质控报告
* - POST /api/v1/admin/iit-projects/:projectId/qc-cockpit/report/refresh 刷新质控报告
*/
import { FastifyInstance } from 'fastify';
import { iitQcCockpitController } from './iitQcCockpitController.js';
export async function iitQcCockpitRoutes(fastify: FastifyInstance) {
// 获取质控驾驶舱数据
fastify.get('/:projectId/qc-cockpit', {
schema: {
description: '获取质控驾驶舱数据(统计 + 热力图)',
tags: ['IIT Admin - 质控驾驶舱'],
params: {
type: 'object',
properties: {
projectId: { type: 'string', description: 'IIT 项目 ID' },
},
required: ['projectId'],
},
response: {
200: {
type: 'object',
properties: {
success: { type: 'boolean' },
data: {
type: 'object',
properties: {
stats: {
type: 'object',
properties: {
qualityScore: { type: 'number' },
totalRecords: { type: 'number' },
passedRecords: { type: 'number' },
failedRecords: { type: 'number' },
warningRecords: { type: 'number' },
pendingRecords: { type: 'number' },
criticalCount: { type: 'number' },
queryCount: { type: 'number' },
deviationCount: { type: 'number' },
passRate: { type: 'number' },
topIssues: {
type: 'array',
items: {
type: 'object',
properties: {
issue: { type: 'string' },
count: { type: 'number' },
severity: { type: 'string' },
},
},
},
},
},
heatmap: {
type: 'object',
properties: {
columns: { type: 'array', items: { type: 'string' } },
rows: {
type: 'array',
items: {
type: 'object',
properties: {
recordId: { type: 'string' },
status: { type: 'string' },
cells: {
type: 'array',
items: {
type: 'object',
properties: {
formName: { type: 'string' },
status: { type: 'string' },
issueCount: { type: 'number' },
recordId: { type: 'string' },
},
},
},
},
},
},
},
},
lastUpdatedAt: { type: 'string' },
},
},
},
},
},
},
}, iitQcCockpitController.getCockpitData.bind(iitQcCockpitController));
// 获取记录质控详情
fastify.get('/:projectId/qc-cockpit/records/:recordId', {
schema: {
description: '获取单条记录的质控详情(含 LLM Trace',
tags: ['IIT Admin - 质控驾驶舱'],
params: {
type: 'object',
properties: {
projectId: { type: 'string', description: 'IIT 项目 ID' },
recordId: { type: 'string', description: '记录 ID' },
},
required: ['projectId', 'recordId'],
},
querystring: {
type: 'object',
properties: {
formName: { type: 'string', description: '表单名称' },
},
},
response: {
200: {
type: 'object',
properties: {
success: { type: 'boolean' },
data: {
type: 'object',
properties: {
recordId: { type: 'string' },
formName: { type: 'string' },
status: { type: 'string' },
data: { type: 'object', additionalProperties: true },
fieldMetadata: { type: 'object', additionalProperties: true },
issues: {
type: 'array',
items: {
type: 'object',
properties: {
field: { type: 'string' },
ruleName: { type: 'string' },
message: { type: 'string' },
severity: { type: 'string' },
actualValue: {},
expectedValue: { type: 'string' },
confidence: { type: 'string' },
},
},
},
llmTrace: {
type: 'object',
properties: {
promptSent: { type: 'string' },
responseReceived: { type: 'string' },
model: { type: 'string' },
latencyMs: { type: 'number' },
},
},
entryTime: { type: 'string' },
},
},
},
},
},
},
}, iitQcCockpitController.getRecordDetail.bind(iitQcCockpitController));
// 获取质控报告
fastify.get('/:projectId/qc-cockpit/report', {
schema: {
description: '获取质控报告(支持 JSON 和 XML 格式)',
tags: ['IIT Admin - 质控驾驶舱'],
params: {
type: 'object',
properties: {
projectId: { type: 'string', description: 'IIT 项目 ID' },
},
required: ['projectId'],
},
querystring: {
type: 'object',
properties: {
format: {
type: 'string',
enum: ['json', 'xml'],
default: 'json',
description: '响应格式json默认或 xmlLLM 友好格式)',
},
},
},
response: {
200: {
type: 'object',
properties: {
success: { type: 'boolean' },
data: {
type: 'object',
properties: {
projectId: { type: 'string' },
reportType: { type: 'string' },
generatedAt: { type: 'string' },
expiresAt: { type: 'string' },
summary: {
type: 'object',
properties: {
totalRecords: { type: 'number' },
completedRecords: { type: 'number' },
criticalIssues: { type: 'number' },
warningIssues: { type: 'number' },
pendingQueries: { type: 'number' },
passRate: { type: 'number' },
lastQcTime: { type: 'string' },
},
},
criticalIssues: { type: 'array' },
warningIssues: { type: 'array' },
formStats: { type: 'array' },
llmFriendlyXml: { type: 'string' },
},
},
},
},
},
},
}, iitQcCockpitController.getReport.bind(iitQcCockpitController));
// 刷新质控报告
fastify.post('/:projectId/qc-cockpit/report/refresh', {
schema: {
description: '强制刷新质控报告(忽略缓存)',
tags: ['IIT Admin - 质控驾驶舱'],
params: {
type: 'object',
properties: {
projectId: { type: 'string', description: 'IIT 项目 ID' },
},
required: ['projectId'],
},
response: {
200: {
type: 'object',
properties: {
success: { type: 'boolean' },
data: { type: 'object' },
},
},
},
},
}, iitQcCockpitController.refreshReport.bind(iitQcCockpitController));
// AI 工作时间线
fastify.get('/:projectId/qc-cockpit/timeline', iitQcCockpitController.getTimeline.bind(iitQcCockpitController));
// 重大事件列表
fastify.get('/:projectId/qc-cockpit/critical-events', iitQcCockpitController.getCriticalEvents.bind(iitQcCockpitController));
// 质控趋势近N天通过率折线
fastify.get('/:projectId/qc-cockpit/trend', iitQcCockpitController.getTrend.bind(iitQcCockpitController));
}