# scaffold 项目之工作流

# 简介

Flowable 是一个基于 Java 的开源轻量级业务流程管理(BPM)平台,用于实现业务流程的自动化、管理和优化

为了方便你快速了解其全貌,我将核心信息整理为下表:

特性维度核心说明
本质与起源轻量级 Java BPM 引擎,是 Activiti 5.x 的分支,支持 BPMN 2.0 标准
核心功能流程设计、部署、实例执行与监控、任务管理、历史数据追踪、集成扩展
架构核心 (服务)通过 RepositoryService , RuntimeService , TaskService , HistoryService 等提供 API
数据表 (ACT_开头)分为流程定义 (RE)、运行时 (RU)、历史 (HI)、身份 (ID)、通用 (GE) 五大类
主要应用场景审批流程(请假、报销)、订单处理、工单流转等需要多人按规则协作的业务自动化
关键优势轻量嵌入、与 Spring/Spring Boot 集成友好、功能全面且可扩展、拥有可视化设计器

# 核心概念与工作原理

理解 Flowable,需要掌握以下几个关键点:

  1. 流程定义与实例

    流程定义:业务的蓝图,通常使用 BPMN 2.0 标准以 XML 格式定义,描述流程步骤、顺序和规则

    流程实例:流程定义的一次具体运行。例如,一个请假流程定义,对应张三和李四各自发起的两次流程实例

  2. 流程引擎与服务
    Flowable 的核心是一个流程引擎,它通过一系列服务接口提供所有功能:

    RepositoryService :管理(部署、查询、挂起)流程定义

    RuntimeService :启动和管理流程实例

    TaskService :处理用户任务(如查询待办、完成任务)

    HistoryService :查询已结束的流程实例历史数据

  3. 任务与审批人分配
    在用户任务节点,可以通过多种方式动态指定处理人

  • 固定分配:在流程图中直接设置用户 ID。
  • 表达式分配:使用 UEL 表达式,如 ${departmentManager} ,在启动流程时传入变量。
  • 监听器分配:通过编写 Java 代码,在任务创建时触发逻辑来指定。
  1. 流程变量
    用于在流程实例运行过程中传递业务数据(如请假天数、审批意见),作用域可以是全局(整个实例)或局部(特定任务)

# 开始使用

首先本项目基于 flowable 专门封装了一个工作流的模块 scaffold-module-bpm

大概流程图:

定义流程模型 -> 发布成流程定义 -> 创建流程实例(就是一个启动的流程)-> 安装定义的流程节点顺序往下走 -> 审批、拒签、会签、委托 等等操作 -> 最终完成

就像水龙头中的水流一样,定义好整个供水系统的蓝图 -> 部署到需要的家家户户 -> 用户打开水龙头开始使用 -> 水从源头过来,途中会有多个闸口,有些检查余额的闸口,有钱放行,没钱不给水流通过(相当于审批) -> 最终出水使用

# 功能点

功能列表功能描述
SIMPLE 设计器仿钉钉 / 飞书设计器,支持拖拽搭建表单流程,10 分钟快速完成审批流程配置
BPMN 设计器基于 BPMN 标准开发,适配复杂业务场景,满足多层级审批及流程自动化需求
会签同一个审批节点设置多个人(如 A、B、C 三人,三人会同时收到待办任务),需全部同意之后,审批才可到下一审批节点
或签同一个审批节点设置多个人,任意一个人处理后,就能进入下一个节点
依次审批(顺序会签)同一个审批节点设置多个人(如 A、B、C 三人),三人按顺序依次收到待办,即 A 先审批,A 提交后 B 才能审批,需全部同意之后,审批才可到下一审批节点
抄送将审批结果通知给抄送人,同一个审批默认排重,不重复抄送给同一人
驳回(退回)将审批重置发送给某节点,重新审批。可驳回至发起人、上一节点、任意节点
转办A 转给其 B 审批,B 审批后,进入下一节点
委派A 转给其 B 审批,B 审批后,转给 A,A 继续审批后进入下一节点
加签允许当前审批人根据需要,自行增加当前节点的审批人,支持向前、向后加签
减签(取消加签)在当前审批人操作之前,减少审批人
撤销(取消流程)流程发起人,可以对流程进行撤销处理
终止系统管理员,在任意节点终止流程实例
表单权限支持拖拉拽配置表单,每个审批节点可配置只读、编辑、隐藏权限
超时审批配置超时审批时间,超时后自动触发审批通过、不通过、驳回等操作
自动提醒配置提醒时间,到达时间后自动触发短信、邮箱、站内信等通知提醒,支持自定义重复提醒频次
父子流程主流程设置子流程节点,子流程节点会自动触发子流程。子流程结束后,主流程才会执行(继续往下下执行),支持同步子流程、异步子流程
条件分支(排它分支)用于在流程中实现决策,即根据条件选择一个分支执行
并行分支允许将流程分成多条分支,不进行条件判断,所有分支都会执行
包容分支(条件分支 + 并行分支的结合体)允许基于条件选择多条分支执行,但如果没有任何一个分支满足条件,则可以选择默认分支
路由分支根据条件选择一个分支执行(重定向到指定配置节点),也可以选择默认分支执行(继续往下执行)
触发节点执行到该节点,触发 HTTP 请求、HTTP 回调、更新数据、删除数据等
延迟节点执行到该节点,审批等待一段时间再执行,支持固定时长、固定日期等
拓展设置流程前置 / 后置通知,节点(任务)前置、后置通知,流程报表,自动审批去重,自定流程编号、标题、摘要,流程报表等

# 具体实现

