Camunda(二):springboot 整合Camunda 使用工作流程(含完整代码)

前言:因为公司有个项目需求要使用到工作流引擎,考查了市面各种的工作流引擎,对比它们之间的优劣势,最后选择Camunda工作流引擎。此前自己对Camunda工作流引擎了解的并不多,所以就记录下自己学习Camunda工作流引擎到springboot项目中整合Camunda工作流引擎使用的过程。

在上一篇文章中已经介绍了Camunda Platform和Modeler创建工作流的玩法了。在本文就将Camunda 应用在生产项目的各种审核流程中,重点是springboot整合Camunda ,并使用Camunda 的各种API。因为camunda本来就是一个轻量级的框架,项目中就主要使用Camunda 的工作流的流程流转和审核,流程的创建和前端bpmn页面流程编辑器部分这里就不介绍使用了。因为在经历几个生产项目,无论是使用flowable或者Camunda 工作流框架,流程bpmn文件都是提前通过其他工具创建好,然后放在项目中加载部署的,项目主要是对应的流程的流转和审核。不多废话,下面就直接进入正题。

一、引入Camunda依赖

springboot引入Camunda要考虑版本兼容性的问题,我的springboot版本2.5.x,所以我引入Camunda的版本是7.16.x,

要看springboot对应Camunda的具体的版本:

https://docs.camunda.org/manual/7.19/user-guide/spring-boot-integration/version-***patibility/

<dependency>
    <groupId>org.camunda.bpm.springboot</groupId>
    <artifactId>camunda-bpm-spring-boot-starter</artifactId>
    <version>7.16.0</version>
</dependency>
二、在yml配置camunda

camunda的详细配置说明:

https://docs.camunda.org/manual/latest/user-guide/spring-boot-integration/configuration/#camunda-engine-properties

这里就说几个比较重要的配置

camunda.bpm.auto-deployment-enabled :流程是否应该自动部署,默认为true。

很重要camunda.bpm.deployment-resource-pattern:自动部署位置。一开始我学习我不理解为什么bpmn文件无论放在那里都能自动部署。

默认位置:

classpath*😗*/*.bpmn,

classpath*😗*/*.bpmn20.xml,

classpath*😗*/*.dmn,

classpath*😗*/*.dmn11.xml,

classpath*😗*/*.cmmn,

classpath*😗*/*.cmmn10.xml,

classpath*😗*/*.cmmn11.xml

camunda.bpm.database.schema-update: 如果应应用自动架构更新,请使用 [true、false、create、create-drop、drop-create] 之一,默认为true。

camunda.bpm.database.type: 底层数据库的类型。可能的值:h2、mysql、mariadb、oracle、postgres、mssql、db2。

camunda.bpm.admin-user.id: 用户名

camunda.bpm.admin-user.password: 初始化密码

camunda.bpm.admin-user.firstName: 附加(可选)用户属性 ,默认为“id”的值

camunda.bpm.admin-user.lastName: 附加(可选)用户属性 ,默认为“id”的值

camunda.bpm.filter.create:“显示全部”过滤器的名称。如果设置,则会在启动时创建一个显示所有任务的新过滤器。

下面是在实际项目中的配置

# camunda配置
camunda:
  bpm:
    admin-user:
      id: camunda
      password: camunda
      first-name: admin
    filter:
      create: All tasks
    database:
      type: mysql
      schema-update: true
    auto-deployment-enabled: true
    deployment-resource-pattern: classpath:/processes/*.bpmn
三、初始化camunda数据库,执行建表脚本(很重要

这里需要手动建好camunda相关的表,不提前建好的话,到导致项目中的camunda的相关的bean没办法自动注入到spring容器中,会无法正常启动项目。camunda的相关脚本语句,去官网找到对应版本(此例中Camunda版本为7.16.0)的sql脚本文件:

https://camunda.***/download/

解压后,找到./configuration/sql目录下即可找到sql文件,engine和identity都执行。项目中使用的是mysql数据,所以执行mysql的脚本。

四、camunda数据库表的说明(扩展)

PS: 这一步跟项目无关,但是能加深对camunda的理解,而且对项目中出现问题的排查能提供很好的帮助。

Camunda bpm流程引擎的数据库由多个表组成,表名都以ACT开头,第二部分是说明表用途的两字符标识。而Camunda7.16版本共49张表。

