# FMS 删除规则改造待做 > 创建:设计阶段已闭合、实施未开始。明日开工照此清单执行。 > 设计依据:`FMS删除规则引擎设计.md` **v2.0**(权威实现规格)、`FMS模块设计评审.md` v1.2(决策记录)。 > 前提确认:库中无存量规则/关系数据(`sql/` 种子脚本中 `s_relation` 零 insert),**全部按新结构直接建,不做迁移**。 ## 已定案的设计决策(不需要再讨论) | # | 决策 | 出处 | | --- | --- | --- | | 1 | `s_relation.b_on_delete` 单列四值:`restrict`(默认)/ `cascade` / `archive` / `none`;语义统一为「删除**被引用方**时,**引用方**怎么办」;被引用方由基数判定(many_to_one=目标 / one_to_many=源 / one_to_one=源) | 删除规则 v2.0 §3 | | 2 | 引用保护退出 `s_rule`,由 `b_on_delete='restrict'`(默认)表达;「有引用时拒绝删除」假列及全部联动补丁删除 | 删除规则 v2.0 §3 | | 3 | `s_rule` 新结构:`b_module_id` 直存、显式 `b_kind`(condition/sql)、去掉 `b_scope_type`/`b_scope_id`/`b_hook` | 删除规则 v2.0 §6.2 | | 4 | 中间表模块挂业务域 `module` 节点下、与主表模块平级(规范已写入核心表结构设计 §4.3) | 评审 v1.2 问题四 | | 5 | 模块管理分节改为「数据行为 / 访问控制」,只改 `configGroups` 分组,面板/路由/数据不动;与删除规则前端改造同批实施 | 评审 v1.2 问题二 | ## 待做清单(按执行顺序) ### ① 重写 SQL 脚本 —— 对应设计 v2.0 §6.1 / §6.2 现状:两个脚本还是 v1 结构(`b_on_delete` 默认 `none` 三值、`s_rule` 带 `b_scope_type`/`b_scope_id`/`b_hook`)。 - [ ] `sql/fms_delete_rule.sql` 重写: - `s_relation.b_on_delete`:四值,**默认 `'restrict'`**(现脚本 L18 默认是 `'none'`) - `s_rule` 按新结构重建:`b_module_id varchar(50) not null`、`b_kind varchar(20) not null`(condition / sql)、`b_predicate not null`、`b_message`、`b_canuse`、`b_xh`、审计四列;去掉 `b_scope_type`/`b_scope_id`/`b_hook` - 索引:`ix_s_rule_module on s_rule (b_module_id, b_canuse, b_xh)` - 保留脚本头「可重复执行」说明与执行方式注释 - [ ] `sql/fms_core.sql` 同步:L208 的 `b_on_delete` 注释与默认值、L228-256 的 `s_rule` 段整体替换(与 `fms_delete_rule.sql` 保持一致,注意两脚本职责分工——core 建全表、delete_rule 只做本特性增量,按现有模式来) - [ ] 建表 SQL 的 `b_archived` / `b_archive_datetime` 通用列暂不加——它是 `archive` 行为的**运行时前提**(引用方表需有此列),到后端实现时再决定加在哪些业务表 ### ② 后端引擎 —— 对应设计 v2.0 §6.4 / §6.5 / §6.6 - [ ] 新建 `DeleteRuleChecker`(检查器):`check(connection, 模块, ids, 小闭包)` - 模块条件:清单内每个模块的 `s_rule`(`b_canuse=1`)逐条执行;condition → JSON AST 翻译参数化 SQL(AST 格式见 §6.3,嵌套 ≤8 层、节点 ≤100);sql → `:ids` 替换(OPENJSON 数组参数,绕开 2100 绑定变量上限),查出有结果即记错误,**全程写技术日志**(实际 SQL+参数+行数) - 引用保护:清单内每条记录查它作为被引用方的 `restrict` 入边(`b_on_delete='restrict'` 且 `b_canuse=1`),引用方存在且不在清单内 → 记错误(带引用方单号) - [ ] 新建 `DeleteRuleExecutor`(执行器):从 ids 出发沿 `b_on_delete ∈ {cascade, archive}` 的关系逐层展开删除清单(被删的是被引用方;已装过跳过防环;超 10 层报配置错误) - [ ] `DataController` 加 `POST /data/deleteobj`:`{ moduleCode, ids, dryRun }`;dryRun 返回「将删除 X + Y」摘要;真删开事务**重跑检查**(预览通过不算数)→ archive 打标记 → 先子后父物理删 → 审计日志 - [ ] `DataSaveService.saveTable()` delete 分支接入(§6.5,**重要**): - 真删前按表名反查模块(`b_savetable`),查不到跳过(日志表等) - 同事务调检查器;**小闭包语义**:本次 saveobjt 所有 deletes 覆盖的 id 传给检查器,引用方也在被删之列不算外部引用 - 不过 → 抛 BusinessException → 整个 saveobjt 回滚 - [ ] 错误消息生成:condition 自动生成人话(带实际值 + 字段格式化:金额 ¥、日期格式);sql 规则用 `b_message` 的 `{列名}` 占位符填充;错误按规则归类、每类带数量和前几条示例 ### ③ 前端改造 —— 对应设计 v2.0 §8 步骤 5 + 评审问题二 - [ ] `ModuleRelationPanel.vue`:删除相关从三栏位(`b_relation_type` / `b_on_delete` / `_hasRefRule`)收敛为 `b_relation_type` + `b_on_delete`(四值下拉,默认 restrict) - 删掉:`_hasRefRule` 假列、`setRefRule`、改关系主键时作废旧键规则及提示(L326-334)、填不全四列禁用 checkbox - `many_to_one` + `cascade` 不再界面禁用,改保存校验拦截(对齐「校验落后端」原则——若后端引擎做此校验,前端可只做提示) - [ ] `ModuleRulePanel.vue`:去掉引用规则只读区 + `navigate` 跳转;列表只剩模块级规则(condition / sql) - [ ] `ModuleRuleEditModal.vue` + `ruleUtils.js`: - `parseRulePredicate` 三态推断改两态(读 `b_kind`,不再看首字符 `{`) - 删掉 `relationScopeId` / `parseRelationScopeId` / `isCompleteRelationKey` / `REF_RULE_HINT` / `createRefRuleRow` / `RULE_SCOPE_RELATION` - `createRuleRow` 带 `b_kind`;新增默认 `condition` - [ ] `module-management/index.vue`: - 删 `refRules` / `refRules_org` / `refRulesReverse` 三组数据及加载、diff、清理逻辑(L875-898 关系级规则清理段改为只清模块级) - `configGroups` 分节调整:「数据行为 → 模块关联(含删除行为) / 删除规则 / 自动编码」「访问控制 → 权限」;section key 不动 - [ ] `FmsModuleListPage.vue` `deleteSelected`(L877):直删改两步——先 `deleteobj dryRun=true`,有错误弹错误列表,无错误弹确认框(含连带摘要),确认后真删 ### ④ 验收检查 - [ ] 全局搜索无旧导出残留:`relationScopeId`、`createRefRuleRow`、`REF_RULE_HINT`、`b_scope_type`、`b_scope_id`、`_hasRefRule`、`setRefRule` - [ ] `pnpm check`(lint + fmt + build)通过 - [ ] 跑受影响模块的前端测试;`ruleUtils` / 面板相关 spec 按新契约更新(测试约定见 `fms-vue/tests/README.md`:文件同名、中文用例名) - [ ] 后端:`mvnw test`;引擎核心逻辑(检查器翻译、闭包语义、环检测)补测试 - [ ] 手工链路验证:配一条「审核中不可删」→ 列表删被拦(人话提示)→ 表单里删行同样被拦(saveobjt 回滚)→ cascade 关系连带展开预览 → restrict 默认生效 ## 注意事项(动工前再读一遍) 1. **`sql/fms_delete_rule.sql` 现有 L22 是 `drop table if exists s_rule`**——虽然确认无数据,重写后仍保留 drop+create 模式,但执行前先 `select count(*)` 确认一次 2. 规则引擎是**当前最大缺口**:现在界面上配的删除规则完全不生效(后端 `s_rule` 相关零实现)。②完成后配置才有意义,①③可以和②并行 3. 执行 SQL 用 `RunSqlFile`:`java -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" tools/migration/RunSqlFile.java ../sql/fms_delete_rule.sql --dry-run` 先 dry-run 4. 改前端遵守 `开发规范.md`:改前先读、最小修改、不为兼容旧名留转发别名(调用方可控,直接迁移删旧代码) 5. `s_rule` 主键仍是雪花 `b_id`(前端 `nextIdApi` 取号,与业务表一致);**配置表不用 nextIdApi 的规则是「模块配置表用业务键」**——`s_rule` 无天然业务键,属例外(v2.0 §6.2 已注明)