# 创建 scaffold-module-bpm 模块

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>scaffold</artifactId>
        <groupId>cn.tzzfj.scaffold</groupId>
        <version>${revision}</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>scaffold-module-bpm</artifactId>
    <packaging>jar</packaging>
    <name>${project.artifactId}</name>
    <description>
        bpm 包下,业务流程管理(Business Process Management),我们放工作流的功能。
        例如说:流程定义、表单配置、审核中心(我的申请、我的待办、我的已办)等等
        bpm 解释:https://baike.baidu.com/item/BPM/1933
        工作流基于 Flowable 6 实现,分成流程定义、流程表单、流程实例、流程任务等功能模块。
    </description>
    <dependencies>
        <dependency>
            <groupId>cn.tzzfj.scaffold</groupId>
            <artifactId>scaffold-module-system</artifactId>
            <version>${revision}</version>
        </dependency>
        <!-- 省略其他的依赖 -->
     	
        <!-- Flowable 工作流相关 -->
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter-process</artifactId>
        </dependency>
        <dependency>
            <groupId>org.flowable</groupId>
            <artifactId>flowable-spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
</project>

# 创建蓝图(流程模型)

# controller
package cn.tzzfj.scaffold.module.bpm.controller.admin.definition;
/**
 * <p> Project: scaffold - BpmModelController  </p>
 *
 * 管理后台 - 流程模型
 * 
 * @author Tz
 * @date 2025/10/25 15:26
 * @version 1.0.0
 * @since 1.0.0
 */
@Tag(name = "管理后台 - 流程模型")
@RestController
@RequestMapping("/bpm/model")
@Validated
public class BpmModelController {
    @Resource
    private BpmModelService modelService;
    @Resource
    private BpmFormService formService;
    @Resource
    private BpmCategoryService categoryService;
    @Resource
    private BpmProcessDefinitionService processDefinitionService;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private DeptApi deptApi;
    @GetMapping("/list")
    @Operation(summary = "获得模型分页")
    @Parameter(name = "name", description = "模型名称", example = "Tz")
    public CommonResult<List<BpmModelRespVO>> getModelList(@RequestParam(value = "name", required = false) String name) {
        List<Model> list = modelService.getModelList(name);
        if (CollUtil.isEmpty(list)) {
            return success(Collections.emptyList());
        }
        // 获得 Form 表单
        Set<Long> formIds = convertSet(list, model -> {
            BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
            return metaInfo != null ? metaInfo.getFormId() : null;
        });
        Map<Long, BpmFormDO> formMap = formService.getFormMap(formIds);
        // 获得 Category Map
        Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(
                convertSet(list, Model::getCategory));
        // 获得 Deployment Map
        Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(
                convertSet(list, Model::getDeploymentId));
        // 获得 ProcessDefinition Map
        List<ProcessDefinition> processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds(
                deploymentMap.keySet());
        Map<String, ProcessDefinition> processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId);
        // 获得 User Map、Dept Map
        Set<Long> userIds = convertSetByFlatMap(list, model -> {
            BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
            return metaInfo != null ? metaInfo.getStartUserIds().stream() : Stream.empty();
        });
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
        Set<Long> deptIds = convertSetByFlatMap(list, model -> {
            BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
            return metaInfo != null && metaInfo.getStartDeptIds() != null ? metaInfo.getStartDeptIds().stream() : Stream.empty();
        });
        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(deptIds);
        