ACT_RE_* : 'RE’表示流程资源存储,这个前缀的表包含了流程定义和流程静态资源(图片,规则等)
(最重要)ACT_RU_* : 'RU’表示流程运行时。 这些运行时的表,包含流程实例,任务,变量,Job等运行中的数据。 Camunda只在流程实例执行过程中保存这些数据,在流程结束时就会删除这些记录, 这样运行时表的数据量最小,可以最快运行
ACT_ID_* : 'ID’表示组织用户信息,比如用户,组等,
ACT_HI_* : 'HI’表示流程历史记录。 这些表包含历史数据,比如历史流程实例,变量,任务等
ACT_GE_* : ‘GE’表示流程通用数据

要记住一个很重要的一个点:流程运行时的数据是存在ACT_RU相关的表上,当流程结束后,该流程相关的数据就会被物理删除掉,能减少很多数据量,提高流程的性能。而想要看相关流程的过程记录,则需要到ACT_HI相关的表上去查找

流程引擎的最核心表是流程定义、流程执行、流程任务、流程变量和事件订阅表。它们之间的关系见下面的UML模型。

下面是项目中排查问题用的比较多,比较重要的几张表的说明

act_id_user(用户表)
字段名称 字段类型 可否为空 描述
ID_ varchar(64) 主键
REV_ int(11) NULL 版本
FIRST_ varchar(255) NULL
LAST_ varchar(255) NULL
EMAIL_ varchar(255) NULL 邮件
PWD_ varchar(255) NULL 密码
SALT_ varchar(255) NULL 盐值
LOCK_EXP_TIME_ datetime NULL 锁定过期时间
ATTEMPTS_ int(11) NULL 尝试次数
PICTURE_ID_ varchar(64) NULL 图片ID
act_ge_bytearray(二进制数据表)
字段名称 字段类型 可否为空 描述
ID_ varchar(64) 主键
REV_ int(11) NULL 版本
NAME_ varchar(255) NULL 名称
DEPLOYMENT_ID_ varchar(64) NULL 部署ID
BYTES_ longblob NULL 字节内容
GENERATED_ tinyint(4) NULL 是否系统生成(0用户创建,null系统生成)
TENANT_ID_ varchar(64) NULL 租户ID
TYPE_ int(11) NULL 类型
CREATE_TIME_ datetime NULL 创建时间
ROOT_PROC_INST_ID_ varchar(64) NULL 流程实例根ID
REMOVAL_TIME_ datetime NULL 删除时间
act_re_procdef(流程定义表)

流程定义表,包含所有已部署的流程定义,诸如版本详细信息、资源名称或挂起状态等信息。

字段名称 字段类型 可否为空 描述
ID_ varchar(64) 主键
REV_ int(11) NULL 版本
CATEGORY_ varchar(255) NULL 流程定义的Namespace分类
NAME_ varchar(255) NULL 流程定义名称
KEY_ varchar(255) 流程定义KEY
VERSION_ int(11) 流程定义版本号
DEPLOYMENT_ID_ varchar(64) NULL 部署ID
RESOURCE_NAME_ varchar(4000) NULL 资源名称
DGRM_RESOURCE_NAME_ varchar(4000) NULL DGRM资源名称
HAS_START_FORM_KEY_ tinyint(4) NULL 是否有启动表单
SUSPENSION_STATE_ int(11) NULL 流程挂起
TENANT_ID_ varchar(64) NULL 租户ID
VERSION_TAG_ varchar(64) NULL 版本标签
HISTORY_TTL_ int(11) NULL
STARTABLE_ tinyint(1) 是否是可启动流程
act_ru_execution(流程运行时表)—最重要

BPMN流程运行时记录表。该表时整个流程引擎的核心表,它包括流程定义、父级执行、当前活动和有关执行状态的不同元数据等信息。

字段名称 字段类型 可否为空 描述
ID_ varchar(64) 主键
REV_ int(11) NULL 版本
ROOT_PROC_INST_ID_ varchar(64) NULL 流程实例根ID
PROC_INST_ID_ varchar(64) NULL 流程实例ID
BUSINESS_KEY_ varchar(255) NULL 业务KEY
PARENT_ID_ varchar(64) NULL 流程父实例ID
PROC_DEF_ID_ varchar(64) NULL 流程定义ID
SUPER_EXEC_ varchar(64) NULL 父流程实例对应的执行
SUPER_CASE_EXEC_ varchar(64) NULL 父案例实例对应的执行
CASE_INST_ID_ varchar(64) NULL 案例实例ID
ACT_ID_ varchar(255) NULL 节点ID
ACT_INST_ID_ varchar(64) NULL 节点实例ID
IS_ACTIVE_ tinyint(4) NULL 是否激活
IS_CONCURRENT_ tinyint(4) NULL 是否并行
IS_SCOPE_ tinyint(4) NULL 是否多实例范围
IS_EVENT_SCOPE_ tinyint(4) NULL 是否事件多实例范围
SUSPENSION_STATE_ int(11) NULL 挂起状态
CACHED_ENT_STATE_ int(11) NULL 缓存状态
SEQUENCE_COUNTER_ bigint(20) NULL 序列计数器
TENANT_ID_ varchar(64) NULL 租户ID
act_ru_identitylink(流程运行时表)

