Skip to content
Merged

Dev #267

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1eb2a2d
chore: 升级 codingapi.framework 到 17.3.2
xlorne Nov 10, 2026
180aede
fix: 并签未配置比例时按全部通过处理并补充合并审批多模式验证 (#265)
xlorne Nov 12, 2026
ca87ff1
fix: 并签未配置比例时按全部通过处理并补充合并审批多模式验证 (#265)
xlorne Nov 12, 2026
0f1aadb
feat: 多人审批节点新增减签能力 (#266)
xlorne Nov 12, 2026
d09dc02
Merge branch 'feature/issue-265-merge-audit-verify' into dev
xlorne Nov 12, 2026
3891514
Merge branch 'feature/issue-266-reduce-audit' into dev
xlorne Nov 12, 2026
8856ebb
test: 补充数据层直接增删审批人的机制验证用例 (#264)
xlorne Nov 12, 2026
6463299
docs: issue 回复必须关联代码提交(仓库 / 分支 / 提交号 / PR)
xlorne Nov 12, 2026
ec6343f
fix: 减签动作排在加签之后,测试改用按类型取动作 (#266)
xlorne Nov 12, 2026
62cc879
feat: 加签去重、减签单人校验与合并审批批量能力声明
xlorne Nov 12, 2026
08760b7
chore: 更新前端子模块指针至 dev(batchable 与减签候选人)
xlorne Nov 12, 2026
483981d
fix: 合并审批单条详情按记录计算按钮,批量能力仅限制减签
xlorne Nov 12, 2026
5a8888c
test: 补充未开启记录合并时的减签展示用例并同步前端子模块
xlorne Nov 12, 2026
c748ee7
chore: 同步前端子模块指针(PC/移动端减签能力对齐)
xlorne Nov 12, 2026
ff417b4
docs: 对齐减签/加签与合并审批文档,长段落表格化
xlorne Nov 12, 2026
94e4b91
chore: 同步前端子模块指针(合并审批表单数据修复)
xlorne Nov 12, 2026
23e2040
chore: 同步前端子模块指针(合并审批表单视图重渲染崩溃修复)
xlorne Nov 12, 2026
f00791a
Update flow-frontend to latest main
xlorne Nov 12, 2026
1c13577
Update flow-frontend to latest main
xlorne Nov 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Flow Engine 是一个企业级工作流引擎,基于 Java 17 和 Spring Boot 3
| 包路径 | 职责 |
|--------|------|
| `com.codingapi.flow.node` | 19 种流程节点类型 |
| `com.codingapi.flow.action` | 8 种动作类型 |
| `com.codingapi.flow.action` | 9 种动作类型 |
| `com.codingapi.flow.strategy` | 节点策略和工作流策略 |
| `com.codingapi.flow.script` | Groovy 脚本运行时 |
| `com.codingapi.flow.repository` | 仓储接口定义 |
Expand Down Expand Up @@ -57,14 +57,14 @@ REST 层通过请求参数 `mockKey` 分流到 Mock 实例(见 `FlowRecordCont

### 节点 / 动作 / 策略三元组

19 种节点(`node/nodes/`)× 8 种动作(`action/actions/`),行为由策略(`strategy/node/`、`strategy/workflow/`)驱动而非继承扩展:
19 种节点(`node/nodes/`)× 9 种动作(`action/actions/`),行为由策略(`strategy/node/`、`strategy/workflow/`)驱动而非继承扩展:
- `NodeStrategyManager` 持有节点策略(如 `RevokeStrategy`、`OperatorLoadStrategy`、`RecordMergeStrategy`)
- `ActionManager` 持有节点动作
- 节点类型常量见 `StartNode.NODE_TYPE`(= `NodeType.START.name()`),`FlowRecord.isNodeType(String)` 用于按类型判定

### 事件(IFlowEvent + EventPusher)

8 种事件:开始 / 待办 / 已办 / 完成 / 催办 / 撤销(`FlowRecordRevokeEvent`)/ 删除(`FlowRecordDeleteEvent`)/ 子流程重置(`FlowSubProcessResetEvent`)。操作完成后用 `EventPusher.push(event)` 推送;记录类事件携带 `FlowRecord` + `mock` 标记,重置事件携带新旧聚合组快照。外部可用 Spring `@EventListener` 订阅。
9 种事件:开始 / 待办 / 已办 / 完成 / 催办 / 撤销(`FlowRecordRevokeEvent`)/ 删除(`FlowRecordDeleteEvent`)/ 子流程重置(`FlowSubProcessResetEvent`)/ 减签(`FlowRecordReduceAuditEvent`)。操作完成后用 `EventPusher.push(event)` 推送;记录类事件携带 `FlowRecord` + `mock` 标记,重置事件携带新旧聚合组快照。外部可用 Spring `@EventListener` 订阅。

### API 模块划分

Expand Down Expand Up @@ -105,7 +105,7 @@ cd flow-engine-example && mvn spring-boot:run
4. 修复需测试先行并完成本地编译/测试验证;
5. 完成后**只推送该 issue 的特性分支到 GitHub**:提交内容、推送与 issue 关联均以该特性分支为准,**不合并 `dev`**;是否合并 `dev` 由人工决定,任务完成时仅提示分支与 PR 链接;
6. 提交信息使用 `Closes #<issue编号>` 关联 issue;进入默认分支后自动关闭,未自动关闭时需用户确认后手动关闭;
7. **特性分支推送到 GitHub 后再回复 issue**,说明修复内容、验证结果与推送分支/提交;未推送前不得发布完成回复(定位回复不受此限)。
7. **特性分支推送到 GitHub 后再回复 issue**,说明修复内容、验证结果与推送分支/提交;未推送前不得发布完成回复(定位回复不受此限);**回复涉及代码改动时必须附「仓库 + 分支 + 提交号(SHA) + PR 链接」,无代码改动需注明**。

### 基本规范

Expand Down
4 changes: 2 additions & 2 deletions docs/Integration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@
| [快速开始](./quick-start.md) | 最小集成步骤:引入依赖 → 提供 Bean → 编写流程 → 发起流程 |
| [流程用户体系](./user-integration.md) | `FlowOperatorGateway` / `IFlowOperator` / `GatewayContext` / 线程缓存 / 当前登录人 |
| [流程默认脚本](./script-integration.md) | 12 种脚本类型、`GroovyScriptRequest` 完整 API、`$bind`、默认脚本替换、脚本生命周期 |
| [流程事件机制](./event-integration.md) | 8 种事件、`EventPusher`、`IHandler` 订阅、异步分发管道、事务变体、推送时机 |
| [流程事件机制](./event-integration.md) | 9 种事件、`EventPusher`、`IHandler` 订阅、异步分发管道、事务变体、推送时机 |
| [仓储抽象与持久化](./repository-integration.md) | `IRepositoryHolder`、12 个仓储接口(含 11 个 JPA 实现 + 需自行实现的 `NodeViewJavaScriptRepository`)、脚本仓储、锁机制 |
| [Spring Boot 自动配置](./auto-configuration.md) | `AutoConfiguration` 8 个 Bean、4 个 Register、必选/可选 Bean 清单 |
| [REST API](./rest-api.md) | 5 个 Controller、`mockKey`/`operatorId` 分流机制、请求/响应结构 |
| [Mock 模式](./mock-mode.md) | 全内存沙箱 `MockInstance`、15 分钟过期、接入方式 |
| [节点/动作/策略扩展](./extension-points.md) | 19 种节点 × 8 种动作 × 15 种节点策略 × 2 种流程策略,扩展方式 |
| [节点/动作/策略扩展](./extension-points.md) | 19 种节点 × 9 种动作 × 15 种节点策略 × 2 种流程策略,扩展方式 |
| [编程式 API](./programmatic-api.md) | `WorkflowBuilder`/`FlowFormBuilder` 构建流程、`FlowService` 服务编排、查询服务 |

## 核心架构速览
Expand Down
21 changes: 10 additions & 11 deletions docs/Integration/event-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public interface IAsyncEvent extends IEvent {} // 异步事件标记 ←

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

### 1.2 八种事件
### 1.2 九种事件

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

Expand All @@ -40,19 +40,17 @@ public interface IAsyncEvent extends IEvent {} // 异步事件标记 ←
| `FlowRecordRevokeEvent` | 撤销(被撤销的后置记录) | `FlowRecord currentRecord, boolean mock` | `getCurrentRecord()` / `isMock()` |
| `FlowRecordUrgeEvent` | 催办(每条待办) | `FlowRecord, IFlowOperator urgeOperator, boolean mock` | `getFlowRecord()` / `getUrgeOperator()` / `isMock()` |
| `FlowRecordDeleteEvent` | 删除未流转实例(开始节点) | `FlowRecord, boolean mock` | `getFlowRecord()` / `isMock()` |
| `FlowRecordReduceAuditEvent` | 减签(每条被撤下的记录) | `FlowRecord, long reduceOperatorId, boolean mock` | `getFlowRecord()` / `getReduceOperatorId()` / `isMock()` |
| `FlowSubProcessResetEvent` | 子流程数据重置(issue #219) | `SubProcessRecord oldRecord, SubProcessRecord newRecord, long resetRecordId, IFlowOperator resetOperator, boolean mock` | `getOldRecord()` / `getNewRecord()` / `getResetRecordId()` / `getResetOperator()` / `isMock()` |

**子流程重置事件订阅须知**:

- 事件提醒业务方子流程数据已被重置:`oldRecord` 为被取代的旧聚合组快照(含全部旧实例流程id),
`newRecord` 为重置后的新聚合组——继承实例沿用原流程id,重建实例为新流程id,
重建实例的 `getSourceProcessId()` 记录其替换的旧实例流程id,据此完成旧 → 新流程id映射;
- 事件经异步线程池分发、到达顺序不保证(重置同时伴随被作废记录的 `RevokeEvent`
与重建实例的 `StartEvent`/`TodoEvent`),订阅方应按「同一 `parentRecordId` + 节点维度下
**最大聚合组 id 为活跃组**」做幂等覆盖式收敛,禁止增量累加;
- 事件的 `oldRecord` id 与订阅方当前活跃组 id 不一致时,说明存在丢失的重置事件,
应通过节点记录查询(`/api/cmd/record/processNodes`)全量对账;
- 旧实例记录不会被删除(仅聚合组标记已取代),订阅方收到事件后应自行将旧流程id的数据标记失效。
| 关注点 | 约定 |
|---|---|
| 新旧聚合组 | `oldRecord` 为被取代的旧聚合组快照(含全部旧实例流程id);`newRecord` 为新聚合组,继承实例沿用原流程id、重建实例为新流程id,重建实例的 `getSourceProcessId()` 指向被替换的旧实例 |
| 收敛方式 | 事件异步、到达顺序不保证(同时伴随 `RevokeEvent`/`StartEvent`/`TodoEvent`);按「同一 `parentRecordId` + 节点维度下最大聚合组 id 为活跃组」幂等覆盖,禁止增量累加 |
| 丢事件对账 | `oldRecord` id 与订阅方当前活跃组不一致时,视为丢事件,调用 `/api/cmd/record/processNodes` 全量对账 |
| 旧数据 | 旧实例记录不删除(仅标记聚合组已取代),订阅方需自行将旧流程id数据标记失效 |

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

Expand All @@ -72,7 +70,7 @@ public class FlowRecordUrgeEvent implements IFlowEvent {

- 基本信息:`getId()`、`getProcessId()`、`getWorkTitle()`、`getWorkCode()`、`getNodeId()`、`getNodeType()`、`getNodeName()`、`getTitle()`、`getFormData()`(`Map<String,Object>`)
- 操作人:`getCurrentOperatorId/Name()`、`getSubmitOperatorId/Name()`、`getCreateOperatorId/Name()`、`getForwardOperatorId/Name()`
- 状态:`isTodo()`、`isDone()`、`isFinish()`、`isAutoDone()`、`isShow()`、`isNotEndNode()`
- 状态:`isTodo()`、`isDone()`、`isFinish()`、`isAutoDone()`、`isShow()`、`isNotEndNode()`、`isPendingApproval()`(待审批,含顺序审批未激活的隐藏记录)
- 抄送标记:`isNotify()`
- 动作:`getActionId()`、`getActionType()`、`getActionName()`、`getAdvice()`
- 并行:`getParallelId()`、`getParallelBranchNodeId()`、`getParallelBranchTotal()`
Expand Down Expand Up @@ -113,6 +111,7 @@ EventPusher.push(new FlowRecordStartEvent(flowRecord, session.isMock()));
| `RejectAction.run()` | 当前记录 `DoneEvent` + 回退新记录 `TodoEvent` |
| `ReturnAction.run()` | 当前记录 `DoneEvent` + 退回节点新记录 `TodoEvent` |
| `AddAuditAction.run()` | 加签记录 `TodoEvent` |
| `ReduceAuditAction.run()` | 每条被撤下记录 `ReduceAuditEvent` |
| `TransferAction.run()` | 当前记录 `DoneEvent` + 转交人新记录 `TodoEvent` |
| `DelegateAction.run()` | 当前记录 `DoneEvent` + 委托人新记录 `TodoEvent` |
| `EndNode.fillNewRecord()` | `FinishEvent`(流程正常完成) |
Expand Down
31 changes: 17 additions & 14 deletions docs/Integration/extension-points.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,28 @@ SUB_PROCESS, TRIGGER
| `TriggerNode` | `TRIGGER` | 触发节点(`TriggerScript` 副作用) |
| `SubProcessNode` | `SUB_PROCESS` | 子流程节点 |

### 1.2 动作(8 种)
### 1.2 动作(9 种)

`ActionType` 枚举(`flow-engine-framework/.../action/ActionType.java`):

```
SAVE, PASS, REJECT, ADD_AUDIT, DELEGATE, RETURN, TRANSFER, CUSTOM
保存 通过 拒绝 加签 委派 退回 转办 自定义
SAVE, PASS, REJECT, ADD_AUDIT, REDUCE_AUDIT, DELEGATE, RETURN, TRANSFER, CUSTOM
保存 通过 拒绝 加签 减签 委派 退回 转办 自定义
```

| 动作 | 特有配置 | 说明 |
|---|---|---|
| `SaveAction` | — | 仅保存表单,不流转 |
| `PassAction` | — | 通过 → 推进入下一节点;多人审批按策略自动办结/激活 |
| `RejectAction` | `ActionRejectScript` | 拒绝 → 退回指定节点/退回上级/终止流程(`TERMINATE`) |
| `AddAuditAction` | `OperatorLoadScript` + `maxOperatorCount` | 加签(追加审批人) |
| `DelegateAction` | `OperatorLoadScript` + `maxOperatorCount` | 委派(他人代审) |
| `ReturnAction` | — | 退回(目标由 `FlowAdvice.backNode` 提供) |
| `TransferAction` | `OperatorLoadScript` + `maxOperatorCount` | 转办 |
| `CustomAction` | `ActionCustomScript` + `triggerFrontEvent` | 自定义动作(脚本返回动作类型) |
| 动作 | 特有配置 | 批量提交 | 说明 |
|---|---|---|---|
| `SaveAction` | — | 支持 | 仅保存表单,不流转 |
| `PassAction` | — | 支持 | 通过 → 推进入下一节点;多人审批按策略自动办结/激活 |
| `RejectAction` | `ActionRejectScript` | 支持 | 拒绝 → 退回指定节点/退回上级/终止流程(`TERMINATE`) |
| `AddAuditAction` | `OperatorLoadScript` + `maxOperatorCount` | 支持 | 加签;目标已在本节点待办队列时拒绝 |
| `ReduceAuditAction` | `OperatorLoadScript` | **不支持** | 减签;仅循序/并签节点,仅剩 1 名待审批人时拒绝 |
| `DelegateAction` | `OperatorLoadScript` + `maxOperatorCount` | 支持 | 委派(他人代审) |
| `ReturnAction` | — | 支持 | 退回(目标由 `FlowAdvice.backNode` 提供) |
| `TransferAction` | `OperatorLoadScript` + `maxOperatorCount` | 支持 | 转办 |
| `CustomAction` | `ActionCustomScript` + `triggerFrontEvent` | 支持 | 自定义动作(脚本返回动作类型) |

> `batchable()` 由 `IFlowAction` 声明,默认 `true`;`ReduceAuditAction` 为 `false`,detail 响应中以 `actions[].batchable` 下发。

### 1.3 节点策略(15 种)

Expand Down Expand Up @@ -155,7 +158,7 @@ IFlowAction#run
| 子类 | 典型工厂方法与错误场景 |
|---|---|
| `FlowValidationException` | `fieldReadOnly`(字段只读)、`operatorCountExceeded(nodeId, max)`(超出最大可选人数)、`operatorOutOfRange`(超出可选范围)、`required` 等 |
| `FlowExecutionException` | `scriptExecutionError`、`routerNodeNotFound`、`operatorCountExceeded`、`invalidJumpTarget(nodeType)`、`errorTriggerLoop`、`subProcessLoop` 等 |
| `FlowExecutionException` | `scriptExecutionError`、`routerNodeNotFound`、`operatorCountExceeded`、`invalidJumpTarget(nodeType)`、`errorTriggerLoop`、`subProcessLoop`、`addAuditOperatorAlreadyInTodo`、`reduceAuditSingleApprover`、`reduceAuditTargetNotFound` 等 |
| `FlowStateException` | `repositoryNotRegistered`、`recordAlreadyDone`、`operatorNotMatch`、`workflowAlreadyDisable`、`recordNotSupportRevoke` 等 |
| `FlowNotFoundException` | `workflow(workCode)`、`record(recordId)`、`node(nodeId)`、`operator(operatorId)`、`action(actionId)` |
| `FlowPermissionException` | `accessDenied(operation)` |
Expand Down
13 changes: 12 additions & 1 deletion docs/Integration/mock-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,18 @@ public void clear(String key); // 注销并 cancel 过期调
### 2.3 `MockRepositoryHolder`(`mock/MockRepositoryHolder.java`)

- 实现 `IRepositoryHolder`,构造时 `new` 出 9 个内存仓储(`mock/repository/` 下):
`DelayTaskRepositoryMockImpl`、`FlowRecordRepositoryMockImpl`、`FlowTodoMergeRepositoryMockImpl`、`FlowTodoRecordRepositoryMockImpl`、`ParallelBranchRepositoryMockImpl`、`SubProcessRepositoryMockImpl`、`UrgeIntervalRepositoryMockImpl`、`WorkflowRuntimeRepositoryMockImpl`、`WorkflowVersionRepositoryMockImpl`。

| 仓储实现 | 对应数据 |
|---|---|
| `FlowRecordRepositoryMockImpl` | 流程记录 |
| `FlowTodoRecordRepositoryMockImpl` | 待办记录 |
| `FlowTodoMergeRepositoryMockImpl` | 待办合并关系 |
| `WorkflowRuntimeRepositoryMockImpl` | 运行时快照 |
| `WorkflowVersionRepositoryMockImpl` | 流程版本 |
| `SubProcessRepositoryMockImpl` | 子流程聚合 |
| `ParallelBranchRepositoryMockImpl` | 并行分支 |
| `DelayTaskRepositoryMockImpl` | 延迟任务 |
| `UrgeIntervalRepositoryMockImpl` | 催办间隔 |
- 流程设计(`WorkflowRepository`)使用**真实传入**的仓储 —— Mock 模式可复用生产已保存的流程设计。
- `operatorAssignmentCache`(HashMap,key = `processId:nodeId`)模拟「发起人设定操作人」分配。
- `createFlowActionService(session)` 传 `allowDisabledAction = true`(Mock 下允许执行停用流程的动作)。
Expand Down
27 changes: 14 additions & 13 deletions docs/Integration/programmatic-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,16 +184,17 @@ workflowService.saveWorkflowVersion(new WorkflowVersion(workflow), true, true);

引擎内部以不可变 `FlowSession` 传递执行上下文,业务方在脚本/事件处理中可读取:

```java
session.getCurrentOperator() // 当前审批人
session.getCreatedOperator() // 流程创建者
session.getSubmitOperator() // 提交人
session.getWorkflow() // 流程设计
session.getCurrentNode() // 当前节点
session.getCurrentAction() // 当前动作
session.getCurrentRecord() // 当前记录
session.getFormData("fieldCode") // 表单字段值
session.isMock() // 是否 Mock
session.matchNextNodes() // 匹配的下一节点列表
session.updateSession(node) // 不可变式切换会话(内部流转用)
```
| 方法 | 返回 | 用途 |
|---|---|---|
| `getCurrentOperator()` | `IFlowOperator` | 当前审批人 |
| `getCreatedOperator()` | `IFlowOperator` | 流程创建者 |
| `getSubmitOperator()` | `IFlowOperator` | 提交人 |
| `getWorkflow()` | `Workflow` | 流程设计 |
| `getCurrentNode()` | `IFlowNode` | 当前节点 |
| `getCurrentAction()` | `IFlowAction` | 当前动作 |
| `getCurrentRecord()` | `FlowRecord` | 当前记录 |
| `getCurrentNodeRecords()` | `List<FlowRecord>` | 当前节点实例的全部记录(含隐藏) |
| `getFormData(code)` | `Object` | 表单字段值 |
| `isMock()` | `boolean` | 是否 Mock |
| `matchNextNodes()` | `List<IFlowNode>` | 匹配的下一节点 |
| `updateSession(node)` | `FlowSession` | 不可变式切换会话(内部流转用) |
Loading
Loading