陕西住房建设厅官方网站,系统开发的一般过程,阿里云学生wordpress,怎样查看别人的网站是怎么建设Flowable初体验
Flowable是什么
Flowable 是一个使用 Java 编写的轻量级业务流程引擎#xff0c;常用于需要人工审批相关的业务#xff0c;比如请假、报销、采购等业务。
为什么要使用工作流呢#xff1f; 对于复杂的业务流程#xff0c;通过数据库的状态字段难以控制和…Flowable初体验
Flowable是什么
Flowable 是一个使用 Java 编写的轻量级业务流程引擎常用于需要人工审批相关的业务比如请假、报销、采购等业务。
为什么要使用工作流呢 对于复杂的业务流程通过数据库的状态字段难以控制和维护工作流引擎则更易于维护和拓展 工作流的流程图更加直观流程走到了哪里一目了然
Flowable初体验
官网例子Getting Started · Flowable Open Source Documentation
照着官网的例子做即可都有详细的步骤。
SpringBoot整合Flowable
添加maven依赖
新建maven工程pom.xml引入如下依赖 dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactId/dependencydependencygroupIdorg.flowable/groupIdartifactIdflowable-spring-boot-starter/artifactIdversion6.7.2/version/dependency/dependenciesflowable默认使用H2数据库这是一种内存型数据库我们需要将数据持久化这里使用的MySQLSpringBoot版本则是使用2.7.5
配置信息
由于使用了flowable的starter先看在spring.factories配置了哪些自动装配类
可以看到这里有个自动装配类org.flowable.spring.boot.ProcessEngineAutoConfiguration 这里有很多Properties结尾的类都是可以自定义的属性以后再详细看。先看FlowableProperties这个类
ConfigurationProperties(prefix flowable
)
public class FlowableProperties {private boolean checkProcessDefinitions true;private boolean asyncExecutorActivate true;private boolean asyncHistoryExecutorActivate true;private boolean restApiEnabled;private String activityFontName Arial;private String labelFontName Arial;private String annotationFontName Arial;private String deploymentName SpringBootAutoDeployment;private String databaseSchemaUpdate true;private String databaseSchema;private boolean useLockForDatabaseSchemaUpdate false;/** deprecated */Deprecatedprivate boolean isDbIdentityUsed true;private boolean isDbHistoryUsed true;private HistoryLevel historyLevel;private String processDefinitionLocationPrefix;private ListString processDefinitionLocationSuffixes;private boolean jpaEnabled;private ListString customMybatisMappers;private ListString customMybatisXMLMappers;protected boolean formFieldValidationEnabled;private Duration lockPollRate;private Duration schemaLockWaitTime;private boolean enableHistoryCleaning;private String historyCleaningCycle;DurationUnit(ChronoUnit.DAYS)private Duration historyCleaningAfter;private int historyCleaningBatchSize;private boolean historyCleaningSequential;public FlowableProperties() {this.historyLevel HistoryLevel.AUDIT;this.processDefinitionLocationPrefix classpath*:/processes/;this.processDefinitionLocationSuffixes Arrays.asList(**.bpmn20.xml, **.bpmn);this.jpaEnabled true;this.formFieldValidationEnabled false;this.lockPollRate Duration.ofSeconds(10L);this.schemaLockWaitTime Duration.ofMinutes(5L);this.enableHistoryCleaning false;this.historyCleaningCycle 0 0 1 * * ?;this.historyCleaningAfter Duration.ofDays(365L);this.historyCleaningBatchSize 100;this.historyCleaningSequential false;}
}这里有很多属性先挑几个看一下 checkProcessDefinitions表示是否检查classpath*:/processes/目录下的流程定义。默认值为true表示要检查且会自动部署流程。若设置为false则不会检查也就相当于关闭了自动部署流程的功能。 asyncExecutorActivate异步执行器是否开启默认值为true用于开启异步任务 processDefinitionLocationPrefix流程定义所在位置的前缀默认值为processDefinitionLocationPrefix可以自定义为自己喜欢的目录。 processDefinitionLocationSuffixes流程定义文件名后缀默认支持.bpmn20.xml, .bpmn文件。
在application.yml文件中添加配置信息如果不添加就使用默认配置
server:port: 8888spring:datasource:username: rootpassword: root# nullCatalogMeansCurrenttrue加了这个参数才会自动生成表url: jdbc:mysql://localhost:3306/flowable_study?serverTimezoneAsia/ShanghaiuseSSLfalsenullCatalogMeansCurrenttrue# flowable打印sql语句
logging:level:org.flowable.engine.impl.persistence.entity.*: debugorg.flowable.task.service.impl.persistence.entity.*: debug打印flowable的SQL语句是为了方便分析调用的api底层做了什么。
表结构简单分析
启动SpringBoot项目则会在数据库flowable_study中自动生成79张表
其中
APP 表示这都是跟应用程序相关的表。CMMN 表示这都是跟 CMMN 协议相关的表。COCONTENT表示这都是跟内容引擎相关的表。DMN 表示这都是跟 DMN 协议相关的表。EVTEVENT表示这都是跟事件相关的表。FOFORM表示这都是跟表单相关的表。GEGENERAL表示这都是通用表适用于各种用例的。HIHISTORY这些是包含历史数据的表。当从运行时表中删除数据时历史表仍然包含这些已完成实例的所有信息。IDIDENTITY表示这都是跟用户身份认证相关的表。PROCDEFPROCESSDEFINE 表示这都是跟记录流程定义相关的表。REREPOSITORY表示这都是跟流程的定义、流程的资源等等包含了静态信息相关的表。RURUNTIME代表运行时这些是包含尚未完成的流程、案例等的运行时数据的运行时表。Flowable 仅在执行期间存储运行时数据并在实例结束后删除记录这使运行时表保持小而快。CHANNEL 表示这都是跟泳道相关的表。EV 表示这个是跟 FLW_ 搭配的在这里似乎并没有一个明确的含义相关的表也都是跟 Liquibase 相关的。EVENT 表示这都是跟事件相关的表。
暂时需要关注GE、RE、ID、RU、HI这5类相关的表。
流程部署
自动部署
根据配置信息部分的介绍可以知道flowable集成到SpringBoot后SpringBoot程序启动时就会扫描resources/processes目录下后缀为.bpmn20.xml, .bpmn的流程文件并且会自动部署。
手动部署
自动部署不方便对流程进行修改一旦修改要重启应用才会生效很麻烦而手动部署则可以解决此问题。手动部署又分为两种情况 直接上传流程文件进行部署 通过前端流程设计流程模型拿到流程资源进行部署
上传流程文件
这个主要是写接口便于通过接口进行部署更新 public String deployProcess(MultipartFile file){if (file null){return 流程部署文件不能为空;}String deploymentId null;try {Deployment deploy repositoryService.createDeployment().addInputStream(file.getOriginalFilename(), file.getInputStream()).deploy();deploymentId deploy.getId();} catch (IOException e) {System.out.println(流程部署失败);return 流程部署失败: e.getMessage();}return deploymentId;}流程模型部署
对于当前前后端分离的开发模式一般是前端通过模型设计器设计好流程模型后再进行部署 /*** 流程部署* param modelId 模型id* return*/PostMapping(value /deploy/{modelId})public Result deploy(PathVariable String modelId){Model model repositoryService.getModel(modelId);// 获取model的xml内容byte[] source repositoryService.getModelEditorSource(modelId);// 部署流程Deployment deployment repositoryService.createDeployment().name(model.getName()).key(model.getKey()).category(model.getCategory()).addBytes(model.getName() .bpmn, source).deploy();return Result.ok(操作成功,deployment.getId());}这里的思路就是获取模型的流程图二进制资源进行部署更新。
流程实例
流程定义部署之后需要创建流程实例才算是真正的发起了一个流程。流程定义和流程实例可以类比类和对象的关系流程定义可以看作是类是对象的模板而流程实例可以看作是对象。启动流程实例的几种方式 org.flowable.engine.FormService#submitStartFormData org.flowable.engine.RuntimeService#startProcessInstanceByKey org.flowable.engine.RuntimeService#startProcessInstanceByKeyAndTenantId org.flowable.engine.RuntimeService#startProcessInstanceById org.flowable.engine.RuntimeService#startProcessInstanceWithForm org.flowable.engine.RuntimeService#startProcessInstanceByMessage org.flowable.engine.RuntimeService#startProcessInstanceByMessageAndTenantId
最常用的是org.flowable.engine.RuntimeService#startProcessInstanceByKey这里的key指的是流程xml文件里面的id
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qpcQncZM-1683429303847)(file://F:\学习笔记\工作流相关\images\流程定义key.png?msec1683427681427)]
通过如下方法启动流程 流程启动后便可以拿到流程实例。
流程变量
流程变量分为以下三类 全局变量会存入数据库整个流程实例过程中都可以使用 局部变量会存入数据库针对某个Execution或Task有效其他节点不可见 瞬时变量不存入数据库目前我没用过
在流程实例没有结束之前变量会被存入到表act_ru_variable、act_hi_varinst中流程结束后只保留act_hi_varinst表中的记录每个变量占一条记录。
下面主要看下对于全局变量和局部变量要怎样设置和使用
全局变量
启动时设置
通过org.flowable.engine.RuntimeService#startProcessInstanceByXXX方法启动流程时有一个可选参数用于在流程实例创建及启动时设置变量如 运行时设置 org.flowable.engine.RuntimeService#setVariable org.flowable.engine.TaskService#setVariable
局部变量
局部变量在运行时设置针对某个Execution或Task有效其他节点不可见 org.flowable.engine.RuntimeService#setVariablesLocal org.flowable.engine.TaskService#setVariablesLocal
变量的使用
流程变量常用于 条件判断如排他网关出线中使用变量判断流程应该走哪条分支 审批人组的占位通过变量可以达到动态设置审批人的效果 事件监听器使用Bean的名字 多实例使用变量或变量表达式
参考官网The Flowable API · Flowable Open Source Documentation
流程任务
流程任务包括系统服务任务、用户任务、脚本任务等这里针对的是用户任务。
待签收任务
待签收的任务就是待认领的任务 一个任务发给不同的人处理任何一个人都可以抢办任务即个人待签收任务 一个任务发给同一个岗位处理的只需要是这个岗位的其中一人处理即可这种情况就是岗位待签收的任务
个人待签收
个人待签收任务即当前用户可以认领的任务认领签收完成之后就变成了个人的待办任务了。查询个人待签收任务的方法如下
taskService.createTaskQuery().taskCandidateUser(userId).list()userId就是要查询的用户id或者其他唯一标识符一般通过id或者工号来标识。
如果想自定义sql查询也是可以的
SELECTRES.*
FROMACT_RU_TASK RES
WHERERES.ASSIGNEE_ IS NULL
AND EXISTS (SELECTLINK.ID_FROMACT_RU_IDENTITYLINK LINKWHERELINK.TYPE_ candidateAND LINK.TASK_ID_ RES.ID_AND (LINK.USER_ID_ ?)
)api接口执行的结果就是执行了上面的sql
岗位待签收
岗位对于系统来说可以认为是角色也就是说你可以认领这个角色下的任务变成自己的待办任务通过api接口查询
taskService.createTaskQuery().taskCandidateGroupIn(Arrays.asList(roleArr)).list();roleArr就是用户的角色id数组。
也可以通过sql查询
SELECTRES.*
FROMACT_RU_TASK RES
WHERERES.ASSIGNEE_ IS NULL
AND EXISTS (SELECTLINK.ID_FROMACT_RU_IDENTITYLINK LINKWHERELINK.TYPE_ candidateAND LINK.TASK_ID_ RES.ID_AND ((LINK.GROUP_ID_ IN(?, ?, ?)))
)将个人待签收任务和岗位待签收任务合并就是所有的待签收任务了。
可以通过api实现也可以通过sql自己查询
api查询
taskService.createTaskQuery().or().taskCandidateUser(userId).taskCandidateGroupIn(Arrays.asList(roleArr)).list()sql查询
SELECTRES.*
FROMACT_RU_TASK RES
WHERERES.ASSIGNEE_ IS NULL
AND EXISTS (SELECTLINK.ID_FROMACT_RU_IDENTITYLINK LINKWHERELINK.TYPE_ candidateAND LINK.TASK_ID_ RES.ID_AND ((LINK.USER_ID_ ?) OR (LINK.GROUP_ID_ IN(?, ?, ?)))
)签收任务
签收任务就是认领任务将待签收任务变成待办任务
taskService.claim(taskId,userId);也可以使用
taskService.setAssignee(taskId,userId);两者的区别就是如果该任务已经有人认领了 taskService.claim会抛出异常认领失败 taskService.setAssignee 不会抛出异常userId成为新的任务办理人
退回已签收
当你后悔签收了任务且没有处理任务之前是可以退回已经签收的任务的
设置任务办理人为空即可
taskService.setAssignee(taskId, null);这样就能将已签收的任务退回其他人可进行签收处理。
待办任务
查询待办
待办任务即流程的办理的人的任务。
也是两种方式
通过api查询
taskService.createTaskQuery().taskAssignee(userId).list()通过sql查询
SELECTRES.*
FROMACT_RU_TASK RES
WHERERES.ASSIGNEE_ ?完成待办
完成待办就是完成自己的任务驱动流程走向下一个节点
taskService.complete(taskId,variables);taskId就是当前要完成的任务id variables就是流程变量类型是MapString, Object一般需要传递一些变量比如采购金额是多少审批是否通过设置下一个节点的审批人等信息
已办任务
查询已办
已办任务一般通过查询历史数据来得到的可以通过HistoryService来查询
historyService.createHistoricTaskInstanceQuery().finished().taskAssignee(userId).list()当然也可以自己写sql查询
SELECTRES.*
FROMACT_HI_TASKINST RES
WHERERES.ASSIGNEE_ ?
AND RES.END_TIME_ IS NOT NULL这里的关键就是END_TIME_ 不为 NULL
任务转办与委派
任务转办
任务的转办就是将任务交给另外一个人全权处理
Task task taskService.createTaskQuery().taskId(taskId).singleResult();
// 设置原办理人是任务的委派人
taskService.setOwner(taskId,task.getAssignee());
// 设置任务新的办理者
taskService.setAssignee(taskId, targetUserId);收到转办任务的人通过下面的方式就可以完成任务
taskService.complete(taskId)任务委派
委派就是给任务设置代理人让他帮忙解决问题
Task task taskService.createTaskQuery().taskId(taskId).singleResult();
// 设置原办理人是任务的所有者
taskService.setOwner(taskId,task.getAssignee());
// 设置任务代理人
taskService.delegateTask(taskId,targetUserId);代理人解决任务而不是完成任务
taskService.resolveTask(taskId)转办与委派的区别 任务委派只是委派人将当前的任务交给被委派人进行审批解决 任务后又重新回到委派人身上。 为什么是解决呢?而不是完成是因为当被委派人直接完成任务时任务就不会回到委派人。 任务转办就是把任务全权交给转办的人处理不会回到原办理人。
参考链接工作流操作-委派、转办_asarao的博客-CSDN博客
多实例任务
多实例任务是指将任务分配给多个人处理当满足给定的条件时流程才会到下一个节点。根据处理任务顺序的差异可分为 串行多实例当上一个人处理了任务下一个人才会接收到待办任务。例如某个审批节点需要A、B、C三个人审批可能的顺序是A审批完成后B才能接收到审批任务B审批完成后C才会接收到任务进行审批依次进行。 并行多实例审批人可以同时进行审批这就是并行。例如某个审批节点需要A、B、C三个人审批则A、B、C可以同时审批不需要等其他人审批完成就可以审批。
配置参数 collection集合 传入List参数一般为用户ID集合 elementVariable元素变量List中单个参数的名称自定义变量名称 loopCardinality基数循环次数 isSequential是否串行 completionCondition完成条件任务出口条件 nrOfInstances实例总数 nrOfCompletedInstances已完成的实例个数 loopCounter已经循环的次数 nrOfActiveInstances未完成的实例个数
串行多实例
在流程设计里将isSequential设置为true即可
multiInstanceLoopCharacteristics isSequentialtrue...
/multiInstanceLoopCharacteristics图形上显示就就是三条水平的横线 并行多实例
在流程设计里将isSequential设置为false即可
multiInstanceLoopCharacteristics isSequentialfalse...
/multiInstanceLoopCharacteristics图形上显示就就是三条垂直的横线