124 lines
7.2 KiB
Markdown
124 lines
7.2 KiB
Markdown
# sql —— 工作流模块 SQL 脚本
|
||
|
||
本目录是从原型 `fms-api` 迁移工作流引擎时一并带过来的 SQL Server 脚本,用于把工作流的
|
||
表、视图、模块/菜单/权限注册和示例数据建到目标库。
|
||
|
||
> **本目录不包含 `fms_core.sql`。** 目标项目 g3soft-erp 的库已经具备核心表
|
||
> (`s_module` / `s_field` / `s_module_schema` / `s_menu` / `s_power` / `s_user_power` /
|
||
> `b_user` / `b_dept` 等)。**如果目标库缺少这些核心表,本目录的脚本无法独立完成初始化**,
|
||
> 请先建好核心表再执行。
|
||
|
||
## 执行顺序
|
||
|
||
按目录名前缀的数字顺序执行:
|
||
|
||
| 顺序 | 脚本 | 作用 | 幂等 |
|
||
|---|---|---|---|
|
||
| 1 | `00_core_companion/workflow_audit_tables.sql` | `s_log_audit` / `s_log_audit_field`(**可选**,用于 §11 审计) | 是 |
|
||
| 2 | `10_workflow/01_fms_workflow.sql` | 13 张 `wf_*` 表 + 索引(只建表,不含数据) | 否(重复执行会报表已存在) |
|
||
| 3 | `10_workflow/02_fms_workflow_org_ext.sql` | 组织主数据扩展:`b_dept.b_manager_id`、`b_user.b_leader_id`、`b_position` / `b_user_position` / `b_org_role` / `b_user_org_role` | 是 |
|
||
| 4 | `20_registration/10_fms_workflow_module.sql` | 4 个只读视图 + `s_module` / `s_field` / `s_module_schema` / `s_menu_module` 注册 | 是(先删后建) |
|
||
| 5 | `20_registration/11_fms_workflow_menu.sql` | 流程管理 / 审批中心菜单 + `s_i18n` | 是 |
|
||
| 6 | `20_registration/12_fms_workflow_power.sql` | 菜单权限点 + 动作权限点 + 授予 `g3soft` | 是 |
|
||
| 6.5 | `20_registration/13_table_test_menu.sql` | 表格测试根菜单(`/table-test`)+ `s_i18n` | 是 |
|
||
| 6.6 | `20_registration/14_dashboard_menu.sql` | 首页根菜单(`/dashboard`)+ `s_i18n` | 是 |
|
||
| 7 | `30_patches/01_fms_workflow_index_patch.sql` | 补充索引(见下) | 是 |
|
||
| 8 | `90_mock/01_fms_workflow_mock.sql` | **示例**流程定义 `expense_approval`(14 节点 / 17 连线 / 13 审批人规则) | 否 |
|
||
| 9 | `90_mock/02_fms_workflow_runtime_mock.sql` | **示例**运行数据:演示机构/用户 + 6 个实例 | 否 |
|
||
|
||
**第 2 步不幂等**:`01_fms_workflow.sql` 是纯 `create table`。表已存在时会报错,需要先 drop
|
||
(脚本头部有说明)。第 8、9 步是示例数据,**生产环境不要执行**。
|
||
|
||
### 第 6 步的顺序陷阱
|
||
|
||
`12_fms_workflow_power.sql` 的动作权限点是这样生成的:
|
||
|
||
```sql
|
||
select distinct b_module_id from dbo.wf_binding where b_canuse = 1
|
||
```
|
||
|
||
**`wf_binding` 为空时,动作权限点会生成 0 条。** 三个缓解手段,按需选用:
|
||
|
||
1. 先跑 mock(第 8 步,它含一条 binding)再跑第 6 步;或先在应用里保存一条流程绑定;
|
||
2. 应用侧本身会自愈——`WorkflowConfigService.saveBinding` 每次保存绑定都会调用
|
||
`ensureActionPowers(moduleId)`,按 `action.{模块}.{动作}` 幂等补点;
|
||
3. 该脚本可重复执行,binding 补上后再跑一次即可。
|
||
|
||
动作权限点写入 `s_power (b_type='action', b_object_type='module', b_object_id=模块, b_action=动作)`,
|
||
与 `SqlPermissionService.requireAction` 的匹配列一致,两边天然对齐。
|
||
|
||
## 组织主数据扩展(第 3 步)不是可选项
|
||
|
||
`02_fms_workflow_org_ext.sql` 建的表/列是 8 种审批人里 4 种的前提:
|
||
|
||
| 审批人类型 | 依赖 |
|
||
|---|---|
|
||
| `dept_manager` | `b_dept.b_manager_id` |
|
||
| `initiator_manager` | `b_user.b_leader_id` |
|
||
| `position` | `b_position` / `b_user_position` |
|
||
| `org_role` | `b_org_role` / `b_user_org_role` |
|
||
|
||
不执行该脚本时,这 4 类审批人会**静默解析为空**,再由节点兜底策略(fallback / 置 `error`)接管。
|
||
|
||
## mock 数据的模块依赖
|
||
|
||
`90_mock/02_fms_workflow_runtime_mock.sql` 使用模块 `v_b_othercompany` 作为绑定目标。
|
||
目标库若没有该 `s_module` 行,请把它替换为库里真实存在的业务模块,或先补建该模块。
|
||
|
||
## 运行时语义备忘(与本目录脚本相关的两条约定)
|
||
|
||
- **任务超时只标记、不自动改流程**:超时任务会被置 `expired` 并从待办列表过滤掉,
|
||
实例保持 `running`,由管理员通过加签 / 转派处理。**不自动提升串行后继、不置 `error`、不自动通过。**
|
||
因此串行会签链中若当前审批人超时,链路会停住等待人工介入。
|
||
- **审计探写**:`WorkflowAuditWriter` 每事务探测 `s_log_audit` 是否存在;不存在时写一条
|
||
`wf_outbox(audit.skipped)` 并继续。**库里没有审计表不算故障**,不会阻断审批。
|
||
|
||
## 菜单现在是动态的(第 6.5 步的背景)
|
||
|
||
前端侧栏菜单已改为**动态**:读 `s_menu` 表构建菜单树(对照 fms-vue 的 `stores/menus.js`
|
||
+ `AppSidebar`),不再使用前端常量菜单。因此**新增页面必须两步都做**:
|
||
|
||
1. 在前端加一条静态路由(`web/src/router/index.ts`,数据库存不了 Vue 组件);
|
||
2. 在 `s_menu` 里录一条菜单(决定是否出现在侧栏、叫什么、排第几)。
|
||
|
||
`13_table_test_menu.sql` 就是第 2 步的示例:把「表格测试」页挂成**根菜单**,路由 `/table-test`。
|
||
|
||
**权限是白名单语义**:`s_power` 里没有 `menu.{菜单编码}`、或用户未被授权,侧栏就**不显示**该菜单、
|
||
路由守卫也会拦到 `/403`。`g3soft` 超管前后端都提权、不受此限,调试时先用它登录最省事;
|
||
要给普通账号开放,用该脚本末尾注释掉的「可选:给普通账号授权」那段。
|
||
|
||
### ⚠️ 菜单与路由必须成对,否则点进去是 404
|
||
|
||
**`s_menu` 里有 `b_route` 的每一条页面菜单,前端都必须有一条 path 完全相同的静态路由。**
|
||
少一条,点击该菜单就会落到 router 末尾的 `/:pathMatch(.*)*` 通配路由 → 显示 404 缺省页。
|
||
|
||
实测过的坑:只给已实现的页面建路由、其余菜单不建,会表现为「**菜单点进去全是 404**」——
|
||
而 Vite dev server 对任意路径都返回 HTTP 200(SPA fallback),所以浏览器网络面板里
|
||
**看不到 404 状态码**,容易误判成后端或代理问题。真正的判据是**路由是否匹配**,不是 HTTP 状态。
|
||
|
||
当前 `s_menu` 的 13 条页面菜单与前端路由已逐条对齐:
|
||
|
||
| s_menu `b_route` | 页面 | 状态 |
|
||
|---|---|---|
|
||
| `/dashboard` | 首页 | 占位(`views/dashboard/index.vue`) |
|
||
| `/table-test` | 表格测试 | **已实现**(G3Table 完整范例) |
|
||
| `/approval/todo`、`/initiated`、`/done`、`/cc` | 审批中心 | 占位 |
|
||
| `/base/othercompany`、`/contact`、`/otherdata` | 基础资料 | 占位 |
|
||
| `/system/user`、`/system/workflow`、`/system/log/login`、`/system/log/audit` | 系统管理 | 占位 |
|
||
|
||
占位页统一复用 `web/src/components/PlaceholderPage.vue`:把真实页面写进对应视图文件即可,
|
||
**路由与菜单都不用再改**。
|
||
|
||
## 补充索引说明(第 7 步)
|
||
|
||
| 索引 | 解决什么 |
|
||
|---|---|
|
||
| `ix_wf_binding_version (b_version_id)` | 发布校验与删除定义按 `b_version_id` 直查,原索引不以它打头 |
|
||
| `ix_wf_task_completed_by (b_completed_by, b_completed_at)` | 「我已处理」列表过滤列原无索引 |
|
||
| `ix_wf_task_node_run (b_node_run_id, b_status, b_seq, b_id)` | 串行会签提升、兄弟任务取消 |
|
||
|
||
## 行尾
|
||
|
||
本目录 `*.sql` 一律 LF(根 `.gitattributes` 已声明 `*.sql text eol=lf`)。
|
||
从 fms 仓拷贝时已做 LF 转换。
|