        // 最总组合信息返回
        return success(BpmModelConvert.INSTANCE.buildModelList(list,
                formMap, categoryMap, deploymentMap, processDefinitionMap, userMap, deptMap));
    }
    @GetMapping("/get")
    @Operation(summary = "获得模型")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('bpm:model:query')")
    public CommonResult<BpmModelRespVO> getModel(@RequestParam("id") String id) {
        Model model = modelService.getModel(id);
        if (model == null) {
            return null;
        }
        byte[] bpmnBytes = modelService.getModelBpmnXML(id);
        BpmSimpleModelNodeVO simpleModel = modelService.getSimpleModel(id);
        return success(BpmModelConvert.INSTANCE.buildModel(model, bpmnBytes, simpleModel));
    }
    @PostMapping("/create")
    @Operation(summary = "新建模型")
    @PreAuthorize("@ss.hasPermission('bpm:model:create')")
    public CommonResult<String> createModel(@Valid @RequestBody BpmModelSaveReqVO createRetVO) {
        return success(modelService.createModel(createRetVO));
    }
    @PutMapping("/update")
    @Operation(summary = "修改模型")
    @PreAuthorize("@ss.hasPermission('bpm:model:update')")
    public CommonResult<Boolean> updateModel(@Valid @RequestBody BpmModelSaveReqVO modelVO) {
        modelService.updateModel(getLoginUserId(), modelVO);
        return success(true);
    }
    @PutMapping("/update-sort-batch")
    @Operation(summary = "批量修改模型排序")
    @Parameter(name = "ids", description = "编号数组", required = true, example = "1,2,3")
    public CommonResult<Boolean> updateModelSortBatch(@RequestParam("ids") List<String> ids) {
        modelService.updateModelSortBatch(getLoginUserId(), ids);
        return success(true);
    }
    @PostMapping("/deploy")
    @Operation(summary = "部署模型")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('bpm:model:deploy')")
    public CommonResult<Boolean> deployModel(@RequestParam("id") String id) {
        modelService.deployModel(getLoginUserId(), id);
        return success(true);
    }
    @PutMapping("/update-state")
    @Operation(summary = "修改模型的状态", description = "实际更新的部署的流程定义的状态")
    @PreAuthorize("@ss.hasPermission('bpm:model:update')")
    public CommonResult<Boolean> updateModelState(@Valid @RequestBody BpmModelUpdateStateReqVO reqVO) {
        modelService.updateModelState(getLoginUserId(), reqVO.getId(), reqVO.getState());
        return success(true);
    }
    @Deprecated
    @PutMapping("/update-bpmn")
    @Operation(summary = "修改模型的 BPMN")
    @PreAuthorize("@ss.hasPermission('bpm:model:update')")
    public CommonResult<Boolean> updateModelBpmn(@Valid @RequestBody BpmModeUpdateBpmnReqVO reqVO) {
        modelService.updateModelBpmnXml(reqVO.getId(), reqVO.getBpmnXml());
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除模型")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('bpm:model:delete')")
    public CommonResult<Boolean> deleteModel(@RequestParam("id") String id) {
        modelService.deleteModel(getLoginUserId(), id);
        return success(true);
    }
    @DeleteMapping("/clean")
    @Operation(summary = "清理模型")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('bpm:model:clean')")
    public CommonResult<Boolean> cleanModel(@RequestParam("id") String id) {
        modelService.cleanModel(getLoginUserId(), id);
        return success(true);
    }
    // ========== 仿钉钉 / 飞书的精简模型 =========
    @GetMapping("/simple/get")
    @Operation(summary = "获得仿钉钉流程设计模型")
    @Parameter(name = "modelId", description = "流程模型编号", required = true, example = "a2c5eee0-eb6c-11ee-abf4-0c37967c420a")
    public CommonResult<BpmSimpleModelNodeVO> getSimpleModel(@RequestParam("id") String modelId){
        return success(modelService.getSimpleModel(modelId));
    }
    @Deprecated
    @PostMapping("/simple/update")
    @Operation(summary = "保存仿钉钉流程设计模型")
    @PreAuthorize("@ss.hasPermission('bpm:model:update')")
    public CommonResult<Boolean> updateSimpleModel(@Valid @RequestBody BpmSimpleModelUpdateReqVO reqVO) {
        modelService.updateSimpleModel(getLoginUserId(), reqVO);
        return success(Boolean.TRUE);
    }
}
# service:
package cn.tzzfj.scaffold.module.bpm.service.definition;
/**
 * <p> Project: scaffold - BpmModelServiceImpl  </p>
 *
 * 流程模型实现:主要进行 Flowable {@link Model} 的维护
 * 
 * @author Tz
 * @date 2025/10/25 15:26
 * @version 1.0.0
 * @since 1.0.0
 */
