Springboot 项目搭建activiti流程项目demo
首先在pom文件中添加依赖:
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti5-engine</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
//数据库依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
更改配置文件application.yml:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3366/activiti?useUnicode=true&characterEncoding=UTF-8&nullCatalogMeansCurrent=true&serverTimezone=UTC
username: root
password: 123456
data: activiti.cfg.xml
在resources资源文件下添加xml配置文件。文件名必须为:activiti.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3366/activiti??useUnicode=true&characterEncoding=UTF-8&nullCatalogMeansCurrent=true&serverTimezone=UTC"></property>
<property name="jdbcUsername" value="root"></property>
<property name="jdbcPassword" value="123456"></property>
<property name="databaseSchemaUpdate" value="true"></property>
</bean>
</beans>
在整个项目中,引包的时候要注意,因为包不一样对应程序可能不同。
添加activiti测试类,自动将activiti用到的表加入数据库中:
package activiti;
import jdk.nashorn.internal.ir.CallNode;
import org.activiti5.engine.ProcessEngine;
import org.activiti5.engine.ProcessEngineConfiguration;
import org.junit.Test;
public class ActivitiText {
//两种方法都可
@Test
public void testInit(){
//创建流程引擎配置对象
ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
configuration.setJdbcDriver("com.mysql.cj.jdbc.Driver");
configuration.setJdbcUrl("jdbc:mysql://localhost:3306/activiti_1?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC");
configuration.setJdbcUsername("root");
configuration.setJdbcPassword("root");
//创建表
configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
//构建流程引擎
ProcessEngine ProcessEngine = configuration.buildProcessEngine();
}
@Test
public void creatTable(){
ProcessEngine processEngine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml").buildProcessEngine();
}
}
运行后发现数据库中自动加入了16张表,activiti.6.0.0应该为26张。是因为目前没有写相关流程业务。
在idea中添加插件,因为本人未找到actiBMPN插件,所以添加了activiti BPMN。
创建activiti配置类:
package com.dsr.config;
import lombok.SneakyThrows;
import org.activiti.engine.*;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.io.IOException;
import java.lang.management.PlatformLoggingMXBean;
@Configuration
public class ActivitiConfig {
@Autowired
private DataSource dataSource;
@Autowired
private PlatformTransactionManager platformTransactionManager;
@Bean
public SpringProcessEngineConfiguration springProcessEngineConfiguration(){
SpringProcessEngineConfiguration processEngineConfiguration = new SpringProcessEngineConfiguration();
processEngineConfiguration.setDataSource(dataSource);
processEngineConfiguration.setTransactionManager(platformTransactionManager);
processEngineConfiguration.setDatabaseSchemaUpdate("true");
Resource[] resources = null;
try{
//流程资源目录
resources = new PathMatchingResourcePatternResolver().getResources("classpath*:bpmn/*.bpmn");
} catch (IOException e){
e.printStackTrace();
}
processEngineConfiguration.setDeploymentResources(resources);
return processEngineConfiguration;
}
@Bean
public ProcessEngineFactoryBean processEngineFactoryBean(){
ProcessEngineFactoryBean processEngine = new ProcessEngineFactoryBean();
processEngine.setProcessEngineConfiguration(springProcessEngineConfiguration());
return processEngine;
}
@Bean
public RepositoryService repositoryService() throws Exception{
return processEngineFactoryBean().getObject().getRepositoryService();
}
@Bean
public RuntimeService runtimeService() throws Exception{
return processEngineFactoryBean().getObject().getRuntimeService();
}
@Bean
public TaskService taskService() throws Exception{
return processEngineFactoryBean().getObject().getTaskService();
}
@Bean
public HistoryService historyService() throws Exception{
return processEngineFactoryBean().getObject().getHistoryService();
}
@SneakyThrows
@Bean
public ManagementService managementService(ProcessEngine processEngine) {
return processEngineFactoryBean().getObject().getManagementService();
}
@SneakyThrows
@Bean
public IdentityService identityService(ProcessEngine processEngine) {
return processEngineFactoryBean().getObject().getIdentityService();
}
}
若运行测试类后,未能自动添加表,可以在写完配置类后,运行程序:
package com.dsr;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication(scanBasePackages = {"com.dsr"})
//@MapperScan("com.dsr.mapper")
@MapperScan("com.dsr.mapper")
@ComponentScan({"com.dsr.config"})
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class,args);
}
}
在配置文件中配置了getResources(“classpath*:bpmn/*.bpmn”),所以需要在resources文件下创建对应的bpmn文件夹。在bpmn文件夹下创建文件:
创建好之后,右击选择View BPMN之后打开文件,在空白处右击即可开始画流程图。(需要设置ID、name等)
画好之后连线问题,注意点击图标后可以在右上角看见箭头,后需要再点一次进行连接。
出现这样才可以连线。
流程图画好之后,可以看到对应的xml文件新增了内容:
创建实体类,创建时须在数据库中有对应表,这里用到了Lombok如何集成可以见之前博客:
package com.dsr.dao;
import lombok.Data;
import java.util.Date;
@Data
public class ActivitiTask {
private String id;
private String activiti;
private String name;
private Date startTime;
}
package com.dsr.dao;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Date;
@Data
@AllArgsConstructor
public class Activiti {
/**
* 申请人
*/
private String applyUser;
private int days;
private String reason;
private Date applyTime;
private String applyStatus;
/**
* 审核人
*/
private String auditor;
private String result;
private Date auditTime;
}
创建activiti接口:
package com.dsr.service;
import com.dsr.dao.Activiti;
import com.dsr.dao.ActivitiTask;
import java.util.List;
public interface AcitivitiService {
public Boolean startActiviti(Activiti act,String username);
public List<String> myActiviti(String username);
public List<ActivitiTask> myApproval(String name);
public Boolean passApproval(String name,ActivitiTask task);
public List<Activiti> myActivitiRecord(String name);
public List<Activiti> myApprovalRecord(String name);
}
创建接口实现类:
package com.dsr.service.impl;
import com.dsr.dao.Activiti;
import com.dsr.dao.ActivitiTask;
import com.dsr.service.AcitivitiService;
import com.dsr.util.ActivitiUtil;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.sql.Date;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
RepositoryService: 流程仓库Service,用于管理流程仓库,例如:部署,删除,读取流程资源
IdentityService:身份Service,可以管理,查询用户,组之间的关系
RuntimeService:运行时Service,可以处理所有正在运行状态的流程实例,任务等
TaskService:任务Service,用于管理,查询任务,例如:签收,办理,指派等
HistoryService:历史Service,可以查询所有历史数据,例如:流程实例,任务,活动,变量,附件等
FormService:表单Service,用于读取和流程,任务相关的表单数据
ManagementService:引擎管理Service,和具体业务无关,主要是可以查询引擎配置,数据库,作业等
DynamicBpmnService:一个新增的服务,用于动态修改流程中的一些参数信息等,是引擎中的一个辅助的服务
*/
@Service
@Slf4j
public abstract class ActivitiServiceImpl implements AcitivitiService {
private static final String PROCESS_KEY = "demo1";
private static final String NEXT_ASSIGNEE = "dsr";
@Resource
private RuntimeService runtimeService;
@Resource
private IdentityService identityService;
@Resource
private TaskService taskService;
@Resource
private HistoryService historyService;
@Resource
private RepositoryService repositoryService;
@Override
public Boolean startActiviti(Activiti act, String username) {
log.info("========流程开始========");
/*认证用户的作用是设置流程发起人:在流程开始之前设置,会自动在表ACT_HI_PROCINST 中的START_USER_ID_中设置用户ID
用来设置启动流程的人员ID,引擎会自动把用户ID保存到activiti:initiator中*/
try{
identityService.setAuthenticatedUserId(username);
//开始
ProcessInstance pro = runtimeService.startProcessInstanceByKey(PROCESS_KEY);
String id = pro.getId();
Task currentTask = taskService.createTaskQuery().processInstanceId(pro.getId()).singleResult();
String taskId = currentTask.getId();
// 申明任务人
//taskService.claim(currentTask.getId(), userName);
taskService.setAssignee(taskId,username);
Map<String,Object> vars = new HashMap<>(4);
vars.put("applyUser", username);
vars.put("days", act.getDays());
vars.put("reason", act.getReason());
//在此方法中,Vaction 是申请时的具体信息,在完成“申请报销”任务时,可以将这些信息设置成参数。
taskService.complete(currentTask.getId(),vars);
Task task = taskService.createTaskQuery().processInstanceId(id).singleResult();
String taskNext = task.getId();
taskService.setAssignee(taskNext,NEXT_ASSIGNEE);
} catch (Exception e)
{
e.printStackTrace();
}
return true;
}
@Override
public List<String> myActiviti(String username) {
List<ProcessInstance> instanceList = runtimeService.createProcessInstanceQuery().startedBy(username).list();
List<String> activitiList = new ArrayList<>();
for(ProcessInstance instance : instanceList){
Activiti activiti =getActiviti(instance);
activitiList.add(String.valueOf(activiti));
}
return activitiList;
}
private Activiti getActiviti(ProcessInstance instance){
Integer days = runtimeService.getVariable(instance.getId(), "days", Integer.class);
String reason = runtimeService.getVariable(instance.getId(), "reason", String.class);
Activiti activiti = new Activiti();
activiti.setApplyUser(instance.getStartUserId());
activiti.setDays(days);
activiti.setReason(reason);
Date startTime = (Date) instance.getStartTime(); // activiti 6 才有
activiti.setApplyTime(startTime);
activiti.setApplyStatus(instance.isEnded() ? "申请结束" : "等待审批");
return activiti;
};
@Override
public List<ActivitiTask> myApproval(String name) {
name = "dsr";
List<Task> taskList = taskService.createTaskQuery().taskAssignee(name).orderByTaskCreateTime().desc().list();
List<ActivitiTask> activitiTaskList = new ArrayList<>();
for(Task task : taskList){
ActivitiTask activitiTask = new ActivitiTask();
activitiTask.setId(task.getId());
activitiTask.setName(task.getName());
activitiTask.setStartTime((Date) task.getCreateTime());
String instanceId = task.getProcessInstanceId();
ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
Activiti activiti = getActiviti(instance);
activitiTask.setActiviti(activiti);
activitiTaskList.add(activitiTask);
}
return activitiTaskList;
}
@Override
public Boolean passApproval(String name, ActivitiTask task) {
name = "dsr";
String taskId = task.getId();
String result = task.getActiviti();
Map<String, Object> vars = new HashMap<>();
vars.put("result", result);
vars.put("auditor", name);
//Date time = new Date();
vars.put("auditTime",new java.util.Date());
//taskService.claim(taskId, userName);
taskService.setAssignee(taskId, name);
taskService.complete(taskId, vars);
return true;
}
@Override
public List<Activiti> myActivitiRecord(String name) {
List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery()
.processDefinitionKey(PROCESS_KEY).startedBy(name).finished().orderByProcessInstanceEndTime().desc().list();
List<Activiti> activitiList = new ArrayList<>();
for(HistoricProcessInstance historic : historicProcessInstances){
Activiti activiti = new Activiti();
activiti.setApplyUser(historic.getStartUserId());
activiti.setApplyTime((Date) historic.getStartTime());
activiti.setApplyStatus("申请结束");
List<HistoricVariableInstance> varInstanceList = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(historic.getId()).list();
ActivitiUtil.setVars(activiti, varInstanceList);
activitiList.add(activiti);
}
return activitiList;
}
@Override
public List<Activiti> myApprovalRecord(String name) {
name = "dsr";
List<HistoricProcessInstance> hisProInstance = historyService.createHistoricProcessInstanceQuery()
.processDefinitionKey(PROCESS_KEY).involvedUser(name).finished()
.orderByProcessInstanceEndTime().desc().list();
List<String> auditTaskNameList = new ArrayList<>();
auditTaskNameList.add("经理审批");
auditTaskNameList.add("老板审批");
List<Activiti> activitiList = new ArrayList<>();
for (HistoricProcessInstance hisInstance : hisProInstance) {
Activiti activiti = new Activiti();
activiti.setApplyUser(hisInstance.getStartUserId());
activiti.setApplyStatus("申请结束");
activiti.setApplyTime((Date) hisInstance.getStartTime());
List<HistoricVariableInstance> varInstanceList = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(hisInstance.getId()).list();
ActivitiUtil.setVars(activiti, varInstanceList);
activitiList.add(activiti);
}
return activitiList;
}
}
最后则需要创建对应controler类和html文件。
package com.dsr.controller;
import com.dsr.dao.Activiti;
import com.dsr.dao.ActivitiTask;
import com.dsr.service.AcitivitiService;
import org.activiti.engine.identity.User;
import org.activiti.engine.repository.Model;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
import java.util.List;
@Controller
@RequestMapping("/activiti")
public class ActivitiController {
@Autowired
private AcitivitiService acitivitiService;
@RequestMapping(value = "/allRecord", method = RequestMethod.GET)
public String allRecord(HttpSession session, Model model){
//myActiviti();
List<Activiti> list = acitivitiService.myActiviti();
model.add("list",list);
//myApproval();
List<ActivitiTask> list3 = acitivitiService.myApproval(session);
model.addAttribute("list3",list3);
//myActivitiRecord();
List<Activiti> list2 = acitivitiService.myActivitiRecord(session);
model.addAttribute("list2",list2);
//myApprovalRecord();
List<Activiti> list4 = acitivitiService.myActivitiRecord(session);
model.addAttribute("list4",list4);
return "activitiList";
}
@RequestMapping(value = "/createActiviti", method = RequestMethod.POST)
@ResponseBody
public Object createActiviti(Activiti vac,HttpSession session){
ResponseMsg<Object> msg = new ResponseMsg<Object>();
SecurityProperties.User user=(SecurityProperties.User)session.getAttribute("user");
String userName = user.getUsername();
activitiService.startActiviti(vac, userName);
msg.setCode(0);
return msg;
}
//我正在申请的假
@RequestMapping(value = "/myActiviti", method = RequestMethod.GET)
public List<Activiti> myActiviti(HttpSession session) {
User user=(User)session.getAttribute("user");
String userName = user.getUsername();
List<Activiti> list = acitivitiService.myActiviti(userName);
return list;
}
//待我审核的请假
@RequestMapping(value = "/myApproval", method = RequestMethod.GET)
public List<ActivitiTask> myApproval(HttpSession session) {
User user=(User)session.getAttribute("user");
//String userName = user.getUsername();
String userName = "huangxu2";
List<ActivitiTask> list3 = acitivitiService.myApproval(userName);
return list3;
}
@RequestMapping(value = "/passApproval", method = RequestMethod.POST)
@ResponseBody
public Object passApproval(String id,String result,HttpSession session) {
ResponseMsg<Object> msg = new ResponseMsg<Object>();
User user=(User)session.getAttribute("user");
ActivitiTask activitiTask = new ActivitiTask();
Activiti activiti = new Activiti();
activitiTask.setId(id);
activiti.setResult(result);
activitiTask.setActiviti(activiti);
String userName = user.getLastName();
acitivitiService.passApproval(userName, activitiTask);
msg.setCode(0);
return msg;
}
//我申请过的假
@RequestMapping(value = "/myActivitiRecord", method = RequestMethod.GET)
public List<Activiti> myActivitiRecord(HttpSession session) {
User user=(User)session.getAttribute("user");
String userName = user.getLastName();
List<Activiti> list2 = acitivitiService.myActivitiRecord(userName);
return list2;
}
//我的审核记录
@RequestMapping(value = "/myApprovalRecord", method = RequestMethod.GET)
public List<Activiti> myApprovalRecord(HttpSession session) {
User user=(User)session.getAttribute("user");
String userName = user.getFirstName();
List<Activiti> list4 = acitivitiService.myApprovalRecord(userName);
return list4;
}
}
若之前在springboot中未能加在web模块,可以在idea中配置:
点击最后一个选择Modules->Web->
添加后可以看到:
在webapp中添加html文件:
<!DOCTYPE html>
<html lang="UTF-8">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div ng-controller="myAudit">
<h2 ng-init="myAudit()">待我审核</h2>
<table border="0">
<tr>
<td>任务名称</td>
<td>任务时间</td>
<td>申请人</td>
<td>申请时间</td>
<td>天数</td>
<td>事由</td>
<td>操作</td>
</tr>
<tr ng-repeat="vacTask in vacTaskList">
<td>{{vacTask.name}}</td>
<td>{{vacTask.startTime | date:'yyyy-MM-dd HH:mm:ss'}}</td>
<td>{{vacTask.vac.applyUser}}</td>
<td>{{vacTask.vac.applyTime | date:'yyyy-MM-dd HH:mm:ss'}}</td>
<td>{{vacTask.vac.days}}</td>
<td>{{vacTask.vac.reason}}</td>
<td>
<button type="button" ng-click="passAudit(vacTask.id, 1)">审核通过</button>
<button type="button" ng-click="passAudit(vacTask.id, 0)">审核拒绝</button>
</td>
</tr>
</table>
</div>
app.controller("myAudit", function ($scope, $http, $window) {
$scope.vacTaskList = [];
$scope.myAudit = function () {
$http.get(
"/myAudit"
).then(function (response) {
$scope.vacTaskList = response.data;
})
};
$scope.passAudit = function (taskId, result) {
$http.post(
"/passAudit",
{
"id": taskId,
"vac": {
"result": result >= 1 ? "审核通过" : "审核拒绝"
}
}
).then(function (response) {
if (response.data === true) {
alert("操作成功!");
$window.location.reload();
} else {
alert("操作失败!");
}
})
}
});
</body>
</html>
由于目前controller层和html文件还存在问题,controller层采用了shiro,本人正在调试,后续会更新正确的controller层和html文件。 shiro依赖配置:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.4.8</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!-- 配置 fork 进行热部署支持 -->
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>