运行时流程人员表,主要存储当前节点参与者的信息

字段名称 字段类型 可否为空 描述
ID_ varchar(64) 主键
REV_ int(11) NULL 版本
GROUP_ID_ varchar(255) NULL 用户组ID
TYPE_ varchar(255) NULL 类型
USER_ID_ varchar(255) NULL 用户ID
TASK_ID_ varchar(64) NULL 任务ID
PROC_DEF_ID_ varchar(64) NULL 流程定义ID
TENANT_ID_ varchar(64) NULL 租户ID
act_ru_task( 流程运行时任务表)

流程运行时任务表,包含所有正在运行的流程实例的所有打开的任务,包括诸如相应的流程实例、执行以及元数据(如创建时间、办理人或到期时间)等信息。

字段名称 字段类型 可否为空 描述
ID_ varchar(64) 主键
REV_ int(11) NULL 版本
EXECUTION_ID_ varchar(64) NULL 流程执行ID
PROC_INST_ID_ varchar(64) NULL 流程实例ID
PROC_DEF_ID_ varchar(64) NULL 流程定义ID
CASE_EXECUTION_ID_ varchar(64) NULL 案例执行ID
CASE_INST_ID_ varchar(64) NULL 案例实例ID
CASE_DEF_ID_ varchar(64) NULL 案例定义ID
NAME_ varchar(255) NULL 名称
PARENT_TASK_ID_ varchar(64) NULL 父任务ID
DESCRIPTION_ varchar(4000) NULL 描述
TASK_DEF_KEY_ varchar(255) NULL 任务定义KEY
OWNER_ varchar(255) NULL 委托人
ASSIGNEE_ varchar(255) NULL 办理人
DELEGATION_ varchar(64) NULL 委托状态
PRIORITY_ int(11) NULL 优先级
CREATE_TIME_ datetime NULL 创建时间
DUE_DATE_ datetime NULL 截止时间
FOLLOW_UP_DATE_ datetime NULL 跟踪时间
SUSPENSION_STATE_ int(11) NULL 挂起状态
TENANT_ID_ varchar(64) NULL 租户ID
act_hi_***ment(历史流程审批意见表)

历史流程审批意见表,存放历史流程的审批意见。

字段名称 字段类型 可否为空 描述
ID_ varchar(64) 主键
TYPE_ varchar(255) NULL 类型(event事件、***ment意见)
TIME_ datetime 时间
USER_ID_ varchar(255) NULL 处理人
TASK_ID_ varchar(64) NULL 任务ID
ROOT_PROC_INST_ID_ varchar(64) NULL 流程实例跟ID
PROC_INST_ID_ varchar(64) NULL 流程实例ID
ACTION_ varchar(255) NULL 行为类型
MESSAGE_ varchar(4000) NULL 基本内容
FULL_MSG_ longblob NULL 全部内容
TENANT_ID_ varchar(64) NULL 租户ID
REMOVAL_TIME_ datetime NULL 移除时间
act_hi_detail(历史的流程运行详情表)

历史的流程运行变量详情记录表。流程中产生的变量详细,包括控制流程流转的变量,业务表单中填写的流程需要用到的变量等。

