Files
AIclinicalresearch/docs/03-业务模块/DC-数据清洗整理/02-技术设计/技术设计文档:工具 A - 医疗数据超级合并器 (The Super Merger).md
HaHafeng d4d33528c7 feat(dc): Complete Phase 1 - Portal workbench page development
Summary:
- Implement DC module Portal page with 3 tool cards
- Create ToolCard component with decorative background and hover animations
- Implement TaskList component with table layout and progress bars
- Implement AssetLibrary component with tab switching and file cards
- Complete database verification (4 tables confirmed)
- Complete backend API verification (6 endpoints ready)
- Optimize UI to match prototype design (V2.html)

Frontend Components (~715 lines):
- components/ToolCard.tsx - Tool cards with animations
- components/TaskList.tsx - Recent tasks table view
- components/AssetLibrary.tsx - Data asset library with tabs
- hooks/useRecentTasks.ts - Task state management
- hooks/useAssets.ts - Asset state management
- pages/Portal.tsx - Main portal page
- types/portal.ts - TypeScript type definitions

Backend Verification:
- Backend API: 1495 lines code verified
- Database: dc_schema with 4 tables verified
- API endpoints: 6 endpoints tested (templates API works)

Documentation:
- Database verification report
- Backend API test report
- Phase 1 completion summary
- UI optimization report
- Development task checklist
- Development plan for Tool B

Status: Phase 1 completed (100%), ready for browser testing
Next: Phase 2 - Tool B Step 1 and 2 development
2025-12-02 21:53:24 +08:00

4.8 KiB
Raw Blame History

技术设计文档:工具 A - 医疗数据超级合并器 (The Super Merger)

文档类型 Technical Design Document (TDD)
对应 PRD PRD_工具A_超级合并器_V2.md
版本 V2.0 (架构升级:访视基准 + 时间窗)
状态 Draft
核心目标 构建一个基于 Web 的 ETL 工具,解决临床科研中“一对多”数据对齐难题,实现基于时间窗的精准合并。

1. 总体架构设计 (Architecture Overview)

鉴于处理 Excel 文件(解析、合并、写入)是 CPU 密集型和内存敏感型操作,为了避免阻塞 Node.js 主线程,我们采用 “异步任务队列 + 流式处理” 的架构模式。

1.1 系统架构图

graph TD
Client[React 前端 (Wizard UI)]

subgraph API\_Server \[Fastify API 服务\]  
    UploadAPI\[上传接口\]  
    TaskAPI\[任务状态接口\]  
    ConfigAPI\[配置接口\]  
end  
  
subgraph Async\_Worker \[后台处理 Worker\]  
    BullMQ\[BullMQ 队列\]  
    Merger\[智能合并引擎 (Time-Window Joiner)\]  
    ExcelParser\[ExcelJS 解析器\]  
    DateEngine\[日期归一化引擎\]  
end  
  
subgraph Storage \[数据存储\]  
    PG\[(PostgreSQL 业务库)\]  
    FileSys\[临时文件存储 (Local/S3)\]  
    Redis\[(Redis 缓存/队列)\]  
end

Client \--1.上传文件--\> UploadAPI  
UploadAPI \--保存临时文件--\> FileSys  
Client \--2.提交基准与时间窗配置--\> ConfigAPI  
ConfigAPI \--创建任务--\> PG  
ConfigAPI \--推入队列--\> BullMQ  
BullMQ \--消费任务--\> Merger  
Merger \--读取辅表(全量)--\> FileSys  
Merger \--读取主表(流式)--\> FileSys  
Merger \--流式合并与写入--\> FileSys  
Merger \--更新状态--\> PG  
Client \--3.轮询/WS 进度--\> TaskAPI  
Client \--4.下载结果--\> API\_Server

2. 技术选型 (Tech Stack)

基于现有技术栈的针对性选择:

层级 技术组件 选型理由
前端 React 19 + Ant Design 5 利用 AntD 的 Steps, Upload, Tree (树状选择器) 快速构建 UI。
后端框架 Fastify 5.x 高性能 HTTP 框架,适合高并发 I/O。
Excel 处理 ExcelJS 核心组件。支持流式读写 (Streaming I/O),这是处理大数据量不崩的关键。
日期处理 Day.js + CustomParseFormat 新增。处理“时间地狱”的核心库,需要极强的容错解析能力。
任务队列 BullMQ + Redis 必须异步处理。合并逻辑复杂,耗时较长,必须用队列。
数据库 PostgreSQL 15 + Prisma 存储任务状态、文件元数据。不建议将原始 Excel 数据存入 PG
验证库 Zod 用于校验前端提交的复杂映射配置结构。

2.1 关键技术决策 (ADR): 为什么不用 Python (Pandas)?

虽然 Python Pandas 在数据合并上代码更简洁,但针对本工具的场景,我们决定坚持使用 Node.js,理由如下:

  1. 流式处理优势: Pandas 倾向于全量加载内存,容易 OOM。Node.js 的 Stream API 天然支持背压,能稳定处理“数据膨胀”问题。
  2. 架构一致性: 避免引入 Python Runtime 带来的运维成本和 IPC 开销。
  3. 结论: 对于精确匹配和逻辑清洗Node.js 性能足够且更可控。

3. 数据库设计 (Database Schema)

Prisma Schema 定义

// 任务状态枚举
enum TaskStatus {
PENDING
PROCESSING
COMPLETED
FAILED
}

// 合并任务表
model MergeTask {
id String @id @default(uuid())
userId String
status TaskStatus @default(PENDING)
progress Int @default(0)

// 核心配置字段 (V2 更新)
// 结构: {
// anchorFileId: string,
// anchorKeys: { id: "住院号", time: "入院日期" },
// window: { daysBefore: 7, daysAfter: 7 },
// files: [{ id: "f2", timeCol: "报告时间", columns: ["白细胞"] }]
// }
config Json?

resultUrl String?
report Json? // 质量报告 { totalRows: 1000, dropped: 50, matchRate: "95%" }
errorMsg String?
createdAt DateTime @default(now())

files SourceFile[]
}

// 源文件表
model SourceFile {
id String @id @default(uuid())
taskId String
task MergeTask @relation(fields: [taskId], references: [id])
filename String
filepath String
headers Json // ["住院号", "姓名", "入院日期"]
rowCount Int
fileSize Int
uploadedAt DateTime @default(now())
}