博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
初玩Activiti7一套组合拳,连连败退之下竟突破神功,且看SpringBoot整治Activiti7小老弟!
阅读量:3906 次
发布时间:2019-05-23

本文共 19645 字,大约阅读时间需要 65 分钟。

15.SpringBoot-Activiti技术

1.1 技术介绍

​ Activiti 7是Alfresco经过实战考验的Activiti工作流引擎的演变,完全被采用在云环境中运行。它是根据 Cloud Native 应用程序概念构建的,与之前的Activiti版本在架构方面有所不同。

​ 基于我的理解,帮助开发人员进行流程控制,而工作流的概念就是一个制度的体现,管理体系的体现。

​ 举例来讲:小到家庭琐事,现在有个老李想和朋友,可是他身上没有钱,他需要得到老婆的同意,然后再由老婆拨款,才能出门和朋友吃饭。

​ 更一步案例,现在老李正在读小学,马上要考试了,可是他今天生病了,需要请假,但是这次考试又很重要,他不想放弃。这时就是学校的管理制度起作用了,老师不仅同意了老李的病假,还为老李破例开通第二套试卷考试,老李在休息得当的情况下,顺利的完成了考试,考出高分。

​ 而上升到公司,行业管理等等,其实也是人与人之间的确认信息。

· 较多使用场合

​ 订单、报价处理、合同审核、客户电话处理、供应链管理。

​ 土地开发审核、资源开发审核、人力资源、办公软件之类。

1.2 技术注意点

1.2.1 流程图
  1. 流程图必须有一个或多个默认流
  2. 流程判断必须带有参数条件
  3. 流程图的权限控制:assignee(不包括)、candidateUsers(哪些用户)、candidateGroups(哪些角色)
1.2.2 流程图ID

​ 这个一定要修改,不能出现同名,否则就会出现这个错误。

The deployment contains process definitions with the same key (process id attribute), this is not…

https://blog.csdn.net/qq_41520636/article/details/118304495

1.3 技术具体demo文件路径

1.4 demo的UML图

1.5 demo的技术代码实现

1.5.1 环境准备

​ SpringBoot:2.0.4.RELEASE

​ JDK:1.8

​ Activiti:7.1.0.M2

​ MySQL:8.0.21

​ 开发软件:IDEA

​ 流程插件:actiBPM

1.5.2 安装插件

​ Files → Settings → Plugins → 搜索actiBPM → 下载安装 → 搜索JBoss jBPM → 下载安装

1.5.3 插件试玩

在这里插入图片描述

创建bpnmFile文件

在这里插入图片描述
在这里插入图片描述

创建好后,可以通过修改文件后缀名查看里面的代码

在这里插入图片描述
打开team01.xml

将xml更改为png格式

在这里插入图片描述
在这里插入图片描述

1.5.4 创建activiti特有数据库

​ 因为利用的是springboot,就不用使用xml配置,还要代码注入后,才能启动。

​ 启动流程

yml配置 → ProcessEngine(流程引擎)→ RuntimeService(流程运行管理类)

​ → TaskService(流程运行管理类)

​ → RepositoryService(资源管理类)

​ → HistoryService(历史管理类)

​ → ManagementService(引擎管理类)

​ 在此之前,你需要先知道Activiti7属于高版本,已经舍弃掉了三张表,这三张就是权限管理,由于过于简单舍弃,选择Spring Security来控制权限。

​ 你必须在你的本地创建一个activiti的数据库,来存放新增的表。

实战例子