字段名称 字段类型 可否为空 描述
ID_ varchar(64) 主键
TYPE_ varchar(255) 类型
PROC_DEF_KEY_ varchar(255) NULL 流程定义KEY
PROC_DEF_ID_ varchar(64) NULL 流程定义ID
ROOT_PROC_INST_ID_ varchar(64) NULL 流程实例根ID
PROC_INST_ID_ varchar(64) NULL 流程实例ID
EXECUTION_ID_ varchar(64) NULL 流程执行ID
CASE_DEF_KEY_ varchar(255) NULL 案例定义KEY
CASE_DEF_ID_ varchar(64) NULL 案例定义ID
CASE_INST_ID_ varchar(64) NULL 案例实例ID
CASE_EXECUTION_ID_ varchar(64) NULL 案例执行ID
TASK_ID_ varchar(64) NULL 任务ID
ACT_INST_ID_ varchar(64) NULL 节点实例ID
VAR_INST_ID_ varchar(64) NULL 流程变量记录ID
NAME_ varchar(255) 名称
VAR_TYPE_ varchar(255) NULL 变量类型
REV_ int(11) NULL 版本
TIME_ datetime 时间戳
BYTEARRAY_ID_ varchar(64) NULL 二进制数据对应ID
DOUBLE_ double NULL double类型值
LONG_ bigint(20) NULL long类型值
TEXT_ varchar(4000) NULL 文本类型值
TEXT2_ varchar(4000) NULL 文本类型值2
SEQUENCE_COUNTER_ bigint(20) NULL 序列计数器
TENANT_ID_ varchar(64) NULL 租户ID
OPERATION_ID_ varchar(64) NULL
REMOVAL_TIME_ datetime NULL 移除时间

五、编写camunda的工具类。

其实完成前面3步,可以说springboot已经整合camunda成功了,但是一个项目中引入一个框架主要是为了使用,camunda官方提供了很多API,参考官方文档:https://docs.camunda.org/manual/7.18/reference/rest/

但是在camunda不同的流程的审核所 使用的API是大多数是相同的,所以在项目中封装一个工具类提供使用。

java">/**
 * camunda 流程工具类
 */
@***ponent
@Slf4j
public class FlowUtil {
    private static final FlowUtil util = new FlowUtil();

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @PostConstruct
    public void initialize() {
        util.repositoryService = repositoryService;
        util.runtimeService = runtimeService;
        util.taskService = taskService;
        util.historyService = historyService;
    }
    /**
     * 部署流程,参数本地bpmn的路径
     * @param filePath  bpmn路径
     * @param name  流程名称
     */
    public static void deployment(String filePath,String name){
        log.info("开始部署本地审批流程:{},文件地址:{}",name,filePath);
        util.repositoryService.createDeployment()
                .name(name)
                .addClasspathResource(filePath)
                .deploy();

        log.info("完成部署本地审批流程:{},文件地址:{}",name,filePath);
    }

    /**
     * 启动流程
     * @param key 流程标识,需唯一,如果存在相同key则会启动版本号最新的流程
     * @param businessKey  业务id
     * @param map 流程变量(包含节点审批人及业务判断变量等)
     */
    public static void startProcess(String key, Object businessKey, Map<String,Object> map){
        log.info("开始启动审批流程:{},业务源:{},流程变量: {}",key,businessKey.toString(), JSONUtil.toJsonStr(JSONUtil.parse(map)));
        ProcessInstance processInstance = util.runtimeService.startProcessInstanceByKey(key, businessKey.toString(), map);
        log.info("完成启动审批流程:{},业务源:{},流程实例ID:{}",key,businessKey.toString(), processInstance.getId());
    }

    /**
     * 销毁流程
     * @param key 流程标识,需唯一,如果存在相同key则会启动版本号最新的流程
     * @param businessKey  业务id
     * @param reason 销毁原因
     * @param reason 审批人
     */
    public static void destroyProcess(String key, String businessKey, String reason,String assignee){
        //查询待办任务
        List<Task> tasks = FlowUtil.getTaskByCandidateUserAndBusinessKey(key, businessKey, assignee);
        if(CollUtil.isEmpty(tasks)){
            log.info("{}完成审批任务失败:{}--{}",assignee,key,businessKey);
            throw new ResultException("暂未到您审批");
        }
        FlowUtil.claimTask(tasks.get(0).getId(),assignee);
        List<Task> taskList = FlowUtil.getTasksByBusinessKey(key, businessKey);
        if(CollUtil.isEmpty(taskList)) return;
        log.info("开始销毁审批流程:{},业务源:{},流程实例ID:{}:",key,businessKey, taskList.get(0).getProcessInstanceId());
        util.runtimeService.deleteProcessInstance(taskList.get(0).getProcessInstanceId(), reason);
        log.info("完成销毁审批流程:{},业务源:{},流程实例ID:{}",key,businessKey, taskList.get(0).getProcessInstanceId());
    }

