深度解析:Flowable + Vue3 企业级流程架构设计——为什么 若依RuoYi Office 的 BPM 能真正落地?

0 阅读23分钟

深度解析:Flowable + Vue3 企业级流程架构设计——为什么 若依RuoYi Office 的 BPM 能真正落地?

🌐 文档地址ruoyioffice.com | 📦 源码1gitee.com/yqzy1688/ru… |📦 源码2gitee.com/yqzy1688/ru… |📦 源码3github.com/yuqing2026/… | 💬 :17156169080(备注「RuoYi Office」)

很多开源项目都说自己"集成了工作流",但真正能在企业场景中跑起来的又有几个?流程设计器只是冰山一角——审批人怎么分配?业务表单怎么集成?状态怎么同步?权限怎么控制? 这些才是 BPM 落地的核心难题。本文从 RuoYi Office 的实际源码出发,带你看懂一套经过生产验证的企业级流程架构。

引言:企业级 BPM 到底难在哪?

如果你只是想跑一个"请假→审批→完成"的 Demo,那任何一个 BPM 框架都能做到。但当你面对真实的企业场景:

  • 🤯 几十种业务单据(请假、用印、用车、会议室、采购、合同...)都要走流程
  • 🏢 复杂的组织架构:审批人可能是角色、部门负责人、多级领导、发起人自选
  • 📋 表单多样性:有些表单简单到拖拽就能生成,有些复杂到必须自定义开发
  • 🔄 状态联动:流程审批通过后,业务单据要自动更新状态、触发后续操作
  • 🏗️ 微服务架构:BPM 模块和业务模块跨服务部署,如何解耦通信?

这时候你会发现,大部分"集成了工作流"的开源项目,在这些场景面前根本不堪一击。

RuoYi Office 是一个基于 Spring Cloud Alibaba + Vue 3 + Vben Admin 构建的企业管理一体化平台,涵盖 OA、HRM、CRM、ERP 等 14 大业务模块,每个模块都有大量需要走审批流程的业务单据。正是这种复杂的业务需求,倒逼出了一套真正经过实战检验的 BPM 架构blog-bpm-home-workspace.png

▲ RuoYi Office 工作台首页:待办任务 99+、我的单据、已办任务、抄送我的,一站式流程工作台


一、架构全景:五层分离的流程引擎设计

1.1 核心引擎选型

维度选型为什么这么选
流程引擎Flowable 7.0.1轻量、高性能、BPMN 2.0 全兼容,社区活跃度远超 Activiti
集成方式flowable-spring-boot-starter-processSpring Boot Starter 自动配置,开箱即用
历史级别audit记录流程活动和任务历史,满足审计需求又不过度存储
表结构管理database-schema-update: true自动更新 Flowable 表结构,降低运维成本

为什么选 Flowable 而不是 Camunda 或 Activiti?

Flowable 7.x 是 Activiti 核心团队出走后的延续项目,继承了 Activiti 的全部优点并持续进化。相比 Camunda 的商业化路线,Flowable 的社区版功能更完整。与停滞的 Activiti 相比,Flowable 的版本迭代更加活跃,对 Spring Boot 3.x、Jakarta EE 的支持也更及时。

1.2 五层架构模型

