引入依赖:
org.activiti
activiti-spring-boot-starter-basic
6.0.0
com.alibaba
druid
0.2.16
# activiti default configuration
#在activiti的默认配置中,process-definition-location-prefix 是指定activiti流程描述文件的前缀(即路径),
#启动时,activiti就会去寻找此路径下的流程描述文件,并且自动部署;suffix 是一个String数组,表示描述文件的默 认后缀名,默认以上两种。
activiti:
database-schema-update: true #是否每次都更新数据库
# 自动部署验证设置:true-开启(默认)、false-关闭
check-process-definitions: true # activti是否自动部署
process-definition-location-prefix: classpath:/processes/
#process-definition-location-suffixes: .bpmn
history-level: full
#db-history-used: true
db-identity-used: false
https://blog.csdn.net/a8532156/article/details/78427545
package com.shiro_learning.demo.config.activiti;
import org.activiti.engine.*;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.io.IOException;
/**
创建activiti提供的各种服务
*/
@Configuration
public class ActivitiConfig{
private Logger logger = LoggerFactory.getLogger(ActivitiConfig.class);
@Value("${spring.activiti.database-schema-update}")
private String databaseSchemaUpdate;
@Value("${spring.activiti.db-identity-used}")
private boolean dbIdentityUsed;
@Value("${spring.datasource.url}")
private String dbUrl;
@Bean
public ProcessEngine processEngine(DataSourceTransactionManager transactionManager, DataSource dataSource) throws IOException {
logger.info("==========activiti=======开始==============");
SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration();
/* 自动部署已有的流程文件
作用相当于 (据bpmn文件部署流程repositoryService.createDeployment().addClasspathResource("singleAssignee.bpmn").deploy();)*/
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(ResourceLoader.CLASSPATH_URL_PREFIX + "processes/*.bpmn");
configuration.setTransactionManager(transactionManager);
//设置数据源
configuration.setDataSource(dataSource);
//是否每次都更新数据库
//configuration.setDatabaseSchemaUpdate(databaseSchemaUpdate);
configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE);
//configuration.setAsyncExecutorActivate(false);
configuration.setDeploymentResources(resources);
//设置是否使用activti自带的用户体系
configuration.setDbIdentityUsed(dbIdentityUsed);
return configuration.buildProcessEngine();
}
/**
* 工作流仓储服务
* @param processEngine
* @return
*/
@Bean
public RepositoryService repositoryService(ProcessEngine processEngine) {
return processEngine.getRepositoryService();
}
/**
* 工作流运行服务
* @param processEngine
* @return
*/
@Bean
public RuntimeService runtimeService(ProcessEngine processEngine) {
return processEngine.getRuntimeService();
}
/**
* 工作流任务服务
* @param processEngine
* @return
*/
@Bean
public TaskService taskService(ProcessEngine processEngine) {
return processEngine.getTaskService();
}
/**
* 工作流历史数据服务
* @param processEngine
* @return
*/
@Bean
public HistoryService historyService(ProcessEngine processEngine) {
return processEngine.getHistoryService();
}
/**
* 工作流管理服务
* @param processEngine
* @return
*/
@Bean
public ManagementService managementService(ProcessEngine processEngine) {
return processEngine.getManagementService();
}
/**
* 工作流唯一服务
* @param processEngine
* @return
*/
@Bean
public IdentityService identityService(ProcessEngine processEngine) {
return processEngine.getIdentityService();
}
}
package com.shiro_learning.demo.controller;
import com.shiro_learning.demo.entity.Activiti;
import com.shiro_learning.demo.entity.ActivitiTask;
import com.shiro_learning.demo.entity.ResponseMsg;
import com.shiro_learning.demo.entity.User;
import com.shiro_learning.demo.service.ActivitiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpSession;
import java.util.List;
@Controller
@RequestMapping("/activity")
public class ActivitiController {
@Autowired
private ActivitiService activitiService;
@RequestMapping(value = "/allRecord", method = RequestMethod.GET)
//@ResponseBody
public String allRecord(HttpSession session,Model model) {
//myActiviti();
List list = myActiviti(session);
model.addAttribute("list",list);
//myApproval();
List list3 = myApproval(session);
model.addAttribute("list3",list3);
//myActivitiRecord();
List list2 = myActivitiRecord(session);
model.addAttribute("list2",list2);
//myApprovalRecord();
List list4 = myApprovalRecord(session);
model.addAttribute("list4",list4);
return "activitiList";
}
@RequestMapping(value = "/createActiviti", method = RequestMethod.POST)
@ResponseBody
public Object createActiviti(Activiti vac,HttpSession session){
ResponseMsg
package com.shiro_learning.demo.entity;
import java.util.Date;
public class ActivitiTask {
private String id;
private String name;
private Activiti activiti;
private Date createTime;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Activiti getActiviti() {
return activiti;
}
public void setActiviti(Activiti activiti) {
this.activiti = activiti;
}
}
package com.shiro_learning.demo.entity;
import java.io.Serializable;
import java.util.Date;
public class Activiti implements Serializable{
/**
* 申请人
*/
private String applyUser;
private int days;
private String reason;
private Date applyTime;
private String applyStatus;
/**
* 审核人
*/
private String auditor;
private String result;
private Date auditTime;
public String getApplyUser() {
return applyUser;
}
public void setApplyUser(String applyUser) {
this.applyUser = applyUser;
}
public int getDays() {
return days;
}
public void setDays(int days) {
this.days = days;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public Date getApplyTime() {
return applyTime;
}
public void setApplyTime(Date applyTime) {
this.applyTime = applyTime;
}
public String getApplyStatus() {
return applyStatus;
}
public void setApplyStatus(String applyStatus) {
this.applyStatus = applyStatus;
}
public String getAuditor() {
return auditor;
}
public void setAuditor(String auditor) {
this.auditor = auditor;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public Date getAuditTime() {
return auditTime;
}
public void setAuditTime(Date auditTime) {
this.auditTime = auditTime;
}
}
在这里插入代码片
package com.shiro_learning.demo.service;
import com.shiro_learning.demo.entity.Activiti;
import com.shiro_learning.demo.entity.ActivitiTask;
import java.util.List;
public interface ActivitiService {
public Boolean startActiviti(Activiti vac, String userName);
public List myActiviti(String userName);
public List myApproval(String userName);
public Boolean passApproval(String userName, ActivitiTask activitiTask);
public List myActivitiRecord(String userName);
public List myApprovalRecord(String userName);
}
package com.shiro_learning.demo.service.impl;
import com.shiro_learning.demo.util.ActivitiUtil;
import com.shiro_learning.demo.entity.Activiti;
import com.shiro_learning.demo.entity.ActivitiTask;
import com.shiro_learning.demo.service.ActivitiService;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
/**
RepositoryService: 流程仓库Service,用于管理流程仓库,例如:部署,删除,读取流程资源
IdentityService:身份Service,可以管理,查询用户,组之间的关系
RuntimeService:运行时Service,可以处理所有正在运行状态的流程实例,任务等
TaskService:任务Service,用于管理,查询任务,例如:签收,办理,指派等
HistoryService:历史Service,可以查询所有历史数据,例如:流程实例,任务,活动,变量,附件等
FormService:表单Service,用于读取和流程,任务相关的表单数据
ManagementService:引擎管理Service,和具体业务无关,主要是可以查询引擎配置,数据库,作业等
DynamicBpmnService:一个新增的服务,用于动态修改流程中的一些参数信息等,是引擎中的一个辅助的服务
*/
@Service
public class ActivitiServiceImpl implements ActivitiService {
private Logger logger = LoggerFactory.getLogger(ActivitiServiceImpl.class);
//所运行工作流的名字
//private static final String PROCESS_DEFINE_KEY = "test.bpmn";
private static final String PROCESS_DEFINE_KEY = "demo2";
private static final String NEXT_ASSIGNEE = "huangxu2";
@Resource
private RuntimeService runtimeService;
@Resource
private IdentityService identityService;
@Resource
private TaskService taskService;
@Resource
private HistoryService historyService;
@Resource
private RepositoryService repositoryService;
/* //工作流运行服务
@Autowired
private RuntimeService runtimeService;
//工作流唯一服务
@Autowired
private IdentityService identityService;
//工作流任务服务
@Autowired
private TaskService taskService;
//工作流管理服务
@Autowired
private HistoryService historyService;
@Autowired
private RepositoryService repositoryService;
*/
/**
* 开始流程
* @param vac
* @param userName
* @return
*/
@Override
public Boolean startActiviti(Activiti vac, String userName) {
logger.info("method startActivityDemo begin....");
/*认证用户的作用是设置流程发起人:在流程开始之前设置,会自动在表ACT_HI_PROCINST 中的START_USER_ID_中设置用户ID
用来设置启动流程的人员ID,引擎会自动把用户ID保存到activiti:initiator中*/
try{
identityService.setAuthenticatedUserId(userName);
// 开始流程
ProcessInstance pi = runtimeService.startProcessInstanceByKey(PROCESS_DEFINE_KEY);
String processId = pi.getId();
logger.info("===============processId==================="+processId);
// 查询当前任务
Task currentTask = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
String taskId1 = currentTask.getId();
logger.info("===============taskId1==================="+taskId1);
// 申明任务人
//taskService.claim(currentTask.getId(), userName);
taskService.setAssignee(taskId1, userName);
Map vars = new HashMap<>(4);
vars.put("applyUser", userName);
vars.put("days", vac.getDays());
vars.put("reason", vac.getReason());
//在此方法中,Vaction 是申请时的具体信息,在完成“申请请假”任务时,可以将这些信息设置成参数。
//完成第一步申请
taskService.complete(currentTask.getId(), vars);
// 到了下一个任务, 应该在此处指派任务由谁来处理
// 重新获取当前任务
Task task = taskService.createTaskQuery().processInstanceId(processId).singleResult();
String taskId2 = task.getId();
logger.info("===============taskId2==================="+taskId2);
taskService.setAssignee(taskId2, NEXT_ASSIGNEE);
}catch (Exception e){
e.printStackTrace();
}
return true;
}
@Override
public List myActiviti(String userName) {
List instanceList = runtimeService.createProcessInstanceQuery().startedBy(userName).list();
List activitisList = new ArrayList<>();
for (ProcessInstance instance : instanceList) {
Activiti activiti = getActiviti(instance);
activitisList.add(activiti);
}
return activitisList;
}
/**
* 查询需要自己审批
* @param userName
* @return
*/
@Override
public List myApproval(String userName) {
userName = "huangxu2";
List taskList = taskService.createTaskQuery().taskAssignee(userName)
.orderByTaskCreateTime().desc().list();
// 多此一举 taskList中包含了以下内容(用户的任务中包含了所在用户组的任务)
// Group group = identityService.createGroupQuery().groupMember(userName).singleResult();
// List list = taskService.createTaskQuery().taskCandidateGroup(group.getId()).list();
// taskList.addAll(list);
List activitiTaskList = new ArrayList<>();
for (Task task : taskList) {
ActivitiTask activitiTask = new ActivitiTask();
activitiTask.setId(task.getId());
activitiTask.setName(task.getName());
activitiTask.setCreateTime(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;
}
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 = instance.getStartTime(); // activiti 6 才有
activiti.setApplyTime(startTime);
activiti.setApplyStatus(instance.isEnded() ? "申请结束" : "等待审批");
return activiti;
}
/**
* Activiti任务认领
* taskService.setAssignee(String taskId, String userId);
* taskService.claim(String taskId, String userId);
* taskService.setOwner(String taskId, String userId);
* setAssignee和claim两个的区别是在认领任务时,claim会检查该任务是否已经被认领,如果被认领则会抛出ActivitiTaskAlreadyClaimedException ,而setAssignee不会进行这样的检查,其他方面两个方法效果一致。
* setOwner和setAssignee的区别在于,setOwner是在代理任务时使用,代表着任务的归属者,而这时,setAssignee代表的是代理办理者,
*
* 举个例子来说,公司总经理现在有个任务taskA,去核实一下本年度的财务报表,他现在又很忙没时间,于是将该任务委托给其助理进行办理,此时,就应该这么做
* taskService.setOwner(taskA.getId(), 总经理.getId());
* taskService.setAssignee/claim(taskA.getId(), 助理.getId());
*/
/**
* 审批操作
* @param userName
* @param activitiTask
* @return
*/
/**
* 同理,result是审批的结果,也是在完成审批任务时需要传入的参数;taskId是刚才老板查询到的当前需要自己完成的审批任务ID。
* (如果流程在这里设置分支,可以通过判断result的值来跳转到不同的任务)
*/
@Override
public Boolean passApproval(String userName, ActivitiTask activitiTask) {
userName = "huangxu2";
String taskId = activitiTask.getId();
String result = activitiTask.getActiviti().getResult();
Map vars = new HashMap<>();
vars.put("result", result);
vars.put("auditor", userName);
vars.put("auditTime", new Date());
//taskService.claim(taskId, userName);
taskService.setAssignee(taskId, userName);
taskService.complete(taskId, vars);
return true;
}
/**
* 查询已完成的请假记录
* 由于已完成的请假在数据库runtime表中查不到(runtime表只保存正在进行的流程示例信息),所以需要在history表中查询。
* @param userName
* @return
*/
@Override
public List myActivitiRecord(String userName) {
List hisProInstance = historyService.createHistoricProcessInstanceQuery()
.processDefinitionKey(PROCESS_DEFINE_KEY).startedBy(userName).finished()
.orderByProcessInstanceEndTime().desc().list();
List activitiList = new ArrayList<>();
for (HistoricProcessInstance hisInstance : hisProInstance) {
Activiti activiti = new Activiti();
activiti.setApplyUser(hisInstance.getStartUserId());
activiti.setApplyTime(hisInstance.getStartTime());
activiti.setApplyStatus("申请结束");
List varInstanceList = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(hisInstance.getId()).list();
ActivitiUtil.setVars(activiti, varInstanceList);
activitiList.add(activiti);
}
return activitiList;
}
/**
* 我审批的记录列表
* @param userName
* @return
*/
@Override
public List myApprovalRecord(String userName) {
userName = "huangxu2";
List hisProInstance = historyService.createHistoricProcessInstanceQuery()
.processDefinitionKey(PROCESS_DEFINE_KEY).involvedUser(userName).finished()
.orderByProcessInstanceEndTime().desc().list();
List auditTaskNameList = new ArrayList<>();
auditTaskNameList.add("经理审批");
auditTaskNameList.add("总监审批");
List activitiList = new ArrayList<>();
for (HistoricProcessInstance hisInstance : hisProInstance) {
/*List hisTaskInstanceList = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(hisInstance.getId()).processFinished()
.taskAssignee(userName)
.taskNameIn(auditTaskNameList)
.orderByHistoricTaskInstanceEndTime().desc().list();
boolean isMyAudit = false;
for (HistoricTaskInstance taskInstance : hisTaskInstanceList) {
if (taskInstance.getAssignee().equals(userName)) {
isMyAudit = true;
}
}
if (!isMyAudit) {
continue;
}*/
Activiti activiti = new Activiti();
activiti.setApplyUser(hisInstance.getStartUserId());
activiti.setApplyStatus("申请结束");
activiti.setApplyTime(hisInstance.getStartTime());
List varInstanceList = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(hisInstance.getId()).list();
ActivitiUtil.setVars(activiti, varInstanceList);
activitiList.add(activiti);
}
return activitiList;
}
}
package com.shiro_learning.demo.util;
import org.activiti.engine.history.HistoricVariableInstance;
import java.lang.reflect.Field;
import java.util.List;
public class ActivitiUtil {
/**
* 将历史参数列表设置到实体中去
* @param entity 实体
* @param varInstanceList 历史参数列表
*/
public static void setVars(T entity, List varInstanceList) {
Class> tClass = entity.getClass();
try {
for (HistoricVariableInstance varInstance : varInstanceList) {
Field field = tClass.getDeclaredField(varInstance.getVariableName());
if (field == null) {
continue;
}
field.setAccessible(true);
field.set(entity, varInstance.getValue());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
activiti 插件安装:https://blog.csdn.net/gozhuyinglong/article/details/80336765
activiti 乱码处理: http://www.cnblogs.com/mymelody/p/6049291.html
activiti 任务认领: https://www.cnblogs.com/boulder/p/3658528.html
activiti 报错 SQL语句报com.alibaba.druid.sql.parser.ParserException: TODO IDENTIFIER cross
处理方法:https://www.cnblogs.com/zhuangjixiang/p/4147802.html
https://www.oschina.net/question/253454_107787
整合activiti的 druid 要求的版本为0.2.16 或 1.0.9
activiti工作流6大service服务的简单介绍:https://blog.csdn.net/a8532156/article/details/78427545