    /**
     * 领取任务
     * @param taskId  任务id
     * @param candidateUser  候选用户名
     */
    public static void claimTask(String taskId,String candidateUser){
        Task task = util.taskService.createTaskQuery()
                .taskId(taskId)
                .taskCandidateUser(candidateUser)
                .singleResult();
        if(Objects.isNull(task)){
            log.info("{}领取审批任务失败:{}",candidateUser,taskId);
            throw new ResultException("任务领取失败");
        }
        util.taskService.claim(taskId,candidateUser);
    }

    /**
     * 完成任务
     * @param taskId  任务id
     * @param assignee  用户名
     */
    public static void ***pleteTask(String taskId,String assignee){
        Task task = util.taskService.createTaskQuery()
                .taskId(taskId)
                .taskAssignee(assignee)
                .singleResult();
        if(Objects.isNull(task)){
            log.info("{}完成审批任务失败:{}",assignee,taskId);
            throw new ResultException("任务完成失败");
        }
        util.taskService.***plete(taskId);
    }

    /**
     * 领取并完成任务
     * @param taskId  任务id
     * @param assignee  用户名
     */
    public static void claimAnd***pleteTask(String taskId,String assignee){
        Task task = util.taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();
        if(Objects.isNull(task)){
            log.info("{}审批任务不存在:{}",assignee,taskId);
            throw new ResultException("审批任务不存在");
        }
        if(StrUtil.isNotBlank(task.getAssignee())){
            if(!task.getAssignee().equalsIgnoreCase(assignee)){
                log.info("{}完成审批任务失败:{}",assignee,taskId);
                throw new ResultException("暂未到您审批");
            }
            util.taskService.***plete(taskId);
            return;
        }
        Task unClaimTask = util.taskService.createTaskQuery()
                .taskId(taskId)
                .taskCandidateUser(assignee)
                .singleResult();
        if(Objects.isNull(unClaimTask)){
            log.info("{}完成审批任务失败:{}",assignee,taskId);
            throw new ResultException("暂未到您审批");
        }
        util.taskService.claim(taskId,assignee);
        util.taskService.***plete(taskId);
    }

    /**
     * 查询待办任务,参数:候选用户、业务id
     * @param candidateUser 候选用户
     * @param key 流程标识,需唯一,如果存在相同key则会启动版本号最新的流程
     * @param businessKey  业务id
     * @return
     */
    public static List<Task> getTaskByCandidateUserAndBusinessKey(String key,String businessKey,String candidateUser){
        return util.taskService.createTaskQuery()
                .processInstanceBusinessKey(businessKey)
                .processDefinitionKey(key)
                .taskCandidateUser(candidateUser)
                .list();

    }

    /**
     * 查询待办任务,参数: 业务id
     * @param key 流程标识,需唯一,如果存在相同key则会启动版本号最新的流程
     * @param businessKey  业务id
     * @return
     */
    public static List<Task> getTasksByBusinessKey(String key, String businessKey){
        return util.taskService.createTaskQuery()
                .processDefinitionKey(key)
                .processInstanceBusinessKey(businessKey)
                .list();
    }

    /**
     * 查询待办任务,参数: 业务id
     * @param key 流程标识,需唯一,如果存在相同key则会启动版本号最新的流程
     * @param businessKey  业务id
     * @param assignee  用户名
     * @return
     */
    public static boolean claimAnd***pleteTask(String key, String businessKey,String assignee){
        //查询待办任务
        List<Task> tasks = FlowUtil.getTaskByCandidateUserAndBusinessKey(key, businessKey, assignee);
        if(CollUtil.isEmpty(tasks)){
            log.info("{}完成审批任务失败:{}--{}",assignee,key,businessKey);
            throw new ResultException("暂未到您审批");
        }
        for (Task task : tasks) {
            FlowUtil.claimAnd***pleteTask(task.getId(),assignee);
        }
        if(CollUtil.isEmpty(FlowUtil.getTasksByBusinessKey(key,businessKey))){
            return true;
        }else{
            return false;
        }
    }

    /**
     * 查询历史任务
     * @param key
     * @param businessKey
     * @return
     */
    public static List<HistoricTaskInstance> getHistoricTask(String processInstanceId,String key, String businessKey){
        return util.historyService.createHistoricTaskInstanceQuery()
                .processInstanceBusinessKey(businessKey)
                .processInstanceId(processInstanceId)
                .processDefinitionKey(key)
                .list();
    }

