当前位置: 首页 > news >正文

java每日精进 7.26【流程设计5.0(中间事件+结束事件)】

1.中间事件核心概念

  • 定位:位于开始事件与结束事件之间,影响流程路由但不启动/终止流程。

  • 两类中间事件

    • 捕获事件(Catch):等待外部触发(如消息、信号到达)。

    • 抛出事件(Throw):主动触发事件(如发送信号、抛出异常)。

  • 与边界事件区别:边界事件附着在活动节点上,而中间事件独立存在于连线上。


1.1 中间抛出事件

1. 空中间抛出事件

  • 作用:流程状态标记点,通过监听器执行自定义逻辑。

  • XML示例

    <process id="noneEventProcess"><intermediateThrowEvent id="statusMarker"><extensionElements><flowable:executionListener event="start"class="com.example.ProcessStatusListener"/> <!-- 监听状态变更 --></extensionElements></intermediateThrowEvent>
    </process>
  • 使用场景:记录流程进入关键节点(如"订单已审核"),触发数据库日志记录。

  • 定义创建了一个简单流程,其中包含一个无类型中间抛出事件,当流程执行到这个事件时,会触发指定的 Java 类(MyExecutionListener)中的逻辑。这种模式通常用于在流程执行过程中插入自定义业务逻辑、发送通知或与外部系统交互等场景。


2. 信号中间抛出事件

在流程执行过程中发送特定消息的机制

实例说明

具体实例

假设我们有一个订单处理流程,当订单确认后需要向支付系统发送支付请求,这就是消息中间抛出事件的典型应用场景。

BPMN XML 完整定义:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100524/BPMN20.xsd"id="Definitions_1"targetNamespace="http://camunda.org/examples"><!-- 定义消息 --><message id="paymentRequestMsg" name="订单支付请求" /><message id="paymentResponseMsg" name="支付结果响应" /><!-- 订单流程 - 包含消息中间抛出事件 --><process id="orderProcess" name="订单处理流程" isExecutable="true"><startEvent id="start" /><sequenceFlow id="flow1" sourceRef="start" targetRef="confirmOrder" /><userTask id="confirmOrder" name="确认订单" /><sequenceFlow id="flow2" sourceRef="confirmOrder" targetRef="sendPaymentRequest" /><!-- 消息中间抛出事件:发送支付请求 --><intermediateThrowEvent id="sendPaymentRequest"><messageEventDefinition messageRef="paymentRequestMsg" /></intermediateThrowEvent><sequenceFlow id="flow3" sourceRef="sendPaymentRequest" targetRef="waitPaymentResponse" /><!-- 消息中间捕获事件:等待支付响应 --><intermediateCatchEvent id="waitPaymentResponse"><messageEventDefinition messageRef="paymentResponseMsg" /></intermediateCatchEvent><sequenceFlow id="flow4" sourceRef="waitPaymentResponse" targetRef="end" /><endEvent id="end" /></process><!-- 支付流程 - 接收支付请求 --><process id="paymentProcess" name="支付处理流程" isExecutable="true"><!-- 消息开始事件:接收支付请求 --><startEvent id="paymentStart"><messageEventDefinition messageRef="paymentRequestMsg" /></startEvent><sequenceFlow id="flow5" sourceRef="paymentStart" targetRef="processPayment" /><serviceTask id="processPayment" name="处理支付" /><sequenceFlow id="flow6" sourceRef="processPayment" targetRef="sendPaymentResponse" /><!-- 消息中间抛出事件:发送支付响应 --><intermediateThrowEvent id="sendPaymentResponse"><messageEventDefinition messageRef="paymentResponseMsg" /></intermediateThrowEvent><sequenceFlow id="flow7" sourceRef="sendPaymentResponse" targetRef="paymentEnd" /><endEvent id="paymentEnd" /></process>
</definitions>
// 1. 部署流程定义
RepositoryService repositoryService = processEngine.getRepositoryService();
repositoryService.createDeployment().addClasspathResource("order-payment-process.bpmn20.xml").deploy();// 2. 启动订单流程
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance orderProcessInstance = runtimeService.startProcessInstanceByKey("orderProcess");
System.out.println("订单流程已启动,ID: " + orderProcessInstance.getId());// 3. 完成"确认订单"任务
TaskService taskService = processEngine.getTaskService();
Task confirmTask = taskService.createTaskQuery().processInstanceId(orderProcessInstance.getId()).singleResult();
taskService.complete(confirmTask.getId());// 此时流程会执行到sendPaymentRequest节点,发送支付请求消息
// 支付流程会自动启动并处理支付(因为有消息开始事件)// 4. 模拟支付完成后,发送支付响应消息
// 查找等待支付响应的执行实例
Execution execution = runtimeService.createExecutionQuery().processInstanceId(orderProcessInstance.getId()).messageEventSubscriptionName("paymentResponseMsg").singleResult();// 发送响应消息
runtimeService.messageEventReceived("paymentResponseMsg", execution.getId());
System.out.println("支付响应已发送,订单流程将继续执行");

  • 作用:全局广播信号,触发所有匹配的信号捕获事件(包括开始/边界/中间事件)。

  • XML定义

    <signal id="alertSignal" name="紧急告警" /> <!-- 信号定义 -->
    <process id="signalProcess"><intermediateThrowEvent id="throwAlert"><signalEventDefinition signalRef="alertSignal" /> <!-- 抛出信号 --></intermediateThrowEvent>
    </process>
  • 触发效果

    • 并行流程中订阅了alertSignal信号边界事件会中断当前任务。

    • 信号开始事件启动新流程实例。

  • 消息中间抛出事件

    • 是 BPMN 中的一种事件类型,用于在流程执行到特定节点时主动发送消息
    • 实现的是点对点通信模式,需要有明确的消息接收方
    • 通常用于不同流程之间或流程与外部系统之间的通信
  • XML 定义解析

    • <message> 标签:定义消息元数据,id是唯一标识,name是可读名称
    • <intermediateThrowEvent> 标签:定义中间抛出事件
    • <messageEventDefinition> 标签:将事件与特定消息关联,messageRef引用消息的id
  • 接收机制

    • 必须有对应的消息捕获事件(如消息中间捕获事件或消息边界事件)来接收此消息
    • 形成了 "发送 - 接收" 的消息传递模式
  • 整个流程包含两个子流程:订单处理流程和支付处理流程
  • 订单流程在确认订单后,通过 "消息中间抛出事件" 发送支付请求
  • 支付流程通过 "消息开始事件" 接收支付请求并处理
  • 支付完成后,支付流程通过另一个 "消息中间抛出事件" 发送支付响应
  • 订单流程通过 "消息中间捕获事件" 等待并接收支付响应,然后继续执行后续流程

