20260928171336

This commit is contained in:
oneao committed 2026-09-28 17:13:37 +08:00
1 parent f22e67fc85
commit cbd49ded6f
34 files changed
+738 -7842

No files matched your search

@@ -1,7 +1,7 @@
# FMS 通用保存接口(saveobjt)条件更新设计
> 状态:设计稿(已补齐首期实现约束)
> 涉及:`fms-api` 通用保存链路、`fms-vue` 审批类动作
> 涉及:`fms-api` 通用保存链路、`fms-vue` 审批类动作、workflow 草稿插件式接入评估
---
@@ -822,6 +822,154 @@ select ... from dbo.demo_order with (UPDLOCK, HOLDLOCK) where b_id = ?
---
## 14. Workflow 插件式接入评估
### 14.1 结论
可以把 workflow 的**前端调用入口**做成插件,并让插件把「草稿配置持久化」编排成一个
`saveobjt` 请求;但不能把 workflow 后端实现整体搬到 `fms-vue/src/services`,也不能让前端
直接用 `saveobjt` 改运行时表。
这里要区分两件事:
| 层 | 可以插件化的内容 | 不能下沉到前端的内容 |
| --- | --- | --- |
| 前端 services | 请求构造、草稿 diff、多个表块合并、错误码映射、能力发现 | 权限判定、事务、锁、幂等的最终保证 |
| `saveobjt` | draft 配置表的原子增删改、`guards`、`$original` 冲突检测 | 流程校验、版本发布、节点推进、消息副作用 |
| workflow 后端 | 配置策略适配器、发布和运行命令 | 不能被浏览器代码替代 |
因此,所谓“移动到前端 services”应理解为**移动调用适配层**,不是移动业务权威。
前端插件只是 `saveobjt` 和现有 workflow command API 的统一外观;数据库权限、审计列和最终
约束仍由服务端负责。
### 14.2 接口分类和迁移边界
| 现有接口 / 能力 | 插件方案 | 原因 |
| --- | --- | --- |
| `definition/save` | 可改为 `saveobjt` 草稿配置适配器,但要保留定义编码校验和创建初始版本的后端策略 | 新建定义会同时创建 `wf_version`,不能让两次独立请求代替一个事务 |
| `version/draft/save` | **首选迁移对象**:一个 `saveobjt` 请求更新版本、删除旧节点/连线/规则并插入新快照 | 这是纯配置快照写入,但必须有 draft guard 和并发冲突检测 |
| `version/validate` | 保留后端命令;前端可先做同构校验 | 需要读取模块字段白名单,前端校验不能成为最终规则 |
| `version/publish` / `version/retire` | 保留后端命令 | 发布校验、checksum、不可变版本和状态迁移必须在同一事务内完成 |
| `version/copy` | 保留后端命令 | 需要分配新版本号并复制三张子表,存在并发唯一性问题 |
| `binding/save/delete` | 默认保留后端命令;仅在增加 workflow 专用 save policy 后再评估 | 绑定要求已发布版本、优先级唯一,并会创建动作权限点 |
| `runtime/submit`、`task/action`、`claim`、`transfer`、`add-approver`、`withdraw`、`cancel`、`urge` | **禁止迁移为 `saveobjt` 直写** | 这些动作会锁定实例/任务,写审计,推进节点,投影业务状态,写 outbox,并依赖 `request_id` 幂等 |
| `todo/done/graph/detail/cc` 查询 | 可以继续由插件封装现有查询 API | 查询不改变运行状态 |
| `cc/read` | 默认保留专用接口 | 需要校验当前用户是抄送接收人;不能用无权限的通用更新替代 |
运行时表 `wf_instance`、`wf_node_run`、`wf_task`、`wf_action`、`wf_cc`、`wf_outbox` 不得登记为
普通 `saveobjt` 数据模块。前端传入这些表时,服务端应在保存入口直接拒绝,而不是依赖插件代码
“自觉不调用”。这条是安全边界,不是 UI 约定。
### 14.3 草稿插件的服务契约
插件不直接暴露数据库表给业务页面,而是提供稳定的领域方法:
```js
export const workflowPlugin = {
code: 'workflow',
capabilities: {
draftPersistence: 'saveobjt',
publish: 'command',
runtime: 'command',
},
loadDraft,
saveDraft,
validateVersion,
publishVersion,
submit,
act,
}
```
页面只依赖 `workflowPlugin`,不依赖 `saveObjectApi`,也不拼 `wf_*` 表块。插件内部的
`saveDraft` 才负责把领域草稿转换成保存请求;这样将来从 `saveobjt` 切回专用接口时,页面
无需改动。
建议的最小请求形状如下(示意,子表删除行必须是完整唯一键):
```json
[
{
"table": "wf_version",
"key_field": "b_id",
"updates": [
{
"b_id": "expense_v1",
"b_canvas_json": "{...}",
"$original": { "b_row_version": 7 }
}
],
"guards": {
"updates": [
{
"condition": "b_status = 'draft'",
"message": "流程草稿已发布或停用,不能继续修改",
"retryable": false
}
]
}
},
{
"table": "wf_node",
"key_field": "b_id",
"deletes": [{ "b_id": "old_node_1" }],
"inserts": [{ "b_id": "new_node_1", "b_version_id": "expense_v1", "...": "..." }],
"guards": {
"deletes": [
{
"condition": "exists (select 1 from dbo.wf_version v where v.b_id = {b_version_id} and v.b_status = 'draft')",
"refs": ["wf_version"],
"message": "流程草稿已发布,不能删除节点",
"retryable": false
}
],
"inserts": [
{
"condition": "exists (select 1 from dbo.wf_version v where v.b_id = {b_version_id} and v.b_status = 'draft')",
"refs": ["wf_version"],
"message": "流程草稿已发布,不能新增节点",
"retryable": false
}
]
}
}
]
```
上例要求 `wf_version` 有可比较的 `b_row_version`。当前运行表已有该列,但配置版本表还没有;
若启用草稿插件,应给 `wf_version` 增加版本列,并在每次草稿保存成功后递增。否则只能保护
“仍是 draft”,不能防止两个设计器互相覆盖整个快照。节点、连线和审批人规则的删除与插入
必须和版本行在同一 `saveobjt` 请求中完成,不能拆成多个请求。
### 14.4 服务端前置条件
草稿插件上线前必须增加 workflow 专用保存策略(可以是 `DataSaveService` 内的白名单适配器,
不要求新增一套 HTTP 协议):
1. 只允许 `wf_definition`、`wf_version`、`wf_node`、`wf_edge`、`wf_actor_rule` 的 draft 写入;
`wf_binding` 先继续走专用服务,运行时表一律拒绝。
2. 校验表块之间的 `b_version_id`、节点/连线引用和主版本存在性;不能只依赖外键或前端。
3. `wf_version.b_status = 'draft'` 是必需 guard;已发布版本不得通过普通局部更新修改配置。
4. 由服务端继续补审计列、校验当前用户的 workflow 管理权限,并统一返回 2002/2003。
5. 发布前仍必须调用后端 validator;`saveDraft` 成功不等于流程可发布。
6. 记录插件写入的 request id,至少能定位版本、操作者和保存结果;可重试动作仍使用后端幂等键。
未满足以上条件时,前端应继续调用 `version/draft/save`,不要为了“插件化”而绕过已有
`WorkflowConfigService`。
### 14.5 推荐迁移顺序和验收
1. 先新增 `workflowPlugin` 外观,内部仍调用现有 `/workflow/**`,保证页面调用面先稳定。
2. 给 `wf_version` 增加 `b_row_version`,实现草稿保存的 `saveobjt` 适配器和表白名单;保留旧接口作为回滚开关。
3. 用并发设计器、已发布版本、半成品节点引用、重复提交和事务回滚用例验证后,再切换默认实现。
4. 最后再评估 `definition/save`;`publish`、`runtime` 和 `cc/read` 不纳入本次迁移。
验收必须至少包括:两个设计器同时保存时一成一败并返回 2003;发布与草稿保存并发时不能修改已发布版本;
节点删除/插入任一步失败时版本和全部子表都回滚;前端无法通过 `saveobjt` 写入 `wf_task` 或 `wf_instance`;
运行时审批的锁、幂等、审计和业务状态投影回归用例全部通过。
---
## 附录 A:Demo(可直接执行)
### A.1 建表