--- title: 流程运行 description: 送审、审批动作、转派加签、审批中心与进度查询 pageClass: g3-api --- # 流程运行 运行侧接口,前缀 `/workflow/runtime/**`。前端**只能提交意图**:改状态、建实例、推进节点、写待办全部在后端一个事务里完成;前端不允许直接改 `wf_instance` / `wf_task` / `wf_cc` 或业务表状态。 流程的**读取**走通用接口(待办/已办/进度这类有专门的运行侧查询接口,见下)。 > 前置:模块要先有**启用的流程绑定**(见[流程配置](/api/workflow)),否则送审会直接报「没有匹配到流程绑定」。 ## 送审 ### `POST` `/workflow/runtime/submit` 按模块 + 业务事件匹配绑定,建实例并激活首个节点。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `moduleId` | string | 是 | 模块编码 | | `businessId` | string | 是 | 业务主键(**按字符串传**,不依赖业务表主键类型) | | `businessNo` | string | | 业务单号,仅用于展示 | | `event` | string | | 业务事件,默认 `submit`;可选 `submit` / `resubmit` / `change` | | `context` | object | | 业务字段,参与条件分支与审批人解析(如金额、部门) | | `idempotencyKey` | string | | 幂等键,重复提交返回同一实例 | ```json { "moduleId": "v_b_othercompany", "businessId": "1234567890", "businessNo": "HT-2026-001", "event": "submit", "context": { "b_amount": 3200, "b_dept_id": "D002" }, "idempotencyKey": "submit-1234567890-v1" } ``` ```json { "code": 0, "message": "已提交", "data": { "instanceId": "01a134fe-bde5-7058-a6b9-5f7e4f86b991", "status": "running", "currentSummary": { "summary": "待审批:部门负责人审批" }, "pendingTaskCount": 1, "duplicated": false } } ``` - `duplicated` 为 `true` 表示**这次请求被判为重复**(幂等键已存在),没有产生新副作用,`instanceId` 是既有实例。 - 送审时会把发起人、机构写进运行变量;审批人解析为空时实例直接进入 `error`,不会跳过节点。 ## 审批动作 ### `POST` `/workflow/runtime/task/action` | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `taskId` | string | 是 | 待办任务 ID | | `action` | string | 是 | `approve` / `reject` / `return` | | `comment` | string | | 审批意见 | | `idempotencyKey` | string | | 幂等键,重复审批只生效一次 | 三个动作的差异: | action | 节点内是否立即生效 | 结果 | |---|---|---| | `approve` | 否,凑够应签人数才完成节点 | 节点完成后继续路由;全部走完则实例 `approved` | | `reject` | 是,第一票即生效 | 按节点配置的驳回策略走:终止 / 退回 / 退回上一节点 | | `return` | 是 | 直接退回发起人,实例 `returned` | ```json { "code": 0, "message": "处理成功", "data": { "instanceId": "01a134fe-bde5-7058-a6b9-5f7e4f86b991", "status": "running", "currentSummary": { "summary": "审批中:部门负责人审批" }, "pendingTaskCount": 0, "duplicated": false } } ``` > **注意 `status` 是实例状态**,不是本次动作的结果。同一节点还有其他人待签时,状态仍是 `running`。 ### `POST` `/workflow/runtime/task/claim` 抢占待办。仅当节点配置了 `allowClaim` 且调用者**确实是该任务的候选审批人**时可用。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `taskId` | string | 是 | 待办任务 ID | 候选审批人取自任务生成时落下的**审批人快照**,不是当前任务表——所以组织架构后来变了也不会影响已产生任务的归属判定。不是候选人会被拒绝。 ### `POST` `/workflow/runtime/task/transfer` 转交待办给他人。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `taskId` | string | 是 | 待办任务 ID | | `toUserId` | string | 是 | 接收人(必须是存在且启用的用户) | | `comment` | string | | 转交说明 | 原任务被取消,**新建一条任务**给接收人,并记录转交动作与通知事件。 ### `POST` `/workflow/runtime/task/add-approver` 加签:给当前节点追加审批人。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `taskId` | string | 是 | 同节点任一任务,用于定位节点 | | `userIds` | string[] | 是 | 追加的审批人 | | `comment` | string | | 说明 | 已是该节点活动审批人的会被跳过;加签会自动顺延应签人数,不会让节点永远凑不满票。 ### `POST` `/workflow/runtime/task/urge` 催办:**只发提醒,不改任务状态**(也不要求调用者是审批人,凭动作权限即可)。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `taskId` | string | 是 | 待办任务 ID | | `comment` | string | | 催办说明 | ## 实例操作 ### `POST` `/workflow/runtime/instance/withdraw` 撤回。发起人撤回运行中的实例。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `instanceId` | string | 是 | 流程实例 ID | | `comment` | string | | 撤回原因 | - 只能撤回**运行中**的实例;实例进入 `withdrawn`,全部未完成待办被取消。 - 发起人撤回自己发起的实例不需要额外权限;非发起人需要 `withdraw` 动作权限。 - 撤回后重新提交会产生**新实例**与新快照。 ### `POST` `/workflow/runtime/instance/cancel` 取消(管理员)。需要 `workflow_admin` 动作权限。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `instanceId` | string | 是 | 流程实例 ID | | `comment` | string | | 取消原因 | 实例进入 `cancelled`,其中明原因会写入实例摘要与取消动作。 ## 审批中心 ### `POST` `/workflow/runtime/todo/list` 我的待办:分配给我(含已抢占)且未处理完的任务。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `pageNo` | int | | 页码,默认 1 | | `pageSize` | int | | 每页条数,默认 50,**上限 200** | ```json { "code": 0, "message": "操作成功", "data": { "rows": [ { "task_id": "01a134fe-...", "node_key": "dept_manager", "task_status": "pending", "b_seq": 10, "b_due_at": "2026-10-14T02:00:00Z", "instance_id": "01a134ff-...", "b_business_id": "1234567890", "b_business_no": "HT-2026-001", "instance_status": "running", "initiator_name": "G3Soft", "definition_name": "费用报销审批", "node_name": "部门负责人审批" } ], "total": 1, "pageNo": 1, "pageSize": 50 } } ``` > **分页字段名是 `pageNo` / `pageSize`**,响应里也是这两个键(不是 `page` / `size`)。 > **排序**:按应处理时间升序,**没有时限的排在最后** —— 别拿第一行的 `b_due_at` 当作全表最小值。 ### `POST` `/workflow/runtime/done/list` 我已处理:我实际处理过的任务(含通过、驳回、转交前的处理)。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `pageNo` / `pageSize` | int | | 分页,默认 1 / 50,`pageSize` 上限 200 | ### `POST` `/workflow/runtime/instance/mine` 我发起的流程实例。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `pageNo` / `pageSize` | int | | 分页,默认 1 / 50,`pageSize` 上限 200 | ### `POST` `/workflow/runtime/cc/list` 抄送我的。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `pageNo` / `pageSize` | int | | 分页,默认 1 / 50,`pageSize` 上限 200 | ### `POST` `/workflow/runtime/cc/read` 批量标记抄送已读。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `ccIds` | string[] | 是 | 抄送记录 ID **数组**(注意是复数) | ```json { "ccIds": ["01a134fe-bde5-7058-a6b9-5f7e4f86b991"] } ``` 只允许标记**属于当前用户**的抄送记录,且会记录已读时间。数组为空时报 `1000`「请选择要标记的抄送记录」。 ## 进度与详情 ### `POST` `/workflow/runtime/instance/graph` 实例图:节点与连线的状态 + 时间线,用于「查看审批进度」。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `instanceId` | string | 是 | 流程实例 ID | 返回内容包含: | 项 | 说明 | |---|---| | 节点列表 | 每个节点的状态、应签/已签人数、审批人快照 | | 连线状态 | 哪些连线走过 | | 时间线 | 动作历史(`wf_action`),按发生时间排列 | | `timing` | 节点耗时 `elapsedMs` 与进出时间 | > **超时判定在前端做**:后端只返回节点已耗时与节点配置的 `b_due_hours`,**不判断是否超期、也不算剩余时间**。 > 另外,任务超时被标记为 `expired` 后,会从待办列表移除,但**实例仍保持 `running`**(超时只标记任务,不改流程)。 ### `POST` `/workflow/runtime/instance/by-business` 按业务单据查审批状态:业务详情页的状态条用。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `moduleId` | string | 是 | 模块编码 | | `businessId` | string | 是 | 业务主键 | 返回:最近一次实例、**当前用户在该单据上的待办**、以及该模块是否已配置绑定。 > 即使模块**没有**配置绑定也会正常返回(提示未接入审批),不会报错——业务详情页可以无脑调用。 ### `POST` `/workflow/runtime/task/detail` 任务详情:任务 + 实例 + 节点配置(含表单策略)。 | 字段 | 类型 | 必填 | 说明 | |---|---|---|---| | `taskId` | string | 是 | 待办任务 ID | 返回节点配置里的表单策略(哪些字段只读、哪些必填),审批页据此渲染表单。 ## 常见错误 | 场景 | 表现 | |---|---| | 模块没有启用的绑定 | `1000`,`message` 形如「模块「x」的事件「submit」没有匹配到流程绑定」 | | `event` 不合法 | `1000`,只接受 `submit` / `resubmit` / `change` | | 任务已被他人处理 | `1000`,状态已不是 `pending` / `claimed` | | 流程已结束 | `1000`,实例状态不是 `running` | | 不是候选审批人却抢占 | `1000`,「你不是该任务的候选审批人,无法抢占」 | | 缺少动作权限 | `1000`,「没有模块「x」的动作权限「y」,请联系管理员授权」 | | 并发推进实例 | `1000`,「流程实例已被并发推进(版本不匹配),请刷新后重试」 | | 重复请求(带幂等键) | **不是错误**:`code: 0` + `duplicated: true` |