Camunda入门(四) - 流程应用(支付流程)入门示例

1. 支付流程 - 场景设定

为了快速上手Camunda,结合Camunda官方QuickStart示例
本文设计了如下支付请求实例

  • 由用户发起支付请求(商品名称、金额、支付用户ID)
  • 根据商品判断对应的折扣
  • 折扣后实际支付金额 < 1000则直接 调用支付接口(JavaDelegate)
  • 折扣后实际支付金额 >= 1000则需要征求用户同意
  • 若用户同意后,才可继续调用支付接口,否则支付失败

如上案例需创建以下4个模型:

功能对应文件类型
支付流程模型PaymentProcess.bpmnBPMN
商品折扣决策模型ProductDiscountDecision.dmnDMN
支付请求表单模型PaymentInitForm.formForm
用户确认表单模型PaymentConfirmForm.formForm

2. 业务DB设计

该流程比较简单,且各个活动间数据相差不大,
所以采用整个流程对应一个业务对象的设计。
注: 采用流程实例的BusinessKey来存储对应业务数据的ID

数据库采用Mysql 5.7,
首先新建Camunda流程引擎的对应的数据库Camunda_716(该名称可根据业务需求适当调整),
然后在此库中新建业务数据表biz_payment_process_info,该表具体定义如下:

CREATE TABLE `biz_payment_process_info` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '业务主键',
  `process_instance_id` varchar(36) DEFAULT NULL COMMENT '流程实例ID',
  `product_name` varchar(64) DEFAULT NULL COMMENT '商品名称',
  `product_price` decimal(10,2) DEFAULT NULL COMMENT '商品金额',
  `product_discount` decimal(3,2) DEFAULT NULL COMMENT '商品折扣',
  `product_discount_price` decimal(10,2) DEFAULT NULL COMMENT '商品折扣后金额',
  `payment_assignee` varchar(64) DEFAULT NULL COMMENT '支付用户ID',
  `approval_result` tinyint(3) unsigned DEFAULT '0' COMMENT '用户是否同意付款(0:未确认,1:同意, 2:不同意)',
  `approval_time` datetime DEFAULT NULL COMMENT '用户确认时间',
  `payment_result` tinyint(3) unsigned DEFAULT '0' COMMENT '支付结果(0:未支付,1:成功, 2:失败)',
  `payment_time` datetime DEFAULT NULL COMMENT '支付时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付流程 - 业务数据';

注: Camunda流程引擎相关的其他表可在Camunda流程应用启动时连接同一数据库后自动创建。

3. 流程模型设计

在这里插入图片描述

3.1 新建BPMN - 支付流程定义

首先根据之前流程定义,创建如下PaymentProcess.bpmn,
其中Id=PaymentProcess,此属性比较重要,后续通过流程应用启动流程皆需要通过此Id来标识对应的流程定义,
如下图中processDefinitionKey即对应此Id,businessKey可对应业务数据唯一ID。
在这里插入图片描述

注: 一定记得勾选流程的Excutable属性,否则流程不可执行。

在这里插入图片描述

其中Event事件包括:

  • Start Event - 支付请求
    • Form - 关联支付请求表单
  • End Event - 支付成功
  • End Event - 用户拒绝支付

其中Task任务包括:

  • Business Rule Task - 商品折扣规则
  • User Task - 用户确认付款
    • Form - 关联用户确认表单
  • Service Task - 调用支付服务

其中Gateway网关包括:

  • Exclusive Gateway - 是否需要用户确认?
  • Exclusive Gateway - 用户是否同意支付?

3.1.1 任务Task相关设置

新建Task后,如下图选择扳手图标即可设置不同的Task类型。
在这里插入图片描述

Business Rule Task - 商品折扣规则

Business Rule Task - 商品折扣规则任务,主要需要定义如下内容:

  • Id、Name属性
  • DMN绑定 - 设置任务绑定DMN(引用DMN文件中的具体Decision.id),通过单独DMN文件定义决策规则,即定义商品折扣规则。
  • 设置规则输出结果变量 - 设置对Decision中规则表格的输出结果的引用变量名称及类型