@Service
@Validated
@Slf4j
public class BpmModelServiceImpl implements BpmModelService {
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private BpmProcessDefinitionService processDefinitionService;
    @Resource
    private BpmFormService bpmFormService;
    @Resource
    private BpmTaskCandidateInvoker taskCandidateInvoker;
    @Resource
    private HistoryService historyService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private BpmProcessInstanceCopyService processInstanceCopyService;
    @Override
    public List<Model> getModelList(String name) {
        ModelQuery modelQuery = repositoryService.createModelQuery();
        if (StrUtil.isNotEmpty(name)) {
            modelQuery.modelNameLike("%" + name + "%");
        }
        modelQuery.modelTenantId(FlowableUtils.getTenantId());
        return modelQuery.list();
    }
    @Override
    public Long getModelCountByCategory(String category) {
        return repositoryService.createModelQuery()
                .modelCategory(category)
                .modelTenantId(FlowableUtils.getTenantId())
                .count();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createModel(@Valid BpmModelSaveReqVO createReqVO) {
        if (!ValidationUtils.isXmlNCName(createReqVO.getKey())) {
            throw exception(MODEL_KEY_VALID);
        }
        // 1. 校验流程标识已经存在
        Model keyModel = getModelByKey(createReqVO.getKey());
        if (keyModel != null) {
            throw exception(MODEL_KEY_EXISTS, createReqVO.getKey());
        }
        // 2. 创建 Model 对象
        createReqVO.setSort(System.currentTimeMillis()); // 使用当前时间,作为排序
        Model model = repositoryService.newModel();
        BpmModelConvert.INSTANCE.copyToModel(model, createReqVO);
        model.setTenantId(FlowableUtils.getTenantId());
        // 3. 保存模型
        saveModel(model, createReqVO);
        return model.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务
    public void updateModel(Long userId, BpmModelSaveReqVO updateReqVO) {
        // 1. 校验流程模型存在
        Model model = validateModelManager(updateReqVO.getId(), userId);
        // 2. 填充 Model 信息
        BpmModelConvert.INSTANCE.copyToModel(model, updateReqVO);
        // 3. 保存模型
        saveModel(model, updateReqVO);
    }
    /**
     * 保存模型的基本信息、流程图
     *
     * @param model 模型
     * @param saveReqVO 保存信息
     */
    private void saveModel(Model model, BpmModelSaveReqVO saveReqVO) {
        // 1. 保存模型的基础信息
        repositoryService.saveModel(model);
        // 2. 保存流程图
        if (ObjUtil.equals(BpmModelTypeEnum.BPMN.getType(), saveReqVO.getType())
                && StrUtil.isNotEmpty(saveReqVO.getBpmnXml())) {
            updateModelBpmnXml(model.getId(), saveReqVO.getBpmnXml());
        } else if (ObjUtil.equals(BpmModelTypeEnum.SIMPLE.getType(), saveReqVO.getType())
                && saveReqVO.getSimpleModel() != null) {
            // JSON 转换成 bpmnModel
            BpmnModel bpmnModel = SimpleModelUtils.buildBpmnModel(model.getKey(), model.getName(),
                    saveReqVO.getSimpleModel());
            // 保存 Bpmn XML
            updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel));
            // 保存 JSON 数据
            updateModelSimpleJson(model.getId(), saveReqVO.getSimpleModel());
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateModelSortBatch(Long userId, List<String> ids) {
        // 1.1 校验流程模型存在
        List<Model> models = repositoryService.createModelQuery()
                .modelTenantId(FlowableUtils.getTenantId()).list();
        models.removeIf(model -> !ids.contains(model.getId()));
        if (ids.size() != models.size()) {
            throw exception(MODEL_NOT_EXISTS);
        }
        Map<String, Model> modelMap = convertMap(models, Model::getId);
        // 1.2 校验是否为管理员
        ids.forEach(id -> validateModelManager(id, userId));
        // 保存排序
        long sort = System.currentTimeMillis(); // 使用时间戳 - i 作为排序
        for (int i = ids.size() - 1; i > 0; i--) {
            Model model = modelMap.get(ids.get(i));
            // 更新模型
            BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model).setSort(sort);
            model.setMetaInfo(JsonUtils.toJsonString(metaInfo));
            repositoryService.saveModel(model);
            // 更新排序
            processDefinitionService.updateProcessDefinitionSortByModelId(model.getId(), sort);
            sort--;
        }
    }
    private Model validateModelExists(String id) {
        Model model = repositoryService.getModel(id);
        if (model == null) {
            throw exception(MODEL_NOT_EXISTS);
        }
        return model;
    }
    /**
     * 校验是否有流程模型的管理权限
     *
     * @param id     流程模型编号
     * @param userId 用户编号
     * @return 流程模型
     */
    private Model validateModelManager(String id, Long userId) {
        Model model = validateModelExists(id);
        BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
        if (metaInfo == null || !CollUtil.contains(metaInfo.getManagerUserIds(), userId)) {
            throw exception(MODEL_UPDATE_FAIL_NOT_MANAGER, model.getName());
        }
        return model;
    }
    @Override
    @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务
    public void deployModel(Long userId, String id) {
        // 1.1 校验流程模型存在
        Model model = validateModelManager(id, userId);
        BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
        // 1.2 校验流程图
        byte[] bpmnBytes = getModelBpmnXML(model.getId());
        validateBpmnXml(bpmnBytes, metaInfo.getType());
        // 1.3 校验表单已配
        BpmFormDO form = validateFormConfig(metaInfo);
        // 1.4 校验任务分配规则已配置
        taskCandidateInvoker.validateBpmnConfig(bpmnBytes);
        // 1.5 获取仿钉钉流程设计器模型数据
        String simpleJson = getModelSimpleJson(model.getId());
        // 2.1 创建流程定义
        String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, simpleJson,
                form);
        // 2.2 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。
        updateProcessDefinitionSuspended(model.getDeploymentId());
        // 2.3 更新 model 的 deploymentId,进行关联
        ProcessDefinition definition = processDefinitionService.getProcessDefinition(definitionId);
        model.setDeploymentId(definition.getDeploymentId());
        repositoryService.saveModel(model);
    }
    private void validateBpmnXml(byte[] bpmnBytes, Integer type) {
        BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);
        if (bpmnModel == null) {
            throw exception(MODEL_NOT_EXISTS);
        }
        // 1. 没有 StartEvent
        StartEvent startEvent = BpmnModelUtils.getStartEvent(bpmnModel);
        if (startEvent == null) {
            throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS);
        }
        // 2. 校验 UserTask 的 name 都配置了
        List<UserTask> userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
        userTasks.forEach(userTask -> {
            if (StrUtil.isEmpty(userTask.getName())) {
                throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId());
            }
        });
        // 3. 校验第一个用户任务节点的规则类型是否为 “审批人自选”,BPMN 设计器,校验第一个用户任务节点,SIMPLE 设计器,第一个节点固定为发起人所以校验第二个用户任务节点
        UserTask firUserTask = CollUtil.get(userTasks, BpmModelTypeEnum.BPMN.getType().equals(type) ? 0 : 1);
        if (firUserTask == null) {
            return;
        }
        Integer candidateStrategy = parseCandidateStrategy(firUserTask);
        if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) {
            throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, firUserTask.getName());
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteModel(Long userId, String id) {
        // 校验流程模型存在
        Model model = validateModelManager(id, userId);
        // 执行删除
        repositoryService.deleteModel(id);
        // 禁用流程定义
        updateProcessDefinitionSuspended(model.getDeploymentId());
    }
    @Override
    public void cleanModel(Long userId, String id) {
        // 1. 校验流程模型存在
        Model model = validateModelManager(id, userId);
        // 2. 清理所有流程数据
        // 2.1 先取消所有正在运行的流程
        List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery()
                .processDefinitionKey(model.getKey()).list();
        processInstances.forEach(processInstance -> {
            runtimeService.deleteProcessInstance(processInstance.getId(),
                    BpmReasonEnum.CANCEL_BY_SYSTEM.getReason());
            historyService.deleteHistoricProcessInstance(processInstance.getId());
            processInstanceCopyService.deleteProcessInstanceCopy(processInstance.getId());
        });
        // 2.2 再从历史中删除所有相关的流程数据
        List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()
                .processDefinitionKey(model.getKey()).list();
        historicProcessInstances.forEach(historicProcessInstance -> {
            historyService.deleteHistoricProcessInstance(historicProcessInstance.getId());
            processInstanceCopyService.deleteProcessInstanceCopy(historicProcessInstance.getId());
        });
        // 2.3 清理所有 Task
        List<Task> tasks = taskService.createTaskQuery()
                .processDefinitionKey(model.getKey()).list();
        tasks.forEach(task -> taskService.deleteTask(task.getId(),BpmReasonEnum.CANCEL_BY_PROCESS_CLEAN.getReason()));
    }
    @Override
    public void updateModelState(Long userId, String id, Integer state) {
        // 1.1 校验流程模型存在
        Model model = validateModelManager(id, userId);
        // 1.2 校验流程定义存在
        ProcessDefinition definition = processDefinitionService
                .getProcessDefinitionByDeploymentId(model.getDeploymentId());
        if (definition == null) {
            throw exception(PROCESS_DEFINITION_NOT_EXISTS);
        }
        // 2. 更新状态
        processDefinitionService.updateProcessDefinitionState(definition.getId(), state);
    }
    @Override
    public BpmnModel getBpmnModelByDefinitionId(String processDefinitionId) {
        return repositoryService.getBpmnModel(processDefinitionId);
    }
    @Override
    public BpmSimpleModelNodeVO getSimpleModel(String modelId) {
        Model model = validateModelExists(modelId);
        // 通过 ACT_RE_MODEL 表 EDITOR_SOURCE_EXTRA_VALUE_ID_ ,获取仿钉钉快搭模型的 JSON 数据
        String json = getModelSimpleJson(model.getId());
        return JsonUtils.parseObject(json, BpmSimpleModelNodeVO.class);
    }
    @Override
    public void updateSimpleModel(Long userId, BpmSimpleModelUpdateReqVO reqVO) {
        // 1. 校验流程模型存在
        Model model = validateModelManager(reqVO.getId(), userId);
        // 2.1 JSON 转换成 bpmnModel
        BpmnModel bpmnModel = SimpleModelUtils.buildBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModel());
        // 2.2 保存 Bpmn XML
        updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel));
        // 2.3 保存 JSON 数据
        updateModelSimpleJson(model.getId(), reqVO.getSimpleModel());
    }
    /**
     * 校验流程表单已配置
     *
     * @param metaInfo 流程模型元数据
     * @return 表单配置
     */
    private BpmFormDO validateFormConfig(BpmModelMetaInfoVO metaInfo) {
        if (metaInfo == null || metaInfo.getFormType() == null) {
            throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
        }
        // 校验表单存在
        if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) {
            if (metaInfo.getFormId() == null) {
                throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
            }
            BpmFormDO form = bpmFormService.getForm(metaInfo.getFormId());
            if (form == null) {
                throw exception(FORM_NOT_EXISTS);
            }
            return form;
        } else {
            if (StrUtil.isEmpty(metaInfo.getFormCustomCreatePath())
                    || StrUtil.isEmpty(metaInfo.getFormCustomViewPath())) {
                throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG);
            }
            return null;
        }
    }
    @Override
    public void updateModelBpmnXml(String id, String bpmnXml) {
        if (StrUtil.isEmpty(bpmnXml)) {
            return;
        }
        repositoryService.addModelEditorSource(id, StrUtil.utf8Bytes(bpmnXml));
    }
    @SuppressWarnings("JavaExistingMethodCanBeUsed")
    private String getModelSimpleJson(String id) {
        byte[] bytes = repositoryService.getModelEditorSourceExtra(id);
        if (ArrayUtil.isEmpty(bytes)) {
            return null;
        }
        return StrUtil.utf8Str(bytes);
    }
    private void updateModelSimpleJson(String id, BpmSimpleModelNodeVO node) {
        if (node == null) {
            return;
        }
        byte[] bytes = JsonUtils.toJsonByte(node);
        repositoryService.addModelEditorSourceExtra(id, bytes);
    }
    /**
     * 挂起 deploymentId 对应的流程定义
     * <p>
     * 注意:这里一个 deploymentId 只关联一个流程定义
     *
     * @param deploymentId 流程发布 Id
     */
    private void updateProcessDefinitionSuspended(String deploymentId) {
        if (StrUtil.isEmpty(deploymentId)) {
            return;
        }
        ProcessDefinition oldDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(deploymentId);
        if (oldDefinition == null) {
            return;
        }
        processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(),
                SuspensionState.SUSPENDED.getStateCode());
    }
    private Model getModelByKey(String key) {
        return repositoryService.createModelQuery()
                .modelTenantId(FlowableUtils.getTenantId())
                .modelKey(key).singleResult();
    }
    @Override
    public Model getModel(String id) {
        return repositoryService.getModel(id);
    }
    @Override
    public byte[] getModelBpmnXML(String id) {
        return repositoryService.getModelEditorSource(id);
    }
}
# 解释:
  • 创建流程模型, repositoryService.saveModel(model)

  • 添加 表单BPM XML 流程图其他相关配置

    对应的表 act_re_model 并且在字段 meteinfo 保存了额为的扩展数据

    package cn.tzzfj.scaffold.module.bpm.controller.admin.definition.vo.model;
    /**
     * <p> Project: scaffold - BpmModelMetaInfoVO  </p>
     *
     * 流程图标
     * 
     * @author Tz
     * @date 2025/10/25 15:26
     * @version 1.0.0
     * @since 1.0.0
     */
    @Data
    public class BpmModelMetaInfoVO {
        @Schema(description = "流程图标", example = "https://www.tzzfj.cn/scaffold.jpg")
        @URL(message = "流程图标格式不正确")
        private String icon;
        @Schema(description = "流程描述", example = "我是描述")
        private String description;
        @Schema(description = "流程类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
        @InEnum(BpmModelTypeEnum.class)
        @NotNull(message = "流程类型不能为空")
        private Integer type;
        @Schema(description = "表单类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
        @InEnum(BpmModelFormTypeEnum.class)
        @NotNull(message = "表单类型不能为空")
        private Integer formType;
        @Schema(description = "表单编号", example = "1024")
        private Long formId; //formType 为 NORMAL 使用,必须非空
        @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create")
        private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空
        @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view")
        private String formCustomViewPath; // 表单类型为 CUSTOM 时,必须非空
        @Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
        @NotNull(message = "是否可见不能为空")
        private Boolean visible;
        @Schema(description = "可发起用户编号数组", example = "[1,2,3]")
        private List<Long> startUserIds;
        @Schema(description = "可发起部门编号数组", example = "[2,4,6]")
        private List<Long> startDeptIds;
        @Schema(description = "可管理用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2,4,6]")
        @NotEmpty(message = "可管理用户编号数组不能为空")
        private List<Long> managerUserIds;
        @Schema(description = "排序", example = "1")
        private Long sort; // 创建时,后端自动生成
        @Schema(description = "允许撤销审批中的申请", example = "true")
        private Boolean allowCancelRunningProcess;
        @Schema(description = "允许允许审批人撤回任务", example = "false")
        private Boolean allowWithdrawTask;
        @Schema(description = "流程 ID 规则", example = "{}")
        private ProcessIdRule processIdRule;
        @Schema(description = "自动去重类型", example = "1")
        @InEnum(BpmAutoApproveTypeEnum.class)
        private Integer autoApprovalType;
        @Schema(description = "标题设置", example = "{}")
        private TitleSetting titleSetting;
        @Schema(description = "摘要设置", example = "{}")
        private SummarySetting summarySetting;
        @Schema(description = "流程前置通知设置", example = "{}")
        private HttpRequestSetting processBeforeTriggerSetting;
        @Schema(description = "流程后置通知设置", example = "{}")
        private HttpRequestSetting processAfterTriggerSetting;
        @Schema(description = "任务前置通知设置", example = "{}")
        private HttpRequestSetting taskBeforeTriggerSetting;
        @Schema(description = "任务后置通知设置", example = "{}")
        private HttpRequestSetting taskAfterTriggerSetting;
        @Schema(description = "自定义打印模板设置", example = "{}")
        @Valid
        private PrintTemplateSetting printTemplateSetting;
        @Schema(description = "流程 ID 规则")
        @Data
        @Valid
        public static class ProcessIdRule {
            @Schema(description = "是否启用", example = "false")
            @NotNull(message = "是否启用不能为空")
            private Boolean enable;
            @Schema(description = "前缀", example = "XX")
            private String prefix;
            @Schema(description = "中缀", example = "20250120")
            private String infix; // 精确到日、精确到时、精确到分、精确到秒
            @Schema(description = "后缀", example = "YY")
            private String postfix;
            @Schema(description = "序列长度", example = "5")
            @NotNull(message = "序列长度不能为空")
            private Integer length;
        }
        @Schema(description = "标题设置")
        @Data
        @Valid
        public static class TitleSetting {
            @Schema(description = "是否自定义", example = "false")
            @NotNull(message = "是否自定义不能为空")
            private Boolean enable;
            @Schema(description = "标题", example = "流程标题")
            private String title;
        }
        @Schema(description = "摘要设置")
        @Data
        @Valid
        public static class SummarySetting {
            @Schema(description = "是否自定义", example = "false")
            @NotNull(message = "是否自定义不能为空")
            private Boolean enable;
            @Schema(description = "摘要字段数组", example = "[]")
            private List<String> summary;
        }
        @Schema(description = "http 请求通知设置", example = "{}")
        @Data
        public static class HttpRequestSetting {
            @Schema(description = "请求路径", example = "http://127.0.0.1")
            @NotEmpty(message = "请求 URL 不能为空")
            @URL(message = "请求 URL 格式不正确")
            private String url;
            @Schema(description = "请求头参数设置", example = "[]")
            @Valid
            private List<BpmSimpleModelNodeVO.HttpRequestParam> header;
            @Schema(description = "请求头参数设置", example = "[]")
            @Valid
            private List<BpmSimpleModelNodeVO.HttpRequestParam> body;
            /**
             * 请求返回处理设置,用于修改流程表单值
             * <p>
             * key:表示要修改的流程表单字段名 (name)
             * value:接口返回的字段名
             */
            @Schema(description = "请求返回处理设置", example = "[]")
            private List<KeyValue<String, String>> response;
        }
        @Schema(description = "自定义打印模板设置")
        @Data
        public static class PrintTemplateSetting {
            @Schema(description = "是否自定义打印模板", example = "false")
            @NotNull(message = "是否自定义打印模板不能为空")
            private Boolean enable;
            @Schema(description = "打印模板", example = "<p></p>")
            private String template;
        }
    }

# 部署流程(流程定义)

# controller
/**
 * <p> Project: scaffold - BpmProcessDefinitionController  </p>
 *
 * 管理后台 - 流程定义
 * 
 * @author Tz
 * @date 2025/10/25 15:26
 * @version 1.0.0
 * @since 1.0.0
 */
@Tag(name = "管理后台 - 流程定义")
@RestController
@RequestMapping("/bpm/process-definition")
@Validated
public class BpmProcessDefinitionController {
    @Resource
    private BpmProcessDefinitionService processDefinitionService;
    @Resource
    private BpmFormService formService;
    @Resource
    private BpmCategoryService categoryService;
    @GetMapping("/page")
    @Operation(summary = "获得流程定义分页")
    @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
    public CommonResult<PageResult<BpmProcessDefinitionRespVO>> getProcessDefinitionPage(
            BpmProcessDefinitionPageReqVO pageReqVO) {
        PageResult<ProcessDefinition> pageResult = processDefinitionService.getProcessDefinitionPage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(PageResult.empty(pageResult.getTotal()));
        }
        // 获得 Category Map
        Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(
                convertSet(pageResult.getList(), ProcessDefinition::getCategory));
        // 获得 Deployment Map
        Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(
                convertSet(pageResult.getList(), ProcessDefinition::getDeploymentId));
        // 获得 BpmProcessDefinitionInfoDO Map
        Map<String, BpmProcessDefinitionInfoDO> processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap(
                convertSet(pageResult.getList(), ProcessDefinition::getId));
        // 获得 Form Map
        Map<Long, BpmFormDO> formMap = formService.getFormMap(
               convertSet(processDefinitionMap.values(), BpmProcessDefinitionInfoDO::getFormId));
        return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionPage(
                pageResult, deploymentMap, processDefinitionMap, formMap, categoryMap));
    }
    @GetMapping ("/list")
    @Operation(summary = "获得流程定义列表")
    @Parameter(name = "suspensionState", description = "挂起状态", required = true, example = "1") // 参见 Flowable SuspensionState 枚举
    public CommonResult<List<BpmProcessDefinitionRespVO>> getProcessDefinitionList(
            @RequestParam("suspensionState") Integer suspensionState) {
        // 1.1 获得开启的流程定义
        List<ProcessDefinition> list = processDefinitionService.getProcessDefinitionListBySuspensionState(suspensionState);
        if (CollUtil.isEmpty(list)) {
            return success(Collections.emptyList());
        }
        // 1.2 移除不可见的流程定义
        Map<String, BpmProcessDefinitionInfoDO> processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap(
                convertSet(list, ProcessDefinition::getId));
        Long userId = getLoginUserId();
        list.removeIf(processDefinition -> {
            BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionMap.get(processDefinition.getId());
            return processDefinitionInfo == null // 不存在
                    || Boolean.FALSE.equals(processDefinitionInfo.getVisible()) //visible 不可见
                    || !processDefinitionService.canUserStartProcessDefinition(processDefinitionInfo, userId); // 无权限发起
        });
        // 2. 拼接 VO 返回
        return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionList(
                list, null, processDefinitionMap, null, null));
    }
    @GetMapping("/simple-list")
    @Operation(summary = "获得流程定义精简列表", description = "只包含未挂起的流程,主要用于前端的下拉选项")
    public CommonResult<List<BpmProcessDefinitionRespVO>> getSimpleProcessDefinitionList() {
        // 只查询未挂起的流程
        List<ProcessDefinition> list = processDefinitionService.getProcessDefinitionListBySuspensionState(
                SuspensionState.ACTIVE.getStateCode());
        // 拼接 VO 返回,只返回 id、name、key
        return success(convertList(list, definition -> new BpmProcessDefinitionRespVO()
                .setId(definition.getId()).setName(definition.getName()).setKey(definition.getKey())));
    }
    @GetMapping ("/get")
    @Operation(summary = "获得流程定义")
    @Parameter(name = "id", description = "流程编号", required = true, example = "1024")
    @Parameter(name = "key", description = "流程定义标识", required = true, example = "1024")
    public CommonResult<BpmProcessDefinitionRespVO> getProcessDefinition(
            @RequestParam(value = "id", required = false) String id,
            @RequestParam(value = "key", required = false) String key) {
        ProcessDefinition processDefinition = id != null ? processDefinitionService.getProcessDefinition(id)
                : processDefinitionService.getActiveProcessDefinition(key);
        if (processDefinition == null) {
            return success(null);
        }
        BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinition.getId());
        BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId());
        return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(
                processDefinition, null, processDefinitionInfo, null, null, bpmnModel));
    }
}
# service:
package cn.tzzfj.scaffold.module.bpm.service.definition;
/**
 * <p> Project: scaffold - BpmProcessDefinitionServiceImpl  </p>
 *
 * 流程定义实现
 *
 * 主要进行 Flowable {@link ProcessDefinition} 和 {@link Deployment} 的维护
 * 
 * @author Tz
 * @date 2025/10/25 15:26
 * @version 1.0.0
 * @since 1.0.0
 */
