那么首先我们应该知道工作流是干什么的
简单来说工作流就是多个参与者,按照某种预定义的规则,传递业务信息,进行审核的功能一个框架
Activiti就是通过流程引擎processEngine,调用Service,从而操作activiti提供的数据库的23表
数据表分类
ACT_GE_* 通用数据表(GE表示General)
ACT_RE_* 流程定义存储表(RE表示repository)
ACT_ID_* 身份信息表(ID表示IDentity)
ACT_RU_* 运行是数据库表(RU表示runtime)
ACT_HI_* 历史数据库表(HI表示history)
processEnigne 流程引擎
我们可以通过流程引擎来获取一些service
ReopsitoryService :
负责对流程文件的管理,主要操作一些静态文件,比如流程文件中的xml,流程图.bpmn文件等
我们通过repositoryService部署流程对象, 会涉及到两个实体对象,一个是部署对象,一个是资源对象,部署对象和资源对象是一个一对多的关系,那么就可以说明, 一次部署可以包含多个资源文件,最常见的流程部署就是把流程图的xml或者.bpmn图片部署到数据库里面,
RuntimeService :主要是对流程进行控制的API,可以用于启动一个流程实例,针对制定的流程实例,进行暂停,挂起,和继续执行,也可以查询正在运行中的流程实例和执行对象,也可以对流程中的上下文数据进行设置和获取
通过RuntimeService启动流程,processDefinitionKey就是设置的整个流程的Id,businessKey因为整个流程为完成的时候都会存在,那么就可以设置成自己的业务数据表的Id,这样可以和activiti的数据表有一个交互的作用,也可以在自己的业务表中存放processInstanceId或者taskId,这样也可以直接通过业务数据命中该流程的实例,
variables也可以存放一些业务数据,这些都是需要在启动流程的时候的才能存放的
TaskService 主要管理userTask, 也就是人工任务, 可以对人工任务进行增删改查,也可以对用户任务设置指定的操作权限,指定的用户或用户主,同时也可以对用户任务上下文的变量设置或获取
// 通过流程实例Id获取当前任务
Task task = taskService.createTaskQuery()
.processInstanceId(processInstance.getProcessInstanceId())
.singleResult();
// 通过businessKey获取当前任务实例
Task task2 = taskService.createTaskQuery()
.processInstanceBusinessKey("业务表Id")
.singleResult();
// 执行任务
taskService.complete(task.getId());
// 也可以携带参数执行,比如排他网关需要的参数等
taskService.complete(task.getId(), map);
IdentityService 是对用户或者用户主管理,我们可以创建用户或用户主,并维护用户之间的关系
FormService 可以解析出流程定义中的设计表单,对表单的输入类型和格式做数据渲染
HistoryService 主要提供了对运行结束的流程实例的查询功能,也提供了基于流程维度和用户维度的删除操作,方便我们统计流程执行过程的变化,
ManagementService 主要是对流程引擎基础的管理, 一般用的比较少,还提供了对定时任务的管理
DynamicBPMService 动态,侵入性比较高的功能, 他可以动态的对流程定义的模型做修改, 一般不推荐使用
BPMN2.0(Buisness Process Model and Notation)
是一套业务流程模型与符号建模标准
精准的执行语义来描述元素的操作
以XML为载体,以符号可视化业务
BPMN2.0元素
FlowObjects 流对象 包括了事件,活动和网关,这些流对象通过连接对象连接起来,表示数据流转,过程中的数据流转主要通过连接对象来描述的,
ConnectingObjects 连接对象
Swimlanes 泳道 适用于对业务做一个范围维度的区分, 一般通过不同的职能做区分,比如角色和部门来区分不同的范围
Data 数据
Artifacs 描述对象
idea+actiBPM画图工具+jdk8+springBoot2.0+activiti6.0.0+jpa
pom.xml需要的依赖
org.springframework.boot
spring-boot-starter-parent
2.0.2.RELEASE
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-dmn-model</artifactId>
<version>6.0.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<exclusions>
<exclusion>
<artifactId>log4j-api</artifactId>
<groupId>org.apache.logging.log4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>6.0.0</version>
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-app-conf</artifactId>
<version>6.0.0</version>
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
自己的业务数据表
package com.ActivitiDemo.domain;
import com.ActivitiDemo.dto.TaskDto;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.util.StringUtils;
import javax.persistence.*;
import java.util.Date;
@Entity
@Table(name = “t_leave_task”)
public class LeaveTask {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "user_id", columnDefinition = "int(10) COMMENT '请假人ID'")
private Integer userId;
@Column(name = "user_name", columnDefinition = "varchar(20) COMMENT '请假人性名'")
private String userName;
@Column(name = "start_date", columnDefinition = "timestamp COMMENT '请假开始时间'")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date startDate;
@Column(name = "end_date",columnDefinition = "timestamp COMMENT '请假结束时间'")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date endDate;
@Column(name = "leave_type", columnDefinition = "int(10) COMMENT '流程: 0.已提交, 1.主管审批, 2.人事审批, 3.已取消, 4.已完成'")
private LeaveType type;
@Column(name = "leave_cause",columnDefinition = "varchar(50) COMMENT '请假原因'")
private String leaveCause;
@Column(name = "create_date",columnDefinition = "timestamp COMMENT '任务创建时间'")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date createDate;
@Column(name = "review_status",columnDefinition = "int(1) COMMENT '审批结果'")
private Boolean reviewStatus;
@Column(name = "review_date", columnDefinition = "timestamp COMMENT '审批时间'")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date reviewDate;
@Column(name = "review_by", columnDefinition = "varchar(50) COMMENT '审批人'")
private String reviewBy;
@Column(name = "review_in", columnDefinition = "varchar(50) COMMENT '审批原因'")
private String reviewIn;
public LeaveTask() {
}
public LeaveTask(TaskDto dto) {
this.userId = dto.getUserId();
this.userName = dto.getUserName();
this.startDate = dto.getStartDate();
this.endDate = dto.getEndDate();
this.leaveCause = StringUtils.isEmpty(dto.getLeaveCause()) ? null : dto.getLeaveCause();
this.type = LeaveType.tl_approve;
this.createDate = new Date();
this.reviewBy = dto.getUserName();
this.reviewIn = StringUtils.isEmpty(dto.getLeaveCause()) ? null : dto.getLeaveCause();
this.reviewDate = new Date();
this.reviewStatus = dto.getDecision();
// setter和getter方法省略。。。
}
spring-jpa可以根据映射表自动在数据库建立表,有兴趣的可以去看一下相关的API
package com.ActivitiDemo.domain;
public enum LeaveType {
submit_leave("已提交"),
tl_approve("主管审批"),
hr_approve("人事审批"),
error_leave("已取消"),
end_leave("已完成");
private final String desc;
LeaveType(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
package com.ActivitiDemo.domain;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.Date;
import java.util.Optional;
@Repository
public interface LeaveTaskRepository extends CrudRepository<LeaveTask, Long> {
@Query(value = "update LeaveTask set reviewStatus = ?1, reviewBy = ?2, reviewIn = ?3, reviewDate = ?4, type = ?5 where id = ?6")
@Modifying
void updateReviewById(boolean reviewStatus, String reviewBy, String reviewIn, Date reviewDate, LeaveType type, Long id);
/**
* 查询用户是否有未完成订单
*
* @param userId
* @param type0
* @param type1
* @return
*/
Optional<LeaveTask> findByUserIdAndTypeNotAndTypeNot(Integer userId, LeaveType type0, LeaveType type1);
}
package com.ActivitiDemo.service;
import com.ActivitiDemo.domain.LeaveTask;
import com.ActivitiDemo.domain.LeaveTaskRepository;
import com.ActivitiDemo.domain.LeaveType;
import com.ActivitiDemo.dto.ApprovalDto;
import com.ActivitiDemo.dto.TaskDto;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.HttpServerErrorException;
import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@Service
@Transactional
public class ActivitiService {
@Resource
private LeaveTaskRepository leaveTaskRepository;
@Resource
private RuntimeService runtimeService;
@Resource
private TaskService taskService;
@Resource
private RepositoryService repositoryService;
public void postLeaveTask(TaskDto dto) {
// 判断是否已有流程在走
Optional<LeaveTask> optionalLeaveTask = leaveTaskRepository.findByUserIdAndTypeNotAndTypeNot(dto.getUserId(), LeaveType.end_leave, LeaveType.error_leave);
// 流程已存在
if(optionalLeaveTask.isPresent()) {
Task task = taskService.createTaskQuery()
.processInstanceBusinessKey(optionalLeaveTask.get().getId().toString())
.singleResult();
if (!ObjectUtils.isEmpty(task) && "ask_for_leave".equals(task.getTaskDefinitionKey())) {
Map<String, Object> map = new HashMap<>();
map.put("tlApprove", dto.getDecision() ? "Y" : "N");
taskService.complete(task.getId(), map);
// 修改请假单表的审批信息
leaveTaskRepository.updateReviewById(dto.getDecision()
, dto.getUserName()
, StringUtils.isEmpty(dto.getLeaveCause()) ? null : dto.getLeaveCause()
, new Date()
, dto.getDecision() ? LeaveType.tl_approve : LeaveType.error_leave
, optionalLeaveTask.get().getId());
}else {
throw new HttpServerErrorException(HttpStatus.BAD_REQUEST, "错误!");
}
}else {
// 没有流程, 新建
LeaveTask leaveTask = new LeaveTask(dto);
LeaveTask result = leaveTaskRepository.save(leaveTask);
String processDefinitionKey = "LeaveTheProcess";
// 启动流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("LeaveTheProcess", result.getId().toString());
// 获取当前任务
Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult();
Map<String, Object> map = new HashMap<>();
map.put("submitType", dto.getDecision() ? "Y" : "N");
taskService.complete(task.getId(), map);
}
}
/**
* 审批流程
*
* @param dto
*/
public void approveProcess(ApprovalDto dto) {
Task task = taskService.createTaskQuery()
.processInstanceBusinessKey(dto.getLeaveId().toString())
.singleResult();
if (!ObjectUtils.isEmpty(task) && "tl_approve".equals(task.getTaskDefinitionKey())) {
this.completeTlApproveTask(dto, task.getId());
} else if (!ObjectUtils.isEmpty(task) && "hr_approve".equals(task.getTaskDefinitionKey())) {
this.completeHrApproveTask(dto, task.getId());
}
}
/**
* 主管审批
*
* @param dto
*/
public void completeTlApproveTask(ApprovalDto dto, String taskId) {
Map<String, Object> map = new HashMap<>();
map.put("tlApprove", dto.getReviewStatus() ? "Y" : "N");
taskService.complete(taskId, map);
// 修改请假但表的审批信息
leaveTaskRepository.updateReviewById(dto.getReviewStatus(), dto.getReviewBy(), dto.getReviewIn(), new Date(), dto.getReviewStatus() ? LeaveType.hr_approve : LeaveType.submit_leave, dto.getLeaveId());
}
/**
* 人事审批
*
* @param dto
*/
public void completeHrApproveTask(ApprovalDto dto, String taskId) {
Map<String, Object> map = new HashMap<>();
map.put("hrApprove", dto.getReviewStatus() ? "Y" : "N");
taskService.complete(taskId, map);
// 修改请假但表的审批信息
leaveTaskRepository.updateReviewById(dto.getReviewStatus(), dto.getReviewBy(), dto.getReviewIn(), new Date(), dto.getReviewStatus() ? LeaveType.end_leave : LeaveType.submit_leave, dto.getLeaveId());
}
/**
* 请假流程部署
*/
public void deploy() {
repositoryService.createDeployment()
.addClasspathResource("processes/leave_the_process.bpmn")
.name("请假审批流程")
.category("请假")
.deploy();
}
}