1.创建pom
org.springframework.boot
spring-boot-starter-parent
2.0.4.RELEASE
UTF-8
UTF-8
1.8
8.0.21
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-jdbc
org.activiti
activiti-spring-boot-starter
7.1.0.M2
org.springframework.boot
spring-boot-starter-test
test
mysql
mysql-connector-java
${mysql.version}
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-thymeleaf
org.projectlombok
lombok
RELEASE
compile
my-springboot-activiti-demo
org.springframework.boot
spring-boot-maven-plugin
2.准备application.yml
spring:  datasource:    url: jdbc:mysql://localhost:3306/activiti    username: root    password: 123    hikari:      data-source-properties:        useSSL: false        serverTimezone: GMT+8        useUnicode: true        characterEncoding: utf8        # 这个必须要加,否则 Activiti 自动建表会失败        nullCatalogMeansCurrent: true  activiti:    # 保存历史数据级别设置为full最高级别,便于历史数据的追溯    history-level: full    db-history-used: true    # 不启动检查activiti数据库版本是否匹配,提升应用启动效率    database-schema-update: false    #自动检查、部署流程定义文件    check-process-definitions: true    #流程定义文件存放目录,要具体到某个目录    process-definition-location-prefix: classpath:/processes/    # process-definition-location-suffixes: #流程文件格式    #  - **.bpmn20.xml    #  - **.bpmn
3.创建Spring Security的授权用户
@Slf4j@Configuration@EnableWebSecuritypublic class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()); } @Override protected void configure(HttpSecurity http) throws Exception {
http .csrf().disable() .authorizeRequests() .anyRequest() .authenticated() .and() .httpBasic(); } /** * spring security 高版本自带 * @return */ @Bean protected UserDetailsService myUserDetailsService() {
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager(); // 四个普通用户,一个管理员 String[][] usersGroupsAndRoles = {
{
"salaboy", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {
"ryandawsonuk", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {
"erdemedeiros", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"}, {
"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"}, {
"admin", "password", "ROLE_ACTIVITI_ADMIN"}}; for (String[] user : usersGroupsAndRoles) {
List
authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length)); log.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]"); inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]), authoritiesStrings.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()))); } return inMemoryUserDetailsManager; } @Bean public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); }}
4.创建一个授权认证

注意:这只是测试环境,真是环境并不是怎么操作,还是需要用户正常登录授权,这里只是写了死值,方便测试。

@Component@RequiredArgsConstructor(onConstructor_ = @Autowired)public class SecurityUtil {
private final UserDetailsService userDetailsService; public void logInAs(String username) {
UserDetails user = userDetailsService.loadUserByUsername(username); if (user == null) {
throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user"); } SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
@Override public Collection
getAuthorities() {
return user.getAuthorities(); } @Override public Object getCredentials() {
return user.getPassword(); } @Override public Object getDetails() {
return user; } @Override public Object getPrincipal() {
return user; } @Override public boolean isAuthenticated() {
return true; } @Override public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
} @Override public String getName() {
return user.getUsername(); } })); org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username); }}
5.测试代码
@RunWith(SpringRunner.class)@SpringBootTestpublic class SpringBootDemoActivitiApplicationTests {
@Autowired private ProcessRuntime processRuntime; // 流程引擎的抽象,可以获取所有的服务 /** * 列出所有流程定义 */ @Test public void contextLoads() {
// 调用授权 securityUtil.logInAs("salaboy"); // 查询十条流程实例信息 Page
processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0, 10)); processDefinitionPage.getContent().forEach(System.out::println); }}
6.结果

在这里插入图片描述

查询出来的结果
在这里插入图片描述
这里查询出的数据,对应的数据库中的act_re_procdef(流程定义数据表)。
在这里插入图片描述
其实,springboot只要把pom依赖导入,yml数据源配置好后,然后启动springbootapplication服务,数据库就能自动创建,而我们选择使用一个小案例,能够看到具体的调用流程实现。

当我启动springboot服务时,以下四张表会新增数据。

act_ge_property(属性数据表)

在这里插入图片描述
act_re_deployment(部署信息表)
在这里插入图片描述
act_ge_bytearray(二进制数据表)
在这里插入图片描述
act_re_procdef(流程定义数据表)
在这里插入图片描述
在这里插入图片描述

1.5.5 简单审批案例

在这里插入图片描述

详细图解流程运行过程
在这里插入图片描述

