Skip to content

Commit 3f2fafc

Browse files
authored
Merge pull request #130 from codingapi/dev
Dev
2 parents 793548f + 8059b23 commit 3f2fafc

20 files changed

Lines changed: 237 additions & 26 deletions
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# processNodes 数据返回修复
2+
3+
> 编码: 024982 | 日期: 2026-04-28 | 调整类型: Bug修复 | 状态: 草稿
4+
5+
## 调整背景
6+
7+
FlowService 下的 processNodes 函数在返回的数据过程中存在多个问题,影响流程节点展示的准确性。
8+
9+
## 现状描述
10+
11+
### 问题1:历史记录未按 nodeId 合并(核心问题)
12+
13+
`FlowProcessNodeService.processNodes()` 中,为每条 FlowRecord 单独创建一个 ProcessNode:
14+
15+
```java
16+
// FlowProcessNodeService.java:94-96, 107-109
17+
for (FlowRecord historyRecord : historyRecords) {
18+
ProcessNode processNode = new ProcessNode(historyRecord, this.workflow);
19+
nodeList.add(processNode);
20+
}
21+
```
22+
23+
**影响**:当同一节点存在多条 FlowRecord(如多人审批场景),同一 nodeId 会生成多个 ProcessNode,每个仅包含一个审批人,导致前端重复展示同一节点。
24+
25+
### 问题2:`isDone()` 分支将 TODO 记录错误标记为历史节点
26+
27+
`flowRecord.isDone()` 为 true 时,加载该 processId 下的**所有**记录:
28+
29+
```java
30+
// FlowProcessNodeService.java:93
31+
List<FlowRecord> historyRecords = flowRecordService.findFlowRecordByProcessId(this.flowRecord.getProcessId());
32+
```
33+
34+
**影响**:多人审批场景下,已审批完的用户的 `isDone()` 为 true,但同一 processId 下其他用户的记录可能仍在 TODO 状态。这些 TODO 记录会被错误标记为 `STATE_HISTORY`(因为构造函数固定设置 `state = STATE_HISTORY`)。
35+
36+
### 问题3:`equals()``hashCode()` 不一致
37+
38+
```java
39+
// ProcessNode.java:108-119
40+
public boolean equals(Object target) {
41+
// 只比较 nodeId
42+
return targetNode.getNodeId().equals(this.getNodeId());
43+
}
44+
45+
public int hashCode() {
46+
// 包含 nodeId, nodeName, nodeType, state, operators
47+
return Objects.hash(nodeId, nodeName, nodeType, state, operators);
48+
}
49+
```
50+
51+
**影响**:违反 Java equals/hashCode 契约。两个 nodeId 相同但 state/operators 不同的 ProcessNode 会 `equals``hashCode` 不同,在 HashSet/HashMap 中使用时会导致数据丢失。同时影响 `NextNodeLoader` 中的去重逻辑正确性。
52+
53+
## 目标状态
54+
55+
1. 相同 nodeId 的历史记录合并为一个 ProcessNode,operators 列表包含所有审批人
56+
2. TODO 状态的记录不被错误标记为历史节点
57+
3. 修复 equals/hashCode 一致性,确保去重逻辑正确
58+
59+
## 调整范围
60+
61+
### 范围内
62+
63+
- `FlowProcessNodeService.processNodes()` — 历史记录合并逻辑
64+
- `FlowProcessNodeService` 内部类 `NextNodeLoader` — 去重逻辑
65+
- `ProcessNode` — equals/hashCode 修复、新增合并方法
66+
- `ProcessNode(FlowRecord, Workflow)` 构造函数 — 调整 state 赋值逻辑
67+
68+
### 范围外
69+
70+
- FlowRecordService 接口(不修改查询方法签名)
71+
- REST API 层(接口不变)
72+
- 前端展示逻辑
73+
74+
## 约束与前提
75+
76+
- 不改变 `List<ProcessNode>` 返回类型
77+
- 保持现有测试用例通过
78+
- 修改后需执行编译验证
79+
80+
## 不调整的影响
81+
82+
- 多人审批节点在前端重复显示
83+
- 已完成流程的节点状态可能不准确
84+
- 在 HashSet 中使用 ProcessNode 可能丢失数据
85+
86+
## 相关资料
87+
88+
- `FlowProcessNodeService.java` (flow-engine-framework)
89+
- `ProcessNode.java` (flow-engine-framework)
90+
- `FlowRecordService.java` (flow-engine-framework)
91+
- `FlowDetailServiceTest.java` (flow-engine-framework)
92+
93+
## 备注
94+
95+
涉及文件:
96+
- `flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowProcessNodeService.java`
97+
- `flow-engine-framework/src/main/java/com/codingapi/flow/pojo/response/ProcessNode.java`

