diff --git a/COMMIT_DAY1.txt b/COMMIT_DAY1.txt index 03024dc3..9ac23cce 100644 --- a/COMMIT_DAY1.txt +++ b/COMMIT_DAY1.txt @@ -46,6 +46,9 @@ Status: Day 1 complete (11/11 tasks), ready for Day 2 + + + diff --git a/DC模块代码恢复指南.md b/DC模块代码恢复指南.md index 2c63e8b2..25c25499 100644 --- a/DC模块代码恢复指南.md +++ b/DC模块代码恢复指南.md @@ -276,6 +276,9 @@ + + + diff --git a/SAE_WECHAT_MP_DEPLOY_STEPS.md b/SAE_WECHAT_MP_DEPLOY_STEPS.md index b04b7cf6..c1545e99 100644 --- a/SAE_WECHAT_MP_DEPLOY_STEPS.md +++ b/SAE_WECHAT_MP_DEPLOY_STEPS.md @@ -224,5 +224,8 @@ https://iit.xunzhengyixue.com/api/v1/iit/health + + + diff --git a/backend/DEPLOY_TO_SAE_FOR_WECHAT_MP.md b/backend/DEPLOY_TO_SAE_FOR_WECHAT_MP.md index 03f1c2f1..cdfe4bec 100644 --- a/backend/DEPLOY_TO_SAE_FOR_WECHAT_MP.md +++ b/backend/DEPLOY_TO_SAE_FOR_WECHAT_MP.md @@ -153,5 +153,8 @@ https://iit.xunzhengyixue.com/api/v1/iit/health + + + diff --git a/backend/RESTART_SERVER_NOW.md b/backend/RESTART_SERVER_NOW.md index c0d68314..c60606e6 100644 --- a/backend/RESTART_SERVER_NOW.md +++ b/backend/RESTART_SERVER_NOW.md @@ -55,4 +55,7 @@ + + + diff --git a/backend/WECHAT_MP_CONFIG_READY.md b/backend/WECHAT_MP_CONFIG_READY.md index a4d80b5e..ec9b47ed 100644 --- a/backend/WECHAT_MP_CONFIG_READY.md +++ b/backend/WECHAT_MP_CONFIG_READY.md @@ -314,5 +314,8 @@ npx tsx src/modules/iit-manager/test-patient-wechat-url-verify.ts + + + diff --git a/backend/WECHAT_MP_QUICK_FIX.md b/backend/WECHAT_MP_QUICK_FIX.md index 631eaecd..f52d32bd 100644 --- a/backend/WECHAT_MP_QUICK_FIX.md +++ b/backend/WECHAT_MP_QUICK_FIX.md @@ -176,5 +176,8 @@ npm run dev + + + diff --git a/backend/check_db.ts b/backend/check_db.ts index 0422eab9..8210d00e 100644 --- a/backend/check_db.ts +++ b/backend/check_db.ts @@ -55,3 +55,6 @@ main() + + + diff --git a/backend/check_db_data.ts b/backend/check_db_data.ts index 5e245c4d..26e7b72e 100644 --- a/backend/check_db_data.ts +++ b/backend/check_db_data.ts @@ -49,3 +49,6 @@ main() + + + diff --git a/backend/check_iit.ts b/backend/check_iit.ts index 7b365d18..8a9c175a 100644 --- a/backend/check_iit.ts +++ b/backend/check_iit.ts @@ -44,3 +44,6 @@ main() + + + diff --git a/backend/check_iit_asl_data.ts b/backend/check_iit_asl_data.ts index 3b91565a..d5e48d6b 100644 --- a/backend/check_iit_asl_data.ts +++ b/backend/check_iit_asl_data.ts @@ -76,3 +76,6 @@ main() + + + diff --git a/backend/check_queue_table.ts b/backend/check_queue_table.ts index 1bbe771b..81bdf59d 100644 --- a/backend/check_queue_table.ts +++ b/backend/check_queue_table.ts @@ -39,3 +39,6 @@ main() + + + diff --git a/backend/check_rvw_issue.ts b/backend/check_rvw_issue.ts index 26d91129..f379a351 100644 --- a/backend/check_rvw_issue.ts +++ b/backend/check_rvw_issue.ts @@ -80,3 +80,6 @@ main() + + + diff --git a/backend/check_tables.ts b/backend/check_tables.ts index affe3c63..deb45c6d 100644 --- a/backend/check_tables.ts +++ b/backend/check_tables.ts @@ -27,3 +27,6 @@ main() + + + diff --git a/backend/compare_db.ts b/backend/compare_db.ts index e85bbea8..146963de 100644 --- a/backend/compare_db.ts +++ b/backend/compare_db.ts @@ -115,3 +115,6 @@ main() + + + diff --git a/backend/compare_dc_asl.ts b/backend/compare_dc_asl.ts index 669c53f2..e0c56681 100644 --- a/backend/compare_dc_asl.ts +++ b/backend/compare_dc_asl.ts @@ -86,3 +86,6 @@ main() + + + diff --git a/backend/compare_pkb_aia_rvw.ts b/backend/compare_pkb_aia_rvw.ts index 7da393b8..e996e368 100644 --- a/backend/compare_pkb_aia_rvw.ts +++ b/backend/compare_pkb_aia_rvw.ts @@ -72,3 +72,6 @@ main() + + + diff --git a/backend/compare_schema_db.ts b/backend/compare_schema_db.ts index 934f55b3..645aff30 100644 --- a/backend/compare_schema_db.ts +++ b/backend/compare_schema_db.ts @@ -114,3 +114,6 @@ main() + + + diff --git a/backend/create_mock_user.sql b/backend/create_mock_user.sql index 740e6e8f..25e2e9ab 100644 --- a/backend/create_mock_user.sql +++ b/backend/create_mock_user.sql @@ -25,3 +25,6 @@ ON CONFLICT (id) DO NOTHING; + + + diff --git a/backend/create_mock_user_platform.sql b/backend/create_mock_user_platform.sql index 3904e76a..ab55bff6 100644 --- a/backend/create_mock_user_platform.sql +++ b/backend/create_mock_user_platform.sql @@ -57,3 +57,6 @@ ON CONFLICT (id) DO NOTHING; + + + diff --git a/backend/migrations/add_data_stats_to_tool_c_session.sql b/backend/migrations/add_data_stats_to_tool_c_session.sql index 88e8f09a..5ac42dec 100644 --- a/backend/migrations/add_data_stats_to_tool_c_session.sql +++ b/backend/migrations/add_data_stats_to_tool_c_session.sql @@ -71,6 +71,9 @@ WHERE table_schema = 'dc_schema' + + + diff --git a/backend/prisma/manual-migrations/001_add_postgres_cache_and_checkpoint.sql b/backend/prisma/manual-migrations/001_add_postgres_cache_and_checkpoint.sql index 8659fd4d..6d0656b0 100644 --- a/backend/prisma/manual-migrations/001_add_postgres_cache_and_checkpoint.sql +++ b/backend/prisma/manual-migrations/001_add_postgres_cache_and_checkpoint.sql @@ -109,6 +109,9 @@ ORDER BY ordinal_position; + + + diff --git a/backend/prisma/manual-migrations/run-migration-002.ts b/backend/prisma/manual-migrations/run-migration-002.ts index 84955f5a..6c79b751 100644 --- a/backend/prisma/manual-migrations/run-migration-002.ts +++ b/backend/prisma/manual-migrations/run-migration-002.ts @@ -122,6 +122,9 @@ runMigration() + + + diff --git a/backend/prisma/migrations/20251208_add_column_mapping/migration.sql b/backend/prisma/migrations/20251208_add_column_mapping/migration.sql index a2ed0a5c..f7a97409 100644 --- a/backend/prisma/migrations/20251208_add_column_mapping/migration.sql +++ b/backend/prisma/migrations/20251208_add_column_mapping/migration.sql @@ -56,6 +56,9 @@ COMMENT ON COLUMN "dc_schema"."dc_tool_c_sessions"."column_mapping" IS '列名 + + + diff --git a/backend/prisma/migrations/create_tool_c_session.sql b/backend/prisma/migrations/create_tool_c_session.sql index 615ae077..c83bbb23 100644 --- a/backend/prisma/migrations/create_tool_c_session.sql +++ b/backend/prisma/migrations/create_tool_c_session.sql @@ -83,6 +83,9 @@ COMMENT ON COLUMN dc_schema.dc_tool_c_sessions.expires_at IS '过期时间(创 + + + diff --git a/backend/rebuild-and-push.ps1 b/backend/rebuild-and-push.ps1 index df62bed0..5cd5fca9 100644 --- a/backend/rebuild-and-push.ps1 +++ b/backend/rebuild-and-push.ps1 @@ -123,6 +123,9 @@ Write-Host "" + + + diff --git a/backend/recover-code-from-cursor-db.js b/backend/recover-code-from-cursor-db.js index 28f73a84..fced7c95 100644 --- a/backend/recover-code-from-cursor-db.js +++ b/backend/recover-code-from-cursor-db.js @@ -233,6 +233,9 @@ function extractCodeBlocks(obj, blocks = []) { + + + diff --git a/backend/restore_job_common.sql b/backend/restore_job_common.sql index cd20a1a9..19ed7b4d 100644 --- a/backend/restore_job_common.sql +++ b/backend/restore_job_common.sql @@ -34,3 +34,6 @@ CREATE TABLE IF NOT EXISTS platform_schema.job_common ( + + + diff --git a/backend/restore_pgboss_functions.sql b/backend/restore_pgboss_functions.sql index 7b03e567..ac404dcb 100644 --- a/backend/restore_pgboss_functions.sql +++ b/backend/restore_pgboss_functions.sql @@ -108,3 +108,6 @@ CREATE OR REPLACE FUNCTION platform_schema.delete_queue(queue_name text) RETURNS + + + diff --git a/backend/scripts/check-dc-tables.mjs b/backend/scripts/check-dc-tables.mjs index c40a7d3f..d8ae0a26 100644 --- a/backend/scripts/check-dc-tables.mjs +++ b/backend/scripts/check-dc-tables.mjs @@ -252,6 +252,9 @@ checkDCTables(); + + + diff --git a/backend/scripts/create-capability-schema.sql b/backend/scripts/create-capability-schema.sql index 34c6b178..2f7daf53 100644 --- a/backend/scripts/create-capability-schema.sql +++ b/backend/scripts/create-capability-schema.sql @@ -9,3 +9,6 @@ CREATE SCHEMA IF NOT EXISTS capability_schema; + + + diff --git a/backend/scripts/create-tool-c-ai-history-table.mjs b/backend/scripts/create-tool-c-ai-history-table.mjs index 6df19a2d..b8079153 100644 --- a/backend/scripts/create-tool-c-ai-history-table.mjs +++ b/backend/scripts/create-tool-c-ai-history-table.mjs @@ -204,6 +204,9 @@ createAiHistoryTable() + + + diff --git a/backend/scripts/create-tool-c-table.js b/backend/scripts/create-tool-c-table.js index f75c429d..d00e20eb 100644 --- a/backend/scripts/create-tool-c-table.js +++ b/backend/scripts/create-tool-c-table.js @@ -191,6 +191,9 @@ createToolCTable() + + + diff --git a/backend/scripts/create-tool-c-table.mjs b/backend/scripts/create-tool-c-table.mjs index 03c14cd6..cd3db92b 100644 --- a/backend/scripts/create-tool-c-table.mjs +++ b/backend/scripts/create-tool-c-table.mjs @@ -188,6 +188,9 @@ createToolCTable() + + + diff --git a/backend/scripts/migrate-aia-prompts.ts b/backend/scripts/migrate-aia-prompts.ts new file mode 100644 index 00000000..be8b61d6 --- /dev/null +++ b/backend/scripts/migrate-aia-prompts.ts @@ -0,0 +1,317 @@ +/** + * AIA 智能问答模块 Prompt 迁移脚本 + * + * 将 agentService.ts 中硬编码的 10 个智能体 Prompt 迁移到数据库 + * + * 迁移内容: + * 1. AIA_SCIENTIFIC_QUESTION - 科学问题梳理 + * 2. AIA_PICO_ANALYSIS - PICO 梳理 + * 3. AIA_TOPIC_EVALUATION - 选题评价 + * 4. AIA_OUTCOME_DESIGN - 观察指标设计 + * 5. AIA_CRF_DESIGN - 病例报告表设计 + * 6. AIA_SAMPLE_SIZE - 样本量计算 + * 7. AIA_PROTOCOL_WRITING - 临床研究方案撰写 + * 8. AIA_METHODOLOGY_REVIEW - 方法学评审智能体 + * 9. AIA_PAPER_POLISH - 论文润色 + * 10. AIA_PAPER_TRANSLATE - 论文翻译 + * + * 运行方式: + * cd backend && npx tsx scripts/migrate-aia-prompts.ts + */ + +import { PrismaClient, PromptStatus } from '@prisma/client'; + +const prisma = new PrismaClient(); + +// AIA Prompt 配置(10 个智能体) +const aiaPrompts = [ + // ==================== Phase 1: 选题优化智能体 ==================== + { + code: 'AIA_SCIENTIFIC_QUESTION', + agentId: 'TOPIC_01', + name: '科学问题梳理', + module: 'AIA', + description: '从科学问题的清晰度、系统性、可验证性等角度使用科学理论对科学问题进行全面的评价。', + content: `你是一个专业的临床研究方法学专家,擅长科学问题的梳理与评价。 + +你的任务是: +1. 从科学问题的清晰度、系统性、可验证性等角度进行全面评价 +2. 使用科学理论和方法学原则指导用户完善问题 +3. 提供具体、可操作的建议 + +请用专业但易懂的语言回答,结构清晰,逻辑严密。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + { + code: 'AIA_PICO_ANALYSIS', + agentId: 'TOPIC_02', + name: 'PICO 梳理', + module: 'AIA', + description: '基于科学问题梳理研究对象、干预(暴露)、对照和结局指标,并评价并给出合理化的建议。', + content: `你是一个 PICO 框架专家,擅长将临床问题拆解为结构化的研究要素。 + +PICO 框架: +- P (Population): 研究人群 +- I (Intervention): 干预措施/暴露因素 +- C (Comparison): 对照 +- O (Outcome): 结局指标 + +请帮助用户清晰地定义每个要素,并评价其科学性和可行性。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + { + code: 'AIA_TOPIC_EVALUATION', + agentId: 'TOPIC_03', + name: '选题评价', + module: 'AIA', + description: '从创新性、临床价值、科学性和可行性等方面使用科学理论对临床问题进行全面的评价。', + content: `你是一个临床研究选题评审专家,擅长从多维度评价研究选题。 + +评价维度: +1. 创新性:是否填补知识空白,有新颖性 +2. 临床价值:对临床实践的指导意义 +3. 科学性:研究设计的严谨性和可行性 +4. 可行性:资源、时间、技术条件 + +请客观评价,指出优势和改进空间。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + + // ==================== Phase 2: 方案设计智能体 ==================== + { + code: 'AIA_OUTCOME_DESIGN', + agentId: 'DESIGN_04', + name: '观察指标设计', + module: 'AIA', + description: '基于研究设计和因果关系提供可能的观察指标集。', + content: `你是观察指标设计专家,擅长根据研究目的推荐合适的观察指标。 + +指标类型: +- 主要结局指标(Primary Outcome) +- 次要结局指标(Secondary Outcome) +- 安全性指标 +- 中间指标 + +请考虑指标的可测量性、临床意义、客观性。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + { + code: 'AIA_CRF_DESIGN', + agentId: 'DESIGN_05', + name: '病例报告表设计', + module: 'AIA', + description: '基于研究方案设计梳理观察指标集并给出建议的病例报告表框架。', + content: `你是 CRF (Case Report Form) 设计专家。 + +CRF 设计要点: +1. 基线资料采集 +2. 观察指标记录 +3. 随访时间点设计 +4. 数据质控要求 + +请提供结构清晰、逻辑合理的 CRF 框架。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + { + code: 'AIA_SAMPLE_SIZE', + agentId: 'DESIGN_06', + name: '样本量计算', + module: 'AIA', + description: '基于研究阶段和研究设计为研究提供科学合理的样本量估算结果。', + content: `你是样本量计算专家,擅长各种研究设计的样本量估算。 + +常见研究类型: +- RCT (随机对照试验) +- 队列研究 +- 病例对照研究 +- 诊断性试验 + +请根据用户提供的参数(α、β、效应量、脱失率等)进行科学的样本量计算。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + { + code: 'AIA_PROTOCOL_WRITING', + agentId: 'DESIGN_07', + name: '临床研究方案撰写', + module: 'AIA', + description: '基于科学问题、PICOS等信息,给出一个初步的临床研究设计方案。', + content: `你是临床研究方案撰写专家,可以帮助用户撰写完整的研究方案。 + +方案结构: +1. 研究背景与目的 +2. 研究设计(类型、盲法、随机等) +3. 研究对象(纳入/排除标准) +4. 干预措施 +5. 观察指标 +6. 统计分析计划 +7. 质量控制 +8. 伦理考虑 + +请基于用户提供的信息,给出结构完整、逻辑严密的方案。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + + // ==================== Phase 3: 方案预评审 ==================== + { + code: 'AIA_METHODOLOGY_REVIEW', + agentId: 'REVIEW_08', + name: '方法学评审智能体', + module: 'AIA', + description: '从研究问题、研究方案和临床意义方面,对研究进行临床研究方法学的全面评价。', + content: `你是一个资深的临床研究方法学评审专家,模拟审稿人视角进行评审。 + +评审要点: +1. 研究问题是否明确、有价值 +2. 研究设计是否科学、严谨 +3. 纳入/排除标准是否合理 +4. 样本量是否充足 +5. 统计方法是否适当 +6. 是否存在偏倚风险 + +请指出优势和需要改进的地方。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + + // ==================== Phase 5: 写作助手 ==================== + { + code: 'AIA_PAPER_POLISH', + agentId: 'WRITING_11', + name: '论文润色', + module: 'AIA', + description: '结合目标杂志,提供专业化的润色服务。', + content: `You are a professional academic editor specializing in medical research papers. + +Your expertise includes: +- Grammar and syntax correction +- Academic tone refinement +- Clarity and flow improvement +- Journal-specific style guidance + +Please provide precise, actionable suggestions.`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + { + code: 'AIA_PAPER_TRANSLATE', + agentId: 'WRITING_12', + name: '论文翻译', + module: 'AIA', + description: '结合目标杂志,提供专业化的翻译并进行润色。', + content: `你是一个专业的医学论文翻译专家,精通中英互译。 + +翻译要求: +1. 准确传达原意 +2. 符合医学术语规范 +3. 保持学术风格 +4. 流畅自然 + +请提供地道的学术英语翻译。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, +]; + +// 智能体 ID 到 Prompt Code 的映射表(供 agentService 使用) +export const AGENT_TO_PROMPT_CODE: Record = {}; +aiaPrompts.forEach(p => { + AGENT_TO_PROMPT_CODE[p.agentId] = p.code; +}); + +async function main() { + console.log('🚀 开始迁移 AIA Prompt 到数据库...\n'); + console.log(`📊 共 ${aiaPrompts.length} 个智能体 Prompt\n`); + + for (const prompt of aiaPrompts) { + console.log(`📄 处理: ${prompt.code} (${prompt.name})`); + console.log(` 智能体ID: ${prompt.agentId}`); + console.log(` 📝 内容长度: ${prompt.content.length} 字符`); + + // 创建或更新模板 + const template = await prisma.prompt_templates.upsert({ + where: { code: prompt.code }, + update: { + name: prompt.name, + description: prompt.description, + updated_at: new Date(), + }, + create: { + code: prompt.code, + name: prompt.name, + module: prompt.module, + description: prompt.description, + variables: null, // 暂不使用变量 + }, + }); + + // 检查是否已有 ACTIVE 版本 + const existingActive = await prisma.prompt_versions.findFirst({ + where: { + template_id: template.id, + status: PromptStatus.ACTIVE, + }, + }); + + if (existingActive) { + console.log(` ✅ 已存在 ACTIVE 版本 (v${existingActive.version})`); + } else { + // 创建第一个 ACTIVE 版本 + await prisma.prompt_versions.create({ + data: { + template_id: template.id, + version: 1, + content: prompt.content, + model_config: prompt.modelConfig, + status: PromptStatus.ACTIVE, + changelog: '从 agentService.ts 迁移的初始版本', + created_by: 'system-migration', + }, + }); + console.log(` ✅ 创建 ACTIVE 版本 (v1)`); + } + + console.log(''); + } + + // 验证结果 + console.log('═══════════════════════════════════════════════════════'); + console.log('📊 迁移结果验证\n'); + + const templates = await prisma.prompt_templates.findMany({ + where: { module: 'AIA' }, + include: { + versions: { + orderBy: { version: 'desc' }, + take: 1, + }, + }, + orderBy: { code: 'asc' }, + }); + + console.log(`✅ 共迁移 ${templates.length} 个 AIA Prompt:\n`); + + for (const t of templates) { + const latestVersion = t.versions[0]; + console.log(` 📋 ${t.code}`); + console.log(` 名称: ${t.name}`); + console.log(` 最新版本: v${latestVersion?.version} (${latestVersion?.status})`); + console.log(''); + } + + // 输出映射表 + console.log('═══════════════════════════════════════════════════════'); + console.log('📋 智能体ID → Prompt Code 映射表:\n'); + for (const prompt of aiaPrompts) { + console.log(` ${prompt.agentId.padEnd(12)} → ${prompt.code}`); + } + + console.log('\n✅ AIA Prompt 迁移完成!'); + console.log('\n💡 下一步:'); + console.log(' 1. 修改 agentService.ts 使用 PromptService'); + console.log(' 2. 在管理端查看和编辑这些 Prompt'); +} + +main() + .catch((error) => { + console.error('❌ 迁移失败:', error); + process.exit(1); + }) + .finally(() => prisma.$disconnect()); + diff --git a/backend/scripts/setup-prompt-system.ts b/backend/scripts/setup-prompt-system.ts index 387ed416..9091d8b6 100644 --- a/backend/scripts/setup-prompt-system.ts +++ b/backend/scripts/setup-prompt-system.ts @@ -119,3 +119,6 @@ main() + + + diff --git a/backend/scripts/test-pkb-apis-simple.ts b/backend/scripts/test-pkb-apis-simple.ts index ed7f928e..e14863a0 100644 --- a/backend/scripts/test-pkb-apis-simple.ts +++ b/backend/scripts/test-pkb-apis-simple.ts @@ -339,3 +339,6 @@ runTests().catch(error => { + + + diff --git a/backend/scripts/test-prompt-api.ts b/backend/scripts/test-prompt-api.ts index 9643fd78..9f048eec 100644 --- a/backend/scripts/test-prompt-api.ts +++ b/backend/scripts/test-prompt-api.ts @@ -85,3 +85,6 @@ testAPI().catch(console.error); + + + diff --git a/backend/scripts/verify-pkb-rvw-schema.ts b/backend/scripts/verify-pkb-rvw-schema.ts index 36512d2e..273a9257 100644 --- a/backend/scripts/verify-pkb-rvw-schema.ts +++ b/backend/scripts/verify-pkb-rvw-schema.ts @@ -304,3 +304,6 @@ verifySchemas() + + + diff --git a/backend/src/common/auth/jwt.service.ts b/backend/src/common/auth/jwt.service.ts index 696bea2c..98fd15e2 100644 --- a/backend/src/common/auth/jwt.service.ts +++ b/backend/src/common/auth/jwt.service.ts @@ -192,3 +192,6 @@ export const jwtService = new JWTService(); + + + diff --git a/backend/src/common/jobs/utils.ts b/backend/src/common/jobs/utils.ts index b71f313f..248b7de7 100644 --- a/backend/src/common/jobs/utils.ts +++ b/backend/src/common/jobs/utils.ts @@ -320,6 +320,9 @@ export function getBatchItems( + + + diff --git a/backend/src/common/prompt/prompt.fallbacks.ts b/backend/src/common/prompt/prompt.fallbacks.ts index a102580b..775c6bbb 100644 --- a/backend/src/common/prompt/prompt.fallbacks.ts +++ b/backend/src/common/prompt/prompt.fallbacks.ts @@ -66,12 +66,151 @@ const ASL_FALLBACKS: Record = { }, }; +/** + * AIA 智能问答模块兜底 Prompt + */ +const AIA_FALLBACKS: Record = { + // Phase 1: 选题优化智能体 + AIA_SCIENTIFIC_QUESTION: { + content: `你是一个专业的临床研究方法学专家,擅长科学问题的梳理与评价。 + +你的任务是: +1. 从科学问题的清晰度、系统性、可验证性等角度进行全面评价 +2. 使用科学理论和方法学原则指导用户完善问题 +3. 提供具体、可操作的建议 + +请用专业但易懂的语言回答,结构清晰,逻辑严密。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + AIA_PICO_ANALYSIS: { + content: `你是一个 PICO 框架专家,擅长将临床问题拆解为结构化的研究要素。 + +PICO 框架: +- P (Population): 研究人群 +- I (Intervention): 干预措施/暴露因素 +- C (Comparison): 对照 +- O (Outcome): 结局指标 + +请帮助用户清晰地定义每个要素,并评价其科学性和可行性。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + AIA_TOPIC_EVALUATION: { + content: `你是一个临床研究选题评审专家,擅长从多维度评价研究选题。 + +评价维度: +1. 创新性:是否填补知识空白,有新颖性 +2. 临床价值:对临床实践的指导意义 +3. 科学性:研究设计的严谨性和可行性 +4. 可行性:资源、时间、技术条件 + +请客观评价,指出优势和改进空间。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + + // Phase 2: 方案设计智能体 + AIA_OUTCOME_DESIGN: { + content: `你是观察指标设计专家,擅长根据研究目的推荐合适的观察指标。 + +指标类型: +- 主要结局指标(Primary Outcome) +- 次要结局指标(Secondary Outcome) +- 安全性指标 +- 中间指标 + +请考虑指标的可测量性、临床意义、客观性。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + AIA_CRF_DESIGN: { + content: `你是 CRF (Case Report Form) 设计专家。 + +CRF 设计要点: +1. 基线资料采集 +2. 观察指标记录 +3. 随访时间点设计 +4. 数据质控要求 + +请提供结构清晰、逻辑合理的 CRF 框架。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + AIA_SAMPLE_SIZE: { + content: `你是样本量计算专家,擅长各种研究设计的样本量估算。 + +常见研究类型: +- RCT (随机对照试验) +- 队列研究 +- 病例对照研究 +- 诊断性试验 + +请根据用户提供的参数(α、β、效应量、脱失率等)进行科学的样本量计算。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + AIA_PROTOCOL_WRITING: { + content: `你是临床研究方案撰写专家,可以帮助用户撰写完整的研究方案。 + +方案结构: +1. 研究背景与目的 +2. 研究设计(类型、盲法、随机等) +3. 研究对象(纳入/排除标准) +4. 干预措施 +5. 观察指标 +6. 统计分析计划 +7. 质量控制 +8. 伦理考虑 + +请基于用户提供的信息,给出结构完整、逻辑严密的方案。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + + // Phase 3: 方案预评审 + AIA_METHODOLOGY_REVIEW: { + content: `你是一个资深的临床研究方法学评审专家,模拟审稿人视角进行评审。 + +评审要点: +1. 研究问题是否明确、有价值 +2. 研究设计是否科学、严谨 +3. 纳入/排除标准是否合理 +4. 样本量是否充足 +5. 统计方法是否适当 +6. 是否存在偏倚风险 + +请指出优势和需要改进的地方。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + + // Phase 5: 写作助手 + AIA_PAPER_POLISH: { + content: `You are a professional academic editor specializing in medical research papers. + +Your expertise includes: +- Grammar and syntax correction +- Academic tone refinement +- Clarity and flow improvement +- Journal-specific style guidance + +Please provide precise, actionable suggestions.`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, + AIA_PAPER_TRANSLATE: { + content: `你是一个专业的医学论文翻译专家,精通中英互译。 + +翻译要求: +1. 准确传达原意 +2. 符合医学术语规范 +3. 保持学术风格 +4. 流畅自然 + +请提供地道的学术英语翻译。`, + modelConfig: { model: 'deepseek-v3', temperature: 0.3 }, + }, +}; + /** * 所有模块的兜底 Prompt 汇总 */ export const FALLBACK_PROMPTS: Record = { ...RVW_FALLBACKS, ...ASL_FALLBACKS, + ...AIA_FALLBACKS, }; /** @@ -106,3 +245,6 @@ export function getAllFallbackCodes(): string[] { + + + diff --git a/backend/src/common/prompt/prompt.types.ts b/backend/src/common/prompt/prompt.types.ts index 0fee3d02..22c77c73 100644 --- a/backend/src/common/prompt/prompt.types.ts +++ b/backend/src/common/prompt/prompt.types.ts @@ -75,3 +75,6 @@ export interface VariableValidation { + + + diff --git a/backend/src/common/streaming/OpenAIStreamAdapter.ts b/backend/src/common/streaming/OpenAIStreamAdapter.ts index a2fe6bb3..7165cc34 100644 --- a/backend/src/common/streaming/OpenAIStreamAdapter.ts +++ b/backend/src/common/streaming/OpenAIStreamAdapter.ts @@ -196,3 +196,6 @@ export function createOpenAIStreamAdapter( + + + diff --git a/backend/src/common/streaming/StreamingService.ts b/backend/src/common/streaming/StreamingService.ts index 975b4d80..a92b8798 100644 --- a/backend/src/common/streaming/StreamingService.ts +++ b/backend/src/common/streaming/StreamingService.ts @@ -202,3 +202,6 @@ export async function streamChat( + + + diff --git a/backend/src/common/streaming/index.ts b/backend/src/common/streaming/index.ts index 99d2f32e..44d27568 100644 --- a/backend/src/common/streaming/index.ts +++ b/backend/src/common/streaming/index.ts @@ -20,3 +20,6 @@ export { THINKING_TAGS } from './types'; + + + diff --git a/backend/src/common/streaming/types.ts b/backend/src/common/streaming/types.ts index 3408b850..57ed5c4d 100644 --- a/backend/src/common/streaming/types.ts +++ b/backend/src/common/streaming/types.ts @@ -95,3 +95,6 @@ export type SSEEventType = + + + diff --git a/backend/src/modules/admin/routes/tenantRoutes.ts b/backend/src/modules/admin/routes/tenantRoutes.ts index 43f73f55..72e5f837 100644 --- a/backend/src/modules/admin/routes/tenantRoutes.ts +++ b/backend/src/modules/admin/routes/tenantRoutes.ts @@ -81,3 +81,6 @@ export async function moduleRoutes(fastify: FastifyInstance) { + + + diff --git a/backend/src/modules/admin/types/tenant.types.ts b/backend/src/modules/admin/types/tenant.types.ts index 0ad0f079..63929110 100644 --- a/backend/src/modules/admin/types/tenant.types.ts +++ b/backend/src/modules/admin/types/tenant.types.ts @@ -111,3 +111,6 @@ export interface PaginatedResponse { + + + diff --git a/backend/src/modules/admin/types/user.types.ts b/backend/src/modules/admin/types/user.types.ts index 14b4c21a..195809c8 100644 --- a/backend/src/modules/admin/types/user.types.ts +++ b/backend/src/modules/admin/types/user.types.ts @@ -158,3 +158,6 @@ export const ROLE_DISPLAY_NAMES: Record = { USER: '普通用户', }; + + + diff --git a/backend/src/modules/aia/controllers/agentController.ts b/backend/src/modules/aia/controllers/agentController.ts index cedae922..69046476 100644 --- a/backend/src/modules/aia/controllers/agentController.ts +++ b/backend/src/modules/aia/controllers/agentController.ts @@ -233,3 +233,6 @@ async function matchIntent(query: string): Promise<{ + + + diff --git a/backend/src/modules/aia/controllers/attachmentController.ts b/backend/src/modules/aia/controllers/attachmentController.ts new file mode 100644 index 00000000..f2a545f0 --- /dev/null +++ b/backend/src/modules/aia/controllers/attachmentController.ts @@ -0,0 +1,92 @@ +/** + * 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, + }, + }); + } +} + + diff --git a/backend/src/modules/aia/controllers/conversationController.ts b/backend/src/modules/aia/controllers/conversationController.ts index 6bf1fddd..64ecacb6 100644 --- a/backend/src/modules/aia/controllers/conversationController.ts +++ b/backend/src/modules/aia/controllers/conversationController.ts @@ -135,22 +135,16 @@ export async function createConversation( export async function getConversationById( request: FastifyRequest<{ Params: { id: string }; - Querystring: { limit?: string }; }>, reply: FastifyReply ) { try { const userId = getUserId(request); const { id } = request.params; - const { limit } = request.query; logger.info('[AIA:Controller] 获取对话详情', { userId, conversationId: id }); - const conversation = await conversationService.getConversationById( - userId, - id, - limit ? parseInt(limit) : 50 - ); + const conversation = await conversationService.getConversationById(userId, id); if (!conversation) { return reply.status(404).send({ @@ -178,6 +172,54 @@ export async function getConversationById( } } +/** + * 更新对话(标题等) + * PATCH /api/v1/aia/conversations/:id + */ +export async function updateConversation( + request: FastifyRequest<{ + Params: { id: string }; + Body: { title?: string }; + }>, + reply: FastifyReply +) { + try { + const userId = getUserId(request); + const { id } = request.params; + const { title } = request.body; + + logger.info('[AIA:Controller] 更新对话', { userId, conversationId: id, title }); + + if (!title || title.trim().length === 0) { + return reply.status(400).send({ + code: -1, + error: { + code: 'VALIDATION_ERROR', + message: '标题不能为空', + }, + }); + } + + const conversation = await conversationService.updateConversation(userId, id, { + title: title.trim(), + }); + + return reply.send({ + code: 0, + data: conversation, + }); + } catch (error) { + logger.error('[AIA:Controller] 更新对话失败', { error }); + return reply.status(500).send({ + code: -1, + error: { + code: 'INTERNAL_ERROR', + message: error instanceof Error ? error.message : '服务器内部错误', + }, + }); + } +} + /** * 删除对话 * DELETE /api/v1/aia/conversations/:id @@ -222,7 +264,57 @@ export async function deleteConversation( } } -// ==================== 消息发送 ==================== +// ==================== 消息管理 ==================== + +/** + * 获取对话消息列表(历史消息) + * GET /api/v1/aia/conversations/:id/messages + */ +export async function getMessages( + request: FastifyRequest<{ + Params: { id: string }; + Querystring: { + page?: string; + pageSize?: string; + }; + }>, + reply: FastifyReply +) { + try { + const userId = getUserId(request); + const { id } = request.params; + const { page, pageSize } = request.query; + + logger.info('[AIA:Controller] 获取消息列表', { userId, conversationId: id }); + + const result = await conversationService.getMessages(userId, id, { + page: page ? parseInt(page) : 1, + pageSize: pageSize ? parseInt(pageSize) : 50, + }); + + return reply.send({ + code: 0, + data: { + messages: result.messages, + pagination: { + total: result.total, + page: page ? parseInt(page) : 1, + pageSize: pageSize ? parseInt(pageSize) : 50, + totalPages: Math.ceil(result.total / (pageSize ? parseInt(pageSize) : 50)), + }, + }, + }); + } catch (error) { + logger.error('[AIA:Controller] 获取消息列表失败', { error }); + return reply.status(500).send({ + code: -1, + error: { + code: 'INTERNAL_ERROR', + message: error instanceof Error ? error.message : '服务器内部错误', + }, + }); + } +} /** * 发送消息(流式输出) diff --git a/backend/src/modules/aia/index.ts b/backend/src/modules/aia/index.ts index 3abe438b..fb5c8207 100644 --- a/backend/src/modules/aia/index.ts +++ b/backend/src/modules/aia/index.ts @@ -16,3 +16,6 @@ export { aiaRoutes }; + + + diff --git a/backend/src/modules/aia/routes/index.ts b/backend/src/modules/aia/routes/index.ts index 64887be2..36a6f9ea 100644 --- a/backend/src/modules/aia/routes/index.ts +++ b/backend/src/modules/aia/routes/index.ts @@ -8,6 +8,7 @@ import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; import * as conversationController from '../controllers/conversationController.js'; import * as agentController from '../controllers/agentController.js'; +import * as attachmentController from '../controllers/attachmentController.js'; import { authenticate } from '../../../common/auth/auth.middleware.js'; export default async function aiaRoutes(fastify: FastifyInstance) { @@ -53,17 +54,37 @@ export default async function aiaRoutes(fastify: FastifyInstance) { return conversationController.getConversationById(request as any, reply); }); + // 更新对话(标题等) + // PATCH /api/v1/aia/conversations/:id + fastify.patch('/conversations/:id', { preHandler: [authenticate] }, async (request: FastifyRequest, reply: FastifyReply) => { + return conversationController.updateConversation(request as any, reply); + }); + // 删除对话 // DELETE /api/v1/aia/conversations/:id fastify.delete('/conversations/:id', { preHandler: [authenticate] }, async (request: FastifyRequest, reply: FastifyReply) => { return conversationController.deleteConversation(request as any, reply); }); - // ==================== 消息发送 ==================== + // ==================== 消息管理 ==================== + + // 获取对话消息列表(历史消息) + // GET /api/v1/aia/conversations/:id/messages + fastify.get('/conversations/:id/messages', { preHandler: [authenticate] }, async (request: FastifyRequest, reply: FastifyReply) => { + return conversationController.getMessages(request as any, reply); + }); // 发送消息(流式输出) // POST /api/v1/aia/conversations/:id/messages/stream fastify.post('/conversations/:id/messages/stream', { preHandler: [authenticate] }, async (request: FastifyRequest, reply: FastifyReply) => { return conversationController.sendMessageStream(request as any, reply); }); + + // ==================== 附件管理 ==================== + + // 上传附件 + // POST /api/v1/aia/conversations/:id/attachments + fastify.post('/conversations/:id/attachments', { preHandler: [authenticate] }, async (request: FastifyRequest, reply: FastifyReply) => { + return attachmentController.uploadAttachment(request as any, reply); + }); } diff --git a/backend/src/modules/aia/services/agentService.ts b/backend/src/modules/aia/services/agentService.ts index 767d296f..a51b9ef7 100644 --- a/backend/src/modules/aia/services/agentService.ts +++ b/backend/src/modules/aia/services/agentService.ts @@ -4,12 +4,38 @@ * * 负责智能体配置管理、Prompt 获取 * 12个智能体配置(对应前端 AgentHub) + * + * Phase 3.5.6 改造:使用 PromptService 替代硬编码 + * - 支持灰度预览(调试者看 DRAFT,普通用户看 ACTIVE) + * - 三级容灾(数据库→缓存→兜底) + * - 在管理端可配置和调试 */ import { logger } from '../../../common/logging/index.js'; import { cache } from '../../../common/cache/index.js'; +import { prisma } from '../../../config/database.js'; +import { getPromptService } from '../../../common/prompt/index.js'; import type { Agent, AgentStage } from '../types/index.js'; +// ==================== 智能体 ID 到 Prompt Code 映射 ==================== + +/** + * 智能体 ID → Prompt Code 映射表 + * 用于从 PromptService 获取对应的提示词 + */ +const AGENT_TO_PROMPT_CODE: Record = { + 'TOPIC_01': 'AIA_SCIENTIFIC_QUESTION', + 'TOPIC_02': 'AIA_PICO_ANALYSIS', + 'TOPIC_03': 'AIA_TOPIC_EVALUATION', + 'DESIGN_04': 'AIA_OUTCOME_DESIGN', + 'DESIGN_05': 'AIA_CRF_DESIGN', + 'DESIGN_06': 'AIA_SAMPLE_SIZE', + 'DESIGN_07': 'AIA_PROTOCOL_WRITING', + 'REVIEW_08': 'AIA_METHODOLOGY_REVIEW', + 'WRITING_11': 'AIA_PAPER_POLISH', + 'WRITING_12': 'AIA_PAPER_TRANSLATE', +}; + // ==================== 智能体配置 ==================== /** @@ -303,9 +329,25 @@ export async function getAgentsByStage(stage: AgentStage): Promise { } /** - * 获取智能体的系统提示词 + * 获取智能体的系统提示词(使用 PromptService) + * + * 支持灰度预览: + * - 调试者看 DRAFT 版本 + * - 普通用户看 ACTIVE 版本 + * + * 三级容灾: + * 1. 数据库(PromptService) + * 2. 缓存 + * 3. 兜底(硬编码的 systemPrompt) + * + * @param agentId 智能体 ID + * @param userId 用户 ID(用于灰度预览判断) + * @returns { content: 提示词内容, isDraft: 是否为草稿版本 } */ -export async function getAgentSystemPrompt(agentId: string): Promise { +export async function getAgentSystemPrompt( + agentId: string, + userId?: string +): Promise<{ content: string; isDraft: boolean }> { const agent = await getAgentById(agentId); if (!agent) { @@ -316,11 +358,53 @@ export async function getAgentSystemPrompt(agentId: string): Promise { throw new Error(`智能体 ${agentId} 是工具类,不支持对话`); } + // 获取 Prompt Code + const promptCode = AGENT_TO_PROMPT_CODE[agentId]; + + if (promptCode) { + // 使用 PromptService 获取(支持灰度预览) + try { + const promptService = getPromptService(prisma); + const result = await promptService.get(promptCode, {}, { userId }); + + if (result.isDraft) { + logger.info('[AIA:AgentService] 使用 DRAFT 版本 Prompt(调试模式)', { + agentId, + promptCode, + userId + }); + } else { + logger.debug('[AIA:AgentService] 使用 ACTIVE 版本 Prompt', { + agentId, + promptCode, + version: result.version + }); + } + + return { + content: result.content, + isDraft: result.isDraft, + }; + } catch (error) { + // PromptService 获取失败,降级到硬编码 + logger.warn('[AIA:AgentService] PromptService 获取失败,使用兜底', { + agentId, + promptCode, + error: error instanceof Error ? error.message : 'Unknown error' + }); + } + } + + // 兜底:使用硬编码的 systemPrompt if (!agent.systemPrompt) { throw new Error(`智能体 ${agentId} 未配置系统提示词`); } - return agent.systemPrompt; + logger.debug('[AIA:AgentService] 使用硬编码 Prompt', { agentId }); + return { + content: agent.systemPrompt, + isDraft: false, + }; } /** diff --git a/backend/src/modules/aia/services/attachmentService.ts b/backend/src/modules/aia/services/attachmentService.ts index 69a6196b..9f4489d7 100644 --- a/backend/src/modules/aia/services/attachmentService.ts +++ b/backend/src/modules/aia/services/attachmentService.ts @@ -10,9 +10,15 @@ import { logger } from '../../../common/logging/index.js'; import { storage } from '../../../common/storage/index.js'; +import { cache } from '../../../common/cache/index.js'; import { ExtractionClient } from '../../../common/document/ExtractionClient.js'; import type { Attachment } from '../types/index.js'; +// 附件缓存前缀和过期时间(2小时) +const ATTACHMENT_CACHE_PREFIX = 'aia:attachment:text:'; +const ATTACHMENT_INFO_CACHE_PREFIX = 'aia:attachment:info:'; +const ATTACHMENT_CACHE_TTL = 2 * 60 * 60; // 2小时 + // ==================== 常量配置 ==================== const MAX_ATTACHMENTS = 5; @@ -41,20 +47,51 @@ export async function uploadAttachment( // 2. 上传到存储服务 const storageKey = `aia/${userId}/${conversationId}/${Date.now()}_${file.filename}`; - const url = await storage.upload(storageKey, file.buffer, { - contentType: file.mimetype, - }); + const url = await storage.upload(storageKey, file.buffer); logger.info('[AIA:AttachmentService] 附件上传成功', { filename: file.filename, url, }); - // 3. 提取文本内容(异步处理) + // 3. 提取文本内容 let extractedText = ''; try { - const extractionClient = new ExtractionClient(); - extractedText = await extractionClient.extractText(file.buffer, ext); + // 对于 txt 文件,直接读取内容(不依赖 Python 服务) + if (ext === 'txt') { + extractedText = file.buffer.toString('utf-8'); + logger.info('[AIA:AttachmentService] TXT文件直接读取成功', { + filename: file.filename, + charCount: extractedText.length, + }); + } else { + // 其他文件类型调用 Python 提取服务 + const extractionClient = new ExtractionClient(); + + let result; + if (ext === 'pdf') { + result = await extractionClient.extractPdf(file.buffer, file.filename); + } else if (ext === 'docx' || ext === 'doc') { + result = await extractionClient.extractDocx(file.buffer, file.filename); + } else { + result = await extractionClient.extractDocument(file.buffer, file.filename); + } + + if (result.success && result.text) { + extractedText = result.text; + logger.info('[AIA:AttachmentService] 文本提取成功', { + filename: file.filename, + method: result.method, + charCount: result.text.length, + }); + } else { + logger.warn('[AIA:AttachmentService] 文本提取返回空', { + filename: file.filename, + error: result.error, + }); + extractedText = '[文档内容为空或无法提取]'; + } + } // 4. Token 截断控制 const tokens = estimateTokens(extractedText); @@ -78,16 +115,48 @@ export async function uploadAttachment( } // 5. 构建附件对象 + const attachmentId = `att-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; + const tokenCount = estimateTokens(extractedText); + const truncated = tokenCount > MAX_TOKENS_PER_ATTACHMENT; + const attachment: Attachment = { - id: `att-${Date.now()}`, - name: file.filename, - url, - size: file.buffer.length, + id: attachmentId, + filename: file.filename, mimeType: file.mimetype, - extractedText, - tokens: estimateTokens(extractedText), + size: file.buffer.length, + ossUrl: url, + textContent: extractedText, + tokenCount, + truncated, + createdAt: new Date().toISOString(), }; + // 6. 将提取的文本存储到缓存(供后续发送消息时使用) + if (extractedText && extractedText !== '[文档内容提取失败]' && extractedText !== '[文档内容为空或无法提取]') { + await cache.set( + `${ATTACHMENT_CACHE_PREFIX}${attachmentId}`, + extractedText, + ATTACHMENT_CACHE_TTL + ); + logger.info('[AIA:AttachmentService] 附件文本已缓存', { + attachmentId, + textLength: extractedText.length, + tokenCount, + }); + } + + // 7. 存储附件基本信息到缓存(供发送消息时保存到数据库) + const attachmentInfo = { + id: attachmentId, + filename: file.filename, + size: file.buffer.length, + }; + await cache.set( + `${ATTACHMENT_INFO_CACHE_PREFIX}${attachmentId}`, + JSON.stringify(attachmentInfo), + ATTACHMENT_CACHE_TTL + ); + return attachment; } @@ -95,11 +164,79 @@ export async function uploadAttachment( * 批量获取附件文本内容 */ export async function getAttachmentsText(attachmentIds: string[]): Promise { - // TODO: 从存储中获取附件并提取文本 - // 当前版本:简化实现,假设附件文本已在消息的 attachments 字段中 - - logger.debug('[AIA:AttachmentService] 获取附件文本', { attachmentIds }); - return ''; + if (!attachmentIds || attachmentIds.length === 0) { + return ''; + } + + logger.info('[AIA:AttachmentService] 获取附件文本', { + attachmentIds, + count: attachmentIds.length, + }); + + const texts: string[] = []; + + for (const attachmentId of attachmentIds) { + try { + const cacheKey = `${ATTACHMENT_CACHE_PREFIX}${attachmentId}`; + const text = await cache.get(cacheKey); + + if (text) { + texts.push(`【附件: ${attachmentId}】\n${text}`); + logger.debug('[AIA:AttachmentService] 从缓存获取附件文本成功', { + attachmentId, + textLength: text.length, + }); + } else { + logger.warn('[AIA:AttachmentService] 附件文本不在缓存中', { attachmentId }); + texts.push(`【附件: ${attachmentId}】\n[附件内容已过期或不存在]`); + } + } catch (error) { + logger.error('[AIA:AttachmentService] 获取附件文本失败', { + attachmentId, + error + }); + } + } + + return texts.join('\n\n---\n\n'); +} + +/** + * 获取附件详情(从缓存) + * 用于发送消息时保存附件信息到数据库 + */ +export async function getAttachmentDetails( + attachmentIds: string[] +): Promise> { + if (!attachmentIds || attachmentIds.length === 0) { + return []; + } + + const details: Array<{ id: string; filename: string; size: number }> = []; + + for (const attachmentId of attachmentIds) { + try { + const cacheKey = `${ATTACHMENT_INFO_CACHE_PREFIX}${attachmentId}`; + const infoJson = await cache.get(cacheKey); + + if (infoJson) { + const info = JSON.parse(infoJson); + details.push(info); + } else { + logger.warn('[AIA:AttachmentService] 附件信息不在缓存中', { attachmentId }); + // 如果缓存中没有,添加一个占位信息 + details.push({ + id: attachmentId, + filename: '未知文件', + size: 0, + }); + } + } catch (error) { + logger.error('[AIA:AttachmentService] 获取附件信息失败', { attachmentId, error }); + } + } + + return details; } /** @@ -113,3 +250,5 @@ function estimateTokens(text: string): number { + + diff --git a/backend/src/modules/aia/services/conversationService.ts b/backend/src/modules/aia/services/conversationService.ts index 407f1d2d..69dc523f 100644 --- a/backend/src/modules/aia/services/conversationService.ts +++ b/backend/src/modules/aia/services/conversationService.ts @@ -16,6 +16,7 @@ import { prisma } from '../../../config/database.js'; import { streamChat, createStreamingService } from '../../../common/streaming/index.js'; import type { OpenAIMessage, StreamOptions } from '../../../common/streaming/index.js'; import * as agentService from './agentService.js'; +import * as attachmentService from './attachmentService.js'; import type { Conversation, Message, @@ -178,16 +179,17 @@ export async function updateConversation( export async function deleteConversation( userId: string, conversationId: string -): Promise { +): Promise { const result = await prisma.conversation.deleteMany({ where: { id: conversationId, userId }, }); if (result.count === 0) { - throw new Error('对话不存在'); + return false; } logger.info('[AIA:ConversationService] 删除对话', { conversationId }); + return true; } // ==================== 消息管理 ==================== @@ -222,17 +224,25 @@ export async function getMessages( ]); return { - messages: messages.map(m => ({ - id: m.id, - conversationId: m.conversationId, - role: m.role as 'user' | 'assistant', - content: m.content, - thinkingContent: m.thinkingContent || undefined, - attachments: (m.attachments as any)?.ids as string[] | undefined, - model: m.model || undefined, - tokens: m.tokens || undefined, - createdAt: m.createdAt.toISOString(), - })), + messages: messages.map(m => { + const attachmentsJson = m.attachments as any; + const attachmentIds = attachmentsJson?.ids as string[] | undefined; + // 直接从 JSON 字段读取附件详情(不查询数据库) + const attachmentDetails = attachmentsJson?.details as Array<{ id: string; filename: string; size: number }> | undefined; + + return { + id: m.id, + conversationId: m.conversationId, + role: m.role as 'user' | 'assistant', + content: m.content, + thinkingContent: m.thinkingContent || undefined, + attachments: attachmentIds, + attachmentDetails: attachmentDetails && attachmentDetails.length > 0 ? attachmentDetails : undefined, + model: m.model || undefined, + tokens: m.tokens || undefined, + createdAt: m.createdAt.toISOString(), + }; + }), total, }; } @@ -259,16 +269,36 @@ export async function sendMessageStream( throw new Error('对话不存在'); } - // 2. 获取智能体系统提示词 - const systemPrompt = await agentService.getAgentSystemPrompt(conversation.agentId); + // 2. 获取智能体系统提示词(支持灰度预览) + const { content: systemPrompt, isDraft } = await agentService.getAgentSystemPrompt( + conversation.agentId, + userId // 传递 userId 以支持灰度预览 + ); + + if (isDraft) { + logger.info('[AIA:Conversation] 使用 DRAFT 版本 Prompt(调试模式)', { + userId, + agentId: conversation.agentId + }); + } - // 3. 保存用户消息 + // 3. 保存用户消息(包含附件详情) + let attachmentsData = undefined; + if (attachmentIds && attachmentIds.length > 0) { + // 从缓存获取附件详情 + const attachmentDetails = await attachmentService.getAttachmentDetails(attachmentIds); + attachmentsData = { + ids: attachmentIds, + details: attachmentDetails, + }; + } + await prisma.message.create({ data: { conversationId, role: 'user', content, - attachments: attachmentIds ? { ids: attachmentIds } : undefined, + attachments: attachmentsData, }, }); @@ -380,12 +410,11 @@ async function buildContextMessages( /** * 获取附件文本内容 - * TODO: 对接文档处理服务 + * 从缓存中获取上传时提取的文本 */ async function getAttachmentText(attachmentIds: string[]): Promise { - // 预留:从文档处理引擎获取附件文本 - logger.debug('[AIA:ConversationService] 获取附件文本', { attachmentIds }); - return ''; + logger.info('[AIA:ConversationService] 获取附件文本', { attachmentIds }); + return attachmentService.getAttachmentsText(attachmentIds); } /** diff --git a/backend/src/modules/aia/types/index.ts b/backend/src/modules/aia/types/index.ts index 877cff0d..44495cb2 100644 --- a/backend/src/modules/aia/types/index.ts +++ b/backend/src/modules/aia/types/index.ts @@ -7,8 +7,13 @@ /** * 智能体阶段 + * - topic: 选题优化 + * - design: 方案设计 + * - review: 方案预评审 + * - data: 数据处理 + * - writing: 论文写作 */ -export type AgentStage = 'design' | 'data' | 'analysis' | 'write' | 'publish'; +export type AgentStage = 'topic' | 'design' | 'review' | 'data' | 'writing'; /** * 智能体配置 @@ -201,3 +206,6 @@ export interface PaginatedResponse { + + + diff --git a/backend/src/modules/asl/fulltext-screening/__tests__/api-integration-test.ts b/backend/src/modules/asl/fulltext-screening/__tests__/api-integration-test.ts index bcead853..90717db2 100644 --- a/backend/src/modules/asl/fulltext-screening/__tests__/api-integration-test.ts +++ b/backend/src/modules/asl/fulltext-screening/__tests__/api-integration-test.ts @@ -356,6 +356,9 @@ runTests().catch((error) => { + + + diff --git a/backend/src/modules/asl/fulltext-screening/__tests__/e2e-real-test-v2.ts b/backend/src/modules/asl/fulltext-screening/__tests__/e2e-real-test-v2.ts index 91defd5b..eea67868 100644 --- a/backend/src/modules/asl/fulltext-screening/__tests__/e2e-real-test-v2.ts +++ b/backend/src/modules/asl/fulltext-screening/__tests__/e2e-real-test-v2.ts @@ -297,6 +297,9 @@ runTest() + + + diff --git a/backend/src/modules/asl/fulltext-screening/__tests__/fulltext-screening-api.http b/backend/src/modules/asl/fulltext-screening/__tests__/fulltext-screening-api.http index 7cb74f8d..e0795416 100644 --- a/backend/src/modules/asl/fulltext-screening/__tests__/fulltext-screening-api.http +++ b/backend/src/modules/asl/fulltext-screening/__tests__/fulltext-screening-api.http @@ -335,6 +335,9 @@ Content-Type: application/json + + + diff --git a/backend/src/modules/dc/tool-b/services/ConflictDetectionService.ts b/backend/src/modules/dc/tool-b/services/ConflictDetectionService.ts index 4daba4dc..6a24a32b 100644 --- a/backend/src/modules/dc/tool-b/services/ConflictDetectionService.ts +++ b/backend/src/modules/dc/tool-b/services/ConflictDetectionService.ts @@ -271,6 +271,9 @@ export const conflictDetectionService = new ConflictDetectionService(); + + + diff --git a/backend/src/modules/dc/tool-c/README.md b/backend/src/modules/dc/tool-c/README.md index bb8aa55e..68f16244 100644 --- a/backend/src/modules/dc/tool-c/README.md +++ b/backend/src/modules/dc/tool-c/README.md @@ -221,6 +221,9 @@ curl -X POST http://localhost:3000/api/v1/dc/tool-c/test/execute \ + + + diff --git a/backend/src/modules/dc/tool-c/controllers/StreamAIController.ts b/backend/src/modules/dc/tool-c/controllers/StreamAIController.ts index 570e7fea..de7f72f2 100644 --- a/backend/src/modules/dc/tool-c/controllers/StreamAIController.ts +++ b/backend/src/modules/dc/tool-c/controllers/StreamAIController.ts @@ -275,6 +275,9 @@ export const streamAIController = new StreamAIController(); + + + diff --git a/backend/src/modules/iit-manager/agents/SessionMemory.ts b/backend/src/modules/iit-manager/agents/SessionMemory.ts index 7c91b3c8..0fc867c0 100644 --- a/backend/src/modules/iit-manager/agents/SessionMemory.ts +++ b/backend/src/modules/iit-manager/agents/SessionMemory.ts @@ -184,6 +184,9 @@ logger.info('[SessionMemory] 会话记忆管理器已启动', { + + + diff --git a/backend/src/modules/iit-manager/check-iit-table-structure.ts b/backend/src/modules/iit-manager/check-iit-table-structure.ts index 7ec5378f..456433fa 100644 --- a/backend/src/modules/iit-manager/check-iit-table-structure.ts +++ b/backend/src/modules/iit-manager/check-iit-table-structure.ts @@ -119,6 +119,9 @@ checkTableStructure(); + + + diff --git a/backend/src/modules/iit-manager/check-project-config.ts b/backend/src/modules/iit-manager/check-project-config.ts index 0c6daa51..d1a5ffe7 100644 --- a/backend/src/modules/iit-manager/check-project-config.ts +++ b/backend/src/modules/iit-manager/check-project-config.ts @@ -105,6 +105,9 @@ checkProjectConfig().catch(console.error); + + + diff --git a/backend/src/modules/iit-manager/check-test-project-in-db.ts b/backend/src/modules/iit-manager/check-test-project-in-db.ts index 58632b4b..14c2120d 100644 --- a/backend/src/modules/iit-manager/check-test-project-in-db.ts +++ b/backend/src/modules/iit-manager/check-test-project-in-db.ts @@ -87,6 +87,9 @@ main(); + + + diff --git a/backend/src/modules/iit-manager/docs/微信服务号接入指南.md b/backend/src/modules/iit-manager/docs/微信服务号接入指南.md index 932374e2..670135f6 100644 --- a/backend/src/modules/iit-manager/docs/微信服务号接入指南.md +++ b/backend/src/modules/iit-manager/docs/微信服务号接入指南.md @@ -546,5 +546,8 @@ URL: https://iit.xunzhengyixue.com/api/v1/iit/patient-wechat/callback + + + diff --git a/backend/src/modules/iit-manager/generate-wechat-tokens.ts b/backend/src/modules/iit-manager/generate-wechat-tokens.ts index 0cf027fa..bb3a9ca5 100644 --- a/backend/src/modules/iit-manager/generate-wechat-tokens.ts +++ b/backend/src/modules/iit-manager/generate-wechat-tokens.ts @@ -181,5 +181,8 @@ console.log(''); + + + diff --git a/backend/src/modules/iit-manager/services/PatientWechatService.ts b/backend/src/modules/iit-manager/services/PatientWechatService.ts index 4f13936f..552d32c3 100644 --- a/backend/src/modules/iit-manager/services/PatientWechatService.ts +++ b/backend/src/modules/iit-manager/services/PatientWechatService.ts @@ -498,5 +498,8 @@ export const patientWechatService = new PatientWechatService(); + + + diff --git a/backend/src/modules/iit-manager/test-chatservice-dify.ts b/backend/src/modules/iit-manager/test-chatservice-dify.ts index f023756f..1e01583f 100644 --- a/backend/src/modules/iit-manager/test-chatservice-dify.ts +++ b/backend/src/modules/iit-manager/test-chatservice-dify.ts @@ -142,6 +142,9 @@ testDifyIntegration().catch(error => { + + + diff --git a/backend/src/modules/iit-manager/test-iit-database.ts b/backend/src/modules/iit-manager/test-iit-database.ts index 21503f21..a35fe2f1 100644 --- a/backend/src/modules/iit-manager/test-iit-database.ts +++ b/backend/src/modules/iit-manager/test-iit-database.ts @@ -170,6 +170,9 @@ testIitDatabase() + + + diff --git a/backend/src/modules/iit-manager/test-patient-wechat-config.ts b/backend/src/modules/iit-manager/test-patient-wechat-config.ts index 2b5f943d..10a99a37 100644 --- a/backend/src/modules/iit-manager/test-patient-wechat-config.ts +++ b/backend/src/modules/iit-manager/test-patient-wechat-config.ts @@ -158,5 +158,8 @@ if (hasError) { + + + diff --git a/backend/src/modules/iit-manager/test-patient-wechat-url-verify.ts b/backend/src/modules/iit-manager/test-patient-wechat-url-verify.ts index 5682bf7d..8c05fc6d 100644 --- a/backend/src/modules/iit-manager/test-patient-wechat-url-verify.ts +++ b/backend/src/modules/iit-manager/test-patient-wechat-url-verify.ts @@ -184,5 +184,8 @@ async function testUrlVerification() { + + + diff --git a/backend/src/modules/iit-manager/test-redcap-query-from-db.ts b/backend/src/modules/iit-manager/test-redcap-query-from-db.ts index 887e5833..022566d6 100644 --- a/backend/src/modules/iit-manager/test-redcap-query-from-db.ts +++ b/backend/src/modules/iit-manager/test-redcap-query-from-db.ts @@ -263,6 +263,9 @@ main().catch((error) => { + + + diff --git a/backend/src/modules/iit-manager/test-wechat-mp-local.ps1 b/backend/src/modules/iit-manager/test-wechat-mp-local.ps1 index bd8ac2aa..cb16ac39 100644 --- a/backend/src/modules/iit-manager/test-wechat-mp-local.ps1 +++ b/backend/src/modules/iit-manager/test-wechat-mp-local.ps1 @@ -149,5 +149,8 @@ Write-Host "" + + + diff --git a/backend/src/modules/iit-manager/types/index.ts b/backend/src/modules/iit-manager/types/index.ts index 38dbda58..0fabbd09 100644 --- a/backend/src/modules/iit-manager/types/index.ts +++ b/backend/src/modules/iit-manager/types/index.ts @@ -240,6 +240,9 @@ export interface CachedProtocolRules { + + + diff --git a/backend/src/modules/pkb/routes/health.ts b/backend/src/modules/pkb/routes/health.ts index 76fce413..30b40c26 100644 --- a/backend/src/modules/pkb/routes/health.ts +++ b/backend/src/modules/pkb/routes/health.ts @@ -57,3 +57,6 @@ export default async function healthRoutes(fastify: FastifyInstance) { + + + diff --git a/backend/src/modules/rvw/__tests__/api.http b/backend/src/modules/rvw/__tests__/api.http index 885fef21..10f38870 100644 --- a/backend/src/modules/rvw/__tests__/api.http +++ b/backend/src/modules/rvw/__tests__/api.http @@ -135,3 +135,6 @@ Content-Type: application/json + + + diff --git a/backend/src/modules/rvw/__tests__/test-api.ps1 b/backend/src/modules/rvw/__tests__/test-api.ps1 index 68bde5f3..0d96fb34 100644 --- a/backend/src/modules/rvw/__tests__/test-api.ps1 +++ b/backend/src/modules/rvw/__tests__/test-api.ps1 @@ -120,3 +120,6 @@ Write-Host " - 删除任务: DELETE $BaseUrl/api/v1/rvw/tasks/{taskId}" -Foregr + + + diff --git a/backend/src/modules/rvw/index.ts b/backend/src/modules/rvw/index.ts index 2b53c3bd..7fb64c41 100644 --- a/backend/src/modules/rvw/index.ts +++ b/backend/src/modules/rvw/index.ts @@ -34,3 +34,6 @@ export * from './services/utils.js'; + + + diff --git a/backend/src/modules/rvw/services/utils.ts b/backend/src/modules/rvw/services/utils.ts index 408756d4..5e1831c7 100644 --- a/backend/src/modules/rvw/services/utils.ts +++ b/backend/src/modules/rvw/services/utils.ts @@ -125,3 +125,6 @@ export function validateAgentSelection(agents: string[]): void { + + + diff --git a/backend/src/tests/README.md b/backend/src/tests/README.md index 7f0ab3aa..21dcdfad 100644 --- a/backend/src/tests/README.md +++ b/backend/src/tests/README.md @@ -421,6 +421,9 @@ SET session_replication_role = 'origin'; + + + diff --git a/backend/src/tests/verify-test1-database.sql b/backend/src/tests/verify-test1-database.sql index 542dad27..72fb5058 100644 --- a/backend/src/tests/verify-test1-database.sql +++ b/backend/src/tests/verify-test1-database.sql @@ -123,6 +123,9 @@ WHERE key = 'verify_test'; + + + diff --git a/backend/src/tests/verify-test1-database.ts b/backend/src/tests/verify-test1-database.ts index 66740ad9..fee15303 100644 --- a/backend/src/tests/verify-test1-database.ts +++ b/backend/src/tests/verify-test1-database.ts @@ -266,6 +266,9 @@ verifyDatabase() + + + diff --git a/backend/src/types/global.d.ts b/backend/src/types/global.d.ts index e593874c..d10be8ac 100644 --- a/backend/src/types/global.d.ts +++ b/backend/src/types/global.d.ts @@ -56,6 +56,9 @@ export {} + + + diff --git a/backend/sync-dc-database.ps1 b/backend/sync-dc-database.ps1 index aa83b4b6..c3fadeb4 100644 --- a/backend/sync-dc-database.ps1 +++ b/backend/sync-dc-database.ps1 @@ -79,6 +79,9 @@ Write-Host "✅ 完成!" -ForegroundColor Green + + + diff --git a/backend/temp_check.sql b/backend/temp_check.sql index f332df19..4fff8ca1 100644 --- a/backend/temp_check.sql +++ b/backend/temp_check.sql @@ -8,3 +8,6 @@ SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('p + + + diff --git a/backend/test-pkb-migration.http b/backend/test-pkb-migration.http index faa42572..b022d164 100644 --- a/backend/test-pkb-migration.http +++ b/backend/test-pkb-migration.http @@ -171,3 +171,6 @@ DELETE {{baseUrl}}/api/v1/pkb/knowledge/knowledge-bases/{{testKbId}} + + + diff --git a/backend/test-tool-c-advanced-scenarios.mjs b/backend/test-tool-c-advanced-scenarios.mjs index c8e120eb..97623885 100644 --- a/backend/test-tool-c-advanced-scenarios.mjs +++ b/backend/test-tool-c-advanced-scenarios.mjs @@ -366,6 +366,9 @@ runAdvancedTests().catch(error => { + + + diff --git a/backend/test-tool-c-day2.mjs b/backend/test-tool-c-day2.mjs index c6907a2e..e9b3dd07 100644 --- a/backend/test-tool-c-day2.mjs +++ b/backend/test-tool-c-day2.mjs @@ -432,6 +432,9 @@ runAllTests() + + + diff --git a/backend/test-tool-c-day3.mjs b/backend/test-tool-c-day3.mjs index 1c648951..537fbeda 100644 --- a/backend/test-tool-c-day3.mjs +++ b/backend/test-tool-c-day3.mjs @@ -390,6 +390,9 @@ runAllTests() + + + diff --git a/backend/verify_all_users.ts b/backend/verify_all_users.ts index da7ed9df..090896e5 100644 --- a/backend/verify_all_users.ts +++ b/backend/verify_all_users.ts @@ -28,3 +28,6 @@ main() + + + diff --git a/backend/verify_functions.ts b/backend/verify_functions.ts index 45e2ddbc..39a0fe72 100644 --- a/backend/verify_functions.ts +++ b/backend/verify_functions.ts @@ -26,3 +26,6 @@ main() + + + diff --git a/backend/verify_job_common.ts b/backend/verify_job_common.ts index a6fbaa5d..c4ebeb37 100644 --- a/backend/verify_job_common.ts +++ b/backend/verify_job_common.ts @@ -38,3 +38,6 @@ main() + + + diff --git a/backend/verify_mock_user.ts b/backend/verify_mock_user.ts index f3ab53c5..78e113b3 100644 --- a/backend/verify_mock_user.ts +++ b/backend/verify_mock_user.ts @@ -27,3 +27,6 @@ main() + + + diff --git a/backend/verify_system.ts b/backend/verify_system.ts index 46043455..9a76638c 100644 --- a/backend/verify_system.ts +++ b/backend/verify_system.ts @@ -167,3 +167,6 @@ main() + + + diff --git a/deploy-to-sae.ps1 b/deploy-to-sae.ps1 index 1bafd704..0ca82b42 100644 --- a/deploy-to-sae.ps1 +++ b/deploy-to-sae.ps1 @@ -174,6 +174,9 @@ Set-Location .. + + + diff --git a/docs/00-系统总体设计/00-系统当前状态与开发指南.md b/docs/00-系统总体设计/00-系统当前状态与开发指南.md index fae396aa..5e4f9146 100644 --- a/docs/00-系统总体设计/00-系统当前状态与开发指南.md +++ b/docs/00-系统总体设计/00-系统当前状态与开发指南.md @@ -1,14 +1,14 @@ # AIclinicalresearch 系统当前状态与开发指南 -> **文档版本:** v3.5 +> **文档版本:** v3.6 > **创建日期:** 2025-11-28 > **维护者:** 开发团队 -> **最后更新:** 2026-01-16 -> **重大进展:** 🎉 **运营管理端用户管理完成!模块权限系统架构升级!** -> - 🆕 用户管理功能(CRUD + 租户管理 + 模块权限配置) -> - 🏆 模块权限系统改造(版本系统 → 模块代码系统) -> - ✅ user_modules 表(精细化权限控制) -> - ✅ 登录API返回模块列表,前端导航智能过滤 +> **最后更新:** 2026-01-18 +> **重大进展:** 🎉 **AIA V2.1 完成!Prompt管理系统集成!** +> - 🆕 AIA 10个智能体 Prompt 迁移到数据库 +> - 🏆 支持管理端在线配置和调试提示词 +> - ✅ 灰度预览(调试者看DRAFT,普通用户看ACTIVE) +> - ✅ 三级容灾(数据库→缓存→兜底) > **部署状态:** ✅ 生产环境运行中 | 公网地址:http://8.140.53.236/ > **文档目的:** 快速了解系统当前状态,为新AI助手提供上下文 @@ -42,7 +42,7 @@ | 模块代号 | 模块名称 | 核心功能 | 商业价值 | 当前状态 | 优先级 | |---------|---------|---------|---------|---------|--------| -| **AIA** | AI智能问答 | 12个智能体(选题→方案→评审→写作) | ⭐⭐⭐⭐⭐ | 🎉 **V2.0完成(85%)** - 通用能力层架构 | **P0** | +| **AIA** | AI智能问答 | 12个智能体(选题→方案→评审→写作) | ⭐⭐⭐⭐⭐ | 🎉 **V2.1完成(90%)** - Prompt管理集成 | **P0** | | **PKB** | 个人知识库 | RAG问答、私人文献库 | ⭐⭐⭐ | ✅ **核心功能完成(90%)** | P1 | | **ASL** | AI智能文献 | 文献筛选、Meta分析、证据图谱 | ⭐⭐⭐⭐⭐ | 🚧 **正在开发** | **P0** | | **DC** | 数据清洗整理 | ETL + 医学NER(百万行级数据) | ⭐⭐⭐⭐⭐ | ✅ **Tool B完成 + Tool C 99%(异步架构+性能优化-99%+多指标转换+7大功能)** | **P0** | @@ -121,9 +121,43 @@ --- -## 🚀 当前开发状态(2026-01-14) +## 🚀 当前开发状态(2026-01-18) -### 🏆 最新进展:通用能力层重大升级 + AIA V2.0(2026-01-14) +### 🏆 最新进展:AIA V2.1 Prompt管理集成(2026-01-18) + +#### ✅ AIA 模块 Prompt 管理系统集成 + +**功能:** +- ✅ 10 个智能体 Prompt 迁移到数据库(`capability_schema.prompt_templates`) +- ✅ 管理端可在线编辑和调试提示词 +- ✅ 灰度预览(调试者看 DRAFT,普通用户看 ACTIVE) +- ✅ 三级容灾(数据库 → 缓存 → 兜底) +- ✅ 版本管理和回滚 + +**Prompt Code 列表:** + +| Prompt Code | 智能体 | +|-------------|--------| +| `AIA_SCIENTIFIC_QUESTION` | 科学问题梳理 | +| `AIA_PICO_ANALYSIS` | PICO 梳理 | +| `AIA_TOPIC_EVALUATION` | 选题评价 | +| `AIA_OUTCOME_DESIGN` | 观察指标设计 | +| `AIA_CRF_DESIGN` | 病例报告表设计 | +| `AIA_SAMPLE_SIZE` | 样本量计算 | +| `AIA_PROTOCOL_WRITING` | 临床研究方案撰写 | +| `AIA_METHODOLOGY_REVIEW` | 方法学评审智能体 | +| `AIA_PAPER_POLISH` | 论文润色 | +| `AIA_PAPER_TRANSLATE` | 论文翻译 | + +**修改文件:** +- `backend/scripts/migrate-aia-prompts.ts` - 迁移脚本 +- `backend/src/common/prompt/prompt.fallbacks.ts` - 兜底 Prompt +- `backend/src/modules/aia/services/agentService.ts` - 集成 PromptService +- `backend/src/modules/aia/services/conversationService.ts` - 传递 userId + +--- + +### 🏆 历史进展:通用能力层重大升级 + AIA V2.0(2026-01-14) #### ✅ Phase 1: 通用流式响应服务(OpenAI Compatible) diff --git a/docs/02-通用能力层/00-通用能力层清单.md b/docs/02-通用能力层/00-通用能力层清单.md index 11b9e798..30fbb8d3 100644 --- a/docs/02-通用能力层/00-通用能力层清单.md +++ b/docs/02-通用能力层/00-通用能力层清单.md @@ -1,9 +1,10 @@ # 通用能力层清单 -> **文档版本:** v2.0 +> **文档版本:** v2.1 > **创建日期:** 2026-01-14 -> **最后更新:** 2026-01-14 +> **最后更新:** 2026-01-18 > **文档目的:** 列出所有通用能力模块,提供快速调用指南 +> **本次更新:** Ant Design X FileCard 组件使用、Prompt管理 AIA 集成 --- @@ -42,6 +43,7 @@ | 能力 | 路径 | 状态 | 说明 | |------|------|------|------| | **Chat组件** | `shared/components/Chat/` | ✅ 🆕 | AI对话通用组件(V2) | +| **FileCard组件** | `@ant-design/x` | ✅ 🆕 | 文件卡片展示(附件) | | **认证API** | `framework/auth/api.ts` | ✅ | Token管理 | | **API Client** | `common/api/axios.ts` | ✅ | 带认证的axios实例 | | **通用布局** | `shared/layouts/` | ✅ | 主布局、侧边栏 | @@ -95,7 +97,59 @@ data: [DONE]\n\n --- -### 2. Chat 通用组件(🆕 重点推荐) +### 2. Prompt 管理服务 + +**路径:** `backend/src/common/prompt/` + +**功能:** 动态 Prompt 配置、灰度预览、版本管理 + +**核心特性:** +- 灰度预览(DRAFT/ACTIVE 分发) +- Handlebars 模板渲染 +- 变量提取与校验 +- 三级容灾(数据库→缓存→兜底) + +**使用方式:** + +```typescript +import { getPromptService } from '../../../common/prompt'; +import { prisma } from '../../../config/database'; + +// 获取 Prompt(支持灰度预览) +const promptService = getPromptService(prisma); +const { content, isDraft, version } = await promptService.get( + 'AIA_SCIENTIFIC_QUESTION', // Prompt Code + {}, // 变量(如 { userName: '张三' }) + { userId } // 用于灰度判断 +); + +if (isDraft) { + console.log('使用 DRAFT 版本(调试模式)'); +} +``` + +**已集成模块:** +- ✅ RVW - 稿件审查(2个 Prompt) +- ✅ AIA - AI智能问答(10个 Prompt)🆕 2026-01-18 + +**AIA 模块 Prompt Code 列表:** + +| Prompt Code | 智能体 | +|-------------|--------| +| `AIA_SCIENTIFIC_QUESTION` | 科学问题梳理 | +| `AIA_PICO_ANALYSIS` | PICO 梳理 | +| `AIA_TOPIC_EVALUATION` | 选题评价 | +| `AIA_OUTCOME_DESIGN` | 观察指标设计 | +| `AIA_CRF_DESIGN` | 病例报告表设计 | +| `AIA_SAMPLE_SIZE` | 样本量计算 | +| `AIA_PROTOCOL_WRITING` | 临床研究方案撰写 | +| `AIA_METHODOLOGY_REVIEW` | 方法学评审智能体 | +| `AIA_PAPER_POLISH` | 论文润色 | +| `AIA_PAPER_TRANSLATE` | 论文翻译 | + +--- + +### 3. Chat 通用组件(🆕 重点推荐) **路径:** `frontend-v2/src/shared/components/Chat/` @@ -137,7 +191,77 @@ import { --- -### 3. LLM 网关 +### 4. Ant Design X FileCard 组件(🆕 2026-01-18) + +**来源:** `@ant-design/x` (Ant Design X 2.1) + +**功能:** 紧凑的文件卡片展示,适用于聊天界面附件显示 + +**安装:** +```bash +npm install @ant-design/x +``` + +**使用方式:** + +```tsx +import { Attachments } from '@ant-design/x'; + +const { FileCard } = Attachments; + +// 单个文件卡片 + + +// 文件卡片列表(水平排列) + +``` + +**在聊天界面中使用:** + +```tsx +// 将附件与工具栏放在同一行 +
+ + + + {/* 附件列表 */} + {attachments.length > 0 && ( + ({ + uid: att.id, + name: att.filename, + size: att.size, + }))} + /> + )} +
+``` + +**样式特点:** +- 自动识别文件类型图标(PDF/Word/Excel/图片等) +- 自动格式化文件大小(KB/MB) +- 紧凑设计,适合水平排列 +- 支持删除回调 + +**已使用模块:** +- ✅ AIA - AI智能问答(附件上传预览) + +**官方文档:** https://x.ant.design/components/attachments-cn + +--- + +### 5. LLM 网关 **路径:** `backend/src/common/llm/` @@ -769,3 +893,6 @@ await storage.upload('path/file.txt', buffer); + + + diff --git a/docs/02-通用能力层/Postgres-Only异步任务处理指南.md b/docs/02-通用能力层/Postgres-Only异步任务处理指南.md index 5ca9ae55..bf44b222 100644 --- a/docs/02-通用能力层/Postgres-Only异步任务处理指南.md +++ b/docs/02-通用能力层/Postgres-Only异步任务处理指南.md @@ -613,6 +613,9 @@ async saveProcessedData(recordId, newData) { + + + diff --git a/docs/02-通用能力层/快速引用卡片.md b/docs/02-通用能力层/快速引用卡片.md index d81647bf..6ad4f6b7 100644 --- a/docs/02-通用能力层/快速引用卡片.md +++ b/docs/02-通用能力层/快速引用卡片.md @@ -228,3 +228,6 @@ const userId = 'test'; // ❌ 应该用 getUserId(request) + + + diff --git a/docs/02-通用能力层/通用能力层技术债务清单.md b/docs/02-通用能力层/通用能力层技术债务清单.md index e44cc117..7c0d8397 100644 --- a/docs/02-通用能力层/通用能力层技术债务清单.md +++ b/docs/02-通用能力层/通用能力层技术债务清单.md @@ -800,6 +800,9 @@ export const AsyncProgressBar: React.FC = ({ + + + diff --git a/docs/03-业务模块/ADMIN-运营管理端/00-Phase3.5完成总结.md b/docs/03-业务模块/ADMIN-运营管理端/00-Phase3.5完成总结.md index 2ac8d864..462789e6 100644 --- a/docs/03-业务模块/ADMIN-运营管理端/00-Phase3.5完成总结.md +++ b/docs/03-业务模块/ADMIN-运营管理端/00-Phase3.5完成总结.md @@ -297,3 +297,6 @@ Level 3: 兜底Prompt(缓存也失效) + + + diff --git a/docs/03-业务模块/ADMIN-运营管理端/06-开发记录/2026-01-16_用户管理功能与模块权限系统完成.md b/docs/03-业务模块/ADMIN-运营管理端/06-开发记录/2026-01-16_用户管理功能与模块权限系统完成.md index 63705b60..d214e529 100644 --- a/docs/03-业务模块/ADMIN-运营管理端/06-开发记录/2026-01-16_用户管理功能与模块权限系统完成.md +++ b/docs/03-业务模块/ADMIN-运营管理端/06-开发记录/2026-01-16_用户管理功能与模块权限系统完成.md @@ -483,3 +483,6 @@ const pageSize = Number(query.pageSize) || 20; **开发完成时间:** 2026-01-16 13:30 + + + diff --git a/docs/03-业务模块/ADMIN-运营管理端/README.md b/docs/03-业务模块/ADMIN-运营管理端/README.md index f087bd77..fff9685c 100644 --- a/docs/03-业务模块/ADMIN-运营管理端/README.md +++ b/docs/03-业务模块/ADMIN-运营管理端/README.md @@ -217,3 +217,6 @@ ADMIN-运营管理端/ + + + diff --git a/docs/03-业务模块/ADMIN运营与INST机构管理端-文档体系建立完成.md b/docs/03-业务模块/ADMIN运营与INST机构管理端-文档体系建立完成.md index adb9d66d..c47fc08c 100644 --- a/docs/03-业务模块/ADMIN运营与INST机构管理端-文档体系建立完成.md +++ b/docs/03-业务模块/ADMIN运营与INST机构管理端-文档体系建立完成.md @@ -316,3 +316,6 @@ INST-机构管理端/ + + + diff --git a/docs/03-业务模块/AIA-AI智能问答/00-模块当前状态与开发指南.md b/docs/03-业务模块/AIA-AI智能问答/00-模块当前状态与开发指南.md index 19e4155d..9309106d 100644 --- a/docs/03-业务模块/AIA-AI智能问答/00-模块当前状态与开发指南.md +++ b/docs/03-业务模块/AIA-AI智能问答/00-模块当前状态与开发指南.md @@ -1,14 +1,15 @@ # AIA AI智能问答模块 - 当前状态与开发指南 -> **文档版本:** v2.0 +> **文档版本:** v2.1 > **创建日期:** 2026-01-14 > **维护者:** AIA模块开发团队 -> **最后更新:** 2026-01-14 🎉 **V2版本发布 - 通用能力层架构重构完成** +> **最后更新:** 2026-01-18 🎉 **V2.1版本发布 - Prompt管理系统集成完成** > **重大里程碑:** > - 🏆 通用流式响应服务(OpenAI Compatible) > - 🎨 现代感UI(100%还原原型图V11) > - 🚀 Ant Design X 深度集成 > - ✨ 12个智能体配置完成 +> - 🆕 Prompt管理系统集成(灰度预览、版本管理) --- @@ -40,9 +41,33 @@ AIA(AI Intelligent Assistant)模块提供覆盖临床研究全生命周期 ### 当前状态 -- **开发阶段:** ✅ **V2.0 MVP 完成** -- **架构版本:** V2(基于通用能力层重构) -- **完成度:** 85%(核心功能完成,部分高级特性待开发) +- **开发阶段:** ✅ **V2.1 Prompt 管理集成完成** +- **架构版本:** V2.1(集成 PromptService) +- **完成度:** 90%(Prompt 管理已对接,支持灰度预览) + +### ✅ V2.1 新增功能(2026-01-18) + +**Prompt 管理系统集成** +- [x] 10 个智能体 Prompt 迁移到数据库 +- [x] 支持在管理端在线编辑和调试 +- [x] 灰度预览(调试者看 DRAFT,普通用户看 ACTIVE) +- [x] 三级容灾(数据库 → 缓存 → 兜底) +- [x] 版本管理和回滚 + +**Prompt Code 映射表** + +| 智能体 | Prompt Code | +|--------|-------------| +| 科学问题梳理 | `AIA_SCIENTIFIC_QUESTION` | +| PICO 梳理 | `AIA_PICO_ANALYSIS` | +| 选题评价 | `AIA_TOPIC_EVALUATION` | +| 观察指标设计 | `AIA_OUTCOME_DESIGN` | +| 病例报告表设计 | `AIA_CRF_DESIGN` | +| 样本量计算 | `AIA_SAMPLE_SIZE` | +| 临床研究方案撰写 | `AIA_PROTOCOL_WRITING` | +| 方法学评审智能体 | `AIA_METHODOLOGY_REVIEW` | +| 论文润色 | `AIA_PAPER_POLISH` | +| 论文翻译 | `AIA_PAPER_TRANSLATE` | --- @@ -657,3 +682,6 @@ const DEFAULT_MODEL = 'qwen-max'; // 或 'gpt-5', 'claude-4.5' + + + diff --git a/docs/03-业务模块/AIA-AI智能问答/04-开发计划/03-前端组件设计.md b/docs/03-业务模块/AIA-AI智能问答/04-开发计划/03-前端组件设计.md index cd47b096..6f745aa2 100644 --- a/docs/03-业务模块/AIA-AI智能问答/04-开发计划/03-前端组件设计.md +++ b/docs/03-业务模块/AIA-AI智能问答/04-开发计划/03-前端组件设计.md @@ -883,3 +883,6 @@ export interface SlashCommand { + + + diff --git a/docs/03-业务模块/AIA-AI智能问答/06-开发记录/2026-01-18-Prompt管理系统集成.md b/docs/03-业务模块/AIA-AI智能问答/06-开发记录/2026-01-18-Prompt管理系统集成.md new file mode 100644 index 00000000..03633721 --- /dev/null +++ b/docs/03-业务模块/AIA-AI智能问答/06-开发记录/2026-01-18-Prompt管理系统集成.md @@ -0,0 +1,193 @@ +# AIA 模块开发记录 - Prompt 管理系统集成 + +> **日期:** 2026-01-18 +> **开发者:** AI Assistant (Claude) +> **版本:** V2.1 +> **状态:** ✅ 已完成 + +--- + +## 📋 开发目标 + +将 AIA 智能问答模块的 10 个智能体 Prompt 从硬编码迁移到 PromptService,实现: +- 在管理端可视化配置和调试提示词 +- 灰度预览(调试者看 DRAFT,普通用户看 ACTIVE) +- 版本管理和回滚 +- 三级容灾(数据库 → 缓存 → 兜底) + +--- + +## 🎯 完成内容 + +### 1. 数据库迁移脚本 + +**文件:** `backend/scripts/migrate-aia-prompts.ts` + +将 10 个智能体的 Prompt 插入 `capability_schema.prompt_templates` 和 `prompt_versions` 表。 + +**Prompt Code 命名规则(语义化):** + +| 智能体 ID | Prompt Code | 名称 | +|-----------|-------------|------| +| TOPIC_01 | `AIA_SCIENTIFIC_QUESTION` | 科学问题梳理 | +| TOPIC_02 | `AIA_PICO_ANALYSIS` | PICO 梳理 | +| TOPIC_03 | `AIA_TOPIC_EVALUATION` | 选题评价 | +| DESIGN_04 | `AIA_OUTCOME_DESIGN` | 观察指标设计 | +| DESIGN_05 | `AIA_CRF_DESIGN` | 病例报告表设计 | +| DESIGN_06 | `AIA_SAMPLE_SIZE` | 样本量计算 | +| DESIGN_07 | `AIA_PROTOCOL_WRITING` | 临床研究方案撰写 | +| REVIEW_08 | `AIA_METHODOLOGY_REVIEW` | 方法学评审智能体 | +| WRITING_11 | `AIA_PAPER_POLISH` | 论文润色 | +| WRITING_12 | `AIA_PAPER_TRANSLATE` | 论文翻译 | + +**执行命令:** +```bash +cd backend && npx tsx scripts/migrate-aia-prompts.ts +``` + +### 2. 兜底 Prompt + +**文件:** `backend/src/common/prompt/prompt.fallbacks.ts` + +添加 `AIA_FALLBACKS` 对象,包含 10 个智能体的兜底提示词,当数据库和缓存都不可用时使用。 + +### 3. agentService 改造 + +**文件:** `backend/src/modules/aia/services/agentService.ts` + +**改动:** +- 添加 `AGENT_TO_PROMPT_CODE` 映射表 +- 修改 `getAgentSystemPrompt()` 函数: + - 新增 `userId` 参数(用于灰度预览) + - 调用 `PromptService.get()` 获取提示词 + - 返回 `{ content, isDraft }` 结构 + - 失败时降级到硬编码兜底 + +**关键代码:** +```typescript +export async function getAgentSystemPrompt( + agentId: string, + userId?: string +): Promise<{ content: string; isDraft: boolean }> { + const promptCode = AGENT_TO_PROMPT_CODE[agentId]; + + if (promptCode) { + try { + const promptService = getPromptService(prisma); + const result = await promptService.get(promptCode, {}, { userId }); + + if (result.isDraft) { + logger.info('[AIA] 使用 DRAFT 版本 Prompt(调试模式)', { agentId, userId }); + } + + return { content: result.content, isDraft: result.isDraft }; + } catch (error) { + logger.warn('[AIA] PromptService 获取失败,使用兜底', { agentId }); + } + } + + // 兜底:使用硬编码 + const agent = await getAgentById(agentId); + return { content: agent?.systemPrompt || '', isDraft: false }; +} +``` + +### 4. conversationService 改造 + +**文件:** `backend/src/modules/aia/services/conversationService.ts` + +**改动:** +- 调用 `getAgentSystemPrompt()` 时传递 `userId` +- 处理返回的 `{ content, isDraft }` 结构 +- 添加调试模式日志 + +**关键代码:** +```typescript +const { content: systemPrompt, isDraft } = await agentService.getAgentSystemPrompt( + conversation.agentId, + userId // 传递 userId 以支持灰度预览 +); + +if (isDraft) { + logger.info('[AIA:Conversation] 使用 DRAFT 版本 Prompt(调试模式)', { + userId, + agentId: conversation.agentId + }); +} +``` + +### 5. 类型定义修复 + +**文件:** `backend/src/modules/aia/types/index.ts` + +修复 `AgentStage` 类型定义,添加缺失的阶段值: + +```typescript +export type AgentStage = 'topic' | 'design' | 'review' | 'data' | 'writing'; +``` + +--- + +## 📁 修改文件清单 + +| 文件路径 | 操作 | 说明 | +|----------|------|------| +| `backend/scripts/migrate-aia-prompts.ts` | 新增 | 数据库迁移脚本 | +| `backend/src/common/prompt/prompt.fallbacks.ts` | 修改 | 添加 AIA 兜底 Prompt | +| `backend/src/modules/aia/services/agentService.ts` | 修改 | 集成 PromptService | +| `backend/src/modules/aia/services/conversationService.ts` | 修改 | 传递 userId | +| `backend/src/modules/aia/types/index.ts` | 修改 | 修复类型定义 | + +--- + +## 🧪 测试验证 + +### 1. 迁移脚本执行结果 + +``` +✅ 共迁移 10 个 AIA Prompt: + 📋 AIA_CRF_DESIGN - 病例报告表设计 - v1 (ACTIVE) + 📋 AIA_METHODOLOGY_REVIEW - 方法学评审智能体 - v1 (ACTIVE) + 📋 AIA_OUTCOME_DESIGN - 观察指标设计 - v1 (ACTIVE) + ... +``` + +### 2. 功能测试 + +- [x] 管理端可以看到 AIA 的 10 个 Prompt +- [x] 可以编辑和保存草稿 +- [x] 开启调试模式后可以预览 DRAFT 版本 +- [x] 发布后普通用户使用新版本 +- [x] 数据库不可用时自动降级到兜底 + +--- + +## 🔮 后续可优化 + +1. **变量支持** + - 数据库层面已支持(`variables` 字段) + - 代码层面暂未传入变量 + - 后续可添加:`{{目标期刊}}`、`{{研究类型}}` 等 + +2. **A/B 测试** + - 按用户比例分流不同 Prompt 版本 + - 收集效果数据进行对比 + +3. **Prompt 模板** + - 提供常用 Prompt 模板库 + - 支持一键套用和自定义修改 + +--- + +## 📚 相关文档 + +- [ADMIN 模块状态](../../ADMIN-运营管理端/00-模块当前状态与开发指南.md) +- [Prompt 管理系统快速参考](../../../02-技术设计/03-Prompt管理系统快速参考.md) +- [通用能力层清单](../../../02-通用能力层/00-通用能力层清单.md) + +--- + +## 📝 备注 + +本次开发遵循了现有 PromptService 的设计模式,与 RVW 模块集成方式保持一致。 + diff --git a/docs/03-业务模块/ASL-AI智能文献/04-开发计划/05-全文复筛前端开发计划.md b/docs/03-业务模块/ASL-AI智能文献/04-开发计划/05-全文复筛前端开发计划.md index 8f1760ea..753435d8 100644 --- a/docs/03-业务模块/ASL-AI智能文献/04-开发计划/05-全文复筛前端开发计划.md +++ b/docs/03-业务模块/ASL-AI智能文献/04-开发计划/05-全文复筛前端开发计划.md @@ -1293,6 +1293,9 @@ interface FulltextScreeningResult { + + + diff --git a/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端开发完成.md b/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端开发完成.md index fd4e224e..07e91c73 100644 --- a/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端开发完成.md +++ b/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端开发完成.md @@ -407,6 +407,9 @@ GET /api/v1/asl/fulltext-screening/tasks/:taskId/export + + + diff --git a/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端逻辑调整.md b/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端逻辑调整.md index 4cf7220c..179c1eef 100644 --- a/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端逻辑调整.md +++ b/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端逻辑调整.md @@ -350,6 +350,9 @@ Linter错误:0个 + + + diff --git a/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_Day5_全文复筛API开发.md b/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_Day5_全文复筛API开发.md index fea0acc3..63c09b97 100644 --- a/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_Day5_全文复筛API开发.md +++ b/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_Day5_全文复筛API开发.md @@ -509,6 +509,9 @@ Failed to open file '\\tmp\\extraction_service\\temp_10000_test.pdf' + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_AI_Few-shot示例库.md b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_AI_Few-shot示例库.md index 54612d27..1efd0d53 100644 --- a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_AI_Few-shot示例库.md +++ b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_AI_Few-shot示例库.md @@ -575,6 +575,9 @@ df['creatinine'] = pd.to_numeric(df['creatinine'], errors='coerce') + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Bug修复总结_2025-12-08.md b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Bug修复总结_2025-12-08.md index dee1f598..b9e91dac 100644 --- a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Bug修复总结_2025-12-08.md +++ b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Bug修复总结_2025-12-08.md @@ -413,6 +413,9 @@ npm run dev + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Day3开发计划.md b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Day3开发计划.md index dcff81e3..11982531 100644 --- a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Day3开发计划.md +++ b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Day3开发计划.md @@ -990,6 +990,9 @@ export const aiController = new AIController(); + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Day4-5前端开发计划.md b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Day4-5前端开发计划.md index 77e31aef..c2c0a11c 100644 --- a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Day4-5前端开发计划.md +++ b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Day4-5前端开发计划.md @@ -1324,6 +1324,9 @@ npm install react-markdown + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Pivot列顺序优化总结.md b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Pivot列顺序优化总结.md index a42447ea..bc5f196b 100644 --- a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Pivot列顺序优化总结.md +++ b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Pivot列顺序优化总结.md @@ -232,6 +232,9 @@ FMA___基线 | FMA___1个月 | FMA___2个月 + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_方案B实施总结_2025-12-09.md b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_方案B实施总结_2025-12-09.md index 92d60a48..9f57414b 100644 --- a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_方案B实施总结_2025-12-09.md +++ b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_方案B实施总结_2025-12-09.md @@ -390,6 +390,9 @@ formula = "FMA总分(0-100) / 100" + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_缺失值处理_开发进度_2025-12-10.md b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_缺失值处理_开发进度_2025-12-10.md index 51b3209f..f79a1302 100644 --- a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_缺失值处理_开发进度_2025-12-10.md +++ b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_缺失值处理_开发进度_2025-12-10.md @@ -224,6 +224,9 @@ async handleFillnaMice(request, reply) { + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_缺失值处理功能_更新说明.md b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_缺失值处理功能_更新说明.md index 8c74c125..d98aeeca 100644 --- a/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_缺失值处理功能_更新说明.md +++ b/docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_缺失值处理功能_更新说明.md @@ -196,6 +196,9 @@ method: 'mean' | 'median' | 'mode' | 'constant' | 'ffill' | 'bfill' + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-02_工作总结.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-02_工作总结.md index 4c6d0d4d..c8d4755c 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-02_工作总结.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-02_工作总结.md @@ -346,6 +346,9 @@ Changes: + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-06_工具C_Day1开发完成总结.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-06_工具C_Day1开发完成总结.md index bae9efa7..94f768a6 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-06_工具C_Day1开发完成总结.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-06_工具C_Day1开发完成总结.md @@ -418,6 +418,9 @@ cd path; command + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-06_工具C_Day2开发完成总结.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-06_工具C_Day2开发完成总结.md index ae12ab30..6b9af4b5 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-06_工具C_Day2开发完成总结.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-06_工具C_Day2开发完成总结.md @@ -647,6 +647,9 @@ import { logger } from '../../../../common/logging/index.js'; + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_AI对话核心功能增强总结.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_AI对话核心功能增强总结.md index 2e98e519..b148be36 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_AI对话核心功能增强总结.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_AI对话核心功能增强总结.md @@ -651,6 +651,9 @@ Content-Length: 45234 + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Bug修复_DataGrid空数据防御.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Bug修复_DataGrid空数据防御.md index 7013b889..38516ccf 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Bug修复_DataGrid空数据防御.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Bug修复_DataGrid空数据防御.md @@ -303,6 +303,9 @@ Response: + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Day5_Ant-Design-X重构完成.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Day5_Ant-Design-X重构完成.md index 9fc7de0c..b5fcbcab 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Day5_Ant-Design-X重构完成.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Day5_Ant-Design-X重构完成.md @@ -456,6 +456,9 @@ Response: + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Day5最终总结.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Day5最终总结.md index 77749bed..38407823 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Day5最终总结.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Day5最终总结.md @@ -450,6 +450,9 @@ import { ChatContainer } from '@/shared/components/Chat'; + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_UI优化与Bug修复.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_UI优化与Bug修复.md index b703ad4e..7c794548 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_UI优化与Bug修复.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_UI优化与Bug修复.md @@ -360,6 +360,9 @@ const initialMessages = defaultMessages.length > 0 ? defaultMessages : [{ + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_后端API完整对接完成.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_后端API完整对接完成.md index 980e7430..dffff1da 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_后端API完整对接完成.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_后端API完整对接完成.md @@ -400,6 +400,9 @@ python main.py + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_完整UI优化与功能增强.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_完整UI优化与功能增强.md index 712841fc..a4c0cbee 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_完整UI优化与功能增强.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_完整UI优化与功能增强.md @@ -648,6 +648,9 @@ http://localhost:5173/data-cleaning/tool-c + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_工具C_Day4前端基础完成.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_工具C_Day4前端基础完成.md index 7707d1ed..dba9548c 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_工具C_Day4前端基础完成.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_工具C_Day4前端基础完成.md @@ -258,6 +258,9 @@ Day 5 (6-8小时): + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/DC模块重建完成总结-Day1.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/DC模块重建完成总结-Day1.md index 64a1bd6a..fc6169e7 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/DC模块重建完成总结-Day1.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/DC模块重建完成总结-Day1.md @@ -436,6 +436,9 @@ Docs: docs/03-业务模块/DC-数据清洗整理/06-开发记录/DC模块重建 + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase1-Portal页面开发完成-2025-12-02.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase1-Portal页面开发完成-2025-12-02.md index a8bd3757..939ec648 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase1-Portal页面开发完成-2025-12-02.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase1-Portal页面开发完成-2025-12-02.md @@ -411,6 +411,9 @@ const mockAssets: Asset[] = [ + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase2-ToolB-Step1-2开发完成-2025-12-03.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase2-ToolB-Step1-2开发完成-2025-12-03.md index cfc506ab..45796159 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase2-ToolB-Step1-2开发完成-2025-12-03.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase2-ToolB-Step1-2开发完成-2025-12-03.md @@ -395,6 +395,9 @@ frontend-v2/src/modules/dc/ + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Portal页面UI优化-2025-12-02.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Portal页面UI优化-2025-12-02.md index 92c8d203..34238806 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Portal页面UI优化-2025-12-02.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Portal页面UI优化-2025-12-02.md @@ -355,6 +355,9 @@ + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Tool-B-MVP完成总结-2025-12-03.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Tool-B-MVP完成总结-2025-12-03.md index dbd11a1c..24517ffa 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Tool-B-MVP完成总结-2025-12-03.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/Tool-B-MVP完成总结-2025-12-03.md @@ -309,6 +309,9 @@ ConflictDetectionService // 冲突检测(字段级对比) + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-2025-12-03.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-2025-12-03.md index 03264f01..01a80baa 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-2025-12-03.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-2025-12-03.md @@ -358,6 +358,9 @@ + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-Round2-2025-12-03.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-Round2-2025-12-03.md index fd9c2379..3954c378 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-Round2-2025-12-03.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-Round2-2025-12-03.md @@ -321,6 +321,9 @@ + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB浏览器测试计划-2025-12-03.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB浏览器测试计划-2025-12-03.md index 4dc51293..c0df8b0a 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB浏览器测试计划-2025-12-03.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB浏览器测试计划-2025-12-03.md @@ -385,6 +385,9 @@ + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/后端API测试报告-2025-12-02.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/后端API测试报告-2025-12-02.md index 28393b0d..2097bf61 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/后端API测试报告-2025-12-02.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/后端API测试报告-2025-12-02.md @@ -473,6 +473,9 @@ Tool B后端代码**100%复用**了平台通用能力层,无任何重复开发 + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/待办事项-下一步工作.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/待办事项-下一步工作.md index cddb32f3..9f5e6e94 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/待办事项-下一步工作.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/待办事项-下一步工作.md @@ -319,6 +319,9 @@ + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/数据库验证报告-2025-12-02.md b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/数据库验证报告-2025-12-02.md index 2e8ae5c6..a0830a7d 100644 --- a/docs/03-业务模块/DC-数据清洗整理/06-开发记录/数据库验证报告-2025-12-02.md +++ b/docs/03-业务模块/DC-数据清洗整理/06-开发记录/数据库验证报告-2025-12-02.md @@ -250,6 +250,9 @@ $ node scripts/check-dc-tables.mjs + + + diff --git a/docs/03-业务模块/DC-数据清洗整理/07-技术债务/Tool-B技术债务清单.md b/docs/03-业务模块/DC-数据清洗整理/07-技术债务/Tool-B技术债务清单.md index 1a8a10a7..9b97a4d6 100644 --- a/docs/03-业务模块/DC-数据清洗整理/07-技术债务/Tool-B技术债务清单.md +++ b/docs/03-业务模块/DC-数据清洗整理/07-技术债务/Tool-B技术债务清单.md @@ -483,6 +483,9 @@ ${fields.map((f, i) => `${i + 1}. ${f.name}:${f.desc}`).join('\n')} + + + diff --git a/docs/03-业务模块/IIT Manager Agent/02-技术设计/IIT Manager Agent 技术路径与架构设计.md b/docs/03-业务模块/IIT Manager Agent/02-技术设计/IIT Manager Agent 技术路径与架构设计.md index 30f4009f..dd4adf63 100644 --- a/docs/03-业务模块/IIT Manager Agent/02-技术设计/IIT Manager Agent 技术路径与架构设计.md +++ b/docs/03-业务模块/IIT Manager Agent/02-技术设计/IIT Manager Agent 技术路径与架构设计.md @@ -692,3 +692,6 @@ private async processMessageAsync(xmlData: any) { + + + diff --git a/docs/03-业务模块/IIT Manager Agent/04-开发计划/REDCap对接技术方案与实施指南.md b/docs/03-业务模块/IIT Manager Agent/04-开发计划/REDCap对接技术方案与实施指南.md index d90bb5ba..de72c225 100644 --- a/docs/03-业务模块/IIT Manager Agent/04-开发计划/REDCap对接技术方案与实施指南.md +++ b/docs/03-业务模块/IIT Manager Agent/04-开发计划/REDCap对接技术方案与实施指南.md @@ -1083,6 +1083,9 @@ async function testIntegration() { + + + diff --git a/docs/03-业务模块/IIT Manager Agent/04-开发计划/企业微信注册指南.md b/docs/03-业务模块/IIT Manager Agent/04-开发计划/企业微信注册指南.md index a4448731..ac89cd69 100644 --- a/docs/03-业务模块/IIT Manager Agent/04-开发计划/企业微信注册指南.md +++ b/docs/03-业务模块/IIT Manager Agent/04-开发计划/企业微信注册指南.md @@ -223,6 +223,9 @@ Content-Type: application/json + + + diff --git a/docs/03-业务模块/IIT Manager Agent/06-开发记录/2026-01-04-Dify知识库集成开发记录.md b/docs/03-业务模块/IIT Manager Agent/06-开发记录/2026-01-04-Dify知识库集成开发记录.md index 0f5b6b6b..3f280aa8 100644 --- a/docs/03-业务模块/IIT Manager Agent/06-开发记录/2026-01-04-Dify知识库集成开发记录.md +++ b/docs/03-业务模块/IIT Manager Agent/06-开发记录/2026-01-04-Dify知识库集成开发记录.md @@ -647,3 +647,6 @@ REDCap API: exportRecords success { recordCount: 1 } + + + diff --git a/docs/03-业务模块/IIT Manager Agent/06-开发记录/Day2-REDCap实时集成开发完成记录.md b/docs/03-业务模块/IIT Manager Agent/06-开发记录/Day2-REDCap实时集成开发完成记录.md index bf094da1..2e5ea4b6 100644 --- a/docs/03-业务模块/IIT Manager Agent/06-开发记录/Day2-REDCap实时集成开发完成记录.md +++ b/docs/03-业务模块/IIT Manager Agent/06-开发记录/Day2-REDCap实时集成开发完成记录.md @@ -650,6 +650,9 @@ backend/src/modules/iit-manager/ + + + diff --git a/docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成与端到端测试完成记录.md b/docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成与端到端测试完成记录.md index 584d452e..18688b44 100644 --- a/docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成与端到端测试完成记录.md +++ b/docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成与端到端测试完成记录.md @@ -801,5 +801,8 @@ CREATE TABLE iit_schema.wechat_tokens ( + + + diff --git a/docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成开发完成记录.md b/docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成开发完成记录.md index 20ea2234..64f353a4 100644 --- a/docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成开发完成记录.md +++ b/docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成开发完成记录.md @@ -557,6 +557,9 @@ Day 3 的开发工作虽然遇到了多个技术问题,但最终成功完成 + + + diff --git a/docs/03-业务模块/IIT Manager Agent/06-开发记录/Phase1.5-AI对话集成REDCap完成记录.md b/docs/03-业务模块/IIT Manager Agent/06-开发记录/Phase1.5-AI对话集成REDCap完成记录.md index 25a35eed..e91455de 100644 --- a/docs/03-业务模块/IIT Manager Agent/06-开发记录/Phase1.5-AI对话集成REDCap完成记录.md +++ b/docs/03-业务模块/IIT Manager Agent/06-开发记录/Phase1.5-AI对话集成REDCap完成记录.md @@ -326,4 +326,7 @@ AI: "出生日期:2017-01-04 + + + diff --git a/docs/03-业务模块/IIT Manager Agent/06-开发记录/V1.1更新完成报告.md b/docs/03-业务模块/IIT Manager Agent/06-开发记录/V1.1更新完成报告.md index 173f842a..56128af8 100644 --- a/docs/03-业务模块/IIT Manager Agent/06-开发记录/V1.1更新完成报告.md +++ b/docs/03-业务模块/IIT Manager Agent/06-开发记录/V1.1更新完成报告.md @@ -267,6 +267,9 @@ Day 4: REDCap EM(Webhook推送)← 作为增强,而非核心 + + + diff --git a/docs/03-业务模块/IIT Manager Agent/07-技术债务/IIT Manager Agent 技术债务清单.md b/docs/03-业务模块/IIT Manager Agent/07-技术债务/IIT Manager Agent 技术债务清单.md index 9edb1239..cc5a8b4e 100644 --- a/docs/03-业务模块/IIT Manager Agent/07-技术债务/IIT Manager Agent 技术债务清单.md +++ b/docs/03-业务模块/IIT Manager Agent/07-技术债务/IIT Manager Agent 技术债务清单.md @@ -685,3 +685,6 @@ const answer = `根据研究方案[1]和CRF表格[2],纳入标准包括: + + + diff --git a/docs/03-业务模块/INST-机构管理端/00-模块当前状态与开发指南.md b/docs/03-业务模块/INST-机构管理端/00-模块当前状态与开发指南.md index cc146f84..165e904b 100644 --- a/docs/03-业务模块/INST-机构管理端/00-模块当前状态与开发指南.md +++ b/docs/03-业务模块/INST-机构管理端/00-模块当前状态与开发指南.md @@ -442,3 +442,6 @@ export const calculateAvailableQuota = async (tenantId: string) => { + + + diff --git a/docs/03-业务模块/INST-机构管理端/README.md b/docs/03-业务模块/INST-机构管理端/README.md index 212e5951..3055d4c6 100644 --- a/docs/03-业务模块/INST-机构管理端/README.md +++ b/docs/03-业务模块/INST-机构管理端/README.md @@ -315,3 +315,6 @@ https://platform.example.com/t/pharma-abc/login + + + diff --git a/docs/03-业务模块/PKB-个人知识库/06-开发记录/2026-01-07-前端迁移与批处理功能完善.md b/docs/03-业务模块/PKB-个人知识库/06-开发记录/2026-01-07-前端迁移与批处理功能完善.md index 64c844d3..3617bec1 100644 --- a/docs/03-业务模块/PKB-个人知识库/06-开发记录/2026-01-07-前端迁移与批处理功能完善.md +++ b/docs/03-业务模块/PKB-个人知识库/06-开发记录/2026-01-07-前端迁移与批处理功能完善.md @@ -365,3 +365,6 @@ const newResults = resultsData.map((docResult: any) => ({ + + + diff --git a/docs/03-业务模块/PKB-个人知识库/06-开发记录/2026-01-07_PKB模块前端V3设计实现.md b/docs/03-业务模块/PKB-个人知识库/06-开发记录/2026-01-07_PKB模块前端V3设计实现.md index fc77b46a..578f6e2f 100644 --- a/docs/03-业务模块/PKB-个人知识库/06-开发记录/2026-01-07_PKB模块前端V3设计实现.md +++ b/docs/03-业务模块/PKB-个人知识库/06-开发记录/2026-01-07_PKB模块前端V3设计实现.md @@ -238,3 +238,6 @@ const chatApi = axios.create({ + + + diff --git a/docs/03-业务模块/Redcap/01-部署与配置/10-REDCap_Docker部署操作手册.md b/docs/03-业务模块/Redcap/01-部署与配置/10-REDCap_Docker部署操作手册.md index 43fdd055..891ad4eb 100644 --- a/docs/03-业务模块/Redcap/01-部署与配置/10-REDCap_Docker部署操作手册.md +++ b/docs/03-业务模块/Redcap/01-部署与配置/10-REDCap_Docker部署操作手册.md @@ -772,6 +772,9 @@ docker exec redcap-apache php /tmp/create-redcap-password.php + + + diff --git a/docs/03-业务模块/Redcap/README.md b/docs/03-业务模块/Redcap/README.md index 9d062f15..e8625661 100644 --- a/docs/03-业务模块/Redcap/README.md +++ b/docs/03-业务模块/Redcap/README.md @@ -154,6 +154,9 @@ AIclinicalresearch/redcap-docker-dev/ + + + diff --git a/docs/04-开发规范/09-数据库开发规范.md b/docs/04-开发规范/09-数据库开发规范.md index 88d18b47..54ab94b9 100644 --- a/docs/04-开发规范/09-数据库开发规范.md +++ b/docs/04-开发规范/09-数据库开发规范.md @@ -323,3 +323,6 @@ npx tsx check_iit_asl_data.ts + + + diff --git a/docs/04-开发规范/10-模块认证规范.md b/docs/04-开发规范/10-模块认证规范.md index 9a766e5f..d099ef4a 100644 --- a/docs/04-开发规范/10-模块认证规范.md +++ b/docs/04-开发规范/10-模块认证规范.md @@ -191,3 +191,6 @@ interface DecodedToken { + + + diff --git a/docs/05-部署文档/02-SAE部署完全指南(产品经理版).md b/docs/05-部署文档/02-SAE部署完全指南(产品经理版).md index 2164f7c0..50ce8c02 100644 --- a/docs/05-部署文档/02-SAE部署完全指南(产品经理版).md +++ b/docs/05-部署文档/02-SAE部署完全指南(产品经理版).md @@ -890,6 +890,9 @@ ACR镜像仓库: + + + diff --git a/docs/05-部署文档/07-前端Nginx-SAE部署操作手册.md b/docs/05-部署文档/07-前端Nginx-SAE部署操作手册.md index 7f2e2c16..a0e4416c 100644 --- a/docs/05-部署文档/07-前端Nginx-SAE部署操作手册.md +++ b/docs/05-部署文档/07-前端Nginx-SAE部署操作手册.md @@ -1377,6 +1377,9 @@ SAE应用配置: + + + diff --git a/docs/05-部署文档/08-PostgreSQL数据库部署操作手册.md b/docs/05-部署文档/08-PostgreSQL数据库部署操作手册.md index 15c280ef..c1f799a2 100644 --- a/docs/05-部署文档/08-PostgreSQL数据库部署操作手册.md +++ b/docs/05-部署文档/08-PostgreSQL数据库部署操作手册.md @@ -1193,6 +1193,9 @@ docker exec -e PGPASSWORD="密码" ai-clinical-postgres psql -h RDS地址 -U air + + + diff --git a/docs/05-部署文档/10-Node.js后端-Docker镜像构建手册.md b/docs/05-部署文档/10-Node.js后端-Docker镜像构建手册.md index 97a87e9b..cfeda02a 100644 --- a/docs/05-部署文档/10-Node.js后端-Docker镜像构建手册.md +++ b/docs/05-部署文档/10-Node.js后端-Docker镜像构建手册.md @@ -604,6 +604,9 @@ scripts/*.ts + + + diff --git a/docs/05-部署文档/11-Node.js后端-SAE部署配置清单.md b/docs/05-部署文档/11-Node.js后端-SAE部署配置清单.md index 3f2ee118..8ea12ba1 100644 --- a/docs/05-部署文档/11-Node.js后端-SAE部署配置清单.md +++ b/docs/05-部署文档/11-Node.js后端-SAE部署配置清单.md @@ -292,6 +292,9 @@ Node.js后端部署成功后: + + + diff --git a/docs/05-部署文档/12-Node.js后端-SAE部署操作手册.md b/docs/05-部署文档/12-Node.js后端-SAE部署操作手册.md index b56642ae..ce275569 100644 --- a/docs/05-部署文档/12-Node.js后端-SAE部署操作手册.md +++ b/docs/05-部署文档/12-Node.js后端-SAE部署操作手册.md @@ -515,6 +515,9 @@ Node.js后端 (SAE) ← http://172.17.173.88:3001 + + + diff --git a/docs/05-部署文档/13-Node.js后端-镜像修复记录.md b/docs/05-部署文档/13-Node.js后端-镜像修复记录.md index b3d3aaa5..e63d2373 100644 --- a/docs/05-部署文档/13-Node.js后端-镜像修复记录.md +++ b/docs/05-部署文档/13-Node.js后端-镜像修复记录.md @@ -230,6 +230,9 @@ curl http://localhost:3001/health + + + diff --git a/docs/05-部署文档/14-Node.js后端-pino-pretty问题修复.md b/docs/05-部署文档/14-Node.js后端-pino-pretty问题修复.md index 8a688426..49070460 100644 --- a/docs/05-部署文档/14-Node.js后端-pino-pretty问题修复.md +++ b/docs/05-部署文档/14-Node.js后端-pino-pretty问题修复.md @@ -268,6 +268,9 @@ npm run dev + + + diff --git a/docs/05-部署文档/16-前端Nginx-部署成功总结.md b/docs/05-部署文档/16-前端Nginx-部署成功总结.md index 1f80b213..50642e80 100644 --- a/docs/05-部署文档/16-前端Nginx-部署成功总结.md +++ b/docs/05-部署文档/16-前端Nginx-部署成功总结.md @@ -492,6 +492,9 @@ pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432 + + + diff --git a/docs/05-部署文档/17-完整部署实战手册-2025版.md b/docs/05-部署文档/17-完整部署实战手册-2025版.md index 24ffd49a..8be1d443 100644 --- a/docs/05-部署文档/17-完整部署实战手册-2025版.md +++ b/docs/05-部署文档/17-完整部署实战手册-2025版.md @@ -1820,6 +1820,9 @@ curl http://8.140.53.236/ + + + diff --git a/docs/05-部署文档/18-部署文档使用指南.md b/docs/05-部署文档/18-部署文档使用指南.md index 746431f2..924dcdef 100644 --- a/docs/05-部署文档/18-部署文档使用指南.md +++ b/docs/05-部署文档/18-部署文档使用指南.md @@ -368,6 +368,9 @@ crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-se + + + diff --git a/docs/05-部署文档/19-日常更新快速操作手册.md b/docs/05-部署文档/19-日常更新快速操作手册.md index 5c073d53..217e22d4 100644 --- a/docs/05-部署文档/19-日常更新快速操作手册.md +++ b/docs/05-部署文档/19-日常更新快速操作手册.md @@ -690,6 +690,9 @@ docker login --username=gofeng117@163.com \ + + + diff --git a/docs/05-部署文档/文档修正报告-20251214.md b/docs/05-部署文档/文档修正报告-20251214.md index 6bc5c60b..1404b9a5 100644 --- a/docs/05-部署文档/文档修正报告-20251214.md +++ b/docs/05-部署文档/文档修正报告-20251214.md @@ -501,6 +501,9 @@ NAT网关成本¥100/月,对初创团队是一笔开销 + + + diff --git a/docs/07-运维文档/03-SAE环境变量配置指南.md b/docs/07-运维文档/03-SAE环境变量配置指南.md index be5678a1..f777c4ad 100644 --- a/docs/07-运维文档/03-SAE环境变量配置指南.md +++ b/docs/07-运维文档/03-SAE环境变量配置指南.md @@ -406,6 +406,9 @@ curl http://你的SAE地址:3001/health + + + diff --git a/docs/07-运维文档/05-Redis缓存与队列的区别说明.md b/docs/07-运维文档/05-Redis缓存与队列的区别说明.md index 912dbd4d..4578921d 100644 --- a/docs/07-运维文档/05-Redis缓存与队列的区别说明.md +++ b/docs/07-运维文档/05-Redis缓存与队列的区别说明.md @@ -738,6 +738,9 @@ const job = await queue.getJob(jobId); + + + diff --git a/docs/07-运维文档/06-长时间任务可靠性分析.md b/docs/07-运维文档/06-长时间任务可靠性分析.md index 7dbd3529..f3ccec72 100644 --- a/docs/07-运维文档/06-长时间任务可靠性分析.md +++ b/docs/07-运维文档/06-长时间任务可靠性分析.md @@ -505,6 +505,9 @@ processLiteraturesInBackground(task.id, projectId, testLiteratures); + + + diff --git a/docs/07-运维文档/07-Redis使用需求分析(按模块).md b/docs/07-运维文档/07-Redis使用需求分析(按模块).md index 612621c8..4d263500 100644 --- a/docs/07-运维文档/07-Redis使用需求分析(按模块).md +++ b/docs/07-运维文档/07-Redis使用需求分析(按模块).md @@ -982,6 +982,9 @@ ROI = (¥22,556 - ¥144) / ¥144 × 100% = 15,564% + + + diff --git a/docs/08-项目管理/03-每周计划/2025-12-13-Postgres-Only架构改造完成.md b/docs/08-项目管理/03-每周计划/2025-12-13-Postgres-Only架构改造完成.md index 8640027c..a5811011 100644 --- a/docs/08-项目管理/03-每周计划/2025-12-13-Postgres-Only架构改造完成.md +++ b/docs/08-项目管理/03-每周计划/2025-12-13-Postgres-Only架构改造完成.md @@ -1039,6 +1039,9 @@ Redis 实例:¥500/月 + + + diff --git a/docs/08-项目管理/05-技术债务/通用对话服务抽取计划.md b/docs/08-项目管理/05-技术债务/通用对话服务抽取计划.md index cc30999d..ae953bb1 100644 --- a/docs/08-项目管理/05-技术债务/通用对话服务抽取计划.md +++ b/docs/08-项目管理/05-技术债务/通用对话服务抽取计划.md @@ -497,6 +497,9 @@ import { ChatContainer } from '@/shared/components/Chat'; + + + diff --git a/docs/08-项目管理/2026-01-11-数据库事故总结.md b/docs/08-项目管理/2026-01-11-数据库事故总结.md index 6d7ec7bc..fe45f2f9 100644 --- a/docs/08-项目管理/2026-01-11-数据库事故总结.md +++ b/docs/08-项目管理/2026-01-11-数据库事故总结.md @@ -207,3 +207,6 @@ VALUES ('user-mock-001', '13800000000', ..., 'tenant-mock-001', ...); + + + diff --git a/docs/08-项目管理/PKB前端问题修复报告.md b/docs/08-项目管理/PKB前端问题修复报告.md index c812ff65..48b8208a 100644 --- a/docs/08-项目管理/PKB前端问题修复报告.md +++ b/docs/08-项目管理/PKB前端问题修复报告.md @@ -419,3 +419,6 @@ frontend-v2/src/modules/pkb/ + + + diff --git a/docs/08-项目管理/PKB前端验证指南.md b/docs/08-项目管理/PKB前端验证指南.md index 0455467e..6616132b 100644 --- a/docs/08-项目管理/PKB前端验证指南.md +++ b/docs/08-项目管理/PKB前端验证指南.md @@ -281,3 +281,6 @@ npm run dev + + + diff --git a/docs/08-项目管理/PKB功能审查报告-阶段0.md b/docs/08-项目管理/PKB功能审查报告-阶段0.md index 9245c0fa..8baf689f 100644 --- a/docs/08-项目管理/PKB功能审查报告-阶段0.md +++ b/docs/08-项目管理/PKB功能审查报告-阶段0.md @@ -796,3 +796,6 @@ AIA智能问答模块 + + + diff --git a/docs/08-项目管理/PKB和RVW功能迁移计划.md b/docs/08-项目管理/PKB和RVW功能迁移计划.md index 49f72201..8217baee 100644 --- a/docs/08-项目管理/PKB和RVW功能迁移计划.md +++ b/docs/08-项目管理/PKB和RVW功能迁移计划.md @@ -937,6 +937,9 @@ CREATE INDEX idx_rvw_tasks_created_at ON rvw_schema.review_tasks(created_at); + + + diff --git a/docs/08-项目管理/PKB精细化优化报告.md b/docs/08-项目管理/PKB精细化优化报告.md index dca46414..5f95ac10 100644 --- a/docs/08-项目管理/PKB精细化优化报告.md +++ b/docs/08-项目管理/PKB精细化优化报告.md @@ -594,3 +594,6 @@ const typography = { + + + diff --git a/docs/08-项目管理/PKB迁移-超级安全执行计划.md b/docs/08-项目管理/PKB迁移-超级安全执行计划.md index cb7e4562..dbcc07b4 100644 --- a/docs/08-项目管理/PKB迁移-超级安全执行计划.md +++ b/docs/08-项目管理/PKB迁移-超级安全执行计划.md @@ -906,3 +906,6 @@ app.use('/api/v1/knowledge', (req, res) => { + + + diff --git a/docs/08-项目管理/PKB迁移-阶段1完成报告.md b/docs/08-项目管理/PKB迁移-阶段1完成报告.md index 54785830..342b3595 100644 --- a/docs/08-项目管理/PKB迁移-阶段1完成报告.md +++ b/docs/08-项目管理/PKB迁移-阶段1完成报告.md @@ -220,3 +220,6 @@ rm -rf src/modules/pkb + + + diff --git a/docs/08-项目管理/PKB迁移-阶段2完成报告.md b/docs/08-项目管理/PKB迁移-阶段2完成报告.md index fb4c8ed0..cfb5ba3c 100644 --- a/docs/08-项目管理/PKB迁移-阶段2完成报告.md +++ b/docs/08-项目管理/PKB迁移-阶段2完成报告.md @@ -395,3 +395,6 @@ GET /api/v2/pkb/batch-tasks/batch/templates + + + diff --git a/docs/08-项目管理/PKB迁移-阶段2进行中.md b/docs/08-项目管理/PKB迁移-阶段2进行中.md index 0062b112..1a5e8e69 100644 --- a/docs/08-项目管理/PKB迁移-阶段2进行中.md +++ b/docs/08-项目管理/PKB迁移-阶段2进行中.md @@ -39,3 +39,6 @@ import pkbRoutes from './modules/pkb/routes/index.js'; + + + diff --git a/docs/08-项目管理/PKB迁移-阶段3完成报告.md b/docs/08-项目管理/PKB迁移-阶段3完成报告.md index 4285edb0..053f7923 100644 --- a/docs/08-项目管理/PKB迁移-阶段3完成报告.md +++ b/docs/08-项目管理/PKB迁移-阶段3完成报告.md @@ -308,3 +308,6 @@ backend/ + + + diff --git a/docs/08-项目管理/PKB迁移-阶段4完成报告.md b/docs/08-项目管理/PKB迁移-阶段4完成报告.md index 60b36150..476ed0bf 100644 --- a/docs/08-项目管理/PKB迁移-阶段4完成报告.md +++ b/docs/08-项目管理/PKB迁移-阶段4完成报告.md @@ -519,3 +519,6 @@ const response = await fetch('/api/v2/pkb/batch-tasks/batch/execute', { + + + diff --git a/extraction_service/.dockerignore b/extraction_service/.dockerignore index d4dea535..f633f0ea 100644 --- a/extraction_service/.dockerignore +++ b/extraction_service/.dockerignore @@ -75,6 +75,9 @@ models/ + + + diff --git a/extraction_service/operations/__init__.py b/extraction_service/operations/__init__.py index 3b048ac8..eae8e338 100644 --- a/extraction_service/operations/__init__.py +++ b/extraction_service/operations/__init__.py @@ -63,6 +63,9 @@ __version__ = '1.0.0' + + + diff --git a/extraction_service/operations/dropna.py b/extraction_service/operations/dropna.py index 671e445b..1515f129 100644 --- a/extraction_service/operations/dropna.py +++ b/extraction_service/operations/dropna.py @@ -196,6 +196,9 @@ def get_missing_summary(df: pd.DataFrame) -> dict: + + + diff --git a/extraction_service/operations/filter.py b/extraction_service/operations/filter.py index 0e2b7d21..03eb8167 100644 --- a/extraction_service/operations/filter.py +++ b/extraction_service/operations/filter.py @@ -156,6 +156,9 @@ def apply_filter( + + + diff --git a/extraction_service/operations/unpivot.py b/extraction_service/operations/unpivot.py index 27cea058..34bd6d33 100644 --- a/extraction_service/operations/unpivot.py +++ b/extraction_service/operations/unpivot.py @@ -320,6 +320,9 @@ def get_unpivot_preview( + + + diff --git a/extraction_service/test_dc_api.py b/extraction_service/test_dc_api.py index 227304d4..b5a011d9 100644 --- a/extraction_service/test_dc_api.py +++ b/extraction_service/test_dc_api.py @@ -330,6 +330,9 @@ if __name__ == "__main__": + + + diff --git a/extraction_service/test_execute_simple.py b/extraction_service/test_execute_simple.py index 9f6ec3c4..57bfdaec 100644 --- a/extraction_service/test_execute_simple.py +++ b/extraction_service/test_execute_simple.py @@ -96,6 +96,9 @@ except Exception as e: + + + diff --git a/extraction_service/test_module.py b/extraction_service/test_module.py index dda22d0c..67f79ea6 100644 --- a/extraction_service/test_module.py +++ b/extraction_service/test_module.py @@ -76,6 +76,9 @@ except Exception as e: + + + diff --git a/frontend-v2/.dockerignore b/frontend-v2/.dockerignore index dd773e65..2dc0460d 100644 --- a/frontend-v2/.dockerignore +++ b/frontend-v2/.dockerignore @@ -95,6 +95,9 @@ vite.config.*.timestamp-* + + + diff --git a/frontend-v2/docker-entrypoint.sh b/frontend-v2/docker-entrypoint.sh index d84eedf5..a48b4a2b 100644 --- a/frontend-v2/docker-entrypoint.sh +++ b/frontend-v2/docker-entrypoint.sh @@ -62,6 +62,9 @@ exec nginx -g 'daemon off;' + + + diff --git a/frontend-v2/nginx.conf b/frontend-v2/nginx.conf index 66ff4007..2cc987c2 100644 --- a/frontend-v2/nginx.conf +++ b/frontend-v2/nginx.conf @@ -218,6 +218,9 @@ http { + + + diff --git a/frontend-v2/src/common/api/axios.ts b/frontend-v2/src/common/api/axios.ts index 25c5931e..00abed30 100644 --- a/frontend-v2/src/common/api/axios.ts +++ b/frontend-v2/src/common/api/axios.ts @@ -49,3 +49,6 @@ export default apiClient; + + + diff --git a/frontend-v2/src/framework/auth/api.ts b/frontend-v2/src/framework/auth/api.ts index 10a23d70..53b903e1 100644 --- a/frontend-v2/src/framework/auth/api.ts +++ b/frontend-v2/src/framework/auth/api.ts @@ -248,3 +248,6 @@ export async function logout(): Promise { + + + diff --git a/frontend-v2/src/framework/auth/index.ts b/frontend-v2/src/framework/auth/index.ts index 1eb373bb..a0efa503 100644 --- a/frontend-v2/src/framework/auth/index.ts +++ b/frontend-v2/src/framework/auth/index.ts @@ -14,3 +14,6 @@ export * from './api'; + + + diff --git a/frontend-v2/src/framework/auth/moduleApi.ts b/frontend-v2/src/framework/auth/moduleApi.ts index bf0dbfca..2f456c22 100644 --- a/frontend-v2/src/framework/auth/moduleApi.ts +++ b/frontend-v2/src/framework/auth/moduleApi.ts @@ -38,3 +38,6 @@ export async function fetchUserModules(): Promise { + + + diff --git a/frontend-v2/src/modules/admin/components/ModulePermissionModal.tsx b/frontend-v2/src/modules/admin/components/ModulePermissionModal.tsx index f4619edd..86d0f6a3 100644 --- a/frontend-v2/src/modules/admin/components/ModulePermissionModal.tsx +++ b/frontend-v2/src/modules/admin/components/ModulePermissionModal.tsx @@ -118,3 +118,6 @@ const ModulePermissionModal: React.FC = ({ export default ModulePermissionModal; + + + diff --git a/frontend-v2/src/modules/admin/index.tsx b/frontend-v2/src/modules/admin/index.tsx index b741edcb..60927b08 100644 --- a/frontend-v2/src/modules/admin/index.tsx +++ b/frontend-v2/src/modules/admin/index.tsx @@ -29,3 +29,6 @@ const AdminModule: React.FC = () => { export default AdminModule; + + + diff --git a/frontend-v2/src/modules/admin/types/user.ts b/frontend-v2/src/modules/admin/types/user.ts index 6a173c14..d869a14f 100644 --- a/frontend-v2/src/modules/admin/types/user.ts +++ b/frontend-v2/src/modules/admin/types/user.ts @@ -194,3 +194,6 @@ export const TENANT_TYPE_NAMES: Record = { PUBLIC: '公共', }; + + + diff --git a/frontend-v2/src/modules/aia/components/AgentCard.tsx b/frontend-v2/src/modules/aia/components/AgentCard.tsx index a137e3de..550f102e 100644 --- a/frontend-v2/src/modules/aia/components/AgentCard.tsx +++ b/frontend-v2/src/modules/aia/components/AgentCard.tsx @@ -78,3 +78,6 @@ export default AgentCard; + + + diff --git a/frontend-v2/src/modules/aia/components/ChatWorkspace.tsx b/frontend-v2/src/modules/aia/components/ChatWorkspace.tsx index 5f35970c..8b4c3e34 100644 --- a/frontend-v2/src/modules/aia/components/ChatWorkspace.tsx +++ b/frontend-v2/src/modules/aia/components/ChatWorkspace.tsx @@ -17,8 +17,12 @@ import { Download, Paperclip, Lightbulb, + Pencil, + Trash2, + Check, } from 'lucide-react'; import * as LucideIcons from 'lucide-react'; +import { FileCard } from '@ant-design/x'; import { useAIStream } from '@/shared/components/Chat'; import { ThinkingBlock } from '@/shared/components/Chat'; import { getAccessToken } from '@/framework/auth/api'; @@ -26,6 +30,33 @@ import { AGENTS, AGENT_PROMPTS, BRAND_COLORS } from '../constants'; import type { AgentConfig, Conversation, Message } from '../types'; import '../styles/chat-workspace.css'; +/** + * 根据文件扩展名获取 FileCard 的 icon 类型 + */ +const getFileIcon = (filename: string): string => { + const ext = filename.split('.').pop()?.toLowerCase() || ''; + const iconMap: Record = { + pdf: 'pdf', + doc: 'word', + docx: 'word', + xls: 'excel', + xlsx: 'excel', + ppt: 'ppt', + pptx: 'ppt', + txt: 'default', + md: 'markdown', + jpg: 'image', + jpeg: 'image', + png: 'image', + gif: 'image', + mp4: 'video', + mp3: 'audio', + zip: 'zip', + rar: 'zip', + }; + return iconMap[ext] || 'default'; +}; + interface ChatWorkspaceProps { agent: AgentConfig; initialQuery?: string; @@ -55,8 +86,13 @@ export const ChatWorkspace: React.FC = ({ const [messages, setMessages] = useState([]); const [inputValue, setInputValue] = useState(''); const [deepThinkingEnabled, setDeepThinkingEnabled] = useState(true); + const [attachments, setAttachments] = useState>([]); + const [isUploading, setIsUploading] = useState(false); + const [editingConvId, setEditingConvId] = useState(null); + const [editingTitle, setEditingTitle] = useState(''); const messagesEndRef = useRef(null); const textareaRef = useRef(null); + const fileInputRef = useRef(null); const AgentIcon = getIcon(agent.icon); const themeColor = BRAND_COLORS[agent.theme]; @@ -126,12 +162,80 @@ export const ChatWorkspace: React.FC = ({ } }, [agent.id, isCreatingConversation]); - // 初始化:自动创建对话 + // 初始化:加载对话列表 useEffect(() => { - if (!currentConversationId && !isCreatingConversation) { - createConversation(); - } - }, [currentConversationId, isCreatingConversation, createConversation]); + const loadConversations = async () => { + try { + const token = getAccessToken(); + const response = await fetch(`/api/v1/aia/conversations?agentId=${agent.id}`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + if (response.ok) { + const result = await response.json(); + if (result.data?.conversations && result.data.conversations.length > 0) { + setConversations(result.data.conversations.map((c: any) => ({ + id: c.id, + agentId: c.agentId, + title: c.title, + createdAt: new Date(c.createdAt), + updatedAt: new Date(c.updatedAt), + }))); + // 选择最近的对话 + setCurrentConversationId(result.data.conversations[0].id); + } else { + // 没有历史对话,创建新对话 + createConversation(); + } + } else { + // 加载失败,创建新对话 + createConversation(); + } + } catch (error) { + console.error('[ChatWorkspace] 加载对话列表失败:', error); + createConversation(); + } + }; + + loadConversations(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [agent.id]); + + // 加载对话的历史消息 + useEffect(() => { + if (!currentConversationId) return; + + const loadMessages = async () => { + try { + const token = getAccessToken(); + const response = await fetch(`/api/v1/aia/conversations/${currentConversationId}/messages`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + if (response.ok) { + const result = await response.json(); + if (result.data?.messages) { + setMessages(result.data.messages.map((m: any) => ({ + id: m.id, + role: m.role, + content: m.content, + thinking: m.thinkingContent, + attachments: m.attachmentDetails, // 使用附件详情 + createdAt: new Date(m.createdAt), + }))); + } + } + } catch (error) { + console.error('[ChatWorkspace] 加载历史消息失败:', error); + } + }; + + loadMessages(); + }, [currentConversationId]); // 滚动到底部 const scrollToBottom = useCallback(() => { @@ -170,11 +274,17 @@ export const ChatWorkspace: React.FC = ({ } } - // 添加用户消息 + // 获取已上传的附件信息(用于显示) + const uploadedAttachments = attachments + .filter(a => !a.uploading && !a.id.startsWith('temp-')) + .map(a => ({ id: a.id, filename: a.name, size: a.size })); + + // 添加用户消息(包含附件信息) const userMessage: Message = { id: `user-${Date.now()}`, role: 'user', content, + attachments: uploadedAttachments.length > 0 ? uploadedAttachments : undefined, createdAt: new Date(), }; setMessages(prev => [...prev, userMessage]); @@ -196,25 +306,53 @@ export const ChatWorkspace: React.FC = ({ }; setMessages(prev => [...prev, aiMessage]); + // 获取已上传的附件 ID(排除正在上传的) + const uploadedAttachmentIds = attachments + .filter(a => !a.uploading && !a.id.startsWith('temp-')) + .map(a => a.id); + // 调用流式API const result = await sendStreamMessage(content, { conversationId: convId, agentId: agent.id, enableDeepThinking: deepThinkingEnabled, + attachmentIds: uploadedAttachmentIds.length > 0 ? uploadedAttachmentIds : undefined, }); - // 更新对话标题 + // 发送成功后清空附件 + if (uploadedAttachmentIds.length > 0) { + setAttachments([]); + } + + // 更新对话标题(首次发消息时自动命名) if (conversations.find(c => c.id === convId)?.title === '新对话') { - const title = content.length > 20 ? content.slice(0, 20) + '...' : content; + const newTitle = content.length > 20 ? content.slice(0, 20) + '...' : content; + + // 更新前端状态 setConversations(prev => prev.map(c => c.id === convId - ? { ...c, title, updatedAt: new Date() } + ? { ...c, title: newTitle, updatedAt: new Date() } : c ) ); + + // 同步保存到后端数据库 + try { + const token = getAccessToken(); + await fetch(`/api/v1/aia/conversations/${convId}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ title: newTitle }), + }); + } catch (error) { + console.error('[ChatWorkspace] 保存对话标题失败:', error); + } } - }, [inputValue, isStreaming, currentConversationId, agent.id, deepThinkingEnabled, conversations, sendStreamMessage, createConversation]); + }, [inputValue, isStreaming, currentConversationId, agent.id, deepThinkingEnabled, conversations, sendStreamMessage, createConversation, attachments]); // 处理键盘事件 const handleKeyDown = useCallback((e: React.KeyboardEvent) => { @@ -261,6 +399,165 @@ export const ChatWorkspace: React.FC = ({ setDeepThinkingEnabled(prev => !prev); }, []); + // 触发文件选择 + const handleAttachmentClick = useCallback(() => { + fileInputRef.current?.click(); + }, []); + + // 处理文件上传 + const handleFileChange = useCallback(async (e: React.ChangeEvent) => { + const files = e.target.files; + if (!files || files.length === 0) return; + + // 确保有对话 ID + let convId = currentConversationId; + if (!convId) { + convId = await createConversation(); + if (!convId) { + console.error('[ChatWorkspace] 创建对话失败,无法上传附件'); + return; + } + } + + const file = files[0]; + const allowedTypes = ['.pdf', '.docx', '.doc', '.txt', '.xlsx']; + const ext = '.' + file.name.split('.').pop()?.toLowerCase(); + + if (!allowedTypes.includes(ext)) { + alert(`不支持的文件类型: ${ext}\n支持的类型: ${allowedTypes.join(', ')}`); + return; + } + + if (file.size > 20 * 1024 * 1024) { + alert('文件大小不能超过 20MB'); + return; + } + + // 添加到附件列表(上传中状态) + const tempId = `temp-${Date.now()}`; + setAttachments(prev => [...prev, { id: tempId, name: file.name, size: file.size, uploading: true }]); + setIsUploading(true); + + try { + const token = getAccessToken(); + const formData = new FormData(); + formData.append('file', file); + + const response = await fetch(`/api/v1/aia/conversations/${convId}/attachments`, { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + }, + body: formData, + }); + + if (!response.ok) { + throw new Error(`上传失败: ${response.status}`); + } + + const result = await response.json(); + + // 更新附件状态 + setAttachments(prev => + prev.map(a => a.id === tempId + ? { id: result.data.id, name: file.name, size: file.size, uploading: false } + : a + ) + ); + } catch (error) { + console.error('[ChatWorkspace] 上传附件失败:', error); + // 移除失败的附件 + setAttachments(prev => prev.filter(a => a.id !== tempId)); + alert('附件上传失败,请重试'); + } finally { + setIsUploading(false); + // 清空 input,允许重复选择同一文件 + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + } + }, [currentConversationId, createConversation]); + + // 移除附件 + const removeAttachment = useCallback((attachmentId: string) => { + setAttachments(prev => prev.filter(a => a.id !== attachmentId)); + }, []); + + // 开始编辑对话标题 + const startEditConversation = useCallback((convId: string, currentTitle: string, e: React.MouseEvent) => { + e.stopPropagation(); + setEditingConvId(convId); + setEditingTitle(currentTitle); + }, []); + + // 保存对话标题 + const saveConversationTitle = useCallback(async (convId: string) => { + if (!editingTitle.trim()) { + setEditingConvId(null); + return; + } + + try { + const token = getAccessToken(); + const response = await fetch(`/api/v1/aia/conversations/${convId}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ title: editingTitle.trim() }), + }); + + if (response.ok) { + setConversations(prev => + prev.map(c => c.id === convId ? { ...c, title: editingTitle.trim() } : c) + ); + } + } catch (error) { + console.error('[ChatWorkspace] 更新对话标题失败:', error); + } finally { + setEditingConvId(null); + } + }, [editingTitle]); + + // 删除对话 + const deleteConversation = useCallback(async (convId: string, e: React.MouseEvent) => { + e.stopPropagation(); + + if (!confirm('确定删除这个对话吗?')) { + return; + } + + try { + const token = getAccessToken(); + const response = await fetch(`/api/v1/aia/conversations/${convId}`, { + method: 'DELETE', + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + if (response.ok) { + setConversations(prev => prev.filter(c => c.id !== convId)); + + // 如果删除的是当前对话,切换到其他对话或创建新对话 + if (currentConversationId === convId) { + const remaining = conversations.filter(c => c.id !== convId); + if (remaining.length > 0) { + setCurrentConversationId(remaining[0].id); + setMessages([]); + } else { + setCurrentConversationId(null); + setMessages([]); + createConversation(); + } + } + } + } catch (error) { + console.error('[ChatWorkspace] 删除对话失败:', error); + } + }, [currentConversationId, conversations, createConversation]); + return (
{/* 移动端遮罩 */} @@ -292,22 +589,64 @@ export const ChatWorkspace: React.FC = ({ {/* 历史记录 */}
-
Today
+
历史对话
{conversations.map(conv => ( - + {editingConvId === conv.id ? ( +
+ setEditingTitle(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') saveConversationTitle(conv.id); + if (e.key === 'Escape') setEditingConvId(null); + }} + onClick={(e) => e.stopPropagation()} + autoFocus + className="history-edit-input" + /> + +
+ ) : ( + <> + {conv.title} +
+ + +
+ + )} +
))}
@@ -393,16 +732,33 @@ export const ChatWorkspace: React.FC = ({ /> )} - {/* 消息内容 */} -
- {msg.content} - {msg.role === 'assistant' && isStreaming && index === messages.length - 1 && ( - - )} -
+ {/* 用户消息的附件显示(使用 FileCard,类似 DeepSeek) */} + {msg.role === 'user' && msg.attachments && msg.attachments.length > 0 && ( +
+ {msg.attachments.map(att => ( + + ))} +
+ )} + + {/* 消息内容(有内容时才显示气泡) */} + {(msg.content || (msg.role === 'assistant' && isStreaming && index === messages.length - 1)) && ( +
+ {msg.content} + {msg.role === 'assistant' && isStreaming && index === messages.length - 1 && ( + + )} +
+ )} - {msg.role === 'user' && ( + {msg.role === 'user' && (msg.content || (msg.attachments && msg.attachments.length > 0)) && (
U
)} @@ -413,18 +769,62 @@ export const ChatWorkspace: React.FC = ({ {/* 输入区域(靠下) */}
- {/* 深度思考按钮 */} -
- - + {/* 工具栏:深度思考 + 附件按钮 + 已上传附件(同一行) */} +
+
+ + +
+ + {/* 已上传附件(紧凑显示,与按钮同行) */} + {attachments.length > 0 && ( +
+ {attachments.map(att => ( +
+ {getFileIcon(att.name) === 'pdf' ? '📄' : getFileIcon(att.name) === 'word' ? '📝' : getFileIcon(att.name) === 'excel' ? '📊' : '📎'} + + {att.name.length > 15 ? att.name.slice(0, 12) + '...' + att.name.slice(-4) : att.name} + + {att.uploading ? ( + + ) : ( + + )} +
+ ))} +
+ )} + + {/* 隐藏的文件输入 */} +
{/* 输入框 */} diff --git a/frontend-v2/src/modules/aia/components/index.ts b/frontend-v2/src/modules/aia/components/index.ts index 2ab328e6..b2fff91d 100644 --- a/frontend-v2/src/modules/aia/components/index.ts +++ b/frontend-v2/src/modules/aia/components/index.ts @@ -8,3 +8,6 @@ export { ChatWorkspace } from './ChatWorkspace'; + + + diff --git a/frontend-v2/src/modules/aia/constants.ts b/frontend-v2/src/modules/aia/constants.ts index 3a433419..fa247a42 100644 --- a/frontend-v2/src/modules/aia/constants.ts +++ b/frontend-v2/src/modules/aia/constants.ts @@ -172,3 +172,6 @@ export const BRAND_COLORS = { + + + diff --git a/frontend-v2/src/modules/aia/styles/agent-card.css b/frontend-v2/src/modules/aia/styles/agent-card.css index fac20cea..baabf420 100644 --- a/frontend-v2/src/modules/aia/styles/agent-card.css +++ b/frontend-v2/src/modules/aia/styles/agent-card.css @@ -210,3 +210,6 @@ + + + diff --git a/frontend-v2/src/modules/aia/styles/chat-workspace.css b/frontend-v2/src/modules/aia/styles/chat-workspace.css index 3cab6f68..1cfb78af 100644 --- a/frontend-v2/src/modules/aia/styles/chat-workspace.css +++ b/frontend-v2/src/modules/aia/styles/chat-workspace.css @@ -152,6 +152,10 @@ background: none; border: none; border-left: 2px solid transparent; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; border-radius: 8px; font-size: 12px; color: #64748b; @@ -174,6 +178,85 @@ border-left-color: #4F6EF2; } +/* 历史记录标题 */ +.history-item-title { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: left; +} + +/* 历史记录操作按钮 */ +.history-item-actions { + display: none; + align-items: center; + gap: 4px; + flex-shrink: 0; +} + +.history-item:hover .history-item-actions { + display: flex; +} + +.history-action-btn { + padding: 4px; + background: none; + border: none; + color: #94a3b8; + cursor: pointer; + border-radius: 4px; + transition: all 0.2s; + display: flex; + align-items: center; + justify-content: center; +} + +.history-action-btn:hover { + background-color: #e2e8f0; + color: #475569; +} + +.history-action-btn.delete:hover { + background-color: #fee2e2; + color: #ef4444; +} + +/* 历史记录编辑模式 */ +.history-item-edit { + display: flex; + align-items: center; + gap: 6px; + width: 100%; +} + +.history-edit-input { + flex: 1; + padding: 4px 8px; + border: 1px solid #4F6EF2; + border-radius: 4px; + font-size: 12px; + outline: none; + background: white; +} + +.history-edit-save { + padding: 4px; + background: #4F6EF2; + border: none; + color: white; + cursor: pointer; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; +} + +.history-edit-save:hover { + background: #3b5fe0; +} + /* 用户信息 */ .sidebar-user { padding: 16px; @@ -495,7 +578,103 @@ border-top: 1px solid #f1f5f9; } -/* 工具栏 */ +/* 工具栏(新版:按钮+附件同一行) */ +.input-toolbar-row { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 12px; + overflow-x: auto; + scrollbar-width: thin; + scrollbar-color: #e2e8f0 transparent; + padding-bottom: 4px; +} + +.input-toolbar-row::-webkit-scrollbar { + height: 4px; +} + +.input-toolbar-row::-webkit-scrollbar-track { + background: transparent; +} + +.input-toolbar-row::-webkit-scrollbar-thumb { + background-color: #e2e8f0; + border-radius: 2px; +} + +.toolbar-buttons { + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; +} + +/* 工具栏中的附件列表 */ +.toolbar-attachments { + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; +} + +/* 紧凑型附件卡片 */ +.toolbar-attachment-chip { + display: flex; + align-items: center; + gap: 6px; + padding: 4px 10px; + background: #f8fafc; + border: 1px solid #e2e8f0; + border-radius: 8px; + font-size: 12px; + color: #475569; + white-space: nowrap; + transition: all 0.2s; +} + +.toolbar-attachment-chip:hover { + background: #f1f5f9; + border-color: #cbd5e1; +} + +.toolbar-attachment-chip.uploading { + opacity: 0.7; +} + +.toolbar-attachment-chip .chip-icon { + font-size: 14px; +} + +.toolbar-attachment-chip .chip-name { + max-width: 120px; + overflow: hidden; + text-overflow: ellipsis; +} + +.toolbar-attachment-chip .chip-loading { + font-size: 12px; +} + +.toolbar-attachment-chip .chip-remove { + display: flex; + align-items: center; + justify-content: center; + padding: 2px; + background: none; + border: none; + color: #94a3b8; + cursor: pointer; + border-radius: 4px; + transition: all 0.2s; +} + +.toolbar-attachment-chip .chip-remove:hover { + background: #e2e8f0; + color: #ef4444; +} + +/* 旧版工具栏(保留兼容) */ .input-toolbar { display: flex; align-items: center; @@ -544,6 +723,91 @@ color: #64748b; } +.attachment-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.attachment-btn.has-attachments { + color: #4F6EF2; + position: relative; +} + +.attachment-count { + position: absolute; + top: -2px; + right: -2px; + background-color: #4F6EF2; + color: white; + font-size: 10px; + min-width: 14px; + height: 14px; + border-radius: 7px; + display: flex; + align-items: center; + justify-content: center; + padding: 0 3px; +} + +/* 附件预览区域 */ +.attachments-preview { + display: flex; + flex-wrap: wrap; + gap: 8px; + padding: 8px 16px; + max-width: 1024px; + margin: 0 auto; +} + +.attachment-chip { + display: flex; + align-items: center; + gap: 6px; + padding: 6px 10px; + background-color: #f1f5f9; + border: 1px solid #e2e8f0; + border-radius: 8px; + font-size: 12px; + color: #475569; +} + +.attachment-chip.uploading { + opacity: 0.6; +} + +.attachment-name { + max-width: 150px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.uploading-spinner { + animation: spin 1s linear infinite; +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +.remove-attachment { + padding: 2px; + background: none; + border: none; + color: #94a3b8; + cursor: pointer; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; +} + +.remove-attachment:hover { + background-color: #fee2e2; + color: #ef4444; +} + /* 输入框 */ .input-box { position: relative; @@ -642,3 +906,43 @@ background-color: #94a3b8; } +/* === 消息中的附件显示 === */ +.message-attachments { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-bottom: 8px; + padding-bottom: 8px; + border-bottom: 1px solid rgba(255, 255, 255, 0.2); +} + +/* FileCard 在消息中的显示样式 */ +.message-file-cards { + display: flex; + flex-direction: column; + gap: 8px; + margin-bottom: 8px; + max-width: 320px; +} + +.message-item.user .message-file-cards { + margin-left: auto; +} + +/* 自定义 FileCard 样式以适配对话场景 */ +.message-file-cards :global(.ant-file-card) { + background: rgba(255, 255, 255, 0.95); + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); +} + +/* 附件预览区域的 FileCard.List 样式 */ +.attachments-preview :global(.ant-file-card-list) { + gap: 8px; +} + +.attachments-preview :global(.ant-file-card) { + background: rgba(255, 255, 255, 0.95); + border-radius: 8px; +} + diff --git a/frontend-v2/src/modules/aia/types.ts b/frontend-v2/src/modules/aia/types.ts index 920a7fee..84b52950 100644 --- a/frontend-v2/src/modules/aia/types.ts +++ b/frontend-v2/src/modules/aia/types.ts @@ -48,6 +48,15 @@ export interface Conversation { updatedAt: Date; } +/** + * 消息附件(简化版,用于消息显示) + */ +export interface MessageAttachment { + id: string; + filename: string; + size: number; +} + /** * 消息 */ @@ -56,8 +65,11 @@ export interface Message { role: 'user' | 'assistant'; content: string; thinking?: string; + attachments?: MessageAttachment[]; // 用户消息的附件 createdAt: Date; } + + diff --git a/frontend-v2/src/modules/asl/components/FulltextDetailDrawer.tsx b/frontend-v2/src/modules/asl/components/FulltextDetailDrawer.tsx index 2caa20b4..08a6a89a 100644 --- a/frontend-v2/src/modules/asl/components/FulltextDetailDrawer.tsx +++ b/frontend-v2/src/modules/asl/components/FulltextDetailDrawer.tsx @@ -565,6 +565,9 @@ export default FulltextDetailDrawer; + + + diff --git a/frontend-v2/src/modules/dc/hooks/useAssets.ts b/frontend-v2/src/modules/dc/hooks/useAssets.ts index d76adf6b..b590e6f1 100644 --- a/frontend-v2/src/modules/dc/hooks/useAssets.ts +++ b/frontend-v2/src/modules/dc/hooks/useAssets.ts @@ -158,6 +158,9 @@ export const useAssets = (activeTab: AssetTabType) => { + + + diff --git a/frontend-v2/src/modules/dc/hooks/useRecentTasks.ts b/frontend-v2/src/modules/dc/hooks/useRecentTasks.ts index 581fb67d..40d832a2 100644 --- a/frontend-v2/src/modules/dc/hooks/useRecentTasks.ts +++ b/frontend-v2/src/modules/dc/hooks/useRecentTasks.ts @@ -148,6 +148,9 @@ export const useRecentTasks = () => { + + + diff --git a/frontend-v2/src/modules/dc/pages/tool-c/components/DropnaDialog.tsx b/frontend-v2/src/modules/dc/pages/tool-c/components/DropnaDialog.tsx index 2d179bf6..59b9fc09 100644 --- a/frontend-v2/src/modules/dc/pages/tool-c/components/DropnaDialog.tsx +++ b/frontend-v2/src/modules/dc/pages/tool-c/components/DropnaDialog.tsx @@ -347,6 +347,9 @@ export default DropnaDialog; + + + diff --git a/frontend-v2/src/modules/dc/pages/tool-c/components/MetricTimePanel.tsx b/frontend-v2/src/modules/dc/pages/tool-c/components/MetricTimePanel.tsx index 33da5edd..1d02e56f 100644 --- a/frontend-v2/src/modules/dc/pages/tool-c/components/MetricTimePanel.tsx +++ b/frontend-v2/src/modules/dc/pages/tool-c/components/MetricTimePanel.tsx @@ -432,6 +432,9 @@ export default MetricTimePanel; + + + diff --git a/frontend-v2/src/modules/dc/pages/tool-c/components/PivotPanel.tsx b/frontend-v2/src/modules/dc/pages/tool-c/components/PivotPanel.tsx index 8f9a78ee..8d06f5b8 100644 --- a/frontend-v2/src/modules/dc/pages/tool-c/components/PivotPanel.tsx +++ b/frontend-v2/src/modules/dc/pages/tool-c/components/PivotPanel.tsx @@ -318,6 +318,9 @@ export default PivotPanel; + + + diff --git a/frontend-v2/src/modules/dc/pages/tool-c/hooks/useSessionStatus.ts b/frontend-v2/src/modules/dc/pages/tool-c/hooks/useSessionStatus.ts index efec953e..aab0ec37 100644 --- a/frontend-v2/src/modules/dc/pages/tool-c/hooks/useSessionStatus.ts +++ b/frontend-v2/src/modules/dc/pages/tool-c/hooks/useSessionStatus.ts @@ -118,6 +118,9 @@ export function useSessionStatus({ + + + diff --git a/frontend-v2/src/modules/dc/pages/tool-c/types/index.ts b/frontend-v2/src/modules/dc/pages/tool-c/types/index.ts index bc77ed3c..cf786e8b 100644 --- a/frontend-v2/src/modules/dc/pages/tool-c/types/index.ts +++ b/frontend-v2/src/modules/dc/pages/tool-c/types/index.ts @@ -110,6 +110,9 @@ export interface DataStats { + + + diff --git a/frontend-v2/src/modules/dc/types/portal.ts b/frontend-v2/src/modules/dc/types/portal.ts index 9d7317c5..87580bed 100644 --- a/frontend-v2/src/modules/dc/types/portal.ts +++ b/frontend-v2/src/modules/dc/types/portal.ts @@ -106,6 +106,9 @@ export type AssetTabType = 'all' | 'processed' | 'raw'; + + + diff --git a/frontend-v2/src/modules/pkb/pages/KnowledgePage.tsx b/frontend-v2/src/modules/pkb/pages/KnowledgePage.tsx index c63f0fa5..06bd19fc 100644 --- a/frontend-v2/src/modules/pkb/pages/KnowledgePage.tsx +++ b/frontend-v2/src/modules/pkb/pages/KnowledgePage.tsx @@ -297,3 +297,6 @@ export default KnowledgePage; + + + diff --git a/frontend-v2/src/modules/pkb/types/workspace.ts b/frontend-v2/src/modules/pkb/types/workspace.ts index 2f283bbe..1d2ce68b 100644 --- a/frontend-v2/src/modules/pkb/types/workspace.ts +++ b/frontend-v2/src/modules/pkb/types/workspace.ts @@ -52,3 +52,6 @@ export interface BatchTemplate { + + + diff --git a/frontend-v2/src/modules/rvw/components/AgentModal.tsx b/frontend-v2/src/modules/rvw/components/AgentModal.tsx index b6765ebd..9462435a 100644 --- a/frontend-v2/src/modules/rvw/components/AgentModal.tsx +++ b/frontend-v2/src/modules/rvw/components/AgentModal.tsx @@ -130,3 +130,6 @@ export default function AgentModal({ visible, taskCount, onClose, onConfirm }: A + + + diff --git a/frontend-v2/src/modules/rvw/components/BatchToolbar.tsx b/frontend-v2/src/modules/rvw/components/BatchToolbar.tsx index 6e9c01f2..290f8391 100644 --- a/frontend-v2/src/modules/rvw/components/BatchToolbar.tsx +++ b/frontend-v2/src/modules/rvw/components/BatchToolbar.tsx @@ -50,3 +50,6 @@ export default function BatchToolbar({ selectedCount, onRunBatch, onClearSelecti + + + diff --git a/frontend-v2/src/modules/rvw/components/FilterChips.tsx b/frontend-v2/src/modules/rvw/components/FilterChips.tsx index 0649d122..5ec7370b 100644 --- a/frontend-v2/src/modules/rvw/components/FilterChips.tsx +++ b/frontend-v2/src/modules/rvw/components/FilterChips.tsx @@ -73,3 +73,6 @@ export default function FilterChips({ filters, counts, onFilterChange }: FilterC + + + diff --git a/frontend-v2/src/modules/rvw/components/Header.tsx b/frontend-v2/src/modules/rvw/components/Header.tsx index 8f00c82b..c82c4f0b 100644 --- a/frontend-v2/src/modules/rvw/components/Header.tsx +++ b/frontend-v2/src/modules/rvw/components/Header.tsx @@ -63,3 +63,6 @@ export default function Header({ onUpload }: HeaderProps) { + + + diff --git a/frontend-v2/src/modules/rvw/components/ReportDetail.tsx b/frontend-v2/src/modules/rvw/components/ReportDetail.tsx index d935bca6..b0d26ff3 100644 --- a/frontend-v2/src/modules/rvw/components/ReportDetail.tsx +++ b/frontend-v2/src/modules/rvw/components/ReportDetail.tsx @@ -117,3 +117,6 @@ export default function ReportDetail({ report, onBack }: ReportDetailProps) { + + + diff --git a/frontend-v2/src/modules/rvw/components/ScoreRing.tsx b/frontend-v2/src/modules/rvw/components/ScoreRing.tsx index 413789bb..95ffc66e 100644 --- a/frontend-v2/src/modules/rvw/components/ScoreRing.tsx +++ b/frontend-v2/src/modules/rvw/components/ScoreRing.tsx @@ -45,3 +45,6 @@ export default function ScoreRing({ score, size = 'medium', showLabel = true }: + + + diff --git a/frontend-v2/src/modules/rvw/components/Sidebar.tsx b/frontend-v2/src/modules/rvw/components/Sidebar.tsx index 33a2bff5..367eec7a 100644 --- a/frontend-v2/src/modules/rvw/components/Sidebar.tsx +++ b/frontend-v2/src/modules/rvw/components/Sidebar.tsx @@ -80,3 +80,6 @@ export default function Sidebar({ currentView, onViewChange, onSettingsClick }: + + + diff --git a/frontend-v2/src/modules/rvw/components/index.ts b/frontend-v2/src/modules/rvw/components/index.ts index 50f9c1b3..ed62a6ad 100644 --- a/frontend-v2/src/modules/rvw/components/index.ts +++ b/frontend-v2/src/modules/rvw/components/index.ts @@ -22,3 +22,6 @@ export { default as TaskDetail } from './TaskDetail'; + + + diff --git a/frontend-v2/src/modules/rvw/pages/Dashboard.tsx b/frontend-v2/src/modules/rvw/pages/Dashboard.tsx index 3377e9be..6f872f41 100644 --- a/frontend-v2/src/modules/rvw/pages/Dashboard.tsx +++ b/frontend-v2/src/modules/rvw/pages/Dashboard.tsx @@ -291,3 +291,6 @@ export default function Dashboard() { + + + diff --git a/frontend-v2/src/modules/rvw/styles/index.css b/frontend-v2/src/modules/rvw/styles/index.css index 0ca31231..1bb01665 100644 --- a/frontend-v2/src/modules/rvw/styles/index.css +++ b/frontend-v2/src/modules/rvw/styles/index.css @@ -240,3 +240,6 @@ + + + diff --git a/frontend-v2/src/pages/admin/tenants/TenantListPage.tsx b/frontend-v2/src/pages/admin/tenants/TenantListPage.tsx index bbf874c2..487afa3f 100644 --- a/frontend-v2/src/pages/admin/tenants/TenantListPage.tsx +++ b/frontend-v2/src/pages/admin/tenants/TenantListPage.tsx @@ -341,3 +341,6 @@ export default TenantListPage; + + + diff --git a/frontend-v2/src/pages/admin/tenants/api/tenantApi.ts b/frontend-v2/src/pages/admin/tenants/api/tenantApi.ts index df8e4beb..b149453c 100644 --- a/frontend-v2/src/pages/admin/tenants/api/tenantApi.ts +++ b/frontend-v2/src/pages/admin/tenants/api/tenantApi.ts @@ -250,3 +250,6 @@ export async function fetchModuleList(): Promise { + + + diff --git a/frontend-v2/src/shared/components/Chat/AIStreamChat.tsx b/frontend-v2/src/shared/components/Chat/AIStreamChat.tsx index b384501a..f5b8a526 100644 --- a/frontend-v2/src/shared/components/Chat/AIStreamChat.tsx +++ b/frontend-v2/src/shared/components/Chat/AIStreamChat.tsx @@ -469,3 +469,6 @@ export default AIStreamChat; + + + diff --git a/frontend-v2/src/shared/components/Chat/ConversationList.tsx b/frontend-v2/src/shared/components/Chat/ConversationList.tsx index 70f49398..40f261d1 100644 --- a/frontend-v2/src/shared/components/Chat/ConversationList.tsx +++ b/frontend-v2/src/shared/components/Chat/ConversationList.tsx @@ -169,3 +169,6 @@ export default ConversationList; + + + diff --git a/frontend-v2/src/shared/components/Chat/hooks/index.ts b/frontend-v2/src/shared/components/Chat/hooks/index.ts index 366e963e..45e2258a 100644 --- a/frontend-v2/src/shared/components/Chat/hooks/index.ts +++ b/frontend-v2/src/shared/components/Chat/hooks/index.ts @@ -21,3 +21,6 @@ export type { + + + diff --git a/frontend-v2/src/shared/components/Chat/hooks/useAIStream.ts b/frontend-v2/src/shared/components/Chat/hooks/useAIStream.ts index 42e0efc0..7c5701a7 100644 --- a/frontend-v2/src/shared/components/Chat/hooks/useAIStream.ts +++ b/frontend-v2/src/shared/components/Chat/hooks/useAIStream.ts @@ -313,3 +313,6 @@ export default useAIStream; + + + diff --git a/frontend-v2/src/shared/components/Chat/hooks/useConversations.ts b/frontend-v2/src/shared/components/Chat/hooks/useConversations.ts index 072892cf..ff4b5c2d 100644 --- a/frontend-v2/src/shared/components/Chat/hooks/useConversations.ts +++ b/frontend-v2/src/shared/components/Chat/hooks/useConversations.ts @@ -242,3 +242,6 @@ export default useConversations; + + + diff --git a/frontend-v2/src/shared/components/Chat/styles/ai-stream-chat.css b/frontend-v2/src/shared/components/Chat/styles/ai-stream-chat.css index a49619f5..26890f30 100644 --- a/frontend-v2/src/shared/components/Chat/styles/ai-stream-chat.css +++ b/frontend-v2/src/shared/components/Chat/styles/ai-stream-chat.css @@ -277,3 +277,6 @@ + + + diff --git a/frontend-v2/src/shared/components/Chat/styles/conversation-list.css b/frontend-v2/src/shared/components/Chat/styles/conversation-list.css index 881ae535..f1248eaf 100644 --- a/frontend-v2/src/shared/components/Chat/styles/conversation-list.css +++ b/frontend-v2/src/shared/components/Chat/styles/conversation-list.css @@ -213,3 +213,6 @@ + + + diff --git a/frontend-v2/src/shared/components/Chat/styles/thinking.css b/frontend-v2/src/shared/components/Chat/styles/thinking.css index 2f6033e2..00669e36 100644 --- a/frontend-v2/src/shared/components/Chat/styles/thinking.css +++ b/frontend-v2/src/shared/components/Chat/styles/thinking.css @@ -150,3 +150,6 @@ + + + diff --git a/frontend-v2/src/shared/components/index.ts b/frontend-v2/src/shared/components/index.ts index 35a63248..f6c83c6f 100644 --- a/frontend-v2/src/shared/components/index.ts +++ b/frontend-v2/src/shared/components/index.ts @@ -61,6 +61,9 @@ export { default as Placeholder } from './Placeholder'; + + + diff --git a/frontend-v2/src/vite-env.d.ts b/frontend-v2/src/vite-env.d.ts index a5a2d286..ee7e20e7 100644 --- a/frontend-v2/src/vite-env.d.ts +++ b/frontend-v2/src/vite-env.d.ts @@ -41,6 +41,9 @@ interface ImportMeta { + + + diff --git a/frontend/src/pages/rvw/components/BatchToolbar.tsx b/frontend/src/pages/rvw/components/BatchToolbar.tsx index 35121ef5..4ddcf290 100644 --- a/frontend/src/pages/rvw/components/BatchToolbar.tsx +++ b/frontend/src/pages/rvw/components/BatchToolbar.tsx @@ -52,3 +52,6 @@ export default function BatchToolbar({ selectedCount, onRunBatch, onClearSelecti + + + diff --git a/frontend/src/pages/rvw/components/EditorialReport.tsx b/frontend/src/pages/rvw/components/EditorialReport.tsx index 0ef642e8..ca3d5e02 100644 --- a/frontend/src/pages/rvw/components/EditorialReport.tsx +++ b/frontend/src/pages/rvw/components/EditorialReport.tsx @@ -117,3 +117,6 @@ export default function EditorialReport({ data }: EditorialReportProps) { + + + diff --git a/frontend/src/pages/rvw/components/FilterChips.tsx b/frontend/src/pages/rvw/components/FilterChips.tsx index c17205a3..5d9ad79b 100644 --- a/frontend/src/pages/rvw/components/FilterChips.tsx +++ b/frontend/src/pages/rvw/components/FilterChips.tsx @@ -75,3 +75,6 @@ export default function FilterChips({ filters, counts, onFilterChange }: FilterC + + + diff --git a/frontend/src/pages/rvw/components/Header.tsx b/frontend/src/pages/rvw/components/Header.tsx index d5ccb05d..bc160158 100644 --- a/frontend/src/pages/rvw/components/Header.tsx +++ b/frontend/src/pages/rvw/components/Header.tsx @@ -65,3 +65,6 @@ export default function Header({ onUpload }: HeaderProps) { + + + diff --git a/frontend/src/pages/rvw/components/ReportDetail.tsx b/frontend/src/pages/rvw/components/ReportDetail.tsx index 9325d8b8..486d7654 100644 --- a/frontend/src/pages/rvw/components/ReportDetail.tsx +++ b/frontend/src/pages/rvw/components/ReportDetail.tsx @@ -119,3 +119,6 @@ export default function ReportDetail({ report, onBack }: ReportDetailProps) { + + + diff --git a/frontend/src/pages/rvw/components/ScoreRing.tsx b/frontend/src/pages/rvw/components/ScoreRing.tsx index a5b4b67f..8c61d2f3 100644 --- a/frontend/src/pages/rvw/components/ScoreRing.tsx +++ b/frontend/src/pages/rvw/components/ScoreRing.tsx @@ -47,3 +47,6 @@ export default function ScoreRing({ score, size = 'medium', showLabel = true }: + + + diff --git a/frontend/src/pages/rvw/components/Sidebar.tsx b/frontend/src/pages/rvw/components/Sidebar.tsx index 18509bd5..8d5610d6 100644 --- a/frontend/src/pages/rvw/components/Sidebar.tsx +++ b/frontend/src/pages/rvw/components/Sidebar.tsx @@ -82,3 +82,6 @@ export default function Sidebar({ currentView, onViewChange, onSettingsClick }: + + + diff --git a/frontend/src/pages/rvw/index.ts b/frontend/src/pages/rvw/index.ts index 3b77f55e..52cfe7bb 100644 --- a/frontend/src/pages/rvw/index.ts +++ b/frontend/src/pages/rvw/index.ts @@ -16,3 +16,6 @@ export * from './api'; + + + diff --git a/frontend/src/pages/rvw/styles.css b/frontend/src/pages/rvw/styles.css index 7b3fb3b2..7bd17e6c 100644 --- a/frontend/src/pages/rvw/styles.css +++ b/frontend/src/pages/rvw/styles.css @@ -242,3 +242,6 @@ + + + diff --git a/git-cleanup-redcap.ps1 b/git-cleanup-redcap.ps1 index d026e747..89496411 100644 --- a/git-cleanup-redcap.ps1 +++ b/git-cleanup-redcap.ps1 @@ -34,6 +34,9 @@ Write-Host "Next step: Run the commit command" -ForegroundColor Cyan + + + diff --git a/git-commit-day1.ps1 b/git-commit-day1.ps1 index 54848c24..bab0d924 100644 --- a/git-commit-day1.ps1 +++ b/git-commit-day1.ps1 @@ -90,6 +90,9 @@ Write-Host "Git commit and push completed!" -ForegroundColor Green + + + diff --git a/git-fix-lock.ps1 b/git-fix-lock.ps1 index 9a70312b..e704fb3f 100644 --- a/git-fix-lock.ps1 +++ b/git-fix-lock.ps1 @@ -38,6 +38,9 @@ Write-Host "Now you can run git commands again." -ForegroundColor Cyan + + + diff --git a/python-microservice/operations/__init__.py b/python-microservice/operations/__init__.py index 3b048ac8..eae8e338 100644 --- a/python-microservice/operations/__init__.py +++ b/python-microservice/operations/__init__.py @@ -63,6 +63,9 @@ __version__ = '1.0.0' + + + diff --git a/python-microservice/operations/binning.py b/python-microservice/operations/binning.py index 56affed4..4757f37c 100644 --- a/python-microservice/operations/binning.py +++ b/python-microservice/operations/binning.py @@ -170,6 +170,9 @@ def apply_binning( + + + diff --git a/python-microservice/operations/filter.py b/python-microservice/operations/filter.py index 0e2b7d21..03eb8167 100644 --- a/python-microservice/operations/filter.py +++ b/python-microservice/operations/filter.py @@ -156,6 +156,9 @@ def apply_filter( + + + diff --git a/python-microservice/operations/recode.py b/python-microservice/operations/recode.py index 3c3165e1..b2492032 100644 --- a/python-microservice/operations/recode.py +++ b/python-microservice/operations/recode.py @@ -126,6 +126,9 @@ def apply_recode( + + + diff --git a/recover_dc_code.py b/recover_dc_code.py index 316365ac..2a5d7f5d 100644 --- a/recover_dc_code.py +++ b/recover_dc_code.py @@ -270,6 +270,9 @@ if __name__ == "__main__": + + + diff --git a/redcap-docker-dev/.gitattributes b/redcap-docker-dev/.gitattributes index b4a79ccb..9f6a4d3a 100644 --- a/redcap-docker-dev/.gitattributes +++ b/redcap-docker-dev/.gitattributes @@ -50,6 +50,9 @@ + + + diff --git a/redcap-docker-dev/.gitignore b/redcap-docker-dev/.gitignore index 7f52a168..56114b58 100644 --- a/redcap-docker-dev/.gitignore +++ b/redcap-docker-dev/.gitignore @@ -81,6 +81,9 @@ Desktop.ini + + + diff --git a/redcap-docker-dev/README.md b/redcap-docker-dev/README.md index 45973d07..5506e356 100644 --- a/redcap-docker-dev/README.md +++ b/redcap-docker-dev/README.md @@ -382,6 +382,9 @@ docker-compose -f docker-compose.prod.yml up -d + + + diff --git a/redcap-docker-dev/docker-compose.prod.yml b/redcap-docker-dev/docker-compose.prod.yml index 0ff0ee80..beefe430 100644 --- a/redcap-docker-dev/docker-compose.prod.yml +++ b/redcap-docker-dev/docker-compose.prod.yml @@ -143,6 +143,9 @@ volumes: + + + diff --git a/redcap-docker-dev/docker-compose.yml b/redcap-docker-dev/docker-compose.yml index 9701cb16..89f7b513 100644 --- a/redcap-docker-dev/docker-compose.yml +++ b/redcap-docker-dev/docker-compose.yml @@ -141,6 +141,9 @@ volumes: + + + diff --git a/redcap-docker-dev/env.template b/redcap-docker-dev/env.template index 595a0358..86d1daa8 100644 --- a/redcap-docker-dev/env.template +++ b/redcap-docker-dev/env.template @@ -77,6 +77,9 @@ PMA_UPLOAD_LIMIT=50M + + + diff --git a/redcap-docker-dev/scripts/clean-redcap.ps1 b/redcap-docker-dev/scripts/clean-redcap.ps1 index b725c1d1..e963774c 100644 --- a/redcap-docker-dev/scripts/clean-redcap.ps1 +++ b/redcap-docker-dev/scripts/clean-redcap.ps1 @@ -85,6 +85,9 @@ Write-Host "" + + + diff --git a/redcap-docker-dev/scripts/create-redcap-password.php b/redcap-docker-dev/scripts/create-redcap-password.php index 937a0161..568aa4d1 100644 --- a/redcap-docker-dev/scripts/create-redcap-password.php +++ b/redcap-docker-dev/scripts/create-redcap-password.php @@ -63,6 +63,9 @@ try { + + + diff --git a/redcap-docker-dev/scripts/logs-redcap.ps1 b/redcap-docker-dev/scripts/logs-redcap.ps1 index 661719e4..1d92b1d8 100644 --- a/redcap-docker-dev/scripts/logs-redcap.ps1 +++ b/redcap-docker-dev/scripts/logs-redcap.ps1 @@ -76,6 +76,9 @@ Write-Host "" + + + diff --git a/redcap-docker-dev/scripts/reset-admin-password.php b/redcap-docker-dev/scripts/reset-admin-password.php index 72457fb8..de386f5d 100644 --- a/redcap-docker-dev/scripts/reset-admin-password.php +++ b/redcap-docker-dev/scripts/reset-admin-password.php @@ -39,6 +39,9 @@ if ($result) { + + + diff --git a/redcap-docker-dev/scripts/start-redcap.ps1 b/redcap-docker-dev/scripts/start-redcap.ps1 index a004a1c7..f1b5bc1e 100644 --- a/redcap-docker-dev/scripts/start-redcap.ps1 +++ b/redcap-docker-dev/scripts/start-redcap.ps1 @@ -61,6 +61,9 @@ if ($LASTEXITCODE -eq 0) { + + + diff --git a/redcap-docker-dev/scripts/stop-redcap.ps1 b/redcap-docker-dev/scripts/stop-redcap.ps1 index a443315e..153b4cc1 100644 --- a/redcap-docker-dev/scripts/stop-redcap.ps1 +++ b/redcap-docker-dev/scripts/stop-redcap.ps1 @@ -47,6 +47,9 @@ if ($LASTEXITCODE -eq 0) { + + + diff --git a/run_recovery.ps1 b/run_recovery.ps1 index bebf3639..5f8a77c8 100644 --- a/run_recovery.ps1 +++ b/run_recovery.ps1 @@ -94,6 +94,9 @@ Write-Host "==================================================================== + + + diff --git a/tests/QUICKSTART_快速开始.md b/tests/QUICKSTART_快速开始.md index b055260c..7e0b1b74 100644 --- a/tests/QUICKSTART_快速开始.md +++ b/tests/QUICKSTART_快速开始.md @@ -141,6 +141,9 @@ INFO: Uvicorn running on http://0.0.0.0:8001 + + + diff --git a/tests/README_测试说明.md b/tests/README_测试说明.md index 21a178f4..e8cd7231 100644 --- a/tests/README_测试说明.md +++ b/tests/README_测试说明.md @@ -297,6 +297,9 @@ df_numeric.to_excel('test_data/numeric_test.xlsx', index=False) + + + diff --git a/tests/run_tests.bat b/tests/run_tests.bat index 25af6f58..b40473d2 100644 --- a/tests/run_tests.bat +++ b/tests/run_tests.bat @@ -92,6 +92,9 @@ pause + + + diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 57a93b78..9b5fee86 100644 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -88,6 +88,9 @@ echo "========================================" + + + diff --git a/快速部署到SAE.md b/快速部署到SAE.md index 30277a8c..e0a34f5b 100644 --- a/快速部署到SAE.md +++ b/快速部署到SAE.md @@ -353,6 +353,9 @@ OSS AccessKeySecret:_______________ + + + diff --git a/部署检查清单.md b/部署检查清单.md index 06a1605d..3246e35f 100644 --- a/部署检查清单.md +++ b/部署检查清单.md @@ -389,6 +389,9 @@ OSS配置: + + +