activit工作流(一) 概念
activiti介绍
引擎service接口
- RespositoryService: 流程仓库Service,用于管理流程仓库.eg:部署,删除,读取流程资源
- identifyService: 身份Service, 可以管理和查询用户,组之间的关系.(版本7之后删除)
- RuntimeService: 运行时Service,可以处理所有正在运行状态的流程实例,任务等.
- TaskService: 任务Service,用于管理,查询任务, eg:签收,办理,指派等.
- FormService: 表单Service,用于读取和流程任务相关的表单数据(版本7之后删除)
- HistoryService: 历史Service, 可以查询所有历史数据, 例如:流程实例,任务,活动,变量,附件等
- ManagementService: 引擎管理Service, 和具体业务无关,主要是可以查询引擎配置,数据库,作业等
流程设计器
- Eclipse Designer
- Activiti Modeler
架构与组件
- activiti Engine(最核心模块): 解析,执行,创建,管理(任务流程实例), 查询历史记录.
- activiti modeler: 模型设计器
- activiti designer: 流程设计器
- activiti explorer: 用来管理仓库,用户,组,启动流程, 任务管理等,提供rest风格的api
- activiti rest: 提供restful风格的服务,允许客户端以json的方式,语音穷拐的rest api交互,以此跨平台,跨语言
运行第一个activiti程序(springboot)
流程绘制
- idea2019版及之前版本去https://plugins.jetbrains.com/寻找actiBPM插件安装后绘制,
- idea2020版后安装 activiti BPMN visualizer 插件安装后绘制
- eclipse 安装插件后绘制
- 在线编辑流程图生成bpmn文件
生成springboot项目
生成空的springboot项目
pom文件添加依赖
<-- activiti 引擎依赖-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>6.0.0</version>
</dependency>
<-- mysql 数据源依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
配置文件添加数据源与activiti配置.
server:
port: 8090
spring:
activiti:
# database-schema-update: true
check-process-definitions: false
historyLevel: audit
db-history-used: true
application:
name: konghenying-activiti
datasource:
username: konghenying
password: 123456
url: jdbc:mysql://localhost:3306/activiti?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false
driver-class-name: com.mysql.jdbc.Driver
配置项说明:
database-schema-update 是否更新数据库
- false: 默认值。activiti在启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常。(生产环境常用)
- activiti: 会对数据库中所有表进行更新操作。如果表不存在,则自动创建。(开发时常用)
- create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)。(单元测试常用)
- drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)。
historyLevel 历史数据等级
- none: 不保存任何的历史数据,因此,在流程执行过程中,这是最高效的
- activity: 级别高于none,保存流程实例与流程行为,其他数据不保存
- audit: 除activity级别会保存的数据外,还会保存全部的流程任务及其属性。audit为history的默认值
- full: 保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等
db-history-used 检测历史表是否存在(activiti6默认启用,activiti7默认关闭)
check-process-definitions 启动的时候是否自动部署流程
实装流程图
将生成的bpmn文件放入/resources/processes文件夹下.通过代码或者自动部署流程.
常用功能案例:以请假为例
第一步: 流程部署
@Resource
private RepositoryService repositoryService;
@Test
public void deploy(){
String bpmnNameAndKey = "first";
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("processes/first.bpmn")
// .addClasspathResource("processes/first.png")
.key(bpmnNameAndKey)
.category("HR")
.deploy();
System.out.println("流程部署ID\t" + deploy.getId());
System.out.println("流程keyID\t" + deploy.getKey());
System.out.println("流程名称ID\t" + deploy.getName());
System.out.println("流程分类ID\t" + deploy.getCategory());
}
第二步: 启动流程实例
本例中相当于提交一次请假申请
@Resource
private RuntimeService runtimeService;
@Test
public void start(){
String bpmnNameAndKey = "first";
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(bpmnNameAndKey);
System.out.println("流程实例ID\t" + processInstance.getId());
System.out.println("流程定义ID\t" + processInstance.getProcessDefinitionId());
System.out.println("流程定义key\t" + processInstance.getProcessDefinitionKey());
}
第三步: 查找个人代办任务列表
@Resource
private TaskService taskService;
@Test
public void findMyTask(){
String assignee = "张三";
List<Task> taskList = taskService.createTaskQuery().taskAssignee(assignee).list();
if (!taskList.isEmpty()) {
for (Task task : taskList) {
System.out.println("任务ID\t" + task.getId());
System.out.println("任务名称\t" + task.getName());
System.out.println("任务执行人\t" + task.getAssignee());
}
}
}
第四步: 执行代办任务
@Resource
private TaskService taskService;
@Test
public void complet(){
String taskId = "2505";
taskService.complete(taskId);
System.out.println("任务执行完成");
}
第五步: 查看历史流程实例
@Resource
private HistoryService historyService;
@Test
public void history(){
String bpmnNameAndKey = "first";
List<HistoricProcessInstance> historicProcessInstances= historyService.createHistoricProcessInstanceQuery().processDefinitionKey(bpmnNameAndKey).list();
if (!historicProcessInstances.isEmpty()) {
for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {
System.out.println("业务系统key\t" + historicProcessInstance.getBusinessKey());
System.out.println("部署对象ID\t" + historicProcessInstance.getDeploymentId());
System.out.println("执行时长\t" + historicProcessInstance.getDurationInMillis());
System.out.println("流程定义ID\t" + historicProcessInstance.getProcessDefinitionId());
System.out.println("流程定义的key\t" + historicProcessInstance.getProcessDefinitionKey());
System.out.println("流程定义名称\t" + historicProcessInstance.getProcessDefinitionName());
}
}
}
第六步: 查看历史任务
@Resource
private HistoryService historyService;
@Test
public void historyTask(){
List<HistoricTaskInstance> historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().list();
if (!historicTaskInstanceList.isEmpty()){
for (HistoricTaskInstance historicTaskInstance : historicTaskInstanceList) {
System.out.println("任务执行人\t" + historicTaskInstance.getAssignee());
System.out.println("任务名称\t" + historicTaskInstance.getName());
System.out.println("任务ID\t" + historicTaskInstance.getId());
System.out.println("流程实例ID\t" + historicTaskInstance.getProcessInstanceId());
System.out.println("*****************");
}
}
}
查询功能案例:以用户为例
插入测试数据
@Resource
private IdentityService identityService;
@Test
public void insert() {
for (int i = 0; i < 10; i++) {
User user = identityService.newUser(String.valueOf(i));
user.setLastName("张三" + i);
user.setFirstName("法外狂徒" + i);
user.setEmail("邮件" + i);
identityService.saveUser(user);
}
}
查询指定用户名的用户数据
@Resource
private IdentityService identityService;
@Test
public void querySingle() {
User user = identityService.createUserQuery().userFirstName("法外狂徒1").singleResult();
System.out.println(user.getFirstName()+" : " + user.getEmail());
}
查询用户列表数据
@Resource
private IdentityService identityService;
@Test
public void queryList() {
List<User> list = identityService.createUserQuery()
.list();
if (!list.isEmpty()) {
for (User user : list) {
System.out.println(user.getFirstName() + "\t" + user.getEmail());
}
}
}
查询用户列表的分页数据
@Resource
private IdentityService identityService;
@Test
public void pageList(){
// .listPage(firstResult,maxResults)
List<User> users = identityService.createUserQuery().listPage(5,5);
if (!users.isEmpty()){
for (User user : users) {
System.out.println(user.getFirstName()+" : " + user.getEmail());
}
}
}
查询用户数量
@Resource
private IdentityService identityService;
@Test
public void userListCount(){
Long count = identityService.createUserQuery().count();
System.out.println(count);
}
单个字段排序
@Resource
private IdentityService identityService;
@Test
public void querySingleSort() {
List<User> list = identityService.createUserQuery().orderByUserId().asc().list();
if (list.isEmpty()) {
for (User user : list) {
System.out.println(user.getFirstName() + "\t" + user.getEmail());
}
}
}
多个字段排序
注意:一个排序字段后需要加升降序条件.不可省略.否则会被后面的排序条件覆盖.
@Resource
private IdentityService identityService;
@Test
public void queryMultiSort() {
UserQuery userQuery = identityService.createUserQuery().orderByUserId().desc().orderByUserEmail().asc();
List<User> list = userQuery.list();
if (!CollectionUtils.isEmpty(list)) {
for (User user : list) {
System.out.println(user.getFirstName() + "\t" + user.getEmail());
}
}
}
根据条件查询
@Resource
private IdentityService identityService;
@Test
public void queryCondition() {
String firstName = "法外狂徒5";
String firstNameLike = "法外狂徒";
// List<User> list = identityService.createUserQuery().userFirstName(firstName).list();
List<User> list = identityService.createUserQuery().userFirstNameLike("%"+firstNameLike+"%").list();
if (!CollectionUtils.isEmpty(list)) {
for (User user : list) {
System.out.println(user.getFirstName() + "\t" + user.getEmail());
}
}
}
自定义查询
@Resource
private IdentityService identityService;
@Test
public void queryNative() {
String sql = "select * from ACT_ID_USER";
List<User> list = identityService.createNativeUserQuery().sql(sql).list();
if (!CollectionUtils.isEmpty(list)) {
for (User user : list) {
System.out.println(user.getFirstName() + "\t" + user.getEmail());
}
}
}
底层为mybatis查询,mapper文件在org.activiti.db.mapping.entity.User.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.activiti.engine.impl.persistence.entity.UserEntityImpl">
<!-- USER INSERT -->
<insert id="insertUser" parameterType="org.activiti.engine.impl.persistence.entity.UserEntityImpl">
insert into ${prefix}ACT_ID_USER (ID_, REV_, FIRST_, LAST_, EMAIL_, PWD_)
values (
#{id ,jdbcType=VARCHAR},
1,
#{firstName ,jdbcType=VARCHAR},
#{lastName ,jdbcType=VARCHAR},
#{email ,jdbcType=VARCHAR},
#{password ,jdbcType=VARCHAR}
)
</insert>
<insert id="bulkInsertUser" parameterType="java.util.List">
INSERT INTO ${prefix}ACT_ID_USER (ID_, REV_, FIRST_, LAST_, EMAIL_, PWD_) VALUES
<foreach collection="list" item="user" index="index" separator=",">
(#{user.id ,jdbcType=VARCHAR},
1,
#{user.firstName ,jdbcType=VARCHAR},
#{user.lastName ,jdbcType=VARCHAR},
#{user.email ,jdbcType=VARCHAR},
#{user.password ,jdbcType=VARCHAR})
</foreach>
</insert>
<insert id="bulkInsertUser" databaseId="oracle" parameterType="java.util.List">
INSERT ALL
<foreach collection="list" item="user" index="index">
INTO ${prefix}ACT_ID_USER (ID_, REV_, FIRST_, LAST_, EMAIL_, PWD_) VALUES
(#{user.id ,jdbcType=VARCHAR},
1,
#{user.firstName ,jdbcType=VARCHAR},
#{user.lastName ,jdbcType=VARCHAR},
#{user.email ,jdbcType=VARCHAR},
#{user.password ,jdbcType=VARCHAR})
</foreach>
SELECT * FROM dual
</insert>
<!-- USER UPDATE -->
<update id="updateUser" parameterType="org.activiti.engine.impl.persistence.entity.UserEntityImpl">
update ${prefix}ACT_ID_USER set
REV_ = #{revisionNext ,jdbcType=INTEGER},
FIRST_ = #{firstName ,jdbcType=VARCHAR},
LAST_ = #{lastName ,jdbcType=VARCHAR},
EMAIL_ = #{email ,jdbcType=VARCHAR},
PWD_ = #{password ,jdbcType=VARCHAR},
PICTURE_ID_ = #{pictureByteArrayRef ,typeHandler=ByteArrayRefTypeHandler}
where ID_ = #{id}
and REV_ = #{revision}
</update>
<!-- USER DELETE -->
<delete id="deleteUser" parameterType="org.activiti.engine.impl.persistence.entity.UserEntityImpl">
delete from ${prefix}ACT_ID_USER where ID_ = #{id} and REV_ = #{revision}
</delete>
<!-- USER RESULTMAP -->
<resultMap id="userResultMap" type="org.activiti.engine.impl.persistence.entity.UserEntityImpl">
<id property="id" column="ID_" jdbcType="VARCHAR" />
<result property="revision" column="REV_" jdbcType="INTEGER" />
<result property="firstName" column="FIRST_" jdbcType="VARCHAR" />
<result property="lastName" column="LAST_" jdbcType="VARCHAR" />
<result property="email" column="EMAIL_" jdbcType="VARCHAR" />
<result property="password" column="PWD_" jdbcType="VARCHAR" />
<result property="pictureByteArrayRef" column="PICTURE_ID_" typeHandler="ByteArrayRefTypeHandler" />
</resultMap>
<!-- USER SELECT -->
<select id="selectUser" parameterType="string" resultMap="userResultMap">
select * from ${prefix}ACT_ID_USER where ID_ = #{id,jdbcType=VARCHAR}
</select>
<select id="selectUserByQueryCriteria" parameterType="org.activiti.engine.impl.UserQueryImpl" resultMap="userResultMap">
${limitBefore}
select RES.* ${limitBetween}
<include refid="selectUserByQueryCriteriaSql" />
${orderBy}
${limitAfter}
</select>
<select id="selectUserCountByQueryCriteria" parameterType="org.activiti.engine.impl.UserQueryImpl" resultType="long">
select count(RES.ID_)
<include refid="selectUserByQueryCriteriaSql" />
</select>
<sql id="selectUserByQueryCriteriaSql">
from ${prefix}ACT_ID_USER RES
<if test="groupId != null">
inner join ${prefix}ACT_ID_MEMBERSHIP M on RES.ID_ = M.USER_ID_
inner join ${prefix}ACT_ID_GROUP G on M.GROUP_ID_ = G.ID_
</if>
<where>
<if test="id != null">
RES.ID_ = #{id}
</if>
<if test="firstName != null">
and RES.FIRST_ = #{firstName}
</if>
<if test="firstNameLike != null">
and RES.FIRST_ like #{firstNameLike}${wildcardEscapeClause}
</if>
<if test="lastName != null">
and RES.LAST_ = #{lastName}
</if>
<if test="lastNameLike != null">
and RES.LAST_ like #{lastNameLike}${wildcardEscapeClause}
</if>
<if test="fullNameLike != null">
and (RES.FIRST_ like #{fullNameLike}${wildcardEscapeClause} or RES.LAST_ like #{fullNameLike}${wildcardEscapeClause})
</if>
<if test="email != null">
and RES.EMAIL_ = #{email}
</if>
<if test="emailLike != null">
and RES.EMAIL_ like #{emailLike}${wildcardEscapeClause}
</if>
<if test="groupId != null">
and G.ID_ = #{groupId}
</if>
<if test="procDefId != null">
and exists (select ID_ from ${prefix}ACT_RU_IDENTITYLINK where PROC_DEF_ID_ = #{procDefId} and USER_ID_=RES.ID_ )
</if>
</where>
</sql>
<select id="selectUserByNativeQuery" parameterType="java.util.Map" resultMap="userResultMap">
<include refid="org.activiti.engine.db.common.selectByNativeQuery"/>
</select>
<select id="selectUserCountByNativeQuery" parameterType="java.util.Map" resultType="long">
${sql}
</select>
</mapper>
表结构