在这里插入图片描述

注: 关于Business Rule Task中的输出变量类型,包括:

  • single Result(Map<String, Object) - 单行规则多个output,且仅命中唯一规则
  • singleEntry(TypedValue) - 单行规则一个output,且仅命中唯一规则
  • collecEntries(List<Object) - 单行规则一个output,且允许同时命中多条规则
  • resultList(List<Map<String, Object>>) - 单行规则多个output,且允许同时命中多条规则
    在这里插入图片描述
User Task - 用户确认付款

User Task - 用户确认付款任务,主要需要定义如下内容:

  • Id、Name属性
  • 设置用户任务的处理人 - 通过流程变量paymentAssignee(可任意调整变量名)设置需要处理该任务的用户ID。
  • 绑定用户确认的form表单 - 即通过form-key的方式来绑定单独的FORM定义,在后续执行到此处用户任务时,即可在Camunda管理平台TaskList中展示对应的表单。

注:
不建议在BPMN中定义内嵌及关联表单,可由业务应用动态设置流程变量,增加灵活性
此示例中的相关FORM定义皆是为了学习、测试使用。
在这里插入图片描述
在这里插入图片描述

Service Task - 调用支付服务

关于Service Task(调用服务任务)调用服务的方式有多种:

本示例使用调用Java代码方式,即自定义JavaDelegate实现。

Service Task - 调用支付服务任务,主要定义内容如下:

  • Id、Name属性
  • 设置对应Java Delegate实现 - 即执行到当前Service Task时会调用指定的JavaDelegate实现。

在这里插入图片描述

3.1.2 网关Gateway相关设置

本示例中使用的皆是排他网关Exclusive Gateway。
在这里插入图片描述

网关后即产生不同的流程分支,关于分支条件的设置如下:
在这里插入图片描述
在这里插入图片描述

3.2 新建DMN - 商品折扣规则定义

在这里插入图片描述

在这里插入图片描述
点击商品折扣规则的左上角蓝色表格图标后,即可进入规则表格编辑界面,

在这里插入图片描述

3.3 新建FORM - 支付请求表单、用户确认表单

在这里插入图片描述

在这里插入图片描述

4. 启动流程应用

SpringBoot集成Camunda流程应用具体代码参见:
https://gitee.com/luoex/camunda-demo
在这里插入图片描述

在这里插入图片描述

4.1 流程定义自动部署

如前文描述的BPMN、DMN、FORM文件皆可以放在应用代码resource目录下,
支持嵌套文件夹,如resources/bpmn、resources/payment,如下图:
在这里插入图片描述
同时在resources/META-INF下添加processes.xml文件,
该文件用于描述流程应用关于流程自动部署的相关设置,

<process-application
        xmlns="http://www.camunda.org/schema/1.0/ProcessApplication"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <process-archive name="PaymentProcess">
        <process-engine>default</process-engine>
        <!-- 默认会扫描resource(及其子目录下)的*.bpmn, *.cmmn, *.dmn文件 -->
        <!-- 可通过<resource/>指定其他的需要一同部署的资源文件-->
        <!-- 注:formKey中路径需和resource路径一致,如:camunda-forms:deployment:bpmn/payment_confirm.form -->
        <!-- 注:通过Modeler作为附件一起部署,formKey格式为:camunda-forms:deployment:payment_confirm.form -->
        <resource>bpmn/PaymentInitForm.form</resource>
        <resource>bpmn/PaymentConfirmForm.form</resource>
        <properties>
            <property name="isDeleteUponUndeploy">false</property>
            <property name="isScanForProcessDefinitions">true</property>
        </properties>
    </process-archive>

</process-application>

添加如上设置后,在流程应用启动后,即可自动将resources下的流程定义部署到Camunda仓库(数据库)中,

4.2 流程场景示例代码

关于流程应用的几个简单编程场景示例代码如下:

//启动流程 - 如开启支付请求
String businessKey = "111";
ProcessInstance processInstance = this.runtimeService.startProcessInstanceByKey("PaymentProcess", businessKey);

