审批流程图

        如下图,在此流程图中,存在两个UserTask节点,第一个节点是主管审批,第二个节点是产品经理审批,两个节点中间有一个排他网关,此网关用来对主管审批的结果进行判断,如果主管审批通过,则流程走到产品经理审批节点,如果主管审批拒绝,则流程走到结束节点。

        主管审批节点通过UEL表达式${assignManager}动态赋值,产品经理审批节点通过UEL表达式${assignProductLineManager}动态赋值,网关节点通过UEL表达式${isPass}动态赋值。

节点行为

        上文提到,流程流转到 UserTask 节点后,准备执行该节点上的行为,该节点的行为对应的类是 UserTaskActivityBehavior,入口为 executeActivityBehavior(ActivityBehavior, FlowNode)。

/*** 同步执行* @param flowNode*/
protected void executeSynchronous(FlowNode flowNode) {// 省略部分代码// 执行实际的行为,UserTask对应的behavior是UserTaskActivityBehavior,// 这个UserTaskActivityBehavior里面会把task写入到act_ru_taskActivityBehavior activityBehavior = (ActivityBehavior) flowNode.getBehavior();// 当 activityBehavior 不为空,走此方法,此方法后续也会调用 planTakeOutgoingSequenceFlowsOperation// activityBehavior 表示一个节点上拥有的行为// StartEvent 节点的行为是 NoneStartEventActivityBehavior,没有做其它业务,仅仅是过度// UserTask   节点的行为是 UserTaskActivityBehavior,这个行为会把任务写入到数据库后,等待用户完成任务,流程才会继续走下去if (activityBehavior != null) {executeActivityBehavior(activityBehavior, flowNode);} else {logger.debug("No activityBehavior on activity '{}' with execution {}", flowNode.getId(), execution.getId());// 计划执行 TakeOutgoingSequenceFlows 操作,这个操作是一个连线行为,第一步先找出当前节点的出口,第二步从出口走到下一个节点。Context.getAgenda().planTakeOutgoingSequenceFlowsOperation(execution, true);}
}

        ContinueProcessOperation 中的 executeActivityBehavior(ActivityBehavior, FlowNode) 方法实现如下,这里参数 activityBehavior 的值是 UserTaskActivityBehavior 对象的实例。

/*** 执行节点上的行为* @param activityBehavior* @param flowNode*/
protected void executeActivityBehavior(ActivityBehavior activityBehavior,FlowNode flowNode) {logger.debug("Executing activityBehavior {} on activity '{}' with execution {}",activityBehavior.getClass(),flowNode.getId(),execution.getId());if (Context.getProcessEngineConfiguration() != null && Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(ActivitiEventBuilder.createActivityEvent(ActivitiEventType.ACTIVITY_STARTED,flowNode.getId(),flowNode.getName(),execution.getId(),execution.getProcessInstanceId(),execution.getProcessDefinitionId(),flowNode));}try {// 这方法里面后续会执行 planTakeOutgoingSequenceFlowsOperationactivityBehavior.execute(execution);} catch (RuntimeException e) {if (LogMDC.isMDCEnabled()) {LogMDC.putMDCExecution(execution);}throw e;}
}

        UserTaskActivityBehavior 中的 execute(DelegateExecution) 方法实现如下:

public class UserTaskActivityBehavior extends TaskActivityBehavior {// 省略部分代码protected UserTask userTask;// 在部署流程的时候,会把 bpmn20.xml 上解析到的 userTask 传入构造函数中public UserTaskActivityBehavior(UserTask userTask) {this.userTask = userTask;}public void execute(DelegateExecution execution) {CommandContext commandContext = Context.getCommandContext();TaskEntityManager taskEntityManager = commandContext.getTaskEntityManager();// 创建任务,这个 TaskEntity 将会存储在表 act_ru_task 中TaskEntity task = taskEntityManager.create();task.setExecution((ExecutionEntity) execution);task.setTaskDefinitionKey(userTask.getId());// 声明变量,用于临时存储 bpmn20.xml 上 userTask 定义的变量信息,见下面的 图 1.0String activeTaskName = null;String activeTaskDescription = null;String activeTaskDueDate = null;String activeTaskPriority = null;String activeTaskCategory = null;String activeTaskFormKey = null;String activeTaskSkipExpression = null;String activeTaskAssignee = null;String activeTaskOwner = null;List<String> activeTaskCandidateUsers = null;List<String> activeTaskCandidateGroups = null;ProcessEngineConfigurationImpl processEngineConfiguration = Context.getProcessEngineConfiguration();ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();if (Context.getProcessEngineConfiguration().isEnableProcessDefinitionInfoCache()) {// 省去部分代码          } else {// 这里的 userTask 解析自 bpmn20.xml 文件中定义的 UseTask 节点activeTaskName = userTask.getName();activeTaskDescription = userTask.getDocumentation();activeTaskDueDate = userTask.getDueDate();activeTaskPriority = userTask.getPriority();activeTaskCategory = userTask.getCategory();activeTaskFormKey = userTask.getFormKey();activeTaskSkipExpression = userTask.getSkipExpression();activeTaskAssignee = userTask.getAssignee();activeTaskOwner = userTask.getOwner();activeTaskCandidateUsers = userTask.getCandidateUsers();activeTaskCandidateGroups = userTask.getCandidateGroups();}// 省去部分代码  /*** 任务写入 act_ru_task 表中,但只是暂时存到缓存中,后面才会写入数据库*/taskEntityManager.insert(task, (ExecutionEntity) execution);// 省去部分代码  boolean skipUserTask = false;if (StringUtils.isNotEmpty(activeTaskSkipExpression)) {Expression skipExpression = expressionManager.createExpression(activeTaskSkipExpression);skipUserTask = SkipExpressionUtil.isSkipExpressionEnabled(execution, skipExpression) && SkipExpressionUtil.shouldSkipFlowElement(execution, skipExpression);}// Handling assignments need to be done after the task is inserted, to have an idif (!skipUserTask) {// 处理UserTask节点中设置的UEL变量表达式,Assignee、CandidateUsers、CandidateGroups都是在这里解析handleAssignments(taskEntityManager, activeTaskAssignee, activeTaskOwner, activeTaskCandidateUsers, activeTaskCandidateGroups, task, expressionManager, execution);}// 如果有监听 UserTask 创建事件的监听器,就执行该监听器processEngineConfiguration.getListenerNotificationHelper().executeTaskListeners(task, TaskListener.EVENTNAME_CREATE);// 省去部分代码  // 如果 skipUserTask 是true,说明任务不会在UserTask节点等待用户审批,而是直接就走向下个节点// 没有设置跳过 UserTask 节点,所以 skipUserTask == false,不会走下面这个逻辑if (skipUserTask) {taskEntityManager.deleteTask(task, null, false, false);leave(execution);}}    
}

图 1.0

总结

        UserTask 节点上的 UserTaskActivityBehavior 行为,主要工作是解析 bpmn20.xml 流程文件上定义的 UserTask 节点变量信息,得到变量信息后,从 ExecutionEntity 上匹配变量的值,把匹配到的值设置到 TaskEntity 中。将 TaskEntity 存储入库,得益于未设置 skipExpression(跳过表达式),使得流程从 StartEvent 开始后流转到了 UserTask 节点,并在 UserTask 节点上暂时停了下来等待,直到用户完成审批,流程才会继续往下走。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/922352.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/922352.shtml
英文地址,请注明出处:http://en.pswp.cn/news/922352.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度卷积生成对抗网络详解与实现

深度卷积生成对抗网络详解与实现 0. 前言 1. 网络架构 1.1 批归一化 1.2 激活 1.3 上采样 2. 构建 DCGAN 2.1 生成器 2.2 判别器 2.3 训练 DCGAN 0. 前言 深度卷积生成对抗网络 (Deep Convolutional Generative Adversarial Network, DCGAN) 是基于生成对抗网络 (Generative A…

CF607B Zuma -提高+/省选-

CF607B Zuma codeforces 原链接 题目描述 Genos\texttt{Genos}Genos 最近在他的手机上下载了祖玛游戏。在祖玛游戏里&#xff0c;存在 nnn 个一行的宝石&#xff0c;第 iii 个宝石的颜色是 CiC_iCi​。这个游戏的目标是尽快的消灭一行中所有的宝石。 在一秒钟&#xff0c;Ge…

拆分了解HashMap的数据结构

文章目录 前言 一、底层数据结构总览 二、核心组成部分详解 1. 数组&#xff08;哈希表&#xff09; 2. 节点&#xff08;Node&#xff09; 3. 红黑树&#xff08;TreeNode&#xff09; 三、哈希函数与索引计算 四、哈希冲突的解决 五、扩容机制 六、关键特性与注意事…

关于电脑连接不到5g的WiFi时的一些解决办法

方法一、设备管理器重卸载驱动后&#xff0c;重装驱动。方法二、打开控制面板 “控制面板\网络和 Internet\网络连接” &#xff08;亲测有效&#xff09;点击更改适配器配置右击当前的WLAN属性点击配置选择“高级” 802.11a/b/g 无线模式选项栏 值&#xff1a;6.的双…

Mathtype公式批量编号一键设置公式居中编号右对齐

插件[ygtools] 批量编号一键设置公式居中编号右对齐 单栏/多栏均可https://wwon.lanzout.com/i0NRf35vyw8j 下载密码8543

基于ssm的小橘子出行客户体验评价系统[SSM]-计算机毕业设计源码+LW文档

摘要&#xff1a;随着出行行业的快速发展&#xff0c;客户体验评价对于出行服务质量的提升至关重要。本文设计并实现了基于SSM&#xff08;Spring Spring MVC MyBatis&#xff09;框架的小橘子出行客户体验评价系统。该系统涵盖系统用户管理、司机信息管理、客户评价管理等功…

算法日记---二分查找

目录 前言 一、二分查找 1.思想 2.简单二分 3.优点 4.局限性 二、模板 1.基本模板 2.简单例题&#xff08;LeetCode&#xff09; 4.有重复元素的二分 5.0-1问题 总结 前言 本文通过讲解简单的二分查找配合leetcode例题对二分查找本质、模板进行了基础的总结 提示&a…

Level Set(水平集)算法——形象化讲解

目录 维度一&#xff1a;核心思想与比喻&#xff08;它像什么&#xff1f;&#xff09; 维度二&#xff1a;要解决什么问题&#xff1f;&#xff08;它能干嘛&#xff1f;有什么用&#xff1f;&#xff09; 维度三&#xff1a;工作原理&#xff08;它是怎么做到的&#xff1…

DDoS 攻防“军备竞赛”的幕后

谈到 DDoS&#xff08;分布式拒绝服务攻击&#xff09;&#xff0c;很多人会想到“黑客租用肉鸡发流量&#xff0c;网站直接崩”。但事实上&#xff0c;如今的 DDoS 攻防早已变成一场 军备竞赛。攻击者的武器越来越“工业化”&#xff1a;僵尸网络商品化&#xff1a;黑市上&…

如何用 Rust 重写 SQLite 数据库(二):是否有市场空间?

用 Rust 实现一个类似 SQLite 的嵌入式数据库非常有意义&#xff0c;但需要结合具体目标和场景来评估其价值。以下从技术、生态、市场需求和个人成长等多个维度展开分析&#xff0c;并给出结论。一、技术价值&#xff1a;Rust 与数据库的天然契合 SQLite 作为全球装机量最大的数…

【Web】ImaginaryCTF 2025 wp

目录 imaginary-notes certificate codenames-1 passwordless pearl imaginary-notes I made a new note taking app using Supabase! Its so secure, I put my flag as the password to the "admin" account. I even put my anonymous key somewhere in the si…

oracel如何找到外键子表

要找到导致外键约束冲突的子表&#xff08;即包含"child record"的表&#xff09;&#xff0c;可以通过以下SQL查询在Oracle数据库中定位&#xff1a;1. 查询约束基本信息&#xff08;确定父表和子表&#xff09;SELECT owner, constraint_name, table_name AS child…

智源研究院新研究:突破物理世界智能边界的RoboBrain 2.0,将重构具身AI能力天花板

当你对着家用机器人说"把杯子放在笔筒和键盘之间&#xff0c;对齐杯身logo"时&#xff0c;它能精准理解空间关系并执行动作&#xff1b;当多台机器人在超市协作补货时&#xff0c;它们能自主规划轨迹、避免冲突并完成长周期任务——这些曾经出现在科幻电影中的场景&a…

【2025】Office核心组件Microsoft word,Excel,PowerPoint详细使用指南

Office 核心组件使用指南 Microsoft Word 文字处理 Word主要用于创建和编辑文档&#xff0c;如信件、报告、论文等。 2025Office&#x1f517; 1. 界面认识 快速访问工具栏&#xff1a;位于左上角&#xff0c;可自定义保存、撤销、恢复等常用命令。功能区&#xff1a;顶部…

【模型训练篇】VeRL的使用 - RL(PPO)与源码

继续学习字节家的VeRL&#xff0c;今天来看看VeRL的RL&#xff0c;是VeRL系列的第三篇文章&#xff08;话说近期好多大事儿&#xff0c;我司发布了Longcat、韩立结婴、阿里周五发布了QWen-Next都是好东西啊&#xff0c;学不过来了damn&#xff09; 底层分布式能力基础Ray&…

QML Charts组件之折线图的鼠标交互

目录前言相关系列代码示例详解&#xff08;LineSeriesDemo3.qml&#xff09;功能概览运行效果代码说明工程下载参考前言 接上文&#xff08;QML Charts组件之折线图的基础属性&#xff09;&#xff0c;本文将重点介绍LineSeries的鼠标交互&#xff0c;包括&#xff1a;鼠标拖拽…

二值信号量——学习笔记12

本文是笔者在学习 正点原子官方 的《【正点原子】手把手教你学FreeRTOS实时系统》系列视频时整理的笔记。 视频讲解清晰透彻&#xff0c;非常感谢UP主的无私奉献&#xff01;原课程链接如下&#xff1a; &#x1f449; B站视频链接&#xff1a;​​​​​​【正点原子】手把手教…

裸机开发 时钟配置,EPIT

1.概念时钟(clock)&#xff1a;在电子系统中是一个产生稳定、周期性振荡信号的电路或组件。这个信号像节拍器或心跳一样&#xff0c;为数字电路中的各种操作提供同步时序基准。PLL&#xff08;phase locked loop&#xff09;锁相环电路: 倍频PFD&#xff08;phase fractional P…

Linux-文本三剑客(grep、sed、awk)

Linux-文本三剑客前言一、grep二、sed三、awk模式 -- 正则表达式关系表达式、运算符表达模式匹配表达式动作 输出流程控制参数传递&#xff0c;awk接受外部变量统计数组的使用分组统计练习常用内置函数前言 grep、sed、awk 被称为 “文本三剑客”&#xff0c;它们是处理文本文…

主流反爬虫、反作弊防护与风控对抗手段

文章目录1. 写在前面2. 指纹检测3. 行为验证3. 加固防护4. 链路检测5. 风控埋点6. 游客注册7. 数据防护8. 账号权重9. 反调阻断【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、…