实例说明

  1. 整个流程包含两个子流程:订单处理流程和支付处理流程
  2. 订单流程在确认订单后,通过 "消息中间抛出事件" 发送支付请求
  3. 支付流程通过 "消息开始事件" 接收支付请求并处理
  4. 支付完成后,支付流程通过另一个 "消息中间抛出事件" 发送支付响应
  5. 订单流程通过 "消息中间捕获事件" 等待并接收支付响应,然后继续执行后续流程

3. 消息中间抛出事件

  • 作用:向特定接收方发送消息(点对点通信)。

  • XML定义

    <message id="paymentMsg" name="支付请求" /> <!-- 消息定义 -->
    <process id="paymentProcess"><intermediateThrowEvent id="sendPayment"><messageEventDefinition messageRef="paymentMsg" /> <!-- 抛出消息 --></intermediateThrowEvent>
    </process>
  • 接收方:必须存在对应的消息捕获事件(如消息中间捕获事件)。

  • 实例

    // 外部系统通过API触发消息
    runtimeService.messageEventReceived("paymentMsg", executionId); 

    流程在sendPayment节点暂停,直到外部系统调用此API。

  • xml示例

<!-- 消息中间抛出事件:发送支付请求 --><intermediateThrowEvent id="sendPaymentRequest"><messageEventDefinition messageRef="paymentRequestMsg" /></intermediateThrowEvent><sequenceFlow id="flow3" sourceRef="sendPaymentRequest" targetRef="waitPaymentResponse" /><!-- 消息中间捕获事件:等待支付响应 --><intermediateCatchEvent id="waitPaymentResponse"><messageEventDefinition messageRef="paymentResponseMsg" /></intermediateCatchEvent>

4. 补偿中间抛出事件

  • 作用:用于在流程出现异常或需要取消操作时,触发已完成活动的补偿逻辑,恢复到之前的状态。

  • XML定义

    <intermediateThrowEvent id="undoActions"><compensateEventDefinition /> <!-- 触发补偿 -->
    </intermediateThrowEvent>
  • XML 定义解析

    • <intermediateThrowEvent id="undoActions">:定义补偿中间抛出事件
    • <compensateEventDefinition />:标识这是一个补偿事件,用于触发补偿逻辑
  • 补偿规则

  • 触发补偿:当流程执行到补偿中间抛出事件时,会触发已完成活动的补偿处理器
  • 事务回滚:用于撤销之前的操作,恢复系统状态
  • 补偿处理器:为需要补偿的活动定义的撤销逻辑(如取消订单、退款等)
  • 补偿规则详解

    • 反向触发:按活动完成顺序的逆序执行补偿(最后完成的活动最先补偿)
    • 作用域限制:仅补偿同一子流程层级中定义了补偿处理器的活动
    • 多实例活动:需等待所有实例都完成后才会触发补偿
  • 补偿处理器定义

    • 通过边界事件<boundaryEvent>定义,需指定attachedToRef关联到需要补偿的活动
    • 包含<compensateEventDefinition />标识这是一个补偿处理器
  • 实例

    <?xml version="1.0" encoding="UTF-8"?>
    <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:flowable="http://flowable.org/bpmn"xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100524/BPMN20.xsd http://flowable.org/bpmn http://flowable.org/bpmn/current/flowable-bpmn.xsd"id="Definitions_1"targetNamespace="http://flowable.org/examples"><process id="travelBookingProcess" name="旅行预订流程" isExecutable="true"><startEvent id="start" /><sequenceFlow id="flow1" sourceRef="start" targetRef="bookFlight" /><!-- 预订航班服务任务 --><serviceTask id="bookFlight" name="预订航班" flowable:class="com.example.BookFlightService"><extensionElements><flowable:executionListener event="end" class="com.example.LogFlightBookingListener" /></extensionElements></serviceTask><!-- 航班预订的补偿处理器 --><boundaryEvent id="compensateFlight" attachedToRef="bookFlight" cancelActivity="false"><compensateEventDefinition /></boundaryEvent><!-- 补偿航班预订的服务任务 --><serviceTask id="undoFlightBooking" name="取消航班预订" flowable:class="com.example.CancelFlightBookingService"><extensionElements><flowable:executionListener event="end" class="com.example.LogFlightCancellationListener" /></extensionElements></serviceTask><sequenceFlow id="flow2" sourceRef="compensateFlight" targetRef="undoFlightBooking" /><sequenceFlow id="flow3" sourceRef="undoFlightBooking" targetRef="compensationEnd" /><sequenceFlow id="flow4" sourceRef="bookFlight" targetRef="bookHotel" /><!-- 预订酒店服务任务 --><serviceTask id="bookHotel" name="预订酒店" flowable:class="com.example.BookHotelService"><extensionElements><flowable:executionListener event="end" class="com.example.LogHotelBookingListener" /></extensionElements></serviceTask><!-- 酒店预订的补偿处理器 --><boundaryEvent id="compensateHotel" attachedToRef="bookHotel" cancelActivity="false"><compensateEventDefinition /></boundaryEvent><!-- 补偿酒店预订的服务任务 --><serviceTask id="undoHotelBooking" name="取消酒店预订" flowable:class="com.example.CancelHotelBookingService"><extensionElements><flowable:executionListener event="end" class="com.example.LogHotelCancellationListener" /></extensionElements></serviceTask><sequenceFlow id="flow5" sourceRef="compensateHotel" targetRef="undoHotelBooking" /><sequenceFlow id="flow6" sourceRef="undoHotelBooking" targetRef="compensationEnd" /><sequenceFlow id="flow7" sourceRef="bookHotel" targetRef="paymentCheck" /><!-- 检查支付状态网关 --><exclusiveGateway id="paymentCheck" /><!-- 支付成功路径 --><sequenceFlow id="flow8" sourceRef="paymentCheck" targetRef="bookingSuccess"><conditionExpression xsi:type="tFormalExpression">${paymentSuccessful == true}</conditionExpression></sequenceFlow><!-- 支付失败路径 - 触发补偿 --><sequenceFlow id="flow9" sourceRef="paymentCheck" targetRef="undoActions"><conditionExpression xsi:type="tFormalExpression">${paymentSuccessful == false}</conditionExpression></sequenceFlow><!-- 补偿中间抛出事件 --><intermediateThrowEvent id="undoActions"><compensateEventDefinition /></intermediateThrowEvent><sequenceFlow id="flow10" sourceRef="undoActions" targetRef="compensationEnd" /><!-- 预订成功结束事件 --><endEvent id="bookingSuccess" /><!-- 补偿完成结束事件 --><endEvent id="compensationEnd" /></process>
    </definitions>
    // 部署流程
    repositoryService.createDeployment().addClasspathResource("travel-booking-process.bpmn20.xml").deploy();// 启动流程实例
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("travelBookingProcess");// 模拟支付失败的情况
    Task paymentTask = taskService.createTaskQuery().processInstanceId(processInstance.getId()).taskName("检查支付状态").singleResult();// 设置支付失败变量
    Map<String, Object> variables = new HashMap<>();
    variables.put("paymentSuccessful", false);
    taskService.complete(paymentTask.getId(), variables);// 此时流程会执行到undoActions节点,触发补偿
    // 补偿顺序:先取消酒店预订(后完成的活动),再取消航班预订(先完成的活动)

    实例执行流程说明

  • 正常流程:

    • 开始 → 预订航班 → 预订酒店 → 检查支付状态 → 支付成功 → 流程结束
  • 补偿触发流程(当支付失败时):

    • 开始 → 预订航班 → 预订酒店 → 检查支付状态 → 支付失败 → 触发补偿中间事件 (undoActions)
    • 补偿执行顺序(反向执行):
      1. 触发酒店预订的补偿处理器 → 执行取消酒店预订
      2. 触发航班预订的补偿处理器 → 执行取消航班预订
    • 补偿完成 → 流程结束