@Service
@Validated
@Slf4j
public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionService {
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private BpmProcessDefinitionInfoMapper processDefinitionMapper;
    @Resource
    private AdminUserApi adminUserApi;
    @Override
    public ProcessDefinition getProcessDefinition(String id) {
        return repositoryService.getProcessDefinition(id);
    }
    @Override
    public List<ProcessDefinition> getProcessDefinitionList(Set<String> ids) {
        return repositoryService.createProcessDefinitionQuery().processDefinitionIds(ids).list();
    }
    @Override
    public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId) {
        if (StrUtil.isEmpty(deploymentId)) {
            return null;
        }
        return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
    }
    @Override
    public List<ProcessDefinition> getProcessDefinitionListByDeploymentIds(Set<String> deploymentIds) {
        if (CollUtil.isEmpty(deploymentIds)) {
            return emptyList();
        }
        return repositoryService.createProcessDefinitionQuery().deploymentIds(deploymentIds).list();
    }
    @Override
    public ProcessDefinition getActiveProcessDefinition(String key) {
        return repositoryService.createProcessDefinitionQuery()
                .processDefinitionTenantId(FlowableUtils.getTenantId())
                .processDefinitionKey(key).active().singleResult();
    }
    @Override
    public boolean canUserStartProcessDefinition(BpmProcessDefinitionInfoDO processDefinition, Long userId) {
        if (processDefinition == null) {
            return false;
        }
        // 校验用户是否在允许发起的用户列表中
        if (CollUtil.isNotEmpty(processDefinition.getStartUserIds())) {
            return processDefinition.getStartUserIds().contains(userId);
        }
        // 校验用户是否在允许发起的部门列表中
        if (CollUtil.isNotEmpty(processDefinition.getStartDeptIds())) {
            AdminUserRespDTO user = adminUserApi.getUser(userId);
            return user != null
                    && user.getDeptId() != null
                    && processDefinition.getStartDeptIds().contains(user.getDeptId());
        }
        // 都为空,则所有人都可以发起
        return true;
    }
    @Override
    public List<Deployment> getDeploymentList(Set<String> ids) {
        if (CollUtil.isEmpty(ids)) {
            return emptyList();
        }
        List<Deployment> list = new ArrayList<>(ids.size());
        for (String id : ids) {
            addIfNotNull(list, getDeployment(id));
        }
        return list;
    }
    @Override
    public Deployment getDeployment(String id) {
        if (StrUtil.isEmpty(id)) {
            return null;
        }
        return repositoryService.createDeploymentQuery().deploymentId(id).singleResult();
    }
    @Override
    public String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo,
                                          byte[] bpmnBytes, String simpleJson, BpmFormDO form) {
        // 创建 Deployment 部署
        Deployment deploy = repositoryService.createDeployment()
                .key(model.getKey()).name(model.getName()).category(model.getCategory())
                .addBytes(model.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, bpmnBytes)
                .tenantId(FlowableUtils.getTenantId())
                .disableSchemaValidation() // 禁用 XML Schema 验证,因为有自定义的属性
                .deploy();
        // 设置 ProcessDefinition 的 category 分类
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId()).singleResult();
        repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory());
        // 注意 1,ProcessDefinition 的 key 和 name 是通过 BPMN 中的 <bpmn2:process /> 的 id 和 name 决定
        // 注意 2,目前该项目的设计上,需要保证 Model、Deployment、ProcessDefinition 使用相同的 key,保证关联性。
        //          否则,会导致 ProcessDefinition 的分页无法查询到。
        if (!Objects.equals(definition.getKey(), model.getKey())) {
            throw exception(PROCESS_DEFINITION_KEY_NOT_MATCH, model.getKey(), definition.getKey());
        }
        if (!Objects.equals(definition.getName(), model.getName())) {
            throw exception(PROCESS_DEFINITION_NAME_NOT_MATCH, model.getName(), definition.getName());
        }
        // 插入拓展表
        BpmProcessDefinitionInfoDO definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfoDO.class)
                .setModelId(model.getId()).setCategory(model.getCategory()).setProcessDefinitionId(definition.getId())
                .setModelType(modelMetaInfo.getType()).setSimpleModel(simpleJson);
        if (form != null) {
            definitionDO.setFormFields(form.getFields()).setFormConf(form.getConf());
        }
        processDefinitionMapper.insert(definitionDO);
        return definition.getId();
    }
    @Override
    public void updateProcessDefinitionState(String id, Integer state) {
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(id);
        if (processDefinition == null) {
            throw exception(PROCESS_DEFINITION_NOT_EXISTS);
        }
        // 激活
        if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) {
            if (processDefinition.isSuspended()) {
                repositoryService.activateProcessDefinitionById(id, false, null);
            }
            return;
        }
        // 挂起
        if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) {
            //suspendProcessInstances = false,进行中的任务,不进行挂起。
            // 原因:只要新的流程不允许发起即可,老流程继续可以执行。
            if (!processDefinition.isSuspended()) {
                repositoryService.suspendProcessDefinitionById(id, false, null);
            }
            return;
        }
        log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state);
    }
    @Override
    public void updateProcessDefinitionSortByModelId(String modelId, Long sort) {
        processDefinitionMapper.updateByModelId(modelId, new BpmProcessDefinitionInfoDO().setSort(sort));
    }
    @Override
    public BpmnModel getProcessDefinitionBpmnModel(String id) {
        return repositoryService.getBpmnModel(id);
    }
    @Override
    public BpmProcessDefinitionInfoDO getProcessDefinitionInfo(String id) {
        return processDefinitionMapper.selectByProcessDefinitionId(id);
    }
    @Override
    public List<BpmProcessDefinitionInfoDO> getProcessDefinitionInfoList(Collection<String> ids) {
        return processDefinitionMapper.selectListByProcessDefinitionIds(ids);
    }
    @Override
    public PageResult<ProcessDefinition> getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageVO) {
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        query.processDefinitionTenantId(FlowableUtils.getTenantId());
        if (StrUtil.isNotBlank(pageVO.getKey())) {
            query.processDefinitionKey(pageVO.getKey());
        }
        // 执行查询
        long count = query.count();
        if (count == 0) {
            return PageResult.empty(count);
        }
        List<ProcessDefinition> list = query.orderByProcessDefinitionVersion().desc()
                .listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
        return new PageResult<>(list, count);
    }
    @Override
    public List<ProcessDefinition> getProcessDefinitionListBySuspensionState(Integer suspensionState) {
        // 拼接查询条件
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), suspensionState)) {
            query.suspended();
        } else if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), suspensionState)) {
            query.active();
        }
        // 执行查询
        query.processDefinitionTenantId(FlowableUtils.getTenantId());
        return query.list();
    }
}
# 解释:
  • 创建流程模型, repositoryService.saveModel(model)

  • 添加 表单BPM XML 流程图其他相关配置

    对应的表 act_re_model 并且在字段 meteinfo 保存了额为的扩展数据