BPMN.js 自定义属性面板:配置审批人、按钮、表单和数据权限

BPMN.js 属性面板可以配置审批人、按钮、表单和数据权限,但不应该把组织用户、表单结构和权限明细全部塞进 BPMN XML。更稳妥的设计是:模型保存稳定引用与策略,业务服务保存可变目录数据,运行时重新解析并执行授权,必要时保存最小证据快照。

这类扩展真正解决的不是“增加几个输入框”,而是建立一套跨设计器、流程引擎和业务系统都能长期演进的模型协议。实现时应把 Properties Provider、moddle、Vue 业务弹窗、CommandStack、后端校验和运行时解析看成一条完整链路。

在这里插入图片描述

图 1 属性面板编辑模型引用和策略,业务弹窗负责复杂选择,后端负责发布校验、运行时解析与审计。

一、先回答最重要的问题:配置应该存在哪里

审批配置可以分为三类数据:

数据类型 示例 推荐存储位置
流程语义 审批策略、表单引用、按钮策略、字段权限引用 BPMN XML / moddle 扩展
业务目录 用户、部门、角色、表单版本、按钮定义 组织、表单和权限服务
运行证据 实际审批人、按钮授权结果、字段权限快照 任务上下文或审计存储

BPMN XML 适合保存“如何找到和解释配置”,不适合保存完整用户资料、整张表单 Schema 或动态权限结果。否则人员调岗、表单升级和权限调整都会迫使流程模型重新发布。

建议只保存稳定 ID、解析策略和版本约束。例如审批人保存 policyKey 与参数,表单保存 formKey 与版本策略,按钮保存 buttonPolicyKey,字段权限保存规则引用或小型规则快照。

1. 企业审批属性配置的总体分层

一套可维护的属性配置通常包含五层:

  1. BPMN.js Modeler:选中节点、导入导出 XML、触发 CommandStack;
  2. Properties Provider:按元素类型提供属性分组和编辑项;
  3. 企业 moddle:定义 XML 中的属性、扩展元素和命名空间;
  4. Vue Bridge:打开审批人、表单、按钮和权限等复杂弹窗;
  5. 后端服务:发布校验、引用解析、权限授权、快照与审计。

属性面板只是编辑器,不是业务数据库,也不是可信授权端。浏览器里的只读、隐藏和禁用只能改善体验,生产运行时仍要由后端重新计算审批人与操作权限。

二、用 moddle 建立稳定的企业模型协议

企业属性不要混进 Camunda 或其他引擎命名空间,应使用独立命名空间,例如 ent:。这样可以把“企业审批语义”与“引擎执行属性”分开,便于适配 Camunda 7、Camunda 8、Flowable 或其他内核。

{
  "name": "Enterprise",
  "prefix": "ent",
  "uri": "https://example.com/schema/bpmn/enterprise/1.0",
  "types": [{
    "name": "ApprovalConfig",
    "extends": ["bpmn:UserTask"],
    "properties": [
      { "name": "assigneePolicyKey", "isAttr": true, "type": "String" },
      { "name": "formKey", "isAttr": true, "type": "String" },
      { "name": "buttonPolicyKey", "isAttr": true, "type": "String" }
    ]
  }]
}

模型协议至少要有四项治理能力:

  • schemaVersion:标识企业扩展协议版本;
  • 迁移器:导入旧 XML 时转换字段和默认值;
  • 未知扩展保留:普通保存不能静默删除暂不识别的数据;
  • 发布校验:检查引用是否存在、可用且属于当前租户。

三、Properties Provider 只负责编辑模型

新版 Properties Panel 通过 getGroups(element) 返回属性分组。Provider 应根据元素类型和设计模式添加企业配置项,修改时调用 modeling.updateProperties()commandStack.execute()

class ApprovalPropertiesProvider {
  constructor(propertiesPanel) {
    propertiesPanel.registerProvider(500, this);
  }

  getGroups(element) {
    return groups => {
      if (is(element, "bpmn:UserTask")) {
        groups.push(buildApprovalGroup(element));
      }
      return groups;
    };
  }
}

Provider 不应直接查询组织树或完整表单列表。属性面板会频繁刷新,如果每次刷新都调用后端,就会造成请求风暴、焦点丢失和侧栏闪烁。目录数据可以缓存,复杂选择应交给 Vue 弹窗。

四、审批人配置为什么要建模成策略

审批人不是一个字符串,而是一套运行时解析策略。常见策略包括:

  • 指定用户、角色或部门岗位;
  • 发起人、发起人部门负责人或逐级负责人;
  • 根据表单字段选择人员;
  • 由上一个节点或业务服务动态计算;
  • 发起时或任务到达时由用户选择。

模型可以保存如下结构:

{
  "policyKey": "deptLeader",
  "params": { "level": 2, "from": "initiatorDept" },
  "emptyPolicy": "error",
  "duplicatePolicy": "skip"
}

必须明确解析时点、无人可办、重复审批人、审批人是发起人和组织变更等边界。会签还要保存串行或并行、完成比例、拒绝策略和剩余任务处理方式,不能只保存一个 multiInstance=true

1. 按钮配置不是前端按钮数组

“同意、拒绝、退回、转办、加签、暂存”等按钮会触发不同的任务命令、权限检查和审计要求。BPMN XML 中应保存按钮策略引用或动作白名单,而不是颜色、CSS 类名和前端事件函数。

配置内容 应放在模型中 应放在业务服务中
动作代码 approverejectrollback 动作名称、图标、排序
可用条件 表达式或策略引用 策略实现与版本
意见要求 必填、选填、隐藏 文案、提示与国际化
后续行为 提交、退回、转办 命令处理器和审计逻辑

运行时必须由后端根据当前用户、任务状态和业务数据重新计算按钮集合。即使前端隐藏了“退回”,也不能认为接口已经安全。

