# **技术设计文档:工具 C \- 科研数据编辑?(The Research Editor)** | 文档类型 | Technical Design Document (TDD) | | :---- | :---- | | **对应 PRD** | **PRD\_工具C\_科研数据编辑器\_V2.1.md** | | **版本** | **V2.1** (新增 Pivot 算法?Web Worker 架构) | | **状?* | Final Draft | | **核心目标** | 构建一个高性能?Web 端数据编辑器,支?5 万行级数据的实时清洗、变量加工(含长宽转换)与逻辑治理,提供“零延迟”操作体验?| ## **1\. 总体架构设计 (Architecture Overview)** 为了满足 **PRD V2.1** 中“即时反馈”、“撤销重做”以及复杂的“长宽转换”需求,工具 C 采用 **"Local-First" (本地优先)** 架构? 核心策略? 1. **数据驻留?* 数据加载后主要存储在浏览器的 **IndexedDB (Dexie.js)** ?**内存 (Zustand)** 中? 2. **计算下放?* 复杂的计算逻辑(如 Pivot、公式解析)下放?**Web Worker**,避免阻?UI 主线程? ### **1.1 系统架构?* graph TD subgraph Browser\_Layer \[浏览器端 (React SPA)\] UI\_Shell\[UI 壳层: 扁平?Toolbar \+ 智能 Sidebar\] subgraph Core\_Engine \[核心引擎\] GridComponent\[AG Grid (视图?\] StateManager\[Zustand Store (状态层)\] subgraph Worker\_Thread \[Web Worker 线程\] ComputeEngine\[计算引擎 (Math.js / Pivot Alg)\] StatEngine\[统计引擎 (直方?频次)\] end HistoryManager\[Immer Patches (撤销?\] end subgraph Local\_Storage \[持久化层\] Dexie\[Dexie.js (IndexedDB Wrapper)\] end end subgraph Server\_Layer \[服务?(Node.js)\] API\[Fastify API\] S3\[对象存储 (MinIO/OSS)\] end User \--1.操作(如Pivot)--\> UI\_Shell UI\_Shell \--2.发送消?postMessage)--\> Worker\_Thread Worker\_Thread \--3.计算结果--\> StateManager StateManager \--4.更新视图--\> GridComponent StateManager \--5.异步备份--\> Dexie User \--6.保存/导出--\> API ## **2\. 技术选型 (Tech Stack)** | 层级 | 技术组?| 选型理由 | | :---- | :---- | :---- | | **表格核心** | **AG Grid Community** | 唯一能免费支持虚拟滚动、列拖拽、高性能渲染?React 表格库?| | **本地数据?* | **Dexie.js (IndexedDB)** | 相比 localStorage (5MB限制),IndexedDB 容量大且异步,适合存储 5? 行的 JSON 数据集?| | **状态管?* | **Zustand \+ Immer** | Zustand 轻量高效;Immer 用于处理不可变数据结构,?produce ?patches 功能是实?Undo/Redo 的核心?| | **计算引擎** | **Math.js \+ Web Worker** | 解决 JS 浮点数精度问?(0.1+0.2\!=0.3);Web Worker 用于?Pivot 等重计算移出主线程?| | **数据处理** | **Lodash** | 基础的数据操作(分组、过滤、深拷贝)?| | **可视?* | **Ant Design Charts** | 在智能侧边栏中绘制直方图 (Histogram) 和频次图 (Bar)?| ## **3\. 核心模块详细设计** ### **3.1 核心计算引擎 (Compute Engine \- Web Worker)** #### **A. 长宽转换 (Pivot / Reshaping Algorithm) \- V2.1 核心难点** 这是最复杂的计算任务,必须?Web Worker 中执行,否则页面会卡死? * **输入参数?* * data: 原始对象数组 Row\[\] * indexCol: 主键列名 (e.g., 'patient\_id') \- 确定“行? * pivotKeyCol: 区分列名 (e.g., 'visit\_date') \- 确定“列后缀? * valueCols: 值列名数?(e.g., \['wbc', 'bmi'\]) \- 确定“填充值? * **算法逻辑?* 1. **预检?(Guard):** 计算 Unique(pivotKeyCol).length \* valueCols.length。如果生成的潜在列数 \> 1000,抛出错误“生成的列数过多,请先筛选数据”? 2. **分组 (Grouping):** 使用 \_.groupBy(data, indexCol) 按主键分组? 3. **转换 (Transformation):** 遍历每组数据? * 创建一个新行对象,保留主键? * 遍历该组的每一条记录,获取 pivotKeyCol 的值(例如 "2023-01-01")? * 遍历 valueCols,将值映射为 ValueCol\_PivotKey (例如 "wbc\_2023-01-01")? 4. **Schema生成:** 动态生成新?ColumnDefs? * **输出?* { newRows, newColumnDefs } #### **B. 公式变量 (Formula)** * 使用 math.evaluate(formula, row)? * **安全沙箱?* 限制公式中可访问的变量仅为当前行的数据,防止 XSS? * **异常处理?* 处理除以?(Infinity) 和非数字计算 (NaN) 的情况,统一返回 null 或错误标记? ### **3.2 智能侧边栏引?(Insight Engine)** * **触发:** 监听 AG Grid ?onColumnHeaderClicked 事件? * **去抖 (Debounce):** 200ms 延迟计算,防止快速切换列?UI 闪烁? * **统计逻辑:** * **数值列:** 计算 Min, Max, Mean, SD,并使用 Freedman-Diaconis 规则计算直方图的 Bins? * **文本?** 计算 Top 10 频率最高的词? ### **3.3 历史记录与撤销 (History Manager)** * **Undo/Redo 策略:** * **普通操?(编辑/替换):** 记录 patches (Immer)? * **结构性操?(Pivot/拆分/生成新变?:** 由于表结构完全改变,记录 patches 成本过高且难以回滚。策略改为:**在执行此类操作前,强制保存一个全量快?(Checkpoint)**。撤销时直接重载快照? ## **4\. 数据流与存储设计** ### **4.1 浏览器端存储 (Dexie Schema)** 用于暂存用户正在编辑的数据,实现“自动快照”和“崩溃恢复”? const db \= new Dexie('ResearchEditorDB'); db.version(2).stores({ // 项目元数? projects: '++id, name, lastModified, rowCount', // 数据?(Chunks): ?5万行数据切分为多?Chunk 存储,避免单次读写过大导致浏览器崩溃 dataChunks: '\[projectId+chunkIndex\], projectId', // 操作历史 (用于恢复现场) history: 'projectId, stack', // 完整快照 (用于 Pivot 等大操作的回? checkpoints: '++id, projectId, createdAt' }); ### **4.2 后端存储 (PostgreSQL \+ OSS)** 后端仅负责存储“已保存”的快照,不参与实时编辑? model DatasetSnapshot { id String @id @default(uuid()) taskId String // 关联任务 version Int // 版本? // 存储为大?JSON Blob,或者指?OSS 文件路径 (推荐 OSS) // 内容包含:rows\[\], columnDefs\[\], metadata ossKey String createdAt DateTime @default(now()) } ## **5\. API 接口定义** * POST /api/editor/init: 初始化编辑器会话,从 OSS 加载原始文件(如果是从工?A/B 流转过来的)? * POST /api/editor/save: 保存当前快照? * POST /api/editor/export: 请求后端生成 Excel/SPSS 文件? * *Payload:* { rows: \[...\], format: 'spss' } * *说明:* 如果数据量小,直接前?SheetJS 生成;数据量?(\>5MB) 发给后端生成? ## **6\. 性能准入与边?(Performance Guardrails)** | 数据量级 | 策略 | | :---- | :---- | | **\< 50,000 ?* | **全量加载模式**。所有数据都在内?IndexedDB,操作极快?| | **\> 50,000 ?* | **降采样模?(Downsampling)**。前端仅加载?5 万行用于预览和规则制定。导出时,将清洗规则(Recipe)发送给后端,由后端 Worker 对全量数据进行批处理?| ## **7\. 开发计?(Milestones)** 1. **Week 1: 核心网格与存?* * 搭建 React \+ AG Grid 环境? * 实现 SheetJS 导入?Dexie.js 持久化逻辑? 2. **Week 2: 扁平化工具栏?Web Worker** * 搭建 Web Worker 通信架构? * 实现 Formula 计算?Math.js 集成? * 实现 Undo/Redo 栈(Immer)? 3. **Week 3: 复杂计算 (Pivot)** * **重点攻坚?* ?Web Worker 中实?Pivot 算法? * 实现 Pivot ?UI 配置弹窗? 4. **Week 4: 智能侧边栏与导出** * 开发直方图/频次图组?(AntD Charts)? * 实现分箱、映射、填补缺失值逻辑? * 对接后端保存接口