fix(bpm): 超级管理员始终可维护流程模型,修复「你不是该流程的管理员」死锁

问题:流程模型 → 修改 → 保存/发布 报
「操作流程失败,原因:你不是该流程(合同审批)的管理员」,
且无法自愈(怎么改都报同样错误)。

根因:BpmModelServiceImpl.validateModelManager 校验的是数据库中【现有模型】的
managerUserIds,而不是请求里传的新值。历史模型的该字段存在三种脏数据:
- NULL / [](审批模型建好但从没存过管理员)
- 指向已被删除的用户
严格校验会让这些模型彻底变成「无主」,任何账号(包括超级管理员)都改不了,
前端即便提交了正确的新管理员也永远写不进去 —— 死锁。

改动:validateModelManager 增加超级管理员短路:
若当前登录用户是 super_admin,直接放行,不再校验 managerUserIds。
该方法是 updateModel / deployModel / deleteModel / updateModelState /
updateModelSortBatch 等 7 处的统一入口,改一处全部生效。

配套:
- 客户要求「流程管理员固定为超级管理员」,此校验对超管本就不应生效。
- 另有 REV_ 为 NULL 导致保存 500(FlowableOptimisticLockingException)的数据问题,
见 sql/mysql/fix_act_re_model_rev_null.sql。

验证(北方科技 mom-bfkj 实测):
- 「合同审批」managerUserIds=[] → 保存成功,落库为 [1],REV_ 1→2
- 发布接口不再报管理员错误,走到后续业务校验(流程表单未配置,属正常提示)
已添加1个文件
已修改1个文件
36 ■■■■■ 文件已修改
sql/mysql/fix_act_re_model_rev_null.sql 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/mysql/fix_act_re_model_rev_null.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
-- =====================================================================
-- ä¿®å¤ ACT_RE_MODEL.REV_ ä¸º NULL å¯¼è‡´æµç¨‹æ¨¡åž‹ä¿å­˜æŠ¥ 500「系统异常」
--
-- çŽ°è±¡ï¼š
--   æµç¨‹æ¨¡åž‹ â†’ ä¿®æ”¹ â†’ ä¿å­˜ / å‘布,报「操作流程失败,原因:你不是该流程(xxx)的管理员」,
--   æˆ–保存时 500「系统异常」(日志为 FlowableOptimisticLockingException)。
--
-- æ ¹å› ï¼š
--   1) managerUserIds ä¸ºç©ºï¼ˆNULL / [])时,后端 validateModelManager ä»»ä½•人都校验不过,
--      æ¨¡åž‹å½»åº•变成「无主」,谁都无法修改(死锁)→ ç”±åŽç«¯ä»£ç æ”¾å®½ä¸ºã€Œè¶…级管理员始终放行」。
--   2) ACT_RE_MODEL.REV_ æ˜¯ Flowable çš„乐观锁版本列,为 NULL æ—¶
--      UPDATE ... WHERE REV_ = NULL æ°¸è¿œåŒ¹é…ä¸åˆ°ï¼ŒæŠ›ä¹è§‚锁异常 â†’ éœ€è¦æŠŠ NULL è¡¥æˆ 1。
--
-- å½±å“èŒƒå›´ï¼ˆä¿®å¤å‰å®žæµ‹ï¼‰ï¼š
--   åŒ—方科技 mom-bfkj:21 ä¸ªæ¨¡åž‹ä¸­ 14 ä¸ª REV_ ä¸º NULL
--   è“ç¾½çº¸ä¸š mom-lyzy:24 ä¸ªæ¨¡åž‹ä¸­ 14 ä¸ª REV_ ä¸º NULL
--
-- æ‰§è¡Œæœ¬è„šæœ¬çš„åº“ï¼šmom-bfkj、mom-lyzy(两家都要执行)
-- =====================================================================
-- ä¿®å¤ä¹è§‚锁版本号为 NULL çš„记录
UPDATE ACT_RE_MODEL SET REV_ = 1 WHERE REV_ IS NULL;
-- éªŒè¯ï¼šä¸åº”再出现 NULL
SELECT ifnull(cast(REV_ as char), '<NULL>') AS REV, COUNT(*) AS æ¡æ•°
FROM ACT_RE_MODEL
GROUP BY REV_
ORDER BY REV_;
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
@@ -6,6 +6,7 @@
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
@@ -204,6 +205,13 @@
    private Model validateModelManager(String id, Long userId) {
        Model model = validateModelExists(id);
        BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
        // è¶…级管理员始终拥有流程模型的维护权限。
        // èƒŒæ™¯ï¼šåŽ†å²æ¨¡åž‹çš„ managerUserIds å¯èƒ½ä¸ºç©ºï¼ˆNULL / []),或指向已删除 / å·²å˜æ›´çš„用户,
        // ä¸¥æ ¼æ ¡éªŒä¼šè®©è¿™äº›æ¨¡åž‹å†ä¹Ÿæ— æ³•修改(死锁,客户报「你不是该流程(xxx)的管理员」却无法自愈);
        // å¦å¤–客户要求把流程管理员固定为超级管理员,此校验对其不再适用。
        if (SecurityFrameworkUtils.isSuperAdmin()) {
            return model;
        }
        if (metaInfo == null || !CollUtil.contains(metaInfo.getManagerUserIds(), userId)) {
            throw exception(MODEL_UPDATE_FAIL_NOT_MANAGER, model.getName());
        }