Map processVariablesMap = CamundaUtils.convertProcessVariablesFromEntity(processVariables);
ProcessInstance processInstance = this.runtimeService.startProcessInstanceByKey("PaymentProcess", businessKey, processVariablesMap);
        
//查询待处理任务 - 如获取指定用户待确认的支付请求
List<Task> taskList = this.taskService.createTaskQuery()
               .taskAssignee("luo")
               .processDefinitionKey("PaymentProcess")
               .taskDefinitionKey("Task_UserConfirmPayment")
               .orderByTaskCreateTime()
               .listPage(0, 10);
                
//完成任务 - 如确认支付
Map processVariablesMap = CamundaUtils.convertProcessVariablesFromEntity(processVariables);
this.taskService.complete(taskId, processVariablesMap);


//查看历史数据
List<HistoricTaskInstance> historicTaskInstanceList = this.historyService.createHistoricTaskInstanceQuery()
                .taskAssignee("luo")
                .processDefinitionKey("PaymentProcess")
                .taskDefinitionKey("Task_UserConfirmPayment")
                .finished()
                .orderByHistoricActivityInstanceStartTime()
                .listPage(0, 10);

4.3 流程变量与业务数据同步

由于使用了自定义业务数据表biz_payment_process_info记录流程数据,
所以需要在流程执行过程中将流程变量中的相关数据同步到biz_payment_process_info中,

  • 启动流程时 - 初始biz_payment_process_info数据,设置process_instance_id
  • 启动流程时 - 需设置流程实例的businessKey为对应biz_payment_process_info的记录id
  • 用户确认时 - 同步userApproval用户确认结果及折扣金额 到biz_payment_process_info中
  • 调用支付服务时 - 同步paymentResult支付结果及支付时间 到biz_payment_process_info中

关于流程应用的编码及数据同步,可参见:

camunda-process-application/payment-application/…/BizPaymentProcessInfoServiceImpl.java

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.luo.camunda.app.constants.PaymentProcessConstants;
import com.luo.camunda.app.enums.ConfirmResultEnum;
import com.luo.camunda.app.enums.PaymentResultEnum;
import com.luo.camunda.app.mapper.BizPaymentProcessInfoMapper;
import com.luo.camunda.app.model.entity.BizPaymentProcessInfo;
import com.luo.camunda.app.model.param.PaymentConfirmParam;
import com.luo.camunda.app.model.param.PaymentQueryParam;
import com.luo.camunda.app.model.param.PaymentRequestParam;
import com.luo.camunda.app.model.wrapper.PaymentProcessVariablesWrapper;
import com.luo.camunda.app.service.IBizPaymentProcessInfoService;
import com.luo.camunda.app.utils.CommonUtils;
import com.luo.camunda.common.model.param.ProcessVariablesQueryParam;
import com.luo.camunda.common.model.param.TaskQueryParam;
import com.luo.camunda.common.model.vo.TaskVo;
import com.luo.camunda.common.servcie.CamundaCommonService;
import com.luo.camunda.common.utils.CamundaUtils;
import com.luo.demo.sc.base.model.result.RespResult;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 支付流程 - 业务数据 服务实现类
 *
 * @author luohq
 * @since 2022-01-30
 */
@Service
@Slf4j
public class BizPaymentProcessInfoServiceImpl extends ServiceImpl<BizPaymentProcessInfoMapper, BizPaymentProcessInfo> implements IBizPaymentProcessInfoService {