    /**
     * 查询历史实例
     * @param key
     * @param businessKey
     * @return
     */
    public static HistoricProcessInstance getLastProcessInstance(String key, String businessKey){
        return util.historyService.createHistoricProcessInstanceQuery()
                .processDefinitionKey(key)
                .processInstanceBusinessKey(businessKey)
                .orderByProcessInstanceStartTime()
                .desc().list().get(0);
    }

    /**
     * 查询任务
     * @param key
     * @param taskId
     * @return
     */
    public static List<HistoricIdentityLinkLog> getHistoricIdentityLinkLog(String key, String taskId){
        return util.historyService.createHistoricIdentityLinkLogQuery()
                .processDefinitionKey(key)
                .taskId(taskId)
                .list();
    }


    /**
     * 查询任务
     * @param taskId
     * @return
     */
    public static List<IdentityLink> getTaskIdentityLink(String taskId){
        return util.taskService.getIdentityLinksForTask(taskId);
    }
}
五、模拟生产项目中的功能-费用审核流程

下面是费用审核的流程图

5.1 创建费用审核流程(即创建bpmn文件)

创建流程可以使用Camunda Modeler,具体的使用方法在我上一篇文章中有写到,又不了解的可以移步到:

https://blog.csdn.***/qq798867485/article/details/131439688

下面是流程图的创建和每个节点的设置情况



5.2 把bpmn文件放到项目的processes目录

一定要放在这个目录下,因为之前的yml的配置指定了自动部署的目录,不然无法自动部署。除非你在yml配置中不制动自动部署的目录。

5.3 创建费用审核流程

FlowUtil.startProcess(BizFlowTypeEnum.EXPENSE_APPROVE.getKey(),expense.getId(),params);

这个方法就是 创建费用审核流程

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submit(Expense expense) {
        dao.insert(expense);
        List<User> userList = userService.listAll(new UserQueryBo());
        Set<String> ***panyUsers = userList.stream().filter(x -> x.getUserRole() == 1)
                .map(item -> item.getId().toString()).collect(Collectors.toSet());
        Set<String> groupUsers = userList.stream().filter(x -> x.getUserRole() == 2)
                .map(item -> item.getId().toString()).collect(Collectors.toSet());
        Set<String> headUsers = userList.stream().filter(x -> x.getUserRole() == 3)
                .map(item -> item.getId().toString()).collect(Collectors.toSet());

        Map<String,Object> params = new HashMap<>();
        params.put("***panyUsers", ***panyUsers);
        params.put("groupUsers",groupUsers);
        params.put("headUsers",headUsers);
        FlowUtil.startProcess(BizFlowTypeEnum.EXPENSE_APPROVE.getKey(),expense.getId(),params);

    }
5.4 审核费用审核流程

##查看这个审核节点是否为审核流程最后的一个节点

FlowUtil.claimAnd***pleteTask(BizFlowTypeEnum.EXPENSE_APPROVE.getKey(),expense.getId().toString(),expenseQueryBo.getApprovalId().toString())

##驳回

FlowUtil.destroyProcess(BizFlowTypeEnum.EXPENSE_APPROVE.getKey(),expense.getId().toString(),“不想通过”,expenseQueryBo.getApprovalId().toString())

 @Override
    @Transactional(rollbackFor = Exception.class)
    public void approval(Expense expense, ExpenseQueryBo expenseQueryBo) {
        if(expenseQueryBo.getApprovalStatus() == 2){
            //通过
            if(FlowUtil.claimAnd***pleteTask(BizFlowTypeEnum.EXPENSE_APPROVE.getKey(),expense.getId().toString(),expenseQueryBo.getApprovalId().toString())){
                expense.setApprovalStatus(2);
            }else {
                expense.setApprovalStatus(1);
            }
        }else {
            //驳回
            FlowUtil.destroyProcess(BizFlowTypeEnum.EXPENSE_APPROVE.getKey(),expense.getId().toString(),"不想通过",expenseQueryBo.getApprovalId().toString());
            expense.setApprovalStatus(3);
        }

        this.updateById(expense);
    }
六、总结(代码)

学习camunda最主要是理解流程的流转和相关表的结构。上面只是我简单模拟一个费用审核流程的样例,实际生产中的业务代码比这个要复杂,但是核心的camunda的工具类的使用是不变的,变的只是业务流程。

例子完整的代码:https://github.***/gorylee/learnDemo/tree/master/camundaDemo

转载请说明出处内容投诉
CSS教程_站长资源网 » Camunda(二):springboot 整合Camunda 使用工作流程(含完整代码)

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买