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
This commit is contained in:
2025-12-02 21:53:24 +08:00
parent f240aa9236
commit d4d33528c7
83 changed files with 21863 additions and 1601 deletions

View File

@@ -0,0 +1,131 @@
# **PRDTool A \- 医疗数据超级合并器 (The Super Merger)**
| 文档版本 | V2.0 (基准锚定版) |
| :---- | :---- |
| **产品形态** | Web 端工具(分步向导式 Wizard |
| **核心价值** | **解决临床科研中“一对多”数据对齐难题。** 基于“访视Visit”和“时间窗”逻辑将散乱的化验、检查数据精准挂载到住院/门诊记录上。 |
| **目标用户** | 临床医生、科研助理 |
## **一、 产品流程图 (User Flow)**
1\. 数据装载 (Payload) \-\> 2\. 定基准 (Anchor & Window) \-\> 3\. 选列与预览 (Schema) \-\> 4\. 智能合并 \-\> 5\. 结果与流转
## **二、 核心功能需求 (Functional Requirements)**
### **1\. 步骤一:数据装载 (Payload)**
* **P0:** **多文件上传:** 支持拖拽上传 .xlsx, .csv。建议大小 \< 50MB/文件。
* **P0:** **自动预检 (Pre-flight Check)**
* 上传即解析表头。
* **红灯拦截:** 文件加密、表头为空、文件损坏 \-\> 禁止下一步。
* **P1:** **加载配置/模板:** 若用户之前保存过“肺癌门诊合并规则”,允许一键加载,跳过后续配置。
### **2\. 步骤二:定基准 (The Anchor) —— 核心算法逻辑**
此步骤决定了最终大表的“骨架”(行数)和“归类逻辑”。
#### **2.1 主表选择 (The Backbone)**
* **P0:** 用户必须从上传的文件中指定一个作为 **“主表 (Visit Base)”**。
* **定义:** 主表的每一行,代表最终大表的一个基准行(一次就诊/访视)。通常是《住院记录》或《门诊挂号》。
#### **2.2 关键列映射 (Key Mapping)**
* **P0:** **ID 列对齐:** 用户需指定主表的 Patient\_ID 列。系统自动在辅表中寻找同名列,允许人工修正。
* **P0:** **时间基准列:** 用户需指定主表的 Date 列(如“入院日期”)。这将作为时间窗的圆心。
#### **2.3 辅表匹配策略 (Matching Strategy)**
* **P0:** **时间窗配置 (Time Window)**
* 设定规则:辅表的检查时间必须在 \[主表时间 \- X天, 主表时间 \+ Y天\] 范围内。
* **默认值:** $\\pm 7$ 天。
* **P0:** **冲突处理 (Collision Handling)**
* *场景:* 单次时间窗内,辅表有多条记录(如做了两次血常规)。
* *选项 A (默认 \- 纵向展开)* 保留所有记录。主表信息复制,行数增加。
* *选项 B (最近匹配)* 仅保留离主表时间最近的一条,丢弃其他。
### **3\. 步骤三:选列与预览 (The Schema)**
此步骤决定了最终大表的“血肉”(列宽)。
#### **3.1 树状选择器 (Tree Picker)**
* **P0:** 展示所有文件的列结构。
* 📂 主表
* ✅ 住院号
* ✅ 诊断
* 📂 辅表 A (化验)
* ✅ 白细胞
* ⬜ 审核医生 (不勾选)
* **交互:** 支持按文件全选/反选。
#### **3.2 实时结构预览 (Live Schema Preview)**
* **P0:** 在屏幕右侧实时展示\*\*“最终表头结构”\*\*(仅表头,不计算数据)。
* **价值:** 让用户直观看到:“哦,原来我的表会变成这么长,包含这些列”。
### **4\. 步骤四:结果与流转 (The Result)**
* **P0:** **处理进度条:** 显示动态文案(“正在构建哈希索引...”、“正在进行时间窗碰撞...”)。
* **P0:** **黄金预览 (Golden Preview)**
* 必须展示合并结果的 **前 5-10 行** 真实数据。
* 用于用户肉眼核对 ID 是否对齐。
* **P0:** **质量看板:**
* 成功生成行数。
* 丢弃行数ID不匹配或时间窗外
* **P0:** **行动流转:**
* \[下载 Excel\]
* \[发送到 AI 结构化工具\] (流转 Token)
* \[发送到 编辑器清洗\] (流转 Token)
## **三、 核心算法逻辑 (Technical Logic)**
### **1\. 算法名称:基于时间窗的哈希流式连接 (Time-Windowed Stream Hash Join)**
### **2\. 执行伪代码**
// 1\. 内存构建 (Build Phase)
// 将辅表 (Small Tables) 读入内存 Map
const lookupMap \= {
"patient\_001": \[
{ type: "lab", date: "2023-01-02", data: {...} }, // 记录1
{ type: "lab", date: "2023-05-01", data: {...} } // 记录2
\]
};
// 2\. 流式探测 (Probe Phase)
// 逐行读取主表 (Main Table)
streamMainTable.on('data', (mainRow) \=\> {
const pId \= mainRow.id;
const tStart \= mainRow.date \- 7 days;
const tEnd \= mainRow.date \+ 7 days;
// 在辅表中寻找符合时间窗的记录
const matchLabs \= lookupMap\[pId\].filter(lab \=\>
lab.date \>= tStart && lab.date \<= tEnd
);
if (matchLabs.length \=== 0\) {
// 没匹配到,只输出主表信息
output.write({ ...mainRow, lab\_data: null });
} else {
// 匹配到了 (处理一对多)
matchLabs.forEach(lab \=\> {
output.write({ ...mainRow, ...lab.data }); // 纵向展开
});
}
});
## **四、 界面原型参考 (UI Reference)**
请参考 工具A\_超级合并器\_原型设计.tsx (V1.0) 中的 Step 2 和 Step 3 界面。
* **Step 2 重点:** 主表选择单选框、时间列下拉框、策略单选钮。
* **Step 3 重点:** 左右分栏布局(左侧树状勾选,右侧表格骨架预览)。
## **五、 风险规避检查 (Risk Check)**
1. **用户选错主表怎么办?**
* *对策:* 在 Step 2 界面增加显眼的 **Tip**:“主表通常是包含‘入院日期’或‘诊断信息’的表格”。
2. **时间格式乱七八糟怎么办?**
* *对策:* 后端在解析时间列时,必须使用强力的 Parser支持 2023/1/1, 2023-01-01, 44927 等格式)。如果解析失败,归为“时间无效”,不进行匹配。