TraceStudio-dev/docs/web1.0/REFACTOR_FUNCTION_CATEGORY.md
2026-01-07 19:34:45 +08:00

714 lines
18 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🔄 节点功能与目录分离重构
## 📋 需求分析
### 原有问题
`category` 字段承担双重职责:
1. 表示节点功能Loader/Transform/Visualizer→ 用于节点颜色、图标、Handle 显示
2. 表示目录分类 → 用于 NodePalette 树状展示
**冲突**:无法实现 "Loader/CSV"、"Transform/Filter" 这样的二级目录分类
### 解决方案
`category` 和节点功能分离:
- **`function`**: 节点功能Loader/Transform/Visualizer
- **`category`**: 树状目录路径Loader/CSV, Transform/Filter
同时弃用前端的 `node.type` 属性,统一使用 `function` 字段。
---
## ✅ 后端修改main.py
### 所有节点添加 `function` 字段
```python
# 修改前
"CSVLoader": {
"display_name": "CSV 数据加载器",
"category": "Loader", # 既表示功能又表示目录
"node_logic": "standard",
# ...
}
# 修改后
"CSVLoader": {
"display_name": "CSV 数据加载器",
"function": "Loader", # ✅ 节点功能:加载器
"category": "Loader/CSV", # ✅ 树状目录:加载器/CSV
"node_logic": "standard",
# ...
}
```
### 元数据结构完整示例
```python
{
"CSVLoader": {
"display_name": "CSV 数据加载器",
"function": "Loader", # 节点功能
"category": "Loader/CSV", # 树状目录
"node_logic": "standard", # 节点逻辑
"supports_preview": True,
"inputs": [],
"outputs": [{"name": "table", "type": "DataTable"}],
"param_schema": {...}
},
"UTraceLoader": {
"display_name": "UTrace 文件加载器",
"function": "Loader",
"category": "Loader/Trace", # 二级目录Trace
# ...
},
"FilterRows": {
"display_name": "行过滤器",
"function": "Transform",
"category": "Transform/Filter", # 二级目录Filter
# ...
},
"SelectColumns": {
"display_name": "列选择器",
"function": "Transform",
"category": "Transform/Select", # 二级目录Select
# ...
},
"Aggregator": {
"display_name": "数据聚合",
"function": "Transform",
"category": "Transform/Aggregate",
"node_logic": "aggregate",
"allow_multiple_inputs": True, # ✅ 多输入支持
# ...
},
"TimeRangeFilter": {
"display_name": "时间范围过滤",
"function": "Transform",
"category": "Transform/Filter", # 与 FilterRows 共享目录
# ...
},
"ChartVisualizer": {
"display_name": "图表可视化",
"function": "Visualizer",
"category": "Visualizer/Chart",
# ...
},
"TableOutput": {
"display_name": "表格输出",
"function": "Visualizer",
"category": "Visualizer/Table",
# ...
}
}
```
### 完整节点清单
| 节点类型 | function | category | node_logic | allow_multiple_inputs |
|----------|----------|----------|------------|----------------------|
| CSVLoader | Loader | Loader/CSV | standard | false |
| UTraceLoader | Loader | Loader/Trace | standard | false |
| FilterRows | Transform | Transform/Filter | standard | false |
| SelectColumns | Transform | Transform/Select | standard | false |
| Aggregator | Transform | Transform/Aggregate | aggregate | ✅ true |
| TimeRangeFilter | Transform | Transform/Filter | standard | false |
| ChartVisualizer | Visualizer | Visualizer/Chart | standard | false |
| TableOutput | Visualizer | Visualizer/Table | standard | false |
---
## ✅ 前端修改
### 1. UniversalNode.tsx
#### 接口重构
```typescript
// 修改前
interface UniversalNodeProps {
data: {
meta?: {
category?: 'Loader' | 'Transform' | 'Visualizer' // ❌ 混用
}
}
}
// 修改后
interface UniversalNodeProps {
data: {
meta?: {
function?: 'Loader' | 'Transform' | 'Visualizer' // ✅ 节点功能
category?: string // ✅ 树状目录路径(如 "Loader/CSV"
}
}
}
```
#### 常量重命名
```typescript
// 修改前
const CATEGORY_COLORS: Record<string, string> = {
Loader: '#3b82f6',
Transform: '#8b5cf6',
Visualizer: '#22c55e',
}
// 修改后
const FUNCTION_COLORS: Record<string, string> = {
Loader: '#3b82f6',
Transform: '#8b5cf6',
Visualizer: '#22c55e',
}
```
#### 组件实现
```typescript
// 修改前
const category = data.meta?.category || 'Transform'
const headerColor = CATEGORY_COLORS[category] || '#6b7280'
const showInput = category === 'Transform' || category === 'Visualizer'
const showOutput = category === 'Loader' || category === 'Transform'
// 修改后
const nodeFunction = data.meta?.function || 'Transform'
const headerColor = FUNCTION_COLORS[nodeFunction] || '#6b7280'
const showInput = nodeFunction === 'Transform' || nodeFunction === 'Visualizer'
const showOutput = nodeFunction === 'Loader' || nodeFunction === 'Transform'
```
#### Header 显示
```typescript
// 修改前
<div>{categoryIcon[category] || '🔹'}</div>
<div>{category}</div>
// 修改后
<div>{functionIcon[nodeFunction] || '🔹'}</div>
<div>{nodeFunction}</div> {/* 显示 Loader/Transform/Visualizer */}
```
---
### 2. Inspector.tsx
#### 图标映射
```typescript
// 修改前
<span>
{node.type === 'csv' ? '📂' :
node.type === 'transform' ? '⚙️' :
node.type === 'output' ? '📊' : '📦'}
</span>
// 修改后
<span>
{node.data?.meta?.function === 'Loader' ? '📥' :
node.data?.meta?.function === 'Transform' ? '⚙️' :
node.data?.meta?.function === 'Visualizer' ? '📊' : '📦'}
</span>
```
#### 标题显示
```typescript
// 修改前
<h3>{node.type.toUpperCase()}</h3> {/* CSV, TRANSFORM, OUTPUT */}
// 修改后
<h3>{node.data?.meta?.display_name || 'Node'}</h3> {/* CSV 数据加载器 */}
```
#### 系统属性
```typescript
// 修改前
<div>
<label>节点类型</label>
<input value={node.type} disabled /> {/* csv, transform, output */}
</div>
// 修改后
<div>
<label>节点功能</label>
<input value={node.data?.meta?.function || 'Unknown'} disabled /> {/* Loader */}
</div>
<div>
<label>目录分类</label>
<input value={node.data?.meta?.category} disabled /> {/* Loader/CSV */}
</div>
```
---
### 3. Workspace.tsx
#### MiniMap 颜色映射
```typescript
// 修改前
<MiniMap
nodeColor={(node) => {
const category = node.data?.meta?.category
if (category === 'Loader') return '#3b82f6'
if (category === 'Transform') return '#8b5cf6'
if (category === 'Visualizer') return '#22c55e'
return '#64748b'
}}
/>
// 修改后
<MiniMap
nodeColor={(node) => {
const nodeFunction = node.data?.meta?.function
if (nodeFunction === 'Loader') return '#3b82f6'
if (nodeFunction === 'Transform') return '#8b5cf6'
if (nodeFunction === 'Visualizer') return '#22c55e'
return '#64748b'
}}
/>
```
---
### 4. NodePalette.tsx
#### 接口更新
```typescript
// 修改前
interface PluginMeta {
display_name?: string
category?: string // 混用
}
// 修改后
interface PluginMeta {
display_name?: string
function?: string // 节点功能
category?: string // 树状目录
}
```
#### 二级目录解析
```typescript
// 修改前
const groups: Record<string, { key: string; items: Array<...> }> = {}
for (const [k, v] of Object.entries(plugins)) {
const cat = (v.category || 'Other').split('/')[0]
if (!groups[cat]) groups[cat] = { key: cat, items: [] }
groups[cat].items.push({ type: k, meta: v })
}
// 修改后
const groups: Record<string, {
key: string;
items: Array<{ type: string; meta: PluginMeta; subCategory?: string }>
}> = {}
for (const [k, v] of Object.entries(plugins)) {
const categoryPath = v.category || 'Other'
const [mainCat, subCat] = categoryPath.split('/')
if (!groups[mainCat]) {
groups[mainCat] = { key: mainCat, items: [] }
}
groups[mainCat].items.push({
type: k,
meta: v,
subCategory: subCat // 保存二级分类
})
}
```
#### 算子卡片显示
```typescript
// 修改前
<div>
<span>
{group.key === 'Loaders' ? '📂' :
group.key === 'Transforms' ? '⚙️' : '📦'}
</span>
<span>{item.meta.display_name}</span>
</div>
// 修改后
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
{/* 主行:图标 + 名称 */}
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<span>
{item.meta.function === 'Loader' ? '📥' :
item.meta.function === 'Transform' ? '⚙️' :
item.meta.function === 'Visualizer' ? '📊' : '📦'}
</span>
<span>{item.meta.display_name}</span>
</div>
{/* 二级分类标签 */}
{item.subCategory && (
<div style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)' }}>
{item.subCategory} {/* CSV, Trace, Filter, Select */}
</div>
)}
</div>
```
---
## 📊 视觉效果对比
### NodePalette 显示
**修改前**
```
▸ Loaders (2)
📂 CSV 数据加载器
📂 UTrace 文件加载器
▸ Transforms (4)
⚙️ 行过滤器
⚙️ 列选择器
⚙️ 数据聚合
⚙️ 时间范围过滤
```
**修改后**
```
▸ Loader (2)
📥 CSV 数据加载器
CSV ← 二级分类标签
📥 UTrace 文件加载器
Trace ← 二级分类标签
▸ Transform (4)
⚙️ 行过滤器
Filter ← 二级分类标签
⚙️ 列选择器
Select
⚙️ 数据聚合
Aggregate
⚙️ 时间范围过滤
Filter ← 相同二级目录
```
### Inspector 面板
**修改前**
```
┌─ 系统信息 ────────┐
│ 节点 ID: n_xxx │
│ 节点类型: csv │ ← 前端类型(混乱)
│ 分类: Loader │ ← 混用
└──────────────────┘
```
**修改后**
```
┌─ 系统信息 ────────────┐
│ 节点 ID: n_xxx │
│ 节点功能: Loader │ ← 清晰的功能标识
│ 目录分类: Loader/CSV │ ← 完整的目录路径
└──────────────────────┘
```
### 节点本体显示
**修改前**
```
┌─ CSV 数据加载器 ──┐
│ Loader │ ← 显示 category
│ file_path: ... │
└──────────────────┘
```
**修改后**
```
┌─ CSV 数据加载器 ──┐
│ LOADER │ ← 显示 function功能
│ file_path: ... │
└──────────────────┘
```
---
## 🎯 数据流追踪
### 从后端到前端的完整流程
```
1⃣ 后端定义main.py
GET /plugins
{
"CSVLoader": {
"function": "Loader", ← 节点功能
"category": "Loader/CSV" ← 树状目录
}
}
2⃣ NodePalette 接收
解析 category: "Loader/CSV"
├─ mainCat: "Loader" ← 一级目录
└─ subCat: "CSV" ← 二级目录
显示:
Loader (2)
📥 CSV 数据加载器
CSV ← subCategory 标签
3⃣ 拖拽到画布
创建节点:
{
id: "n_xxx",
type: "universal", ← 所有节点统一类型
data: {
meta: {
function: "Loader", ← 用于功能判断
category: "Loader/CSV" ← 用于显示目录
}
}
}
4⃣ UniversalNode 渲染
读取 function: "Loader"
├─ 决定颜色:#3b82f6蓝色
├─ 决定图标:📥
├─ 决定 Handle只显示 Output
└─ 显示文字LOADER
5⃣ Inspector 显示
系统信息:
- 节点功能: Loader ← function
- 目录分类: Loader/CSV ← category
```
---
## 🔍 关键概念对比
### function vs category
| 字段 | 用途 | 值域 | 示例 | 前端使用位置 |
|------|------|------|------|-------------|
| **function** | 节点功能 | Loader, Transform, Visualizer | `"Loader"` | UniversalNode 颜色、图标、Handle 逻辑 |
| **category** | 树状目录 | 任意路径(支持 / 分隔) | `"Loader/CSV"` | NodePalette 分组、Inspector 显示 |
### 节点类型演变
| 阶段 | 前端类型 | 后端类型 | 功能标识 | 目录标识 |
|------|---------|---------|---------|---------|
| **Phase 1** | node.type = 'csv' | N/A | ❌ 混乱 | ❌ 无 |
| **Phase 2** | node.type = 'universal' | meta.category | ⚠️ category 混用 | ⚠️ category 混用 |
| **Phase 3 (现在)** | node.type = 'universal' | meta.function | ✅ function | ✅ category |
---
## 🧪 测试验证
### 测试 1后端 API 验证
```bash
# 访问后端
curl http://127.0.0.1:8000/plugins
# 验证响应包含
{
"plugins": {
"CSVLoader": {
"function": "Loader", ✅
"category": "Loader/CSV", ✅
"allow_multiple_inputs": false
},
"Aggregator": {
"function": "Transform",
"category": "Transform/Aggregate",
"allow_multiple_inputs": true
}
}
}
```
### 测试 2NodePalette 二级目录
```
操作:打开应用
预期:左侧面板显示
✅ Loader (2)
📥 CSV 数据加载器
CSV ← 二级分类标签
📥 UTrace 文件加载器
Trace
✅ Transform (4)
⚙️ 行过滤器
Filter
⚙️ 列选择器
Select
⚙️ 数据聚合
Aggregate
⚙️ 时间范围过滤
Filter ← 相同目录共享
```
### 测试 3节点功能识别
```
操作:拖拽 CSV Loader 到画布
预期:
1. 节点颜色:蓝色(#3b82f6
2. 节点图标:📥 ✅
3. Handle 显示:只有 Output ✅
4. 节点 Header显示 "LOADER" ✅
操作:拖拽 FilterRows 到画布
预期:
1. 节点颜色:紫色(#8b5cf6
2. 节点图标:⚙️ ✅
3. Handle 显示Input + Output ✅
4. 节点 Header显示 "TRANSFORM" ✅
```
### 测试 4Inspector 显示
```
操作:选中 CSV Loader 节点
预期 Inspector 显示:
┌─ 系统信息 ─────────────┐
│ 节点 ID: n_xxx │ ✅
│ 节点功能: Loader │ ✅ (原来是 node.type)
│ 目录分类: Loader/CSV │ ✅ (新增)
└───────────────────────┘
┌─ 参数配置 ─────────────┐
│ file_path: [输入框] │
│ delimiter: [输入框] │
└───────────────────────┘
```
### 测试 5MiniMap 颜色
```
操作:拖拽多种节点到画布
预期 MiniMap 显示:
- Loader 节点:蓝色 ✅
- Transform 节点:紫色 ✅
- Visualizer 节点:绿色 ✅
```
---
## 📁 修改文件清单
### 后端
1. **server/main.py**
- ✅ 所有节点添加 `"function"` 字段
- ✅ 修改 `"category"` 为树状路径(支持 `/` 分隔)
- ✅ Aggregator 添加 `"allow_multiple_inputs": True`
### 前端
2. **web/src/components/nodes/UniversalNode.tsx**
-`CATEGORY_COLORS``FUNCTION_COLORS`
-`meta.category``meta.function`
-`categoryIcon``functionIcon`
- ✅ Header 显示 `nodeFunction` 代替 `category`
3. **web/src/components/Inspector.tsx**
- ✅ 图标映射:`node.type` → `node.data.meta.function`
- ✅ 标题显示:`node.type.toUpperCase()` → `node.data.meta.display_name`
- ✅ 系统属性:删除 `node.type`,添加 `function``category` 显示
4. **web/src/components/Workspace.tsx**
- ✅ MiniMap 颜色:`meta.category` → `meta.function`
5. **web/src/components/NodePalette.tsx**
- ✅ 接口添加 `function` 字段
- ✅ 二级目录解析:`category.split('/')` → `[mainCat, subCat]`
- ✅ 算子卡片:显示 `function` 图标 + `subCategory` 标签
---
## 🎨 设计亮点
### 1. 职责分离
- **function**: 节点功能逻辑颜色、图标、Handle
- **category**: UI 组织结构(树状目录、分组)
### 2. 可扩展性
```python
# 轻松添加三级目录
"category": "Transform/Filter/Advanced"
# 前端自动支持
categoryPath.split('/') ['Transform', 'Filter', 'Advanced']
```
### 3. 语义化
```typescript
// 修改前(混乱)
if (node.type === 'csv') { ... } // 前端类型
if (meta.category === 'Loader') { ... } // 既是功能又是目录
// 修改后(清晰)
if (meta.function === 'Loader') { ... } // 节点功能
if (meta.category.startsWith('Loader/')) { ... } // 目录位置
```
### 4. 向后兼容
- 前端 `node.type` 统一为 `'universal'`,不再依赖具体类型
- 所有节点使用相同的 UniversalNode 组件
- 元数据驱动,易于扩展
---
## 🚀 后续增强建议
### 1. 动态树状目录
```typescript
// 支持折叠二级目录
Loader (2)
CSV (1)
- CSV 数据加载器
Trace (1)
- UTrace 文件加载器
Transform (4)
Filter (2)
- 行过滤器
- 时间范围过滤
Select (1)
Aggregate (1)
```
### 2. 功能徽章
```typescript
// 节点卡片显示功能标签
┌─ CSV 数据加载器 ────────┐
[Loader] [CSV] [Preview]
file_path: ...
└─────────────────────────┘
```
### 3. 搜索增强
```typescript
// 支持按 function 和 category 过滤
搜索:Loader 显示所有 Loader 功能节点
搜索:Filter 显示所有 Filter 目录节点
搜索:Loader/CSV 显示特定路径节点
```
---
**完成时间**2026-01-07
**版本**Function & Category Separation v1.0
**状态**:✅ 重构完成,功能与目录完全分离