    @Resource
    private CamundaCommonService camundaCommonService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RespResult<String> startPaymentProcess(PaymentRequestParam paymentRequestParam) {
        /** 插入支付流程业务数据 */
        BizPaymentProcessInfo bizPaymentProcessInfo = new BizPaymentProcessInfo();
        //拷贝参数中的属性:productName, productPrice, paymentAssignee
        BeanUtils.copyProperties(paymentRequestParam, bizPaymentProcessInfo);
        //设置业务数据默认状态
        bizPaymentProcessInfo.setPaymentResult(PaymentResultEnum.NOT_PAY.getCode());
        bizPaymentProcessInfo.setApprovalResult(ConfirmResultEnum.NOT_CONFIRM.getCode());
        log.info("保存支付流程业务数据,参数:{}", paymentRequestParam);
        Boolean result = this.save(bizPaymentProcessInfo);
        log.info("保存支付流程业务数据,结果:{}", result);

        /** 启动支付流程 */
        log.info("开启支付流程处理,processKey:{}, businessKey:{}", PaymentProcessConstants.PAYMENT_PROCESS_ID, bizPaymentProcessInfo.getId());
        ProcessInstance processInstance = this.camundaCommonService.startProcessInstance(
                PaymentProcessConstants.PAYMENT_PROCESS_ID,
                String.valueOf(bizPaymentProcessInfo.getId()),
                paymentRequestParam);
        log.info("开启支付流程处理,结果:{}", processInstance);


        /** 更新业务数据中的流程实例ID */
        result = this.updateById(BizPaymentProcessInfo.builder()
                .processInstanceId(processInstance.getId())
                .id(bizPaymentProcessInfo.getId())
                .build());
        log.info("更新支付流程业务数据 - 流程实例ID,结果:{}", result);
        return RespResult.successData(bizPaymentProcessInfo.getProcessInstanceId());
    }

    @Override
    public RespResult<TaskVo<BizPaymentProcessInfo>> queryTasks(TaskQueryParam taskQueryParam) {
        log.info("查询待处理任务列表,参数:{}", taskQueryParam);
        RespResult<TaskVo<BizPaymentProcessInfo>> respResult = this.camundaCommonService.queryRuntimeTasks(taskQueryParam, (bizKey) -> {
            return this.getById(Long.valueOf(bizKey));
        });
        log.info("查询待处理任务列表,结果:{}", respResult);
        return respResult;
    }

