/** * 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(默认)或 xml(LLM 友好格式)', }, }, }, 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)); // V3.1: D1-D7 维度统计 fastify.get('/:projectId/qc-cockpit/dimensions', { schema: { description: 'D1-D7 各维度详细统计', tags: ['IIT Admin - 质控驾驶舱'], params: { type: 'object', properties: { projectId: { type: 'string' } }, required: ['projectId'] }, }, }, iitQcCockpitController.getDimensions.bind(iitQcCockpitController)); // V3.1: 按受试者缺失率 fastify.get('/:projectId/qc-cockpit/completeness', { schema: { description: '按受试者返回缺失率', tags: ['IIT Admin - 质控驾驶舱'], params: { type: 'object', properties: { projectId: { type: 'string' } }, required: ['projectId'] }, }, }, iitQcCockpitController.getCompleteness.bind(iitQcCockpitController)); // V3.1: 字段级质控结果(分页) fastify.get('/:projectId/qc-cockpit/field-status', { schema: { description: '字段级质控结果(分页,支持 recordId/eventId/status 筛选)', tags: ['IIT Admin - 质控驾驶舱'], params: { type: 'object', properties: { projectId: { type: 'string' } }, required: ['projectId'] }, querystring: { type: 'object', properties: { recordId: { type: 'string' }, eventId: { type: 'string' }, status: { type: 'string', enum: ['PASS', 'FAIL', 'WARNING'] }, page: { type: 'string', default: '1' }, pageSize: { type: 'string', default: '50' }, }, }, }, }, iitQcCockpitController.getFieldStatus.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)); // V3.1: D6 方案偏离列表 fastify.get('/:projectId/qc-cockpit/deviations', iitQcCockpitController.getDeviations.bind(iitQcCockpitController)); // 字段级问题分页查询(支持按维度/严重程度筛选) fastify.get('/:projectId/qc-cockpit/field-issues', { schema: { description: '从 qc_field_status 分页查询所有问题字段', tags: ['IIT Admin - QC 驾驶舱'], params: { type: 'object', properties: { projectId: { type: 'string' } }, required: ['projectId'] }, querystring: { type: 'object', properties: { page: { type: 'string' }, pageSize: { type: 'string' }, severity: { type: 'string' }, dimension: { type: 'string' }, recordId: { type: 'string' }, }, }, }, }, iitQcCockpitController.getFieldIssues.bind(iitQcCockpitController)); // ============================================================ // GCP 业务报表路由 // ============================================================ fastify.get('/:projectId/qc-cockpit/report/eligibility', { schema: { description: 'D1 筛选入选表 — 入排合规性评估', tags: ['IIT Admin - GCP 报表'], params: { type: 'object', properties: { projectId: { type: 'string' } }, required: ['projectId'] }, }, }, iitQcCockpitController.getEligibilityReport.bind(iitQcCockpitController)); fastify.get('/:projectId/qc-cockpit/report/completeness', { schema: { description: 'D2 数据完整性总览 — L1 项目 + L2 受试者 + L3 事件级统计', tags: ['IIT Admin - GCP 报表'], params: { type: 'object', properties: { projectId: { type: 'string' } }, required: ['projectId'] }, }, }, iitQcCockpitController.getCompletenessReport.bind(iitQcCockpitController)); fastify.get('/:projectId/qc-cockpit/report/completeness/fields', { schema: { description: 'D2 字段级懒加载 — 按 recordId + eventId 返回缺失字段清单', tags: ['IIT Admin - GCP 报表'], params: { type: 'object', properties: { projectId: { type: 'string' } }, required: ['projectId'] }, querystring: { type: 'object', properties: { recordId: { type: 'string' }, eventId: { type: 'string' }, }, required: ['recordId', 'eventId'], }, }, }, iitQcCockpitController.getCompletenessFields.bind(iitQcCockpitController)); fastify.get('/:projectId/qc-cockpit/report/equery-log', { schema: { description: 'D3/D4 eQuery 全生命周期跟踪 — 统计 + 分组 + 全量明细', tags: ['IIT Admin - GCP 报表'], params: { type: 'object', properties: { projectId: { type: 'string' } }, required: ['projectId'] }, }, }, iitQcCockpitController.getEqueryLogReport.bind(iitQcCockpitController)); fastify.get('/:projectId/qc-cockpit/report/deviations', { schema: { description: 'D6 方案偏离报表 — 结构化超窗数据 + 汇总统计', tags: ['IIT Admin - GCP 报表'], params: { type: 'object', properties: { projectId: { type: 'string' } }, required: ['projectId'] }, }, }, iitQcCockpitController.getDeviationReport.bind(iitQcCockpitController)); }