Skip to content

Commit db9403c

Browse files
authored
Merge pull request #220 from codingapi/dev
Dev
2 parents 59976d7 + 3921c09 commit db9403c

23 files changed

Lines changed: 1859 additions & 13 deletions

File tree

docs/Integration/event-integration.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ public interface IAsyncEvent extends IEvent {} // 异步事件标记 ←
2727

2828
同步/异步由 `DomainEventContext.push``instanceof` 判定:`IAsyncEvent` → 异步(线程池执行);`ISyncEvent`/裸 `IEvent` → 同步(发布线程内联执行)。
2929

30-
### 1.2 七种事件
30+
### 1.2 八种事件
3131

32-
所有事件均携带 `FlowRecord`(或当前记录)与 `boolean mock` 标记(Mock 模式为 `true`)。
32+
记录类事件均携带 `FlowRecord`(或当前记录)与 `boolean mock` 标记(Mock 模式为 `true`)。
3333

3434
| 事件类 | 触发时机 | 构造参数 | 访问器 |
3535
|---|---|---|---|
@@ -40,6 +40,19 @@ public interface IAsyncEvent extends IEvent {} // 异步事件标记 ←
4040
| `FlowRecordRevokeEvent` | 撤销(被撤销的后置记录) | `FlowRecord currentRecord, boolean mock` | `getCurrentRecord()` / `isMock()` |
4141
| `FlowRecordUrgeEvent` | 催办(每条待办) | `FlowRecord, IFlowOperator urgeOperator, boolean mock` | `getFlowRecord()` / `getUrgeOperator()` / `isMock()` |
4242
| `FlowRecordDeleteEvent` | 删除未流转实例(开始节点) | `FlowRecord, boolean mock` | `getFlowRecord()` / `isMock()` |
43+
| `FlowSubProcessResetEvent` | 子流程数据重置(issue #219| `SubProcessRecord oldRecord, SubProcessRecord newRecord, long resetRecordId, IFlowOperator resetOperator, boolean mock` | `getOldRecord()` / `getNewRecord()` / `getResetRecordId()` / `getResetOperator()` / `isMock()` |
44+
45+
**子流程重置事件订阅须知**
46+
47+
- 事件提醒业务方子流程数据已被重置:`oldRecord` 为被取代的旧聚合组快照(含全部旧实例流程id),
48+
`newRecord` 为重置后的新聚合组——继承实例沿用原流程id,重建实例为新流程id,
49+
重建实例的 `getSourceProcessId()` 记录其替换的旧实例流程id,据此完成旧 → 新流程id映射;
50+
- 事件经异步线程池分发、到达顺序不保证(重置同时伴随被作废记录的 `RevokeEvent`
51+
与重建实例的 `StartEvent`/`TodoEvent`),订阅方应按「同一 `parentRecordId` + 节点维度下
52+
**最大聚合组 id 为活跃组**」做幂等覆盖式收敛,禁止增量累加;
53+
- 事件的 `oldRecord` id 与订阅方当前活跃组 id 不一致时,说明存在丢失的重置事件,
54+
应通过节点记录查询(`/api/cmd/record/processNodes`)全量对账;
55+
- 旧实例记录不会被删除(仅聚合组标记已取代),订阅方收到事件后应自行将旧流程id的数据标记失效。
4356

4457
范例(`FlowRecordUrgeEvent.java`,唯一带三个字段的事件):
4558

@@ -107,6 +120,7 @@ EventPusher.push(new FlowRecordStartEvent(flowRecord, session.isMock()));
107120
| `FlowUrgeService.urge()` | 每条待办 `UrgeEvent` |
108121
| `FlowRevokeService.revoke()` | 当前记录恢复待办 `TodoEvent` + 被撤销后置记录 `RevokeEvent` |
109122
| `FlowDeleteService.delete()` | `DeleteEvent` |
123+
| `FlowSubProcessResetService.reset()` | 被作废记录链逐条 `RevokeEvent` + 子流程重置 `SubProcessResetEvent`;重建实例经 `FlowCreateService` 推送各自的 `StartEvent` + `TodoEvent` |
110124

111125
> 事件在 `repositoryHolder.saveRecords(...)` **落库之后**推送,保证订阅方看到的记录已持久化。
112126

docs/Integration/rest-api.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ operatorId 请求参数 → 存在 → 使用该 id 作为当前操作人
2727
| POST | `/api/cmd/record/processNodes` | `FlowProcessNodeRequest` | `MultiResponse<ProcessNode>` | 流程流转节点预览 |
2828
| POST | `/api/cmd/record/create` | `FlowCreateRequest` | `SingleResponse<Long>` | 发起流程,返回首条记录 id |
2929
| POST | `/api/cmd/record/action` | `FlowActionRequest` | `SingleResponse<ActionResponse>` | 执行动作(通过/拒绝/加签/委派/退回/转办/自定义/保存) |
30+
| POST | `/api/cmd/record/subProcess/reset` | `FlowSubProcessResetRequest` | `Response` | 子流程数据重置(独立接口,需子流程节点开启重置能力) |
3031
| POST | `/api/cmd/record/urge` | `IdRequest` | `Response` | 催办 |
3132
| POST | `/api/cmd/record/revoke` | `IdRequest` | `Response` | 撤销 |
3233
| POST | `/api/cmd/record/delete` | `IdRequest` | `Response` | 删除(仅未流转实例,开始节点) |
@@ -139,6 +140,20 @@ public interface FlowRecordQueryService {
139140
- `forwardOperatorIds`:加签/委派/转办的目标操作人。
140141
- `backNodeId`:退回动作的目标节点(仅 Start/Approval/Handle 可退回)。
141142

143+
### `FlowSubProcessResetRequest`(子流程数据重置,独立接口)
144+
145+
```json
146+
{
147+
"recordId": 456,
148+
"resetInstanceProcessIds": ["选中重建的子流程实例流程id"],
149+
"advice": "重置说明"
150+
}
151+
```
152+
153+
- 重置不属于审批动作,不经过 `/action` 接口;仅当子流程节点开启 `resettable` 能力且当前待办位于其下游时可调用。
154+
- 无需指定子流程节点:由选中实例流程id定位其所属聚合组,选中实例须同属一个已放行且未取代的聚合组。
155+
- 流程详情 `FlowContent.resetSubProcess` 标识字段表明当前记录是否具备该能力,前端据此提供交互。
156+
142157
### `ActionResponse`
143158

144159
```json
@@ -165,6 +180,7 @@ form(FlowForm 元数据) / fieldPermissions(字段权限)
165180
todos(当前节点待办 Body 列表) / actions / actionList
166181
mergeable / createOperator / currentOperator / flowState / recordState
167182
histories(审批历史) / nodes(NodeOption 可操作节点) / revoke / urge
183+
resetSubProcess(子流程重置能力标识:当前待办位于开启重置能力、已汇聚完成的子流程下游时为 true)
168184
```
169185

170186
### `FlowRecordContent`(列表响应,字段摘要)

docs/capabilities/flow-engine-framework/sub-process.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ symbols:
1515
- SubProcessScript
1616
- SubProcessResultScript
1717
- FlowSubProcessResultService
18+
- FlowSubProcessResetService
19+
- FlowSubProcessResetRequest
20+
- FlowSubProcessResetEvent
1821
- FlowSession.findSubProcessRecords
1922
- ProcessNode.SubProcessBody
2023
---
@@ -75,8 +78,57 @@ def run(request){
7578
- 拼接的祖先流程节点统一返回 `ProcessNode.parentProcessRecord=true`,当前子流程节点为 `false`,供展示层区分记录来源。
7679
- 开关开启时主流程历史包含审批人和审批意见,因此应由流程设计者按数据可见性要求显式配置。
7780

81+
## 子流程数据重置(issue #219
82+
83+
子流程汇聚放行后、下游审批发现数据有误时,可对**已完成的子流程聚合记录**执行退回重走。重置是**独立接口**(不属于常规审批动作),能力完全由业务配置控制:
84+
85+
### 能力开关(子流程节点配置)
86+
87+
`SubProcessStrategy` 新增 `resettable` 布尔配置,**默认关闭**。仅当子流程节点开启该能力时,其汇聚完成后的下游待办记录才具备重置能力,`/api/cmd/record/subProcess/reset` 接口才允许调用;未开启时接口直接拒绝。历史流程定义无该字段时按关闭处理(`fromMap` 兼容)。
88+
89+
### 接口与定位方式
90+
91+
- 请求 `FlowSubProcessResetRequest``recordId`(当前操作的待办记录)+ `resetInstanceProcessIds`(选中重建的实例流程id)+ `advice`(重置说明)。
92+
- **不需要指定子流程节点**:由选中实例的流程id定位其所属聚合组(全部选中实例须同属一个未取代聚合组),重置始终是整个子流程**从头重走一次**——重建实例经由子流程节点配置的创建脚本与自动提交策略重新发起,不跳转、不迁移、不自动跳过任何历史数据。
93+
- 前置约束:当前记录为待办且操作人匹配;目标聚合组已放行(PASSED)、未被取代、全部实例已结束;当前流程无等待中聚合组;当前记录位于**锁定合并节点**(见下)。
94+
95+
### 锁定合并节点(复杂下游拓扑支持)
96+
97+
合并节点 = 子流程放行恢复后,锚点记录之后**首个产生业务记录的节点**。恢复遍历以锚点为来源记录,合并点首代记录均满足 `fromId == 锚点id`,据此从真实执行数据判定(抄送记录不参与判定):
98+
99+
- 串联 / 条件分支 / 触发节点 / 抄送直通:合并节点唯一——条件分支取**实际命中分支**的首个节点,触发与抄送节点不阻断、不产生合并点;
100+
- 并行 / 包容分支扇出:多个分支首节点记录并存时,按记录产生顺序**锁定第一个分支的节点**为合并节点,兄弟分支不可重置;
101+
- **重置仅允许在锁定合并节点的待办记录上发起**,合并节点之后的更深层节点不可重置(重置语义为退回子流程重走,入口固定在汇聚处)。
102+
103+
重置执行时同步清除该流程实例的循环触发标记(`LoopTriggerTraceContext.clearByProcess`),保证重走再次经过下游节点(如抄送)时不被被动式环检测误判为循环。
104+
105+
### 重置语义
106+
107+
- **聚合组建模**:旧聚合组标记 `superseded`(聚合状态保持不变,历史记录保留有效可查),新建一条聚合组取代其成为当前有效数据。新组由两类实例组成:
108+
- **继承实例**(未选中,`Instance.inherited=true`):沿用原实例的流程id与最终状态,不重新执行;
109+
- **重建实例**(选中):基于旧实例的启动记录反推创建请求(沿用原子流程定义、表单数据、流程标题与原发起人),创建全新子流程(新流程id),`Instance.sourceProcessId` 记录其替换的旧实例流程id,供订阅方完成旧 → 新映射。
110+
- **锚点不变**:新组沿用旧组的 `parentRecordId`(原触发记录),保证结果判定后主流程恢复位置不变。
111+
- **记录链作废**:触发锚点之后的主流程记录链以撤销语义作废(含执行重置的当前待办,重置说明写入该记录供审计),主流程退回子流程节点重新等待——**原待办从待办列表消失,新聚合组放行后生成全新的下游待办**;作废链路与 `FlowRevokeService` 一致,逐条推送 `FlowRecordRevokeEvent`
112+
- **结果判定自愈**:重建实例全部结束后,`FlowSubProcessResultService` 按新组判定(结果脚本经上下文可见继承 + 重建实例的完整最终记录集),放行后生成新的下游待办;`complete()` 定位聚合组时跳过已取代组,避免继承实例同属新旧两组时命中旧组。
113+
- **脚本视野**`FlowSession.findSubProcessRecords` 过滤已取代组,重置后脚本仅见当前有效组。
114+
- **事件通知**:推送 `FlowSubProcessResetEvent`(旧组快照 + 新组快照 + 重置记录id + 重置操作人 + mock),提醒业务订阅方子流程数据已被重置;订阅方收敛规则见 `docs/Integration/event-integration.md`
115+
116+
### 可见性(详情标识字段)
117+
118+
重置不是节点动作,不出现在动作列表中。流程详情 `FlowContent` 新增 `resetSubProcess` 布尔标识:当前待办位于「开启重置能力、已汇聚完成」的子流程下游时为 `true`。前端解析到该标识即可自行提供重置交互(呈现方式由使用方决定,不限定于操作列);节点视图中新旧聚合组同时呈现,`ProcessNode.SubProcessBody.superseded``SubProcessInstanceBody.inherited/sourceProcessId` 供前端区分。
119+
120+
### 数据结构兼容性
121+
122+
- `SubProcessRecord` 新增 `superseded` 标记、`Instance` 新增 `inherited`/`sourceProcessId` 字段,均为纯增量:持久化实例 JSON 由 fastjson2 按字段读写,旧数据缺失字段按 `false`/`null` 处理;实体新增可空列 `superseded`,存量行为 `null` 时按未取代处理;
123+
- `SubProcessStrategy.resettable``FlowContent.resetSubProcess` 均为新增字段,旧数据/旧客户端不受影响;
124+
- 重置不删除任何历史记录,旧聚合组与旧实例记录在其原流程内保持有效可查。
125+
78126
## 运行约束
79127

80128
- 主流程等待期间不允许撤销子流程触发记录。
81129
- 未自动提交的子流程草稿在主流程等待期间不允许删除。
82130
- 结果失败的异常策略仅支持跳转节点,不支持返回兜底操作人。
131+
- 子流程重置仅在节点开启 `resettable` 能力时可用,默认关闭。
132+
- 主流程等待期间(存在等待中聚合组)不允许执行子流程重置。
133+
- 结果未放行(ERROR)的聚合组不允许重置。
134+
- 重置仅允许在锁定合并节点的待办上发起;并行/包容分支的兄弟分支与合并节点之后的更深层节点不可重置。

flow-engine-framework/src/main/java/com/codingapi/flow/context/LoopTriggerTraceContext.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ public void clearExpired() {
7575
this.traces.entrySet().removeIf(entry -> now - entry.getValue() > DEFAULT_WINDOW_MILLIS);
7676
}
7777

78+
/**
79+
* 清除指定流程实例的全部触发标记。
80+
* <p>子流程重置等"退回重走"场景会作废既有记录链并重新触发下游节点,
81+
* 旧执行留下的标记不应把合法的重走误判为循环。</p>
82+
*
83+
* @param processId 流程实例标识
84+
*/
85+
public void clearByProcess(String processId) {
86+
String prefix = processId + ":";
87+
this.traces.keySet().removeIf(key -> key.startsWith(prefix));
88+
}
89+
7890
/**
7991
* 清空全部标记(测试隔离用)。
8092
*/

0 commit comments

Comments
 (0)