    @Override
    public RespResult<HistoricTaskInstance> queryHistoryTasks(TaskQueryParam taskQueryParam) {
        return this.camundaCommonService.queryHistoryTasks(taskQueryParam);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RespResult confirmPayment(PaymentConfirmParam paymentConfirmParam) {
        log.info("用户确认支付, 参数:{}", paymentConfirmParam);

        /** 查询当前流程实例已经存在的流程变量 */
        ProcessVariablesQueryParam processVariablesQueryParam = ProcessVariablesQueryParam.builder()
                //.taskId(paymentConfirmParam.getTaskId())
                .processInstanceId(paymentConfirmParam.getProcessInstanceId())
                .build();
        Map<String, Object> existProcessVariables = this.camundaCommonService.getRuntimeProcessVariables(processVariablesQueryParam);
        log.info("获取当前已存在流程变量,结果:{}", existProcessVariables);
        //使用流程变量包装器(便于获取属性)
        PaymentProcessVariablesWrapper existPaymentProcessVariablesWrapper = new PaymentProcessVariablesWrapper(existProcessVariables);

        /** 更新业务流程数据(更新范围:之前流程添加的 + 当前操作添加的 流程变量 ) */
        BizPaymentProcessInfo bizPaymentProcessInfo = BizPaymentProcessInfo.builder()
                .productDiscount(existPaymentProcessVariablesWrapper.getProductDiscount())
                .productDiscountPrice(existPaymentProcessVariablesWrapper.getProductDiscountPrice())
                .approvalResult(paymentConfirmParam.getApprovalResult())
                .approvalTime(LocalDateTime.now())
                .id(Long.valueOf(paymentConfirmParam.getBizKey()))
                .build();
        log.info("更新用户确认信息,参数:{}", bizPaymentProcessInfo);
        Boolean result = this.updateById(bizPaymentProcessInfo);
        log.info("更新用户确认信息,结果:{}", result);


        /** 更新流程引擎(完成用户确认任务 + 添加新的流程变量(用于后续网关判断)) */
        Map<String, Integer> processVariables = CamundaUtils.convertProcessVariablesFromPair(PaymentProcessConstants.PAYMENT_APPROVAL_RESULT_VAR_NAME, paymentConfirmParam.getApprovalResult());
        this.camundaCommonService.completeTask(paymentConfirmParam.getTaskId(), processVariables);

        return RespResult.success();
    }

    @Override
    public RespResult<BizPaymentProcessInfo> queryPayments(PaymentQueryParam paymentQueryParam) {
        IPage<BizPaymentProcessInfo> pageResult = this.page(
                CommonUtils.convertPage(paymentQueryParam),
                Wrappers.<BizPaymentProcessInfo>lambdaQuery().eq(null != paymentQueryParam.getId(), BizPaymentProcessInfo::getId, paymentQueryParam.getId())
                        .eq(null != paymentQueryParam.getProcessInstanceId(), BizPaymentProcessInfo::getProcessInstanceId, paymentQueryParam.getProcessInstanceId())
                        .like(null != paymentQueryParam.getProductName(), BizPaymentProcessInfo::getProductName, paymentQueryParam.getProductName())

                        .ge(null != paymentQueryParam.getProductPriceStart(), BizPaymentProcessInfo::getProductPrice, paymentQueryParam.getProductPriceStart())
                        .le(null != paymentQueryParam.getProductPriceEnd(), BizPaymentProcessInfo::getProductPrice, paymentQueryParam.getProductPriceEnd())

                        .ge(null != paymentQueryParam.getProductDiscountStart(), BizPaymentProcessInfo::getProductDiscount, paymentQueryParam.getProductDiscountStart())
                        .le(null != paymentQueryParam.getProductDiscountEnd(), BizPaymentProcessInfo::getProductDiscount, paymentQueryParam.getProductDiscountEnd())

                        .ge(null != paymentQueryParam.getProductDiscountPriceStart(), BizPaymentProcessInfo::getProductDiscountPrice, paymentQueryParam.getProductDiscountPriceStart())
                        .le(null != paymentQueryParam.getProductDiscountPriceEnd(), BizPaymentProcessInfo::getProductDiscountPrice, paymentQueryParam.getProductDiscountPriceEnd())


                        .eq(null != paymentQueryParam.getApprovalResult(), BizPaymentProcessInfo::getApprovalResult, paymentQueryParam.getApprovalResult())
                        .eq(null != paymentQueryParam.getPaymentResult(), BizPaymentProcessInfo::getPaymentResult, paymentQueryParam.getPaymentResult())
                        .eq(null != paymentQueryParam.getPaymentAssignee(), BizPaymentProcessInfo::getPaymentAssignee, paymentQueryParam.getPaymentAssignee())

                        .ge(null != paymentQueryParam.getCreateTimeStart(), BizPaymentProcessInfo::getCreateTime, paymentQueryParam.getCreateTimeStart())
                        .le(null != paymentQueryParam.getCreateTimeEnd(), BizPaymentProcessInfo::getCreateTime, paymentQueryParam.getCreateTimeEnd())

                        .ge(null != paymentQueryParam.getApprovalTimeStart(), BizPaymentProcessInfo::getApprovalTime, paymentQueryParam.getApprovalTimeStart())
                        .le(null != paymentQueryParam.getApprovalTimeEnd(), BizPaymentProcessInfo::getApprovalTime, paymentQueryParam.getApprovalTimeEnd())

                        .ge(null != paymentQueryParam.getPaymentTimeStart(), BizPaymentProcessInfo::getPaymentTime, paymentQueryParam.getPaymentTimeStart())
                        .le(null != paymentQueryParam.getPaymentTimeEnd(), BizPaymentProcessInfo::getPaymentTime, paymentQueryParam.getPaymentTimeEnd())
        );
        return CommonUtils.convertPageResult(pageResult);
    }

}

4.4 调用支付服务 - JavaDelegate实现

import com.luo.camunda.app.enums.PaymentResultEnum;
import com.luo.camunda.app.model.wrapper.PaymentProcessVariablesWrapper;
import com.luo.camunda.app.model.entity.BizPaymentProcessInfo;
import com.luo.camunda.app.service.IBizPaymentProcessInfoService;
import com.luo.demo.sc.base.execption.MsgRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;

/**
 * 支付活动 - Java代理
 *
 * @author luohq
 * @date 2022-01-31
 */
@Component
@Slf4j
public class PaymentDelegate implements JavaDelegate {