5. 升级中间抛出事件(Escalation)

  • 作用:向父流程传递异常(非错误),不中断当前流程。

  • 基本作用

    • 跨层级通知:向父流程传递特定事件信息
    • 非中断性:抛出事件后,当前流程(子流程)继续执行
    • 异常传递:用于传递需要上级处理的特殊情况(非错误)
  • XML 定义解析

    • <intermediateThrowEvent id="escalateIssue">:定义升级中间抛出事件
    • <escalationEventDefinition escalationRef="managerApproval" />:关联到特定升级定义,escalationRef引用升级事件的 ID
  • 工作机制

    • 子流程中定义升级抛出事件
    • 父流程中需定义对应的升级捕获事件(通常是边界事件)
    • 事件触发后,父流程执行相应处理,子流程继续原有路径
  • 与错误事件的关键区别

    • 错误事件:抛出后会终止当前流程路径,通常用于处理异常情况
    • 升级事件:仅向上传递信息,不影响当前流程执行,用于需要上级关注的业务情况
  • 详细实例:采购审批流程

  • <?xml version="1.0" encoding="UTF-8"?>
    <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:flowable="http://flowable.org/bpmn"xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100524/BPMN20.xsd http://flowable.org/bpmn http://flowable.org/bpmn/current/flowable-bpmn.xsd"id="Definitions_1"targetNamespace="http://flowable.org/examples"><!-- 定义升级事件 --><escalation id="managerApproval" name="需要经理审批" /><!-- 父流程:采购管理流程 --><process id="purchaseManagementProcess" name="采购管理流程" isExecutable="true"><startEvent id="parentStart" /><sequenceFlow id="flow1" sourceRef="parentStart" targetRef="subProcess" /><!-- 子流程:采购审批流程 --><subProcess id="subProcess" name="采购审批流程"><startEvent id="subStart" /><sequenceFlow id="flow2" sourceRef="subStart" targetRef="checkAmount" /><!-- 检查采购金额 --><serviceTask id="checkAmount" name="检查采购金额" flowable:class="com.example.CheckPurchaseAmountService" /><sequenceFlow id="flow3" sourceRef="checkAmount" targetRef="amountGateway" /><!-- 金额判断网关 --><exclusiveGateway id="amountGateway" /><!-- 金额正常路径 --><sequenceFlow id="flow4" sourceRef="amountGateway" targetRef="departmentApproval"><conditionExpression xsi:type="tFormalExpression">${amount <= 10000}</conditionExpression></sequenceFlow><!-- 金额超限路径 - 触发升级 --><sequenceFlow id="flow5" sourceRef="amountGateway" targetRef="escalateIssue"><conditionExpression xsi:type="tFormalExpression">${amount > 10000}</conditionExpression></sequenceFlow><!-- 升级中间抛出事件 --><intermediateThrowEvent id="escalateIssue"><escalationEventDefinition escalationRef="managerApproval" /></intermediateThrowEvent><sequenceFlow id="flow6" sourceRef="escalateIssue" targetRef="departmentApproval" /><!-- 部门审批 --><userTask id="departmentApproval" name="部门审批" /><sequenceFlow id="flow7" sourceRef="departmentApproval" targetRef="subEnd" /><endEvent id="subEnd" /></subProcess><!-- 父流程中捕获升级事件的边界事件 --><boundaryEvent id="catchEscalation" attachedToRef="subProcess"><escalationEventDefinition escalationRef="managerApproval" /></boundaryEvent><sequenceFlow id="flow8" sourceRef="catchEscalation" targetRef="notifyManager" /><!-- 通知经理处理 --><serviceTask id="notifyManager" name="通知经理" flowable:class="com.example.NotifyManagerService" /><sequenceFlow id="flow9" sourceRef="notifyManager" targetRef="parentEnd" /><sequenceFlow id="flow10" sourceRef="subProcess" targetRef="parentEnd" /><endEvent id="parentEnd" /></process>
    </definitions>

    Java 代码实现示例:

  • 检查采购金额的服务类:
  •  
    public class CheckPurchaseAmountService implements JavaDelegate {@Overridepublic void execute(DelegateExecution execution) {// 获取采购金额(实际应用中从流程变量获取)double amount = (double) execution.getVariable("purchaseAmount");System.out.println("检查采购金额: " + amount);// 设置金额变量用于网关判断execution.setVariable("amount", amount);}
    }
    

  • 通知经理的服务类:
  •  
    public class NotifyManagerService implements JavaDelegate {@Overridepublic void execute(DelegateExecution execution) {double amount = (double) execution.getVariable("amount");String purchaseId = (String) execution.getVariable("purchaseId");System.out.println("发送通知给经理: 采购单 " + purchaseId + " 金额为 " + amount + ",超过审批阈值需要关注");// 实际应用中这里会发送邮件、消息等}
    }

// 部署流程定义
repositoryService.createDeployment().addClasspathResource("purchase-approval-process.bpmn20.xml").deploy();// 启动流程实例,设置采购金额为15000(超过10000阈值)
Map<String, Object> variables = new HashMap<>();
variables.put("purchaseId", "PO-" + System.currentTimeMillis());
variables.put("purchaseAmount", 15000.00);ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("purchaseManagementProcess", variables);// 完成部门审批任务
Task departmentTask = taskService.createTaskQuery().processInstanceId(processInstance.getId()).taskName("部门审批").singleResult();taskService.complete(departmentTask.getId());

实例执行流程说明

  1. 正常执行路径:

    • 父流程启动 → 进入子流程 → 检查采购金额(15000 元)
    • 金额超过阈值(10000 元)→ 触发升级中间抛出事件 (escalateIssue)
    • 子流程继续执行 → 部门审批 → 子流程结束 → 父流程结束
  2. 升级事件处理路径(并行发生):

    • 升级事件触发 → 父流程的边界事件 (catchEscalation) 捕获事件
    • 执行通知经理服务 → 完成经理通知流程
  3. 关键特性体现:

    • 升级事件不中断子流程,子流程继续完成部门审批
    • 父流程同时处理升级事件,发送通知给经理
    • 实现了 "异常情况上报" 但 "业务继续处理" 的业务需求

注:父子事件联系如下:

<!-- 父流程中的子流程节点 -->
<subProcess id="subProcess" name="采购审批流程"><!-- 子流程内部逻辑(包含升级抛出事件) -->
</subProcess><!-- 绑定到子流程的升级边界事件 -->
<boundaryEvent id="catchEscalation" attachedToRef="subProcess"><!-- 关联到子流程抛出的升级事件ID --><escalationEventDefinition escalationRef="managerApproval" />
</boundaryEvent>

关键区别总结

事件类型方向触发方式作用范围
信号抛出抛出全局广播跨流程实例
消息抛出抛出点对点发送指定接收方
补偿抛出抛出触发补偿处理器当前作用域
信号/消息捕获捕获等待外部触发当前流程节点
计时器捕获捕获时间到达后自动触发当前节点

最佳实践建议

  1. 空事件:结合监听器实现审计日志或指标统计。

  2. 信号事件:跨流程协同(如主流程通知子流程)。

  3. 消息事件:与外部系统集成(如等待支付回调)。

  4. 补偿事件:实现SAGA事务模式,确保数据一致性。

  5. 升级事件:构建分层异常处理机制(子流程→父流程)。


1.2 中间捕获事件

1. 计时器中间捕获事件

  • 作用:延迟等待(如"3天后继续")。

  • <intermediateCatchEvent id="wait3Days"><timerEventDefinition><timeDuration>PT72H</timeDuration> <!-- 等待72小时 --></timerEventDefinition>
    </intermediateCatchEvent>
  • 实例展示
  • <?xml version="1.0" encoding="UTF-8"?>
    <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100524/BPMN20.xsd"id="Definitions_1"targetNamespace="http://example.org"><process id="leaveRequestProcess" name="假期申请流程" isExecutable="true"><startEvent id="start" /><sequenceFlow id="flow1" sourceRef="start" targetRef="submitRequest" /><!-- 员工提交假期申请 --><userTask id="submitRequest" name="提交假期申请" /><sequenceFlow id="flow2" sourceRef="submitRequest" targetRef="managerApproval" /><!-- 经理审批 --><userTask id="managerApproval" name="经理审批" /><sequenceFlow id="flow3" sourceRef="managerApproval" targetRef="end" /><!-- 绑定到经理审批的边界事件:如果超时未审批则触发 --><boundaryEvent id="approvalTimeout" attachedToRef="managerApproval"><!-- 计时器中间捕获事件定义:等待3天(72小时) --><timerEventDefinition><timeDuration>PT72H</timeDuration> <!-- 时间格式:PT代表时间周期,72H表示72小时 --></timerEventDefinition></boundaryEvent><sequenceFlow id="flow4" sourceRef="approvalTimeout" targetRef="sendReminder" /><!-- 发送审批提醒 --><serviceTask id="sendReminder" name="发送审批提醒" /><sequenceFlow id="flow5" sourceRef="sendReminder" targetRef="managerApproval" /><endEvent id="end" /></process>
    </definitions>

    流程说明:

  • 员工提交假期申请后,流程进入 "经理审批" 环节
  • 同时,绑定在审批环节上的计时器开始计时(等待 72 小时)
  • 如果经理在 3 天内完成审批,流程直接结束
  • 如果 3 天内未审批,计时器事件触发,执行 "发送审批提醒"
  • 发送提醒后,流程回到 "经理审批" 环节,等待经理处理

2. 信号/消息中间捕获事件

信号 / 消息中间捕获事件是 BPMN 中用于接收外部信号或消息的机制,与对应的抛出事件配合使用,实现流程间或流程与外部系统的异步通信。其中:

  • 消息中间捕获事件:接收特定的点对点消息,通常用于两个明确流程之间的通信
  • 信号中间捕获事件:接收广播式信号,可被多个流程实例同时捕获

两者均会使流程暂停,等待对应的事件触发后再继续执行。

消息中间捕获事件实例:订单支付流程

以下是一个电商订单流程的示例,订单创建后会等待支付系统发送的支付完成消息,收到消息后才继续后续的发货流程。

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:flowable="http://flowable.org/bpmn"xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100524/BPMN20.xsd http://flowable.org/bpmn http://flowable.org/bpmn/current/flowable-bpmn.xsd"id="Definitions_1"targetNamespace="http://flowable.org/examples"><!-- 定义支付消息 --><message id="paymentMsg" name="支付完成消息" /><!-- 订单处理流程 --><process id="orderProcess" name="订单处理流程" isExecutable="true"><startEvent id="start" /><sequenceFlow id="flow1" sourceRef="start" targetRef="createOrder" /><!-- 创建订单 --><serviceTask id="createOrder" name="创建订单" flowable:class="com.example.CreateOrderService" /><sequenceFlow id="flow2" sourceRef="createOrder" targetRef="waitPayment" /><!-- 消息中间捕获事件:等待支付完成消息 --><intermediateCatchEvent id="waitPayment"><messageEventDefinition messageRef="paymentMsg" /></intermediateCatchEvent><sequenceFlow id="flow3" sourceRef="waitPayment" targetRef="arrangeDelivery" /><!-- 安排发货 --><serviceTask id="arrangeDelivery" name="安排发货" flowable:class="com.example.ArrangeDeliveryService" /><sequenceFlow id="flow4" sourceRef="arrangeDelivery" targetRef="end" /><endEvent id="end" /></process><!-- 支付处理流程 --><process id="paymentProcess" name="支付处理流程" isExecutable="true"><startEvent id="paymentStart" /><sequenceFlow id="flow5" sourceRef="paymentStart" targetRef="processPayment" /><!-- 处理支付 --><serviceTask id="processPayment" name="处理支付" flowable:class="com.example.ProcessPaymentService" /><sequenceFlow id="flow6" sourceRef="processPayment" targetRef="sendPaymentMsg" /><!-- 消息中间抛出事件:发送支付完成消息 --><intermediateThrowEvent id="sendPaymentMsg"><messageEventDefinition messageRef="paymentMsg" /></intermediateThrowEvent><sequenceFlow id="flow7" sourceRef="sendPaymentMsg" targetRef="paymentEnd" /><endEvent id="paymentEnd" /></process>
</definitions>

Java 代码实现示例:

  1. 创建订单服务类:
public class CreateOrderService implements JavaDelegate {@Overridepublic void execute(DelegateExecution execution) {String orderId = "ORDER-" + System.currentTimeMillis();execution.setVariable("orderId", orderId);System.out.println("订单创建成功,订单ID: " + orderId);// 实际业务中会保存订单信息到数据库}
}
  1. 处理支付服务类:
public class ProcessPaymentService implements JavaDelegate {@Overridepublic void execute(DelegateExecution execution) {String orderId = (String) execution.getVariable("orderId");System.out.println("处理订单 " + orderId + " 的支付...");// 实际业务中会调用支付网关处理支付execution.setVariable("paymentStatus", "SUCCESS");}
}
  1. 流程交互代码:
// 1. 部署流程
repositoryService.createDeployment().addClasspathResource("order-payment-process.bpmn20.xml").deploy();// 2. 启动订单流程
Map<String, Object> orderVariables = new HashMap<>();
ProcessInstance orderInstance = runtimeService.startProcessInstanceByKey("orderProcess", orderVariables);
String orderId = (String) runtimeService.getVariable(orderInstance.getId(), "orderId");
System.out.println("订单流程已启动,等待支付,订单ID: " + orderId);// 3. 启动支付流程,传入订单ID
Map<String, Object> paymentVariables = new HashMap<>();
paymentVariables.put("orderId", orderId);
ProcessInstance paymentInstance = runtimeService.startProcessInstanceByKey("paymentProcess", paymentVariables);// 4. 支付完成后,发送支付消息给订单流程
// 查找等待支付消息的执行实例
Execution waitingExecution = runtimeService.createExecutionQuery().processInstanceId(orderInstance.getId()).messageEventSubscriptionName("paymentMsg").singleResult();// 发送消息触发订单流程继续执行
runtimeService.messageEventReceived("paymentMsg", waitingExecution.getId());
System.out.println("支付完成消息已发送,订单流程将继续执行");

实例执行流程说明

  1. 订单流程启动后,创建订单并执行到waitPayment节点
  2. 此时订单流程暂停,等待paymentMsg消息
  3. 支付流程启动并处理支付,完成后通过sendPaymentMsg节点抛出paymentMsg消息
  4. 调用runtimeService.messageEventReceived()方法将消息传递给订单流程
  5. 订单流程的waitPayment节点捕获到消息,继续执行后续的发货流程

关键特性总结

  • 消息捕获事件与抛出事件通过messageRef关联,必须引用同一个消息 ID
  • 流程会在消息捕获事件处暂停,直到收到对应的消息才继续
  • 消息是点对点的,通常用于两个特定流程之间的通信
  • 适用于需要异步等待外部系统响应的场景(如支付、审批等)

2.结束事件

2.1 空结束事件(None End Event)

2.1.1 解释

  • 定义:空结束事件是 Flowable 中最常见且最简单的结束事件。它用于标记流程或分支的结束,且不抛出任何特定结果。当流程实例或分支到达空结束事件时,流程引擎会终止该流程实例或分支。如果流程实例有多个并行分支,只有当所有分支都到达空结束事件时,整个流程实例才会结束。
  • 特点
    • 不处理抛出结果,相当于流程或分支的“正常结束”。
    • 适用于流程或分支的常规终止场景,无需额外的操作。
  • 使用场景:通常用于流程的自然结束,例如订单处理完成后结束流程。

2.1.2 图形标记

  • 空结束事件在 BPMN 图中表现为一个粗边圆圈,内部没有图标,表示无特定结果类型。
  • 示例图形(文字描述):end-event(粗边圆圈,无内部图标)。

2.1.3 XML 表示

  • 空结束事件的 XML 定义非常简单,仅包含 <endEvent> 标签,无子元素。
  • 示例代码:

    <endEvent id="endEvent1" name="noneEndEvent"></endEvent>

    • id:事件的唯一标识符。
    • name:事件的显示名称(可选)。

2.1.4 使用示例

  • 场景:一个简单的订单处理流程,包含下单、支付和发货步骤,支付成功后到达空结束事件,流程结束。
  • 流程描述:用户下单后,流程进入支付节点,支付成功后到达空结束事件,流程实例终止。
  • XML 示例
    <process id="simpleOrderProcess" name="简单订单流程" isExecutable="true"><startEvent id="startEvent" /><sequenceFlow id="flow1" sourceRef="startEvent" targetRef="orderTask" /><userTask id="orderTask" name="下单" /><sequenceFlow id="flow2" sourceRef="orderTask" targetRef="paymentTask" /><userTask id="paymentTask" name="支付" /><sequenceFlow id="flow3" sourceRef="paymentTask" targetRef="endEvent" /><endEvent id="endEvent" name="noneEndEvent" /></process>
    • 说明:流程从 startEvent 开始,经过 orderTask(下单)和 paymentTask(支付),最后到达 endEvent,结束流程。

2.2 错误结束事件(Error End Event)

2.2.1 解释

  • 定义:错误结束事件在流程到达时抛出错误,并终止当前流程分支。它通常用于子流程中,抛出的错误会被父流程的错误边界事件捕获。错误结束事件需要定义一个错误码(errorRef),以便与错误边界事件匹配。
  • 特点
    • 抛出特定错误码,触发对应的错误边界事件。
    • 仅适用于子流程,父流程通过错误边界事件处理抛出的错误。
  • 使用场景:常用于异常处理,例如支付失败时触发重新支付的逻辑。

2.2.2 图形标记

  • 错误结束事件表现为一个粗边圆圈,内部包含一个全黑错误图标(类似闪电形状),表示抛出错误。
  • 示例图形(文字描述):end-event(粗边圆圈,内部有全黑错误图标)。

2.2.3 XML 表示

  • 错误结束事件的 XML 定义包含 <endEvent> 标签和 <errorEventDefinition> 子元素,errorRef 属性引用流程中定义的错误。
  • 示例代码:

    <error id="theError" />

    <process id="errorEndEventProcess">

    <endEvent id="myErrorEndEvent">

    <errorEventDefinition errorRef="theError" />

    </endEvent>

    </process>

    • <error>:定义错误,id 为错误标识。
    • <errorEventDefinition>:指定抛出的错误,errorRef 引用错误标识。

2.2.4 使用示例

  • 场景:一个订单流程包含支付子流程。如果支付失败,子流程到达错误结束事件,抛出错误,触发父流程的错误边界事件,重新启动支付子流程;如果支付成功,子流程到达空结束事件,继续到发货节点。
  • 流程描述
    1. 用户下单。
    2. 进入支付子流程。
    3. 支付成功:到达空结束事件,子流程结束,进入发货。
    4. 支付失败:到达错误结束事件,抛出错误,触发错误边界事件,重新进入支付子流程。
  • XML 示例(简化版,基于文档提供的内容):
    <error id="payError" errorCode="payErrorCode" /><process id="ErrorEndEventProcess" name="错误结束事件" isExecutable="true"><startEvent id="startEvent" /><sequenceFlow id="flow1" sourceRef="startEvent" targetRef="orderTask" /><userTask id="orderTask" name="下单" /><sequenceFlow id="flow2" sourceRef="orderTask" targetRef="paymentSubProcess" /><subProcess id="paymentSubProcess" name="付款子流程"><startEvent id="subStartEvent" /><sequenceFlow id="subFlow1" sourceRef="subStartEvent" targetRef="paymentTask" /><userTask id="paymentTask" name="付款" /><sequenceFlow id="subFlow2" sourceRef="paymentTask" targetRef="gateway" /><exclusiveGateway id="gateway" /><sequenceFlow id="successFlow" name="支付成功" sourceRef="gateway" targetRef="noneEndEvent"><conditionExpression xsi:type="tFormalExpression">${payResult == true}</conditionExpression></sequenceFlow><endEvent id="noneEndEvent" /><sequenceFlow id="errorFlow" name="支付失败" sourceRef="gateway" targetRef="errorEndEvent"><conditionExpression xsi:type="tFormalExpression">${payResult == false}</conditionExpression></sequenceFlow><endEvent id="errorEndEvent"><errorEventDefinition errorRef="payError" /></endEvent></subProcess><sequenceFlow id="flow3" sourceRef="paymentSubProcess" targetRef="deliveryTask" /><userTask id="deliveryTask" name="发货" /><sequenceFlow id="flow4" sourceRef="deliveryTask" targetRef="endEvent" /><endEvent id="endEvent" /><boundaryEvent id="errorBoundaryEvent" attachedToRef="paymentSubProcess"><errorEventDefinition errorRef="payError" /></boundaryEvent><sequenceFlow id="retryFlow" name="重新付款" sourceRef="errorBoundaryEvent" targetRef="paymentSubProcess" /></process>
    • 说明:支付子流程 (paymentSubProcess) 中,支付失败时到达 errorEndEvent,抛出 payError 错误,被父流程的 errorBoundaryEvent 捕获,重新流转到 paymentSubProcess。

2.3 取消结束事件(Cancel End Event)

2.3.1 解释

  • 定义:取消结束事件专用于 BPMN 事务子流程(Transaction Subprocess)。当到达取消结束事件时,抛出取消事件,必须由事务子流程上的取消边界事件捕获。捕获后,事务被取消,并触发补偿机制(compensation)来回滚相关操作。
  • 特点
    • 仅在事务子流程中使用。
    • 抛出取消事件,触发取消边界事件和补偿机制。
    • 常与补偿事件一起使用,用于事务回滚场景。
  • 使用场景:适用于需要事务一致性的场景,例如订单取消后需要释放库存或退款。

2.3.2 图形标记

  • 取消结束事件表现为一个粗边圆圈,内部包含一个全黑取消图标(通常为“X”形状)。
  • 示例图形(文字描述):cancel-end-event(粗边圆圈,内部有全黑取消图标)。

2.3.3 XML 表示

  • 取消结束事件的 XML 定义包含 <endEvent> 标签和 <cancelEventDefinition> 子元素。
  • 示例代码:

    <process id="cancelEndEventProcess">

    <endEvent id="cancelEndEvent">

    <cancelEventDefinition />

    </endEvent>

    </process>

    • <cancelEventDefinition>:表示取消事件,无需额外属性。

2.3.4 使用示例

  • 场景:一个系统上线事务子流程,包含人工上线任务。如果上线失败,到达取消结束事件,触发取消边界事件,执行自动回滚补偿,并流转到用户排查任务。
  • 流程图描述(文字说明):
  • 流程启动 → 用户提交订单 → 事务子流程(锁定库存 → 支付)。
  • 支付成功 → 空结束事件 → 发货 → 结束。
  • 用户取消订单 → 取消结束事件 → 触发取消边界事件 → 补偿(释放库存、退款) → 自动取消订单 → 结束。
  • XML 示例
    <process id="CompleteTransactionSubProcess" name="完整事务子流程" isExecutable="true"><!-- 错误定义 --><error id="errorFlag" errorCode="500" /><!-- 主流程 --><startEvent id="startEvent" /><sequenceFlow id="flow1" sourceRef="startEvent" targetRef="submitOrderTask" /><userTask id="submitOrderTask" name="用户提交订单"><extensionElements><flowable:formData /><flowable:assigneeType>static</flowable:assigneeType></extensionElements></userTask><sequenceFlow id="flow2" sourceRef="submitOrderTask" targetRef="transactionSubProcess" /><!-- 事务子流程 --><transaction id="transactionSubProcess" name="订单处理事务"><startEvent id="subStartEvent" /><sequenceFlow id="subFlow1" sourceRef="subStartEvent" targetRef="lockInventoryTask" /><serviceTask id="lockInventoryTask" name="锁定库存" flowable:class="com.example.LockInventoryService"><extensionElements><flowable:formData /><flowable:assigneeType>static</flowable:assigneeType></extensionElements></serviceTask><sequenceFlow id="subFlow2" sourceRef="lockInventoryTask" targetRef="paymentTask" /><userTask id="paymentTask" name="用户支付订单"><extensionElements><flowable:formData /><flowable:assigneeType>static</flowable:assigneeType></extensionElements></userTask><sequenceFlow id="subFlow3" sourceRef="paymentTask" targetRef="gateway" /><exclusiveGateway id="gateway" /><!-- 正常路径:支付成功 --><sequenceFlow id="successFlow" name="支付成功" sourceRef="gateway" targetRef="deductInventoryTask"><conditionExpression xsi:type="tFormalExpression">${payResult == true}</conditionExpression></sequenceFlow><serviceTask id="deductInventoryTask" name="扣减库存" flowable:class="com.example.DeductInventoryService" /><sequenceFlow id="subFlow4" sourceRef="deductInventoryTask" targetRef="noneEndEvent" /><endEvent id="noneEndEvent" /><!-- 取消路径:用户取消订单 --><sequenceFlow id="cancelFlow" name="用户取消" sourceRef="gateway" targetRef="cancelEndEvent"><conditionExpression xsi:type="tFormalExpression">${payResult == false}</conditionExpression></sequenceFlow><endEvent id="cancelEndEvent"><cancelEventDefinition /></endEvent><!-- 补偿任务 --><serviceTask id="releaseInventoryTask" name="释放库存" isForCompensation="true" flowable:class="com.example.ReleaseInventoryService" /><serviceTask id="refundTask" name="费用退回" isForCompensation="true" flowable:class="com.example.RefundService" /><boundaryEvent id="compensateInventoryEvent" attachedToRef="lockInventoryTask"><compensateEventDefinition /></boundaryEvent><boundaryEvent id="compensatePaymentEvent" attachedToRef="paymentTask"><compensateEventDefinition /></boundaryEvent><association id="assoc1" associationDirection="One" sourceRef="compensateInventoryEvent" targetRef="releaseInventoryTask" /><association id="assoc2" associationDirection="One" sourceRef="compensatePaymentEvent" targetRef="refundTask" /></transaction><!-- 取消边界事件 --><boundaryEvent id="cancelBoundaryEvent" attachedToRef="transactionSubProcess"><cancelEventDefinition /></boundaryEvent><sequenceFlow id="flow3" sourceRef="cancelBoundaryEvent" targetRef="cancelOrderTask" /><serviceTask id="cancelOrderTask" name="自动取消订单" flowable:class="com.example.CancelOrderService"><extensionElements><flowable:formData /><flowable:assigneeType>static</flowable:assigneeType></extensionElements></serviceTask><sequenceFlow id="flow4" sourceRef="cancelOrderTask" targetRef="endEventCancel" /><endEvent id="endEventCancel" /><!-- 正常路径继续 --><sequenceFlow id="flow5" sourceRef="transactionSubProcess" targetRef="deliveryTask" /><userTask id="deliveryTask" name="发货"><extensionElements><flowable:formData /><flowable:assigneeType>static</flowable:assigneeType></extensionElements></userTask><sequenceFlow id="flow6" sourceRef="deliveryTask" targetRef="endEventSuccess" /><endEvent id="endEventSuccess" />
    </process>
  • 正常路径
    • 从 subStartEvent 开始,执行 lockInventoryTask(锁定库存)和 paymentTask(支付)。
    • 支付成功(payResult == true),通过 gateway 流向 deductInventoryTask(扣减库存),到达 noneEndEvent,事务子流程结束,进入主流程的 deliveryTask(发货)。
  • 取消路径
    • 用户取消订单(payResult == false),通过 gateway 流向 cancelEndEvent,抛出取消事件。
    • cancelBoundaryEvent 捕获取消事件,触发补偿任务(releaseInventoryTask 和 refundTask),释放库存并退款。
    • 流程流向 cancelOrderTask(自动取消订单),最终到达 endEventCancel。
  • 补偿机制
    • releaseInventoryTask 和 refundTask 标记为 isForCompensation="true",通过 compensateEventDefinition 关联到对应的任务,确保取消时回滚操作。
  • 图示
[主流程]|▼
[Start Event] ----> [UserTask: 用户提交订单]|                    name="submitOrderTask"|                    id="submitOrderTask"▼
[Transaction Subprocess: 订单处理事务] ----> [UserTask: 发货] ----> [End Event]id="transactionSubProcess"                    name="deliveryTask"    id="endEventSuccess"|                                            id="deliveryTask"▼[Start Event] ----> [ServiceTask: 锁定库存] ----> [UserTask: 用户支付订单] ----> [Exclusive Gateway]id="subStartEvent"    name="lockInventoryTask"     name="paymentTask"          id="gateway"id="lockInventoryTask"       id="paymentTask"flowable:class="com.example.LockInventoryService"输出参数: payResult (boolean)|| payResult == true▼[ServiceTask: 扣减库存] ----> [None End Event]name="deductInventoryTask"    id="noneEndEvent"id="deductInventoryTask"flowable:class="com.example.DeductInventoryService"|| payResult == false▼[Cancel End Event] ----> [触发 Cancel Boundary Event]id="cancelEndEvent"        id="cancelBoundaryEvent"<cancelEventDefinition />  attachedTo="transactionSubProcess"|| [Compensate Boundary Event] ----> [ServiceTask: 释放库存]|   id="compensateInventoryEvent"    name="releaseInventoryTask"|   attachedTo="lockInventoryTask"   isForCompensation="true"|                                    flowable:class="com.example.ReleaseInventoryService"|| [Compensate Boundary Event] ----> [ServiceTask: 费用退回]|   id="compensatePaymentEvent"      name="refundTask"|   attachedTo="paymentTask"         isForCompensation="true"|                                    flowable:class="com.example.RefundService"|▼
[Cancel Boundary Event] ----> [ServiceTask: 自动取消订单] ----> [End Event]id="cancelBoundaryEvent"     name="cancelOrderTask"         id="endEventCancel"attachedTo="transactionSubProcess" id="cancelOrderTask"<cancelEventDefinition />    flowable:class="com.example.CancelOrderService"

2.3.5 注意事项

  1. 取消结束事件只能用于事务子流程。
  2. 必须有取消边界事件捕获取消事件。
  3. 取消事件需要配合补偿机制,否则会抛出异常(如 FlowableException: No execution found for sub process of boundary cancel event)。

2.4 终止结束事件(Terminate End Event)

2.4.1 解释

  • 定义:终止结束事件用于强制终止整个流程或其所在的范围(scope,如子流程或主流程)。当流程到达终止结束事件时,流程引擎会终止当前范围内的所有活动,包括并行分支。如果在子流程中,终止范围由 terminateAll 属性决定。
  • 特点
    • 可终止整个流程或仅当前子流程。
    • terminateAll="true":终止整个根流程实例,无论事件位置。
    • terminateAll="false"(默认):仅终止当前子流程或实例。
  • 使用场景:适用于需要立即停止所有流程活动的场景,例如紧急取消订单。

2.4.2 图形标记

  • 终止结束事件表现为一个粗边圆圈,内部包含一个全黑实心圆图标
  • 示例图形(文字描述):terminate-end-event(粗边圆圈,内部有全黑实心圆)。

2.4.3 XML 表示

  • 终止结束事件的 XML 定义包含 <endEvent> 标签和 <terminateEventDefinition> 子元素,flowable:terminateAll 属性控制终止范围。
  • 示例代码:

    <endEvent id="terminateEndEvent">

    <terminateEventDefinition flowable:terminateAll="true" />

    </endEvent>

    • flowable:terminateAll:可选,true 表示终止整个流程,false 表示仅终止当前范围。

2.4.4 使用示例

  • 场景:一个订单流程包含并行分支(支付和库存锁定)。如果用户取消订单,到达终止结束事件,立即终止所有分支,流程结束。
  • 流程描述
    1. 流程启动,进入并行网关,分为支付和库存锁定两个分支。
    2. 用户取消订单,到达终止结束事件,终止所有分支,流程结束。
  • XML 示例
    <process id="terminateEndEventProcess" name="终止结束事件" isExecutable="true"><startEvent id="startEvent" /><sequenceFlow id="flow1" sourceRef="startEvent" targetRef="orderTask" /><userTask id="orderTask" name="下单" /><sequenceFlow id="flow2" sourceRef="orderTask" targetRef="parallelGateway" /><parallelGateway id="parallelGateway" /><sequenceFlow id="flow3" sourceRef="parallelGateway" targetRef="paymentTask" /><userTask id="paymentTask" name="支付" /><sequenceFlow id="flow4" sourceRef="paymentTask" targetRef="noneEndEvent" /><endEvent id="noneEndEvent" /><sequenceFlow id="flow5" sourceRef="parallelGateway" targetRef="lockTask" /><serviceTask id="lockTask" name="锁定库存" flowable:class="com.example.LockService" /><sequenceFlow id="flow6" sourceRef="lockTask" targetRef="cancelTask" /><userTask id="cancelTask" name="取消订单" /><sequenceFlow id="flow7" sourceRef="cancelTask" targetRef="terminateEndEvent" /><endEvent id="terminateEndEvent"><terminateEventDefinition flowable:terminateAll="true" /></endEvent></process>
    • 说明:流程通过 parallelGateway 分出支付和库存锁定分支。如果用户在 cancelTask 取消订单,到达 terminateEndEvent,设置 terminateAll="true",整个流程(包括支付分支)立即终止。

2.4.5 注意事项

  1. terminateAll="true" 会终止整个流程实例,适用于需要强制停止所有活动的场景。
  2. terminateAll="false" 仅终止当前子流程,适合局部终止场景。

总结

  • 空结束事件:最简单的结束事件,用于流程或分支的正常结束,无需抛出结果。
  • 错误结束事件:用于子流程抛出错误,触发父流程的错误边界事件,适合异常处理。
  • 取消结束事件:专用于事务子流程,触发取消边界事件和补偿机制,适合事务回滚。
  • 终止结束事件:强制终止流程或子流程,适合紧急停止场景,范围由 terminateAll 属性控制。
  • 共同点:所有结束事件均为“抛出”事件,区别在于抛出结果的类型和处理方式。
http://www.dtcms.com/a/299840.html

相关文章:

  • 检索召回率优化探究一:基于 LangChain 0.3集成 Milvus 2.5向量数据库构建的智能问答系统
  • 全球化2.0 | 云轴科技ZStack亮相阿里云印尼国有企业CXO专家活动
  • FreeMarker模板引擎
  • Windows Server系统安装JDK,一直卡在“应用程序正在为首次使用作准备,请稍候”
  • Vibe Coding | 技术让我们回归了创造的本质
  • hot100-每日温度
  • 字符串缓冲区和正则表达式
  • I/O 软件层次结构
  • 分布式数据库的分布透明性详解
  • 【前端】Vue 3 课程选择组件开发实战:从设计到实现
  • 如何从自定义或本地仓库安装 VsCode 扩展
  • 手写PPO_clip(FrozenLake环境)
  • 统计学08:概率分布
  • 面试实战,问题十二,Spring Boot接收和处理HTTP请求的详细原理,怎么回答
  • AI 编程工具 Trae 重要的升级。。。
  • 二维数组相关学习
  • 栈----3.字符串解码
  • 论文阅读-RaftStereo
  • 2025中国GEO优化白皮书:AI搜索优化趋势+行业数据报告
  • 应急控制HMI的“黄金10秒”设计:紧急场景下的操作路径极速简化技术
  • 嵌入式硬件篇---有线串口通信问题解决
  • PHP语法高级篇(六):面向对象编程
  • MyBatis-Plus 核心注解详解:从表映射到逻辑删除的全方位指南
  • C++/CLI vs 标准 C++ vs C# 语法对照手册
  • 9.3 快速傅里叶变换
  • 深度解析 noisereduce:开源音频降噪库实践
  • 深入理解Redission释放锁过程
  • Blender入门笔记(一)
  • 利用RAII与析构函数避免C++资源泄漏
  • 基于DataX的数据同步实战