Appearance
Feature: 连线系统
一句话目标
用户通过连线构建节点间的数据流向,系统提供方向约束、角色校验、拓扑验证,确保工作流拓扑合理且可执行。
行为约束
约束 1:Handle 方向规则
前置条件: 用户拖拽创建连线 行为: 连线只能从 source handle 连到 target handle;同类型 handle 之间不可相连 后置条件: 非法方向的连线不会被创建
约束 2:连线创建 — 拖拽到 Handle
前置条件: 画布中有两个以上节点 行为: 用户从节点 A 的 source handle 拖拽到节点 B 的 target handle,创建 A → B 连线 后置条件: 连线通过验证后创建并持久化
约束 3:连线创建 — 拖拽到节点体
前置条件: 用户从节点 A 的 source handle 拖拽连线到节点 B 的节点体(非 handle 区域) 行为: 系统自动建立 A(source) → B(target) 的连线,不弹出节点创建菜单 后置条件: 连线通过验证后创建并持久化
约束 4:连线创建 — 拖拽到空白区域
前置条件: 用户从节点 A 的 source handle 拖拽连线到画布空白区域 行为: 在释放位置弹出浮动菜单,用户选择类型后创建新节点并自动建立连线 后置条件: 新节点创建,A → 新节点的连线创建并持久化
约束 5:拓扑验证
前置条件: 用户尝试创建连线 行为: 系统验证三种非法拓扑:自连接(source === target)、循环(会形成有向环)、重复边(相同 source/target/handles) 后置条件: 非法连线不被创建
约束 6:节点连接角色规则
前置条件: 用户尝试创建连线,目标节点为 Image/Video 类型 行为: origin 为 uploaded 的 Image/Video 节点不可作为 target;empty 和 generated 可作为 target;文本节点不受 origin 限制 后置条件: 违反角色规则的连线不被创建
角色矩阵:
| 节点类型 | origin | 可作为 source | 可作为 target |
|---|---|---|---|
| 文本 | 任意 | ✓ | ✓ |
| 图片 | empty | ✓ | ✓ |
| 图片 | uploaded | ✓ | ✗ |
| 图片 | generated | ✓ | ✓ |
| 视频 | empty | ✓ | ✓ |
| 视频 | uploaded | ✓ | ✗ |
| 视频 | generated | ✓ | ✓ |
约束 7:连线随节点删除
前置条件: 画布中有节点及其关联连线 行为: 用户删除节点时,所有关联连线同时被移除 后置条件: 节点和关联连线均被删除并持久化
Acceptance Criteria
- [x] AC-01:自连接(source === target)被阻止
- [x] AC-02:循环连线被阻止
- [x] AC-03:重复边被阻止
- [x] AC-04:删除节点同时移除关联连线
- [x] AC-05:连线拖到空白区域弹出浮动菜单创建节点
- [x] AC-06:连线只能从 source handle 连到 target handle
- [x] AC-07:同类型 handle(source→source 或 target→target)不可相连
- [x] AC-08:从 source handle 拖拽到目标节点 handle 可创建连线
- [x] AC-09:从 source handle 拖拽到目标节点体(非 handle)自动建立 source→target 连线,不弹出创建菜单
- [x] AC-10:origin 为
uploaded的 Image/Video 节点不可作为 target
BDD Scenarios
gherkin
Feature: 连线系统
# AC-01
Scenario: 阻止自连接
Given 画布中有节点 A
When 用户从 A 的 source handle 拖拽到 A 的 target handle
Then 连线不被创建
# AC-02
Scenario: 阻止循环连线
Given 画布中有 A → B 的连线
When 用户尝试从 B 连到 A
Then 连线不被创建
# AC-05
Scenario: 拖拽到空白区域创建节点和连线
Given 画布中有节点 A
When 用户从 A 的 source handle 拖拽到画布空白区域
Then 在释放位置弹出浮动菜单
When 选择 "图片" 节点类型
Then 创建图片节点并自动建立 A → 新节点连线
# AC-06, AC-07
Scenario: Handle 方向规则校验
Given 画布中有节点 A 和节点 B
When 用户从 A 的 source handle 拖拽到 B 的 target handle
Then 连线创建成功
When 用户从 A 的 source handle 拖拽到 B 的 source handle
Then 连线不被创建
# AC-08
Scenario: 拖拽到目标节点 handle 创建连线
Given 画布中有节点 A 和节点 B
When 用户从 A 的 source handle 拖拽到 B 的 target handle
Then A → B 连线创建成功并持久化
# AC-09
Scenario: 拖拽到目标节点体创建连线
Given 画布中有节点 A 和节点 B
When 用户从 A 的 source handle 拖拽到 B 的节点体
Then 自动建立 A(source) → B(target) 连线
And 不弹出节点创建菜单
# AC-10
Scenario: Uploaded 节点不可作为 target
Given 画布中有文本节点 A 和图片节点 B
And B 的 origin 为 "uploaded"
When 用户从 A 连线到 B
Then 连线不被创建
Scenario: Empty 节点可作为 target
Given 画布中有文本节点 A 和图片节点 B
And B 的 origin 为 "empty"
When 用户从 A 连线到 B
Then 连线创建成功TDD 单元测试要点
edge-validation(拓扑验证 + 角色校验,test/unit/flowValidation.test.ts):
- [x] 自连接返回
{ valid: false, reason: 'self-connection' }(AC-01) - [x] 存在 A→B 边时,B→A 检测为 cycle(AC-02)
- [x] 完全相同的 source/target/handles 检测为 duplicate(AC-03)
- [x] 合法边返回
{ valid: true } - [x] 同类型 handle 相连返回非法(AC-07)
- [x] uploaded 图片节点作为 target 返回非法(AC-10)
- [x] uploaded 视频节点作为 target 返回非法(AC-10)
- [x] empty / generated 节点作为 target 返回合法(AC-10)
- [x] 文本节点无论 origin 均可作为 target(AC-10)
- [x] 旧数据(无 origin 字段)图片节点可作为 target — 向后兼容(AC-10)
Out of Scope
- ContentOrigin 字段的实现细节(何时设为 uploaded/generated 等)— 属于节点数据模型职责
- 连线的视觉样式定义(颜色、流光效果参数)— 见 design-system
- 自定义边组件的具体实现方案 — 属于 feature-dev 阶段
- 生成时如何处理不兼容上游输入 — 见 data-flow