    @Resource
    private IBizPaymentProcessInfoService bizPaymentProcessInfoService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void execute(DelegateExecution execution) {
        /** 获取流程相关信息 */
        String bizKey = execution.getProcessBusinessKey();
        String processInstanceId = execution.getProcessInstanceId();
        log.info("调用支付服务,processInstanceId: {}, bizKey: {}", processInstanceId, bizKey);

        PaymentProcessVariablesWrapper paymentProcessVariablesWrapper = new PaymentProcessVariablesWrapper(execution.getVariables());
        log.info("RPC调用支付服务, 流程变量:{}", paymentProcessVariablesWrapper);

        /** 更新业务DB */
        BizPaymentProcessInfo bizPaymentProcessInfo = BizPaymentProcessInfo.builder()
                .id(Long.valueOf(bizKey))
                .paymentResult(PaymentResultEnum.PAY_SUCCESS.getCode())
                .paymentTime(LocalDateTime.now())
                .build();
        log.info("更新支付结果, 参数:{}", bizPaymentProcessInfo);
        Boolean result = this.bizPaymentProcessInfoService.updateById(bizPaymentProcessInfo);
        log.info("更新支付结果, 结果:{}", result);
        //抛出异常,则此task执行失败,回退到上一步
        //throw new MsgRuntimeException("支付异常");
    }

}

4.5 使用业务数据查询代替流程引擎查询

由于使用了自定义业务数据表biz_payment_process_info记录流程数据,
则之后的流程相关数据查询可优先采用查询biz_payment_process_info表来实现,
与流程引擎解耦,可根据自身业务特性优化自定义业务存储实现。
例如:

  • 查询所有支付请求记录
  • 查询指定用户的支付请求记录(payment_assignee=‘luo’)
  • 查询用户不同意支付的请求(approval_result = 2 )
  • 查询支付成功的支付请求(payment_result=1)
  • …可根据自身需求调整业务数据设计

4.6 测试用例

支付示例 - 流程应用的具体调用逻辑可参见测试用例:
camunda-process-application/payment-application/…/CamundaPaymentApplicationTest.java

  • 首先用户开启支付流程 - test01_startPayment
  • 然后用户获取待处理任务 - 待用户确认的支付请求 - test02_getTasks
  • 提交用户确认请求 - test03_confirmPayment
  • 流程应用调用支付服务 - PaymentDelegate
  • 查询流程引擎历史任务 - test04_getHistoryTasks
  • 查询支付数据 - test05_getPayments_all, test06_getPayments_not_confirm, test07_getPayments_with_condition
  • 14
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 26
    评论
camunda-bpm-spring-boot-rest-example是一个使用Camunda BPM和Spring Boot构建的示例项目。Camunda 是一个开源的业务流程管理(BPM)平台,而Spring Boot是一个用于构建Java应用程序的框架。该示例项目结合了两者的优势,提供了一种灵活且强大的方式来开发和管理业务流程。 该示例项目提供了一个RESTful API,允许开发人员通过HTTP请求与Camunda引擎进行交互。通过发送不同的API请求,开发人员可以启动、暂停、完成和取消业务流程实例。还可以查询正在运行的流程实例,获取与业务流程相关的任务列表,并提交任务处理结果。 Camunda 的引擎负责执行业务流程定义的各个任务节点,并管理流程实例的状态。Spring Boot提供了一种简化部署和运行应用程序的方式,使得开发人员能够更加专注于业务流程的实现和逻辑。 该示例项目的优势在于它的易用性和可扩展性。开发人员只需通过简单的配置即可快速启动项目,并开始开发自己的业务流程。同时,Camunda和Spring Boot的结合也使得项目具备了良好的可扩展性,可以轻松地集成其他技术和工具。 总之,camunda-bpm-spring-boot-rest-example是一个使用Camunda BPM和Spring Boot构建的示例项目,提供了一个方便、灵活和可扩展的方式来开发和管理业务流程。它不仅简化了业务流程的开发和部署过程,还提供了一种强大的方式来管理业务流程的执行和监控。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗小爬EX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值