Files
AIclinicalresearch/docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD:Tool A - 医疗数据超级合并器 (The Super Merger).md
HaHafeng 66255368b7 feat(admin): Add user management and upgrade to module permission system
Features - User Management (Phase 4.1):
- Database: Add user_modules table for fine-grained module permissions
- Database: Add 4 user permissions (view/create/edit/delete) to role_permissions
- Backend: UserService (780 lines) - CRUD with tenant isolation
- Backend: UserController + UserRoutes (648 lines) - 13 API endpoints
- Backend: Batch import users from Excel
- Frontend: UserListPage (412 lines) - list/filter/search/pagination
- Frontend: UserFormPage (341 lines) - create/edit with module config
- Frontend: UserDetailPage (393 lines) - details/tenant/module management
- Frontend: 3 modal components (592 lines) - import/assign/configure
- API: GET/POST/PUT/DELETE /api/admin/users/* endpoints

Architecture Upgrade - Module Permission System:
- Backend: Add getUserModules() method in auth.service
- Backend: Login API returns modules array in user object
- Frontend: AuthContext adds hasModule() method
- Frontend: Navigation filters modules based on user.modules
- Frontend: RouteGuard checks requiredModule instead of requiredVersion
- Frontend: Remove deprecated version-based permission system
- UX: Only show accessible modules in navigation (clean UI)
- UX: Smart redirect after login (avoid 403 for regular users)

Fixes:
- Fix UTF-8 encoding corruption in ~100 docs files
- Fix pageSize type conversion in userService (String to Number)
- Fix authUser undefined error in TopNavigation
- Fix login redirect logic with role-based access check
- Update Git commit guidelines v1.2 with UTF-8 safety rules

Database Changes:
- CREATE TABLE user_modules (user_id, tenant_id, module_code, is_enabled)
- ADD UNIQUE CONSTRAINT (user_id, tenant_id, module_code)
- INSERT 4 permissions + role assignments
- UPDATE PUBLIC tenant with 8 module subscriptions

Technical:
- Backend: 5 new files (~2400 lines)
- Frontend: 10 new files (~2500 lines)
- Docs: 1 development record + 2 status updates + 1 guideline update
- Total: ~4900 lines of code

Status: User management 100% complete, module permission system operational
2026-01-16 13:42:10 +08:00

5.7 KiB
Raw Blame History

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 等格式)。如果解析失败,归为“时间无效”,不进行匹配。