RuoYi Office 的 BPM 架构采用严格分层设计,每一层职责明确、边界清晰: image.png``` 关键设计理念

  • BPM API 层作为跨模块通信契约,业务模块(OA、HRM 等)只依赖 bpm-api,不直接依赖 bpm-server
  • 事件驱动解耦:BPM 模块通过 Spring Event 发布状态变更,业务模块监听处理
  • 策略模式扩展:审批人分配、流程行为等均通过策略模式实现,新增策略无需修改核心代码

1.3 Flowable 深度定制配置

RuoYi Office 不是简单地引入 Flowable,而是通过 BpmFlowableConfiguration 进行了深度定制:

@Configuration(proxyBeanMethods = false)
public class BpmFlowableConfiguration {

    @Bean
    public EngineConfigurationConfigurer<SpringProcessEngineConfiguration>
            bpmProcessEngineConfigurationConfigurer(
                ObjectProvider<FlowableEventListener> listeners,
                ObjectProvider<FlowableFunctionDelegate> customFlowableFunctionDelegates,
                BpmActivityBehaviorFactory bpmActivityBehaviorFactory) {
        return configuration -> {
            // 1. 注册全局事件监听器
            configuration.setEventListeners(ListUtil.toList(listeners.iterator()));
            // 2. 替换默认的 ActivityBehaviorFactory,实现自定义审批人分配
            configuration.setActivityBehaviorFactory(bpmActivityBehaviorFactory);
            // 3. 注册自定义函数(流程表达式)
            configuration.setCustomFlowableFunctionDelegates(
                ListUtil.toList(customFlowableFunctionDelegates.stream().iterator()));
        };
    }

    @Bean
    public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(
            BpmTaskCandidateInvoker bpmTaskCandidateInvoker) {
        BpmActivityBehaviorFactory factory = new BpmActivityBehaviorFactory();
        factory.setTaskCandidateInvoker(bpmTaskCandidateInvoker);
        return factory;
    }

    @Bean
    public BpmTaskCandidateInvoker bpmTaskCandidateInvoker(
            List<BpmTaskCandidateStrategy> strategyList,
            AdminUserApi adminUserApi) {
        return new BpmTaskCandidateInvoker(strategyList, adminUserApi);
    }

    @Bean
    public BpmProcessInstanceEventPublisher processInstanceEventPublisher(
            ApplicationEventPublisher publisher) {
        return new BpmProcessInstanceEventPublisher(publisher);
    }
}

三个核心定制点:

  1. BpmActivityBehaviorFactory:替换 Flowable 默认的任务行为工厂,实现多策略审批人分配
  2. 全局事件监听器:捕获流程实例和任务的生命周期事件
  3. BpmProcessInstanceEventPublisher:将 Flowable 事件转换为 Spring ApplicationEvent,实现跨模块解耦

二、双流程设计器:让专业的人用专业的,让普通人也能用

2.1 设计理念

企业中有两类流程设计需求:

需求场景用户角色复杂度举例
标准复杂流程IT/流程管理员采购审批(多分支、并行、子流程)
简单审批流业务管理员请假、用印、用车(串行审批)

RuoYi Office 提供双设计器满足两种需求:

设计器技术方案适用场景特点
BPMN 设计器基于 bpmn-js复杂流程,精细控制标准 BPMN 2.0,功能完备
Simple 设计器仿钉钉/飞书拖拽式简单审批流,快速搭建零门槛,业务人员可自助配置

image.png

image.png

▲ 流程模型管理页面:清晰标注每个流程使用的设计器类型(BPMN / SIMPLE),一目了然

2.2 Simple 设计器的技术实现

Simple 设计器的精妙之处在于——用户看到的是拖拽式界面,底层执行的仍然是标准 BPMN 2.0

用户拖拽配置 → SimpleModel (JSON 树结构) → SimpleModelUtils.buildBpmnModel() → BPMN Model → Flowable 执行

Simple 设计器使用树形数据结构描述流程,支持丰富的节点类型:

节点类型说明BPMN 映射
开始节点流程起点startEvent
审批人节点人工审批userTask
抄送人节点消息抄送serviceTask
条件分支排他网关exclusiveGateway
并行分支并行网关parallelGateway
包容分支包容网关inclusiveGateway
延迟器定时等待intermediateCatchEvent
触发器自动处理serviceTask
结束节点流程终点endEvent

这种设计既让普通用户享受到拖拽式的便利,又保证了底层引擎执行的一致性和标准性。管理员只需要关注"谁审批、什么条件",不需要理解 BPMN 规范。

2.3 Simple 设计器实战:以用印申请流程为例

光说理论不够直观,我们以 OA 模块中的用印申请单为例,看看 Simple 设计器的实际使用效果: image.png

▲ Simple 设计器实战:OA用印申请单的完整审批流程设计

上图展示了一个真实企业场景的用印申请审批流程,每个节点的配置都非常直观:

节点类型审批人策略说明
发起人开始节点已设置(自动填充)流程发起人自动作为第一个节点
部门负责人审批审批人节点发起人的部门负责人自动找到发起人的直属上级审批
印章保管员审批审批人节点指定岗位:OA_印章保管员按岗位匹配,人员变动无需改流程
条件分支条件网关根据「用印方式」自动路由
┣ 借用印章条件分支1表达式:${sealUseMode == 2}外借用章需要额外归还确认
┣ 现场用印条件分支2默认分支(优先级2)现场用印直接结束
申请人归还印章审批人节点发起人自己借出后由申请人确认归还

这个例子完美体现了 Simple 设计器的几个核心能力

  1. 多种审批人策略混合使用:一个流程中同时使用了「部门负责人」「指定岗位」「发起人自己」三种策略
  2. 条件分支自动路由:根据表单字段(用印方式 sealUseMode)动态决定流程走向,零代码实现业务逻辑分支
  3. 业务变量驱动:流程变量直接绑定业务表单字段,无需硬编码
  4. 可视化配置:整个流程所见即所得,业务管理员无需理解 BPMN XML 即可完成配置

💡 注意看条件分支的设计:现场用印走默认分支直接结束,而借用印章则增加了「申请人归还印章」节点——这就是 Simple 设计器对企业审批复杂度的精准把控。

2.4 创建流程的四步走

步骤 1: 基本信息  步骤 2: 表单设计  步骤 3: 流程设计  步骤 4: 更多设置
步骤配置内容说明
基本信息名称、分类、图标、描述定义流程的基础标识
表单设计选择流程表单或配置业务表单路径决定用拖拽表单还是自定义表单
流程设计BPMN 或 Simple 设计器定义流程走向和审批规则
更多设置可见范围、自定义打印等高级配置选项

image.png

▲ 发起流程页面:按 OA 协同办公、人力资源管理、仓库管理等分类展示,员工一键发起


三、表单双模式:拖拽表单 vs 业务表单,灵活适配任何场景

这是 RuoYi Office BPM 架构中最具亮点的设计之一。

3.1 两种表单模式对比

维度流程表单(NORMAL)业务表单(CUSTOM)
定义方式拖拽设计器,生成 JSON 配置自定义开发 Vue 组件 + 后端 Service
存储位置bpm_form各业务模块自有表
前端渲染form-create 动态渲染自定义 Vue 组件
适用场景简单表单,无复杂业务逻辑复杂业务逻辑(印章选择、时间冲突校验等)
开发成本零代码需要前后端开发
灵活性中等极高

3.2 流程表单设计器实战:零代码搭建审批表单

RuoYi Office 内置了强大的可视化流程表单设计器,基于 form-create + antd-designer 构建,支持拖拽式配置: image.png

▲ 流程表单设计器:左侧组件面板 + 中间表单预览 + 右侧属性配置,三栏布局一目了然

设计器提供了丰富的表单组件库,覆盖企业审批的常见场景:

组件分类组件列表企业场景
基础输入输入框、多行输入框、密码输入框、数字输入框事由描述、金额填写、备注信息
选择类单选框、多选框、选择器、级联选择器审批类型、印章名称选择、省市区
日期时间日期、日期区间、时间、时间区间请假时间、用车时间、借用时间
上传类文件上传、单图上传、多图上传审批附件、证明材料、现场照片
高级组件富文本框、手写签名、树形选择、穿梭框审批意见、电子签章、部门选择、人员选择
布局类滑块、开关、评分、网页 iframe满意度评分、条件开关、嵌入外部页面

设计器核心功能

  • 📐 表单布局配置:支持横向/竖向布局、左对齐/右对齐、标签宽度自定义
  • 📱 多尺寸适配:大/中/小三种表单尺寸,适配不同屏幕场景
  • 👁️ 实时预览:拖拽即可实时预览表单效果,支持默认值和校验规则配置
  • 必填校验:每个字段可独立设置必填标识,提交时自动校验
  • 🔄 表单复用:设计好的表单可以被多个流程模型复用,一次设计多处使用

何时选择流程表单?如果你的审批场景是简单的信息收集(如请假事由+时间+附件),没有复杂的业务逻辑和后端数据交互,那么流程表单就是最优选择——零代码、零部署、即拖即用

3.3 前端如何智能切换

流程审批详情页(processInstance/detail/index.vue)通过 formType 智能切换两种渲染模式:

async function getApprovalDetail() {
  const data = await getApprovalDetailApi(param);
  processDefinition.value = data.processDefinition;

  // 核心判断:根据表单类型决定渲染方式
  if (processDefinition.value.formType === BpmModelFormType.NORMAL) {
    // 流程表单:使用 form-create 动态渲染
    setConfAndFields2(
      detailForm,
      processDefinition.value.formConf,
      processDefinition.value.formFields,
      processInstance.value.formVariables,
    );
    // 设置字段权限(只读/编辑/隐藏)
    nextTick().then(() => {
      Object.keys(formFieldsPermission).forEach((item) => {
        setFieldPermission(item, formFieldsPermission[item]);
      });
    });
  } else {
    // 业务表单:动态加载自定义 Vue 组件
    BusinessFormComponent.value = registerComponent(
      data?.processDefinition?.formCustomViewPath || '',
    );
  }
}

这段代码的精妙之处

  • 流程表单form-create 动态渲染路径,表单配置和字段定义都存储在流程定义中,运行时解析渲染
  • 业务表单走动态组件加载路径,通过 formCustomViewPath(如 /oa/seal/sealapply/info/index.vue)加载业务模块自己开发的 Vue 组件
  • 字段权限控制:每个审批节点可以独立配置字段的只读/编辑/隐藏权限,做到审批人只看到该看的内容

3.4 业务表单集成规范

业务表单组件需要遵循统一的接口契约:

// 业务表单组件的 Props 定义
const props = defineProps<{
  id?: number | string;           // 单据 ID
  isApproval?: boolean;           // 是否审批态
  isCopy?: boolean;               // 是否抄送查看模式
  nodeKey?: string;               // 当前节点 Key
  nodeKeyName?: string;           // 当前节点名称
  processDefinition?: any;        // 流程定义信息
  processInstance?: any;          // 流程实例信息
  activityNodes?: any[];          // 审批节点列表
  copyReason?: string;            // 抄送意见
}>();

// 暴露给父组件的方法
defineExpose({
  beforeApproval,     // 审批前校验(可在此做业务验证和数据保存)
  loadData,           // 加载数据
  handleSaveAndSubmit // 保存并提交
});

这套规范确保了任何业务模块开发的表单组件,都能无缝嵌入到统一的审批详情页中。BPM 框架提供基础设施(审批按钮、流程图、审批时间线),业务表单只关注自己的业务逻辑。


四、事件驱动架构:BPM 与业务模块的优雅解耦

4.1 问题背景

在微服务架构下,BPM 模块和业务模块是独立的服务。当流程状态发生变更时(审批通过、驳回、取消),业务模块需要同步更新自己的单据状态。

如果用直接调用:BPM 模块需要知道每个业务模块的接口,产生大量耦合 RuoYi Office 的做法:事件驱动 + 监听器模式,完全解耦

4.2 事件定义

@Data
public class BpmProcessInstanceStatusEvent extends ApplicationEvent {

    @NotNull(message = "事件类型不能为空")
    private BpmEventTypeEnum eventType;         // 事件类型

    private LocalDateTime eventTime;            // 事件发生时间

    @NotNull(message = "流程实例信息不能为空")
    private BpmProcessInstanceInfo processInstanceInfo; // 流程实例信息

    private BpmTaskInfo taskInfo;               // 任务信息(仅任务事件)

    private Map<String, Object> extData;        // 扩展数据
}

事件类型覆盖了流程和任务的完整生命周期:

事件类型说明
PROCESS_INSTANCE_STATUS_CHANGED流程实例状态变更(审批通过/驳回/取消)
PROCESS_INSTANCE_DELETED流程实例被删除
TASK_CREATED任务创建
TASK_CREATED_REENTER任务重新进入开始节点
TASK_APPROVED任务审批通过
TASK_REJECTED任务审批拒绝
TASK_WITHDRAWN任务撤回
TASK_TRANSFERRED任务转办
TASK_DELEGATED任务委派

4.3 FlowBillService:业务模块的统一接入接口

这是 RuoYi Office BPM 架构中最核心的设计之一。FlowBillService 定义在框架公共层,作为所有业务模块接入流程引擎的统一契约

public interface FlowBillService<T extends BillTypeEnum> {

    /** 获取支持的单据类型 */
    T getSupportedBillType();

    /** 更新流程状态 — 必须实现 */
    void updateProcessStatus(String businessKey, Integer status);

    /** 流程审批通过后的业务处理 — 按需重写 */
    default void onProcessApproved(String businessKey) { }

    /** 流程拒绝后的业务处理 — 按需重写 */
    default void onProcessRejected(String businessKey) { }

    /** 流程撤回后的业务处理 — 按需重写 */
    default void onProcessCancelled(String businessKey) { }

    /** 删除业务单据数据 — 按需重写 */
    default void deleteBill(String businessKey) { }
}

设计亮点

  1. 泛型约束<T extends BillTypeEnum> 确保每个实现类绑定到具体的单据类型枚举
  2. 默认实现:通过 default 方法,业务模块只需实现必要的方法(updateProcessStatus),回调方法按需重写
  3. 松耦合:业务模块不需要知道 BPM 内部如何工作,只需要实现这个接口

4.4 通用事件监听器:一套模板服务所有业务模块

@Slf4j
public abstract class AbstractFlowLocalNotificationListener<T extends BillTypeEnum>
        implements ApplicationListener<BpmProcessInstanceStatusEvent> {

    protected abstract SystemEnum getSystem();
    protected abstract FlowBillServiceFactory<T> getFlowBillServiceFactory();

    @Override
    public void onApplicationEvent(BpmProcessInstanceStatusEvent event) {
        // 1. 按模块前缀过滤,只处理本模块的流程事件
        String processDefinitionKey = event.getProcessDefinitionKey();
        if (!FlowProcessPrefixUtils.match(getSystem(), processDefinitionKey)) {
            return;  // 非本模块流程,跳过
        }

        // 2. 按事件类型分发处理
        BpmEventTypeEnum eventType = event.getEventType();
        if (eventType.isProcessInstanceEvent()) {
            handleProcessInstanceEvent(event);  // 流程实例事件
        } else if (eventType.isTaskEvent()) {
            handleTaskEvent(event);              // 任务事件
        }
    }

    /** 更新单据状态 — 通过工厂模式获取对应的 FlowBillService */
    private void updateBillStatus(BpmProcessInstanceStatusEvent message, Integer status) {
        FlowBillService<T> flowBillService = getFlowBillServiceFactory()
            .getServiceByProcessKey(message.getProcessDefinitionKey());
        TenantUtils.executeIgnore(() -> {
            flowBillService.updateProcessStatus(
                message.getProcessInstanceInfo().getBusinessKey(),
                status == null ? message.getProcessInstanceInfo().getStatus() : status);
        });
    }
}

每个业务模块只需要一个极简的实现类

@Component
public class OaLocalNotificationListener
        extends AbstractFlowLocalNotificationListener<OaBillTypeEnum> {

    @Resource
    private OaFlowBillServiceFactory flowBillServiceFactory;

    @Override
    protected SystemEnum getSystem() {
        return SystemEnum.OA;
    }

    @Override
    protected FlowBillServiceFactory<OaBillTypeEnum> getFlowBillServiceFactory() {
        return flowBillServiceFactory;
    }
}

仅仅 15 行代码,OA 模块就完成了与 BPM 的事件对接!HRM、CRM 等模块同理,彻底消除了重复代码。

4.5 事件流转全景图

image.png


五、实战案例:印章申请单的完整流程解析

理论说得再好不如看实际代码。下面以用印申请单(OA 模块最典型的业务表单之一)为例,完整展示 BPM 与业务模块的集成过程。

5.1 后端:SealApplyBillServiceImpl

@Service
@Validated
public class SealApplyBillServiceImpl
        implements SealApplyBillService, FlowBillService<OaBillTypeEnum> {

    @Resource
    private SealApplyBillMapper sealApplyBillMapper;
    @Resource
    private BpmProcessInstanceApi processInstanceApi;

    @Override
    public Long submitSealApplyBill(SealApplyBillSaveReqVO saveReqVO) {
        // 1. 生成单据编号
        if (StringUtils.isBlank(saveReqVO.getBillCode())) {
            saveReqVO.setBillCode(BillCodeUtils.generateBillCode(
                SystemEnum.OA, OaBillTypeEnum.OA_SEAL_APPLY_BILL));
        }

        // 2. 业务校验(外借用章需校验时间冲突)
        if (saveReqVO.getUseMode() != null && saveReqVO.getUseMode() == 2) {
            validateTimeConflict(saveReqVO);
        }

        // 3. 保存业务数据
        SealApplyBillDO sealApplyBill = BeanUtils.toBean(saveReqVO, SealApplyBillDO.class)
                .setProcessStatus(BpmTaskStatusEnum.RUNNING.getStatus());
        sealApplyBillMapper.insertOrUpdate(sealApplyBill);

        // 4. 构建流程变量(标准字段 + 业务自定义字段)
        Map<String, Object> processInstanceVariables =
            BpmProcessVariableUtils.buildBillVariables(saveReqVO);
        processInstanceVariables.put(PV_SEAL_USE_MODE, saveReqVO.getUseMode());

        // 5. 发起流程实例
        String processInstanceId = processInstanceApi.submitProcessInstance(
            Long.valueOf(saveReqVO.getCreator()),
            new BpmProcessInstanceCreateReqDTO()
                .setProcessDefinitionKey(OaBillTypeEnum.OA_SEAL_APPLY_BILL
                    .getProcessDefinitionKey())
                .setVariables(processInstanceVariables)
                .setBusinessKey(String.valueOf(sealApplyBill.getId()))
        ).getCheckedData();

        // 6. 回写流程实例 ID 到业务表
        sealApplyBillMapper.updateById(
            new SealApplyBillDO().setId(sealApplyBill.getId())
                .setProcessInstanceId(processInstanceId));

        return sealApplyBill.getId();
    }

    // ==================== FlowBillService 接口实现 ====================

    @Override
    public OaBillTypeEnum getSupportedBillType() {
        return OaBillTypeEnum.OA_SEAL_APPLY_BILL;
    }

    @Override
    public void updateProcessStatus(String businessKey, Integer status) {
        Long id = Long.parseLong(businessKey);
        SealApplyBillDO updateObj = new SealApplyBillDO().setId(id)
            .setProcessStatus(status);

        // 审批通过时,自动设置用印状态为待处理
        if (APPROVE.getStatus().equals(status)) {
            updateObj.setUseStatus(SealUseStatusEnum.PENDING.getStatus());
        }

        sealApplyBillMapper.updateById(updateObj);
    }

    @Override
    public void deleteBill(String businessKey) {
        Long id = Long.parseLong(businessKey);
        attachmentService.deleteAttachmentByBusiness(
            OaBillTypeEnum.OA_SEAL_APPLY_BILL.getTypeCode(), id);
        sealApplyBillMapper.deleteById(id);
    }
}

关键设计解读

  1. implements FlowBillService<OaBillTypeEnum>:声明本服务支持的单据类型,框架自动注册到工厂
  2. BpmProcessVariableUtils.buildBillVariables():自动从 VO 中提取标准流程变量(单据编号、事由、部门等)
  3. updateProcessStatus():当流程状态变更时,BPM 框架自动回调此方法,业务模块只需更新自己的状态字段
  4. 审批通过联动:当 status == APPROVE 时,自动设置用印状态为"待处理",业务逻辑与流程状态完美衔接

5.2 前端:印章申请业务表单

// oa/seal/sealapply/info/index.vue
const props = defineProps<{
  id?: number | string;
  isApproval?: boolean;
  nodeKeyName?: string;
  processInstance?: any;
  // ... 其他审批相关 props
}>();

/**
 * 审批前置校验 — BPM 审批详情页在用户点击"通过"前会调用此方法
 * 业务表单可以在此做自定义校验和数据保存
 */
async function beforeApproval(): Promise<boolean> {
  // 仅在特定审批节点(如"申请人归还印章")需要校验
  if (props.isApproval && props.nodeKeyName === '申请人归还印章'
      && basicFormRef.value) {
    const { valid } = await basicFormRef.value.validateForm();
    if (!valid) return false;

    const formValues = await basicFormRef.value.getFormValues();
    const data = { ...formData.value, ...formValues };
    await saveSealApplyBill(data); // 审批前自动保存最新数据
  }
  return true;
}

// 暴露方法给 BPM 审批详情页调用
defineExpose({ beforeApproval, loadData, handleSaveAndSubmit });

注意 beforeApproval 的设计:不同的审批节点可能需要不同的操作。比如在"归还印章"节点,审批人需要确认归还信息并保存,而在其他节点则直接通过即可。这种节点级别的业务定制能力,是 RuoYi Office 区别于其他 BPM 系统的重要特征。

5.3 单据上流程结合应用:三标签页详情体验

理解了后端代码后,我们来看看用户实际使用时的体验。在 RuoYi Office 中,所有走流程的业务单据都遵循统一的三标签页详情体验:「单据信息 + 审批信息 + 流程图」。

人事调动申请单为例(也适用于用印申请、用车申请、请假单等所有业务单据):

标签页一:单据信息 — 业务数据的完整呈现

image.png

▲ 单据信息页:顶部单据头(编号/申请人/日期/单位/部门)+ 业务表单主体,支持查看和编辑

单据信息页是业务表单与流程引擎的深度融合体现

  • 单据头区域:统一展示单据编号(HR203-2026030900001)、申请人、申请日期、所属单位、所属部门,所有单据格式一致
  • 业务表单区域:根据 formType 动态加载——流程表单走 form-create 渲染,业务表单走自定义 Vue 组件
  • 字段权限控制:不同审批节点看到的字段权限不同——某些字段对当前审批人「只读」,某些字段「可编辑」,某些字段「隐藏」
  • 操作按钮:根据当前用户身份和流程状态,智能显示「撤回」「关闭」或「审批中」等操作

上图示例为人事调动申请单,包含「基本信息」(员工、工号、性别、手机号、职位、职务)和「调动信息」(异动类型、原部门/职位、变更为部门/职位),完整展现了复杂业务表单的呈现能力。

标签页二:审批信息 — 审批进度与记录一目了然

image.png

▲ 审批信息页:顶部审批进度条 + 底部审批记录表格,实时追踪流程走到哪一步

审批信息页提供了两种维度的审批可视化:

1. 审批进度条(Progress Bar)

以步骤条形式直观展示审批流程的当前进度:

  • 已完成节点(绿色对勾):发起人 — 宇擎源码,已通过
  • 🔵 当前节点(蓝色高亮数字):审批人 — bevan,审批中
  • 待处理节点(灰色数字):审批人 — hr小姐姐、人事,等待中
  • 📍 结束节点:流程终点

2. 审批记录表格(Audit Trail)

字段说明企业价值
审批节点对应流程设计器中的节点名称定位审批环节
审批人实际处理人责任追溯
开始时间 / 结束时间精确到秒效率分析
审批状态审批通过 / 审批中 / 审批拒绝状态追踪
审批建议审批意见和备注沟通留痕

审计价值:每一条审批记录都是不可篡改的操作日志,满足企业内审和外部合规要求。

标签页三:流程图 — 实时可视化流程走向

image.png

▲ 流程图页:以 Simple 设计器视图实时展示流程走向,不同颜色区分节点状态

流程图标签页将流程的当前执行状态以可视化方式呈现,让用户一眼看清:

颜色含义示例
🟢 绿色已完成节点发起人(已通过)、审批人(已通过)
🔵 蓝色边框当前进行中节点审批人(发起人连续部门负责人)
白色/灰色待处理节点审批人(指定岗位:人力资源)、结束

这个三标签页设计的精妙之处

  1. 信息分层:单据数据、审批状态、流程全景三个维度分离,各取所需,不信息过载
  2. 统一体验:无论是 OA 用印、HRM 调动、CRM 合同审批,三标签页体验完全一致
  3. 动态渲染:业务表单区域根据 formType 动态加载,框架无需为每种单据定制页面
  4. 权限精控:审批人在不同节点看到的表单字段权限由流程设计器配置,无需代码控制
// 核心代码:流程详情页根据 formType 决定渲染方式
if (processDefinition.value.formType === BpmModelFormType.NORMAL) {
  // 流程表单 → form-create 动态渲染 JSON 配置
  setConfAndFields2(detailForm, formConf, formFields, formVariables);
} else {
  // 业务表单 → 动态加载自定义 Vue 组件
  BusinessFormComponent.value = registerComponent(formCustomViewPath);
}

💡 这意味着什么? 开发一个新的业务单据(比如「合同审批」),你只需要:① 开发一个 Vue 表单组件;② 实现 FlowBillService 接口;③ 在流程模型中配置表单路径。框架会自动把你的表单嵌入到统一的三标签页详情中,审批进度、审批记录、流程图全部自动生成。

5.4 流程变量:打通业务数据和流程引擎

BpmProcessVariableUtils 自动从业务对象中提取标准字段作为流程变量,供流程条件判断和任务列表展示使用:

流程变量说明来源用途
BILL_CODE单据编号业务 VO 的 billCode任务列表显示
CAUSE事由/说明业务 VO 的 cause流程摘要
DEPT_NAME部门名称业务 VO 的 deptName组织维度条件
COMPANY_NAME公司名称业务 VO 的 companyName组织维度条件
自定义变量业务专属变量手动设置条件分支判断

印章申请单额外添加了 PV_SEAL_USE_MODE(用印方式),可以在流程设计中作为条件判断——比如"外借用印需要增加部门领导审批"。


六、14 种审批人策略:覆盖企业级组织架构的所有场景

审批人分配是 BPM 系统的核心能力。RuoYi Office 使用策略模式,内置了 14 种审批人候选策略:

策略适用场景举例
按角色固定角色审批"财务主管"角色审批报销单
部门成员部门内审批行政部全员可审批用车申请
部门负责人直属上级审批请假由部门负责人审批
多级部门负责人逐级上报金额超过 5 万,需 3 级领导审批
按岗位岗位职责审批"出纳"岗位处理付款
指定用户特定人员审批CEO 签字
发起人本人自审/确认印章归还需发起人确认
发起人部门负责人常见上级审批请假找直属领导
发起人多级部门负责人逐级领导按金额逐级审批
审批人自选灵活指定审批时动态选择下一个审批人
发起人自选发起时指定提交时选择具体审批人
用户组分组审批"合同审核组"审批合同
表单用户字段动态读取表单中的"项目经理"字段值作为审批人
流程表达式高级自定义复杂规则用表达式计算

image.png

6.1 策略模式实现

public interface BpmTaskCandidateStrategy {
    /** 策略类型 */
    BpmTaskCandidateStrategyEnum getStrategy();

    /** 计算候选人用户 ID 列表 */
    Set<Long> calculateUsers(String param);

    /** 是否支持退回 */
    default boolean isReturnSupported() { return true; }
}

BpmTaskCandidateInvoker 作为调度器,在任务创建时根据节点配置调用对应策略:

public class BpmTaskCandidateInvoker {
    private final Map<BpmTaskCandidateStrategyEnum, BpmTaskCandidateStrategy> strategyMap;

    public Set<Long> calculateUsers(DelegateExecution execution) {
        // 1. 从节点配置中获取候选人策略类型和参数
        Integer strategy = ...; // 从 BPMN 扩展属性获取
        String param = ...;
        // 2. 调用对应策略计算候选人
        return strategyMap.get(BpmTaskCandidateStrategyEnum.valueOf(strategy))
            .calculateUsers(param);
    }
}

新增审批人策略只需:实现 BpmTaskCandidateStrategy 接口 → 标注 @Component。无需修改任何核心代码,完美符合开闭原则。


七、任务管理:不只是"通过/驳回"

RuoYi Office 的任务管理远超简单的"通过/驳回",支持企业审批中的各种复杂场景:

操作说明企业场景
审批通过同意当前任务常规审批
审批驳回拒绝当前任务不符合要求,打回
退回退回到指定节点信息填写有误,退回修改
委派委派给他人处理后回到自己需要专业人员帮忙审核
转办转给他人直接处理审批人出差/离职
加签增加审批人金额较大,增加领导审批
减签移除审批人流程优化,减少不必要审批
抄送发送给指定人查看知会相关人员
撤回审批人撤回已提交的审批发现审批有误

任务状态流转:

NOT_START(-1) → WAIT(0) → RUNNING(1) → APPROVE(2) / REJECT(3) / RETURN(5) / CANCEL(4)
                                      → APPROVING(7) → APPROVE(2)
                                      → WITHDRAW(10)
                          SKIP(-2)

image.png

▲ 我的流程页面:展示所有发起的流程,支持查看详情、撤回、删除等操作


八、首页任务中心:让审批效率倍增

8.1 四维任务视角

RuoYi Office 的工作台首页集成了 WorkbenchTaskList 组件,提供四个维度的任务视角:

const tabs = computed(() => [
  { key: 'todo', label: '待办任务', count: statistics.value.todo },
  { key: 'myBill', label: '我的单据', count: statistics.value.myBill },
  { key: 'done', label: '已办任务', count: statistics.value.done },
  { key: 'copy', label: '抄送我的', count: statistics.value.copy },
]);
维度说明用户价值
待办任务等待我审批的任务快速处理审批,不遗漏
我的单据我发起的所有流程追踪审批进度
已办任务我已审批的任务回顾审批历史
抄送我的抄送给我的流程及时了解相关动态

![image.png](openwrite.cn