docs/components/flow-engine-framework_FlowScriptContext.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ description: Groovy 脚本运行时的 $bind 上下文对象,为脚本提供
2222
<dependency>
2323
<groupId>com.codingapi.flow</groupId>
2424
<artifactId>flow-engine-framework</artifactId>
25-
<version>0.0.28</version>
25+
<version>0.0.29</version>
2626
</dependency>
2727
```
2828

docs/components/flow-engine-framework_GatewayContext.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ GatewayContext 是框架内部获取流程操作人(IFlowOperator)数据的
2222
<dependency>
2323
<groupId>com.codingapi.flow</groupId>
2424
<artifactId>flow-engine-framework</artifactId>
25-
<version>0.0.28</version>
25+
<version>0.0.29</version>
2626
</dependency>
2727
```
2828

docs/components/flow-engine-framework_GroovyScriptBind.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ GroovyScriptBind 是脚本编写者在流程 Groovy 脚本中通过 `$bind` 变
2222
<dependency>
2323
<groupId>com.codingapi.flow</groupId>
2424
<artifactId>flow-engine-framework</artifactId>
25-
<version>0.0.28</version>
25+
<version>0.0.29</version>
2626
</dependency>
2727
```
2828

docs/components/flow-engine-framework_GroovyScriptRequest.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ GroovyScriptRequest 是流程 Groovy 脚本中 `request` 参数的实际类型
2222
<dependency>
2323
<groupId>com.codingapi.flow</groupId>
2424
<artifactId>flow-engine-framework</artifactId>
25-
<version>0.0.28</version>
25+
<version>0.0.29</version>
2626
</dependency>
2727
```
2828

docs/components/flow-engine-framework_GroovyWorkflowRequest.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ GroovyWorkflowRequest 用于工作流级别(非会话级别)的脚本场景
2222
<dependency>
2323
<groupId>com.codingapi.flow</groupId>
2424
<artifactId>flow-engine-framework</artifactId>
25-
<version>0.0.28</version>
25+
<version>0.0.29</version>
2626
</dependency>
2727
```
2828

docs/components/flow-engine-framework_IFlowOperator.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ IFlowOperator 是流程引擎中对参与流程的用户(审批人、创建者
2222
<dependency>
2323
<groupId>com.codingapi.flow</groupId>
2424
<artifactId>flow-engine-framework</artifactId>
25-
<version>0.0.28</version>
25+
<version>0.0.29</version>
2626
</dependency>
2727
```
2828

docs/components/flow-engine-framework_RepositoryHolderContext.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ RepositoryHolderContext 是流程引擎运行时的基础设施注册中心。
2222
<dependency>
2323
<groupId>com.codingapi.flow</groupId>
2424
<artifactId>flow-engine-framework</artifactId>
25-
<version>0.0.28</version>
25+
<version>0.0.29</version>
2626
</dependency>
2727
```
2828

docs/components/flow-engine-framework_ScriptRegistryContext.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ ScriptRegistryContext 是流程引擎中所有默认 Groovy 脚本的注册中
2222
<dependency>
2323
<groupId>com.codingapi.flow</groupId>
2424
<artifactId>flow-engine-framework</artifactId>
25-
<version>0.0.28</version>
25+
<version>0.0.29</version>
2626
</dependency>
2727
```
2828

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# 调整计划: processNodes 数据返回修复
2+
3+
> 编码: 024982 | 日期: 2026-04-28 | 类型: 调整任务 | 来源: change | 基于: docs/changes/024982-processNodes-data-fix.md
4+
5+
## 调整目标
6+
7+
修复 `processNodes()` 在返回流程节点数据时的三个问题:历史记录未按 nodeId 合并、TODO 记录被错误标记为历史、equals/hashCode 不一致。
8+
9+
## 影响面(基于指令结果)
10+
11+
- 涉及 Maven 模块: `flow-engine-framework`
12+
- 直接依赖者: `flow-engine-starter-api`(仅引用 ProcessNode 类型作为返回值,无逻辑变更)
13+
- 外部 API / 公共组件影响: 否,REST API 接口 `POST /api/cmd/record/processNodes` 签名和返回结构不变
14+
15+
## 分步策略
16+
17+
将调整拆分为 3 步,每步可独立通过编译验证:
18+
19+
1. **Step 1**: 修复 `ProcessNode` 的 equals/hashCode 一致性,新增合并方法
20+
2. **Step 2**: 修复 `FlowProcessNodeService.processNodes()` 的历史记录合并逻辑与 TODO 过滤
21+
3. **Step 3**: 编译验证 + 现有单测通过
22+
23+
## 新增文件
24+
25+
| 文件路径 | 用途说明 |
26+
|----------|----------|
27+
28+
(无新增文件)
29+
30+
## 修改文件
31+
32+
| 文件路径 | 修改内容 |
33+
|----------|----------|
34+
| `flow-engine-framework/src/main/java/com/codingapi/flow/pojo/response/ProcessNode.java` | 1. `hashCode()` 改为只使用 `nodeId`,与 `equals()` 一致<br>2. 新增 `addOperator(FlowOperatorBody)` 方法,支持向已有 ProcessNode 追加审批人 |
35+
| `flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowProcessNodeService.java` | 1. 新增 `buildHistoryNodes(List<FlowRecord>)` 方法,按 nodeId 合并历史记录<br>2. `processNodes()``isDone()` 分支:过滤仅保留已办记录,使用合并方法<br>3. `processNodes()` 中非 `isDone()` 分支:使用合并方法处理 beforeRecords |
36+
37+
## 移除文件(若有)
38+
39+
| 文件路径 | 移除原因 |
40+
|----------|----------|
41+
42+
(无移除文件)
43+
44+
## 兼容性与迁移
45+
46+
- 保留的 API / 行为: `List<ProcessNode>` 返回类型不变;`ProcessNode` 的公共字段不变;REST API 端点签名不变
47+
- 破坏性变更: `ProcessNode.hashCode()` 返回值变化(之前包含全部字段,现在只包含 nodeId)。如果下游代码将 ProcessNode 存入 HashMap/HashSet,行为会更正(之前因 equals/hashCode 不一致可能导致数据丢失)
48+
- 数据/配置迁移: 无
49+
50+
## 核验机制
51+
52+
| 验证项 | 说明 |
53+
|--------|------|
54+
| 编译 | `mvn compile` 通过 |
55+
| 单测 | `flow-engine-framework` 模块现有测试通过 |
56+
| 打包 | `mvn package` 通过 |
57+
| 行为回归 | `FlowDetailServiceTest.processNodes()` 测试用例通过 |
58+
59+
具体验证命令:
60+
61+
```bash
62+
mvn -pl flow-engine-framework -am compile
63+
mvn -pl flow-engine-framework -am test
64+
mvn package
65+
```
66+
67+
## 执行顺序
68+
69+
### Step 1: 修复 ProcessNode equals/hashCode + 新增合并方法
70+
71+
1. **修改** `flow-engine-framework/src/main/java/com/codingapi/flow/pojo/response/ProcessNode.java`
72+
- `hashCode()` 方法:改为 `return Objects.hash(nodeId);`(与 equals 一致)
73+
- 新增 `addOperator(FlowOperatorBody operator)` 方法:向 `this.operators` 追加审批人
74+
75+
### Step 2: 修复 FlowProcessNodeService 历史记录合并与 TODO 过滤
76+
77+
2. **修改** `flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowProcessNodeService.java`
78+
- 新增私有方法 `buildHistoryNodes(List<FlowRecord> records)`
79+
- 使用 `LinkedHashMap<String, ProcessNode>` 按 nodeId 分组
80+
- 遍历 records,nodeId 相同时调用 `addOperator()` 追加审批人
81+
- 返回合并后的 `List<ProcessNode>`
82+
- 修改 `processNodes()` 方法第 92-103 行(`isDone()` 分支):
83+
- 使用 `records.stream().filter(FlowRecord::isDone)` 过滤仅保留已办记录
84+
-`buildHistoryNodes()` 替代原来的逐条 new ProcessNode
85+
- 修改 `processNodes()` 方法第 106-110 行(非 `isDone()` 分支):
86+
-`buildHistoryNodes()` 替代原来的逐条 new ProcessNode
87+
88+
### Step 3: 编译验证
89+
90+
3. **运行** `mvn -pl flow-engine-framework -am compile` 验证编译通过
91+
4. **运行** `mvn -pl flow-engine-framework -am test` 验证单测通过
92+
93+
## 现状-目标对照表
94+
95+
| 现状位置 | 现状内容 | 目标变更 |
96+
|----------|----------|----------|
97+
| `ProcessNode.java:117-119` | `hashCode` 使用 `nodeId, nodeName, nodeType, state, operators` | 改为只使用 `nodeId`,与 `equals` 一致 |
98+
| `ProcessNode.java` | 无合并方法 | 新增 `addOperator(FlowOperatorBody)` 方法 |
99+
| `FlowProcessNodeService.java:93-97` | `isDone()` 分支加载所有记录逐条创建 ProcessNode | 过滤仅已办记录 + 按 nodeId 合并 |
100+
| `FlowProcessNodeService.java:106-110` |`isDone()` 分支逐条创建 ProcessNode | 按 nodeId 合并 |

0 commit comments

Comments
 (0)