2. 表单绑定和数据权限怎样配合

表单绑定至少要保存 formKey、版本策略和渲染模式。版本策略通常有三种:固定版本、始终使用最新兼容版本、启动实例时冻结版本。审批流程更适合在实例启动或任务创建时冻结关键版本,避免同一实例前后看到不同字段。

数据权限至少包含四个维度:

  1. 字段可见:用户能否看到字段;
  2. 字段可编辑:用户能否修改字段;
  3. 数据范围:子表、关联数据和附件能看到哪些记录;
  4. 动作约束:提交前校验、脱敏、导出和打印限制。

权限规则可以只保存 permissionPolicyKey,也可以在 extensionElements 中保存小型规则快照。规则经常变化且需要统一治理时使用引用;流程必须长期复现当时规则时保存版本或快照。不要在 XML 中复制完整表单 Schema。

五、复杂选择器如何与 Vue 弹窗连接

组织树、表单设计器和字段权限矩阵不适合挤进窄侧栏。推荐在属性项中显示摘要与“配置”按钮,再通过可注入 Bridge 打开 Vue 弹窗。

在这里插入图片描述

图 2 Provider 只传递当前配置,Vue 弹窗返回结构化结果,模型修改统一进入 CommandStack。

export interface DesignerBridge {
  selectAssignee(input: AssigneePolicy): Promise<AssigneePolicy | null>;
  selectForm(input: FormBinding): Promise<FormBinding | null>;
  configureButtons(input: ButtonPolicy): Promise<ButtonPolicy | null>;
  configurePermission(input: PermissionPolicy): Promise<PermissionPolicy | null>;
}

弹窗确认后应一次性提交结构化配置,取消时不修改模型。不要用大量 window.showXxxDialog 和全局回调连接属性面板,否则多设计器实例、异步取消和自动化测试都会变得困难。

六、运行时如何解析配置并保存证据

设计时保存的是策略,任务到达时需要把策略解析为可执行上下文:实际办理人、表单版本、可用按钮、字段权限和数据范围。

在这里插入图片描述

图 3 运行时从模型读取引用,通过业务服务解析后形成任务上下文,并按审计要求保存快照。

推荐统一输出 TaskContext

interface TaskContext {
  assignees: string[];
  form: { key: string; version: string };
  actions: string[];
  fieldPermissions: Record<string, "hidden" | "read" | "edit">;
  resolvedAt: string;
  policyVersions: Record<string, string>;
}

以下情况通常需要保存运行快照:审批人依赖组织关系、权限策略可能更新、表单版本可变、流程需要审计复现或任务会长期挂起。快照应保存解析结果与版本证据,不要复制无关个人资料。

七、校验、安全和版本迁移缺一不可

设计时校验建议分三层:

  • 输入校验:必填、格式、枚举和表达式语法;
  • 模型校验:UserTask 是否绑定审批策略、表单字段是否存在、会签配置是否完整;
  • 发布校验:后端按租户、状态和版本重新检查所有引用。

安全上要遵守三个原则:前端隐藏不等于授权;模型 XML 属于不可信输入;运行时按钮和字段权限必须重新计算。导入外部 XML 时,还要限制扩展属性、脚本和表达式的可用范围。

升级 Properties Panel、bpmn-js 或引擎 Provider 时,不能只看页面能否打开。必须比对导入后的业务对象、导出的 XML、未知扩展保留、运行解析结果和旧模型迁移结果。

八、从当前项目出发怎样渐进改造

当前项目已经有独立的 ych-bpm-designer,并集成审批人设置、变量选择、表单选择、按钮列表、字段权限、任务时限和脚本等业务组件。现有能力可以保留,不需要一次性重写。

建议按以下顺序改造:

  1. 盘点当前 XML 中所有自定义字段和命名空间;
  2. 建立旧模型导入、导出和运行回归样例;
  3. 新增独立 ent: moddle 协议与 schemaVersion
  4. 用 Bridge 适配现有 Vue 弹窗,逐步替换全局回调;
  5. 按审批人、表单、按钮、权限顺序迁移 Provider;
  6. 建立统一 TaskContext 解析器与后端发布校验;
  7. 最后升级依赖,并保持新旧模型并行兼容一段时间。

云程低代码开发平台可以复用已有审批配置弹窗,把长期演进重点放在模型协议、Bridge、运行时解析和版本治理上。
在这里插入图片描述
在这里插入图片描述

九、上线检查清单

  • 企业属性是否使用独立命名空间并带 schemaVersion
  • XML 是否只保存稳定引用、策略和必要快照?
  • 所有模型修改是否进入 CommandStack?
  • 复杂选择是否通过可注入 Bridge 打开 Vue 弹窗?
  • 审批人无人可办、重复和会签边界是否明确?
  • 按钮是否由后端重新授权并记录审计?
  • 表单版本是否在明确时点冻结?
  • 字段可见、可编辑、数据范围和动作约束是否分开?
  • 发布时是否按租户和状态校验所有引用?
  • 旧模型、未知扩展、XML 与运行结果是否有回归测试?

十. 如果只记住五句话

  1. 属性面板编辑模型,不充当业务数据库;
  2. 企业属性使用独立 moddle 命名空间;
  3. 复杂选择放进 Vue 弹窗,通过 Bridge 与 Provider 连接;
  4. 所有修改进入 CommandStack,所有发布经过可信后端;
  5. 运行时重新解析并授权,需要审计时保存最小证据快照。

做到这些,审批人、按钮、表单和数据权限才会成为可复用、可迁移、可审计的平台能力,而不是绑死设计器和流程引擎的私有字段。

posted @ 2026-08-14 08:30  大龄码农有梦想  阅读(7)  评论(0)    收藏  举报