1.发起请假申请流程
@Test    public void testProcessInstance(){
// 流程名 String key = "bohui"; // 申请人 String username = "zhangsan"; HashMap
map = new HashMap<>(); // 启动流程实例时给变量赋值 map.put("assignee1", username); // 启动流程,发送请假请求 ActivitiUtil.startProcessInstanceWithVariables(username, key,"请假", map); }
/**     * 启动流程     * @param username    用户名     * @param processKey  流程 Key => 对应bpmn文件里的id     * @param processName 流程实例名     * @param variables   变量map     */    public static void startProcessInstanceWithVariables(String username,                                                         String processKey, String processName,                                                         HashMap
variables) {
// Security 授权用户(这里的代码上面的授权认证一样) activitiUtil.securityUtil.logInAs(username); // 创建一个运行时流程,并启动该流程 ProcessInstance processInstance = activitiUtil.processRuntime .start(ProcessPayloadBuilder .start() // 启动流程 .withProcessDefinitionKey(processKey) // 流程运行时定义流程实例 .withName(processName) // 流程事务,要做什么 .withVariables(variables) // 流程变量,一些额外的属性 .build()); logger.info("流程实例启动成功: " + processInstance); }

执行结果:

流程实例启动成功: ProcessInstance{id=‘1bac6242-d8e3-11eb-abf7-04d3b0ccfa07’, name=‘请假’, processDefinitionId=‘bohui:1:e2f71a3c-d8bf-11eb-9c08-04d3b0ccfa07’, processDefinitionKey=‘bohui’, parentId=‘null’, initiator=‘zhangsan’, startDate=Tue Jun 29 22:05:49 CST 2021, businessKey=‘null’, status=RUNNING, processDefinitionVersion=‘1’}

数据库新增表

act_hi_actinst(历史节点表)

在这里插入图片描述

act_hi_detail(历史详情表)

在这里插入图片描述

act_hi_identitylink(历史流程人员表)

在这里插入图片描述

act_hi_procinst(历史流程实例表)

在这里插入图片描述

act_hi_taskinst(历史任务实例表)

在这里插入图片描述

act_hi_varinst(历史变量表)

在这里插入图片描述

act_ru_execution(运行时流程执行实例表)

在这里插入图片描述

act_ru_identitylink(运行时流程人员表)

在这里插入图片描述

act_ru_task(运行时任务节点表)

在这里插入图片描述

act_ru_variable(运行时流程变量数据表)

在这里插入图片描述

在这里插入图片描述

2.获取所有流程定义
@Test    public void testQueryTask(){
String assignee = "zhangsan"; // 获取所有流程定义 ActivitiUtil.printTaskList(assignee, 0, 10); }
/**     * 打印指派人所有任务     *     * @param assignee 指派人     * @param startNum 分页开始下标     * @param endNum   分页结束下标     */    public static void printTaskList(String assignee, Integer startNum, Integer endNum) {
// 获取所有任务list Page
tasks = getTaskList(assignee, startNum, endNum); // 任务的总数大于0 if (tasks.getTotalItems() > 0) {
// 有任务时,完成任务 for (org.activiti.api.task.model.Task task : tasks.getContent()) {
logger.info("任务: " + task); } } }
@Autowired    private CustomTaskRuntimeImpl customTaskRuntimeImpl;	private static ActivitiUtil activitiUtil;	/**     * 查询当前指派人的任务     *     * @param assignee 指派人     * @param startNum 分页开始下标     * @param endNum   分页结束下标     * @return 任务list     */    public static Page
getTaskList(String assignee, Integer startNum, Integer endNum) {
// 授权用户 activitiUtil.securityUtil.logInAs(assignee); // return activitiUtil.customTaskRuntimeImpl.tasks(Pageable.of(startNum, endNum)); }
@PreAuthorize("hasRole('ACTIVITI_USER')") // 控制一个方法需要权限调用@Componentpublic class CustomTaskRuntimeImpl implements TaskRuntime {
// 安全管理器 private final SecurityManager securityManager; // 流程运行管理类 private final TaskService taskService; // API任务转换 private final APITaskConverter taskConverter; /** * * @param pageable 可分页 */ @Override public Page
tasks(Pageable pageable) {
// 获取认证用户id String authenticatedUserId = securityManager.getAuthenticatedUserId(); // 认证用户id不为空的场合 if (authenticatedUserId != null && !authenticatedUserId.isEmpty()) {
// 获取认证用户组 List
userGroups = securityManager.getAuthenticatedUserGroups(); // 分页的任务 return tasks(pageable, TaskPayloadBuilder.tasks().withAssignee(authenticatedUserId).withGroups(userGroups).build()); } throw new IllegalStateException("You need an authenticated user to perform a task query"); } /** * * @param pageable 可分页 * @param getTasksPayload 获取多个任务负载 */ @Override public Page
tasks(Pageable pageable, GetTasksPayload getTasksPayload) {
// 创建任务队列 TaskQuery taskQuery = taskService.createTaskQuery(); // 任务负载不为空的场合 if (getTasksPayload == null) {
// 生成任务负载 getTasksPayload = TaskPayloadBuilder.tasks().build(); } // 获取认证用户ID String authenticatedUserId = securityManager.getAuthenticatedUserId(); // 认证用户ID不为空的场合 if (authenticatedUserId != null && !authenticatedUserId.isEmpty()) {
// 获取认证用户组 List
userGroups = securityManager.getAuthenticatedUserGroups(); // 任务负载设置指派人ID getTasksPayload.setAssigneeId(authenticatedUserId); // 任务负载设置用户组 getTasksPayload.setGroups(userGroups); } else {
throw new IllegalStateException("You need an authenticated user to perform a task query"); } // 按照分配组 OR 指派人查询 taskQuery = taskQuery.or() .taskCandidateOrAssigned(getTasksPayload.getAssigneeId(), getTasksPayload.getGroups())// .taskOwner(authenticatedUserId) .endOr(); // 获取流程实例ID不为空的场合 if (getTasksPayload.getProcessInstanceId() != null) {
// 为任务队列设置流程实例ID taskQuery = taskQuery.processInstanceId(getTasksPayload.getProcessInstanceId()); } // 获取父任务ID不为空的场合 if (getTasksPayload.getParentTaskId() != null) {
// 为任务队列设置父任务ID taskQuery = taskQuery.taskParentTaskId(getTasksPayload.getParentTaskId()); } // 将任务队列里的分页list转换为任务list List
tasks = taskConverter.from(taskQuery.listPage(pageable.getStartIndex(), pageable.getMaxItems())); // 返回分页实现类 return new PageImpl<>(tasks, Math.toIntExact(taskQuery.count())); }}

Activiti 开发案例之 API 映射 SQL 查询

https://www.imooc.com/article/279591

查询结果:

任务: TaskImpl{id=‘1bae8529-d8e3-11eb-abf7-04d3b0ccfa07’, owner=‘null’, assignee=‘zhangsan’, name=‘发起’, description=‘null’, createdDate=Tue Jun 29 22:05:49 CST 2021, claimedDate=null, dueDate=null, priority=50, processDefinitionId=‘bohui:1:e2f71a3c-d8bf-11eb-9c08-04d3b0ccfa07’, processInstanceId=‘1bac6242-d8e3-11eb-abf7-04d3b0ccfa07’, parentTaskId=‘null’, formKey=‘null’, status=ASSIGNED, processDefinitionVersion=null, businessKey=null, taskDefinitionKey=_3}

3.进入审批阶段(指定审批人)
@Test    public void testCompleteTask(){
// 申请人 String assignee = "zhangsan"; HashMap
map = new HashMap<>(); // 完成任务时同时指定之后流程的指派人(审批人) map.put("assignee2", "lisi"); // 根据变量条件完成对应任务 ActivitiUtil.completeTaskWithVariables(assignee, map); }
/**     * 完成任务     *     * @param assignee  指派人     * @param variables 变量map     */    public static void completeTaskWithVariables(String assignee, HashMap
variables) {
// 获取默认前十条任务 Page
tasks = getTaskList(assignee, 0, 10); // 任务总数大于零的场合 if (tasks.getTotalItems() > 0) {
// 有任务时,完成任务 for (org.activiti.api.task.model.Task task : tasks.getContent()) {
System.out.println(task); // 完成任务 activitiUtil.taskRuntime.complete( TaskPayloadBuilder.complete() .withTaskId(task.getId()) .withVariables(variables).build()); logger.info(assignee + "完成任务"); } } }

执行结果:

Logged in as: zhangsan

TaskImpl{id=‘1bae8529-d8e3-11eb-abf7-04d3b0ccfa07’, owner=‘null’, assignee=‘zhangsan’, name=‘发起’, description=‘null’, createdDate=Tue Jun 29 22:05:49 CST 2021, claimedDate=null, dueDate=null, priority=50, processDefinitionId=‘bohui:1:e2f71a3c-d8bf-11eb-9c08-04d3b0ccfa07’, processInstanceId=‘1bac6242-d8e3-11eb-abf7-04d3b0ccfa07’, parentTaskId=‘null’, formKey=‘null’, status=ASSIGNED, processDefinitionVersion=null, businessKey=null, taskDefinitionKey=_3}
2021-06-30 00:40:35.753 INFO 16368 — [ main] c.z.a.utils.ActivitiUtil : zhangsan完成任务

数据表出现的变化

在这里插入图片描述

4.进入审批阶段(审批人不通过申请,驳回请求)
@Test    public void testCompleteTask2(){
// 审批人 String assignee = "lisi"; HashMap
map = new HashMap<>(); // 完成任务时同时指定审核为驳回 map.put("audit", false); // 根据变量条件完成对应任务 ActivitiUtil.completeTaskWithVariables(assignee, map); }

执行结果

Logged in as: lisi

TaskImpl{id=‘ba78a61b-d8f8-11eb-bb2a-04d3b0ccfa07’, owner=‘null’, assignee=‘lisi’, name=‘审核’, description=‘null’, createdDate=Wed Jun 30 00:40:35 CST 2021, claimedDate=null, dueDate=null, priority=50, processDefinitionId=‘bohui:1:e2f71a3c-d8bf-11eb-9c08-04d3b0ccfa07’, processInstanceId=‘1bac6242-d8e3-11eb-abf7-04d3b0ccfa07’, parentTaskId=‘null’, formKey=‘null’, status=ASSIGNED, processDefinitionVersion=null, businessKey=null, taskDefinitionKey=_4}
2021-06-30 00:45:05.008 INFO 1940 — [ main] c.z.a.utils.ActivitiUtil : lisi完成任务

数据表出现的变化

在这里插入图片描述

5.进入审批阶段(申请人重新申请请求)
@Testpublic void testCompleteTask3(){
// 申请人 String assignee = "zhangsan"; // 重新提交任务 ActivitiUtil.completeTask(assignee);}
/** * 完成指派人所有任务 * * @param assignee 指派人 */public static void completeTask(String assignee) {
// 获取默认前十条任务 Page
tasks = getTaskList(assignee, 0, 10); // 任务总数大于零的场合 if (tasks.getTotalItems() > 0) {
// 有任务时,完成任务 for (org.activiti.api.task.model.Task task : tasks.getContent()) {
System.out.println(task); // 完成任务 activitiUtil.taskRuntime.complete( TaskPayloadBuilder.complete().withTaskId(task.getId()).build()); logger.info(assignee + "完成任务"); } }}
6.进入审批阶段(审批通过)
@Testpublic void testCompleteTask4(){
// 审批人 String assignee = "lisi"; HashMap
map = new HashMap<>(); // 完成任务时同时指定审核为通过 map.put("audit", true); // 根据变量条件完成对应任务 ActivitiUtil.completeTaskWithVariables(assignee, map);}

执行结果

Logged in as: lisi

TaskImpl{id=‘d0cddac7-d8f9-11eb-8c91-04d3b0ccfa07’, owner=‘null’, assignee=‘lisi’, name=‘审核’, description=‘null’, createdDate=Wed Jun 30 00:48:22 CST 2021, claimedDate=null, dueDate=null, priority=50, processDefinitionId=‘bohui:1:e2f71a3c-d8bf-11eb-9c08-04d3b0ccfa07’, processInstanceId=‘1bac6242-d8e3-11eb-abf7-04d3b0ccfa07’, parentTaskId=‘null’, formKey=‘null’, status=ASSIGNED, processDefinitionVersion=null, businessKey=null, taskDefinitionKey=_4}
2021-06-30 00:48:44.667 INFO 9616 — [ main] c.z.a.utils.ActivitiUtil : lisi完成任务

数据表出现的变化

在这里插入图片描述

完结!原创不易,点个赞再走!

你可能感兴趣的文章
Session & Cookie
查看>>
count lines in a file - wc & nl
查看>>
View the start/end of a file linux
查看>>
General overview of the Linux file system
查看>>
/proc
查看>>
Nginx TCP Load Balance
查看>>
需要注意的食物
查看>>
Nginx upstream schedule strategy
查看>>
Redis Brief Intro
查看>>
Nginx Basic Config
查看>>
Nginx Load Balancer Config
查看>>
Nginx config hight throughput
查看>>
mysql max_connection config
查看>>
Python improve performance
查看>>
mysql interview questions and answers
查看>>
File & File system size limitation for Redhat
查看>>
Python decorator guide
查看>>
interview sorting algorithm summary
查看>>
Python continue, else and pass
查看>>
mysql index and search improve
查看>>