前言
想要在自己的代码中集成activiti这是一个复杂而长期的工作。集成是很容易的但是能和自己的权限管理和业务结合起来,就是一个不断扩展不断提炼的过程。若是一篇文章能全部讲解完毕这是一个困难的事情,这里我只能讲解一些必要的集成过程。
第一步、需要有一个流程图设计器集成到系统中(activiti-designer)。
第二步、需要集成activiti到自己的模块中
说明:中央仓库最后的版本只有7.1.0.M6若是需要集成activiti题最新的版本需要到activiti自己的仓库中下载:
https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases/
尤其是springboot升级到3.x的版本后需要下载高版本的activiti8.x
nacos中的配置
spring:
activiti:
# 数据库的更新策略drop-create
database-schema-update: true
#检查历史表是否存在
db-history-used: true
#校验流程文件,默认校验resources下的processes文件夹里的流程文件
check-process-definitions: false
# 记录历史等级 可配置的历史级别有none, acitivity, audit, full
history-level: full
#default never-fail(关闭springboot自动部署流程)
deployment-mode: never-fail
第三步、接入自己的权限管理系统
说明主要有两个类:UserDetailsService SecurityUtil.java
package com.cxbank.act.service.impl;
import com.cxbank.common.core.constant.SecurityConstants;
import com.cxbank.common.core.domain.R;
import com.cxbank.common.core.exception.ServiceException;
import com.cxbank.common.core.utils.StringUtils;
import com.cxbank.system.api.RemoteUserService;
import com.cxbank.system.api.domain.SysUser;
import com.cxbank.system.api.model.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class MyUserDetailsService implements UserDetailsService {
@Autowired
RemoteUserService remoteUserService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
R<LoginUser> userResult= remoteUserService.getUserInfo(username, SecurityConstants.INNER);
if (R.FAIL == userResult.getCode())
{
throw new ServiceException(userResult.getMsg());
}
if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
{
throw new ServiceException("用户:" + username + " 不存在");
}
LoginUser userInfo = userResult.getData();
SysUser user = userResult.getData().getSysUser();
return new User(user.getUserName(),user.getPassword(),
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ACTIVITI_USER"));
}
}
package com.cxbank.act.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import java.util.Collection;
@Component
public class SecurityUtil {
// 模拟调用了SpringSecurity 登录鉴权
private Logger logger = LoggerFactory.getLogger(SecurityUtil.class);
@Autowired
private 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");
}
logger.info("> Logged in as: " + username);
SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
@Override
public Collection<? extends GrantedAuthority> 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);
}
}
第四步、实现流程节点的Listener
这里面的listener一般有根绝角色找人、根据机构和角色找人、根据用户编号找人这三种,此处我列出其中一种根据角色获取参与者,当然这里面有自己的业务逻辑的代码比如代理、代办、交接的一些逻辑这些可以不用看。
package com.cxbank.act.listener;
import com.cxbank.act.domain.ActExtendAgent;
import com.cxbank.act.domain.ActExtendAgentFlow;
import com.cxbank.act.domain.ActExtendHandover;
import com.cxbank.act.domain.ActExtendHandoverFlow;
import com.cxbank.act.mapper.ActExtendAgentMapper;
import com.cxbank.act.mapper.ActExtendHandoverMapper;
import com.cxbank.act.service.IWorkFlowParticipant;
import com.cxbank.act.utils.ActConstants;
import com.cxbank.common.core.exception.act.NoParticipantException;
import com.cxbank.system.api.domain.WFParticipant;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import org.activiti.engine.impl.el.FixedValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* 功能描述:
*
* @author zhangbo
* @created 2020年11月21日
* @version 1.0.0
*/
@Component(value = "taskSetUserByRoleListener")
public class TaskSetUserByRoleListener implements TaskListener {
@Autowired
IWorkFlowParticipant workFlowParticipant;
@Autowired
private ActExtendAgentMapper actExtendAgentMapper;
@Autowired
private ActExtendHandoverMapper actExtendHandoverMapper;
private static final Logger log = LoggerFactory.getLogger(TaskSetUserByRoleListener.class);
String EVENTNAME_CREATE = "create";
String EVENTNAME_ASSIGNMENT = "assignment";
String EVENTNAME_COMPLETE = "complete";
String EVENTNAME_DELETE = "delete";
private FixedValue roleCode;
public FixedValue getRoleCode() {
return roleCode;
}
public void setRoleCode(FixedValue roleCode) {
this.roleCode = roleCode;
}
/**
*/
private static final long serialVersionUID = 1L;
/*
* (non-Javadoc)
*
* @see
* org.activiti.engine.delegate.TaskListener#notify(org.activiti.engine.delegate
* .DelegateTask)
*/
@Override
public void notify(DelegateTask delegateTask) {
String eventName = delegateTask.getEventName();
Object role = roleCode.getValue(delegateTask);
if (null==role || "".equals(role.toString().trim()) ){
log.error("流程:"+delegateTask.getProcessInstanceId()+"的"+delegateTask.getTaskDefinitionKey()+"节点参与角色流程图中未配置");
throw new NoParticipantException("下一节点的参与者未配置,请联系系统管理员!");
}
//获取当前节点的参与者
List<WFParticipant> userCodes = workFlowParticipant.getParticipantByRole(role.toString().trim());
if(userCodes.size()==0){
log.error("流程:"+delegateTask.getProcessInstanceId()+"的"+delegateTask.getTaskDefinitionKey()+"节点参与角色{}下未配置人员",role.toString().trim());
throw new NoParticipantException("下一节点的参与人员未获取到,请联系系统管理员!");
}
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
String tenantId=delegateTask.getTenantId();
String key=engine.getRepositoryService().createProcessDefinitionQuery().processDefinitionId(delegateTask.getProcessDefinitionId()).singleResult().getKey();
//流程是否为回退
Object isBackVarValue=delegateTask.getVariable(ActConstants.OPERATE_IS_BACK_VAR_NAME);
boolean isBack= (null != isBackVarValue && Boolean.parseBoolean(isBackVarValue.toString()));
List<String> wFParticipants =new ArrayList<String>();
String userId="";
ActExtendHandoverFlow actExtendHandoverFlow=null;
ActExtendAgentFlow actExtendAgentFlow=null;
List<ActExtendHandover> actExtendHandovers=null;
List<ActExtendAgent> actExtendAgents=null;
for (WFParticipant wfp:userCodes){
userId=wfp.getUserCode();
actExtendHandoverFlow =new ActExtendHandoverFlow();
actExtendHandoverFlow.setCreateBy(userId);
actExtendHandoverFlow.setTenantId(tenantId);
actExtendHandoverFlow.setActKey(key);
actExtendHandoverFlow.setProcessInstanceId(delegateTask.getProcessInstanceId());
actExtendHandovers = actExtendHandoverMapper.selectFlowNodeHandoverUsers(actExtendHandoverFlow);
//当有交接用户 且当前节点是退回的节点 直接进行交接
if(null!=actExtendHandovers && actExtendHandovers.size()>0 && isBack){
for (ActExtendHandover actExtendHandover:actExtendHandovers){
wFParticipants.add(actExtendHandover.getReceiverId());
}
}else {
/**
* 当没有交接时候 或 不是回退节点 此时处理代理、代办人员
*/
actExtendAgentFlow = new ActExtendAgentFlow();
actExtendAgentFlow.setCreateBy(userId);
actExtendAgentFlow.setTenantId(tenantId);
actExtendAgentFlow.setActKey(key);
actExtendAgents = actExtendAgentMapper.selectFlowNodeEntrustUsers(actExtendAgentFlow);
if (null == actExtendAgents || actExtendAgents.size() == 0) {
wFParticipants.add(wfp.getUserCode());
continue;
}
boolean b=true;
for (ActExtendAgent actExtendAgent : actExtendAgents) {
if (ActConstants.entrustType.DO_STH_FOR_SB.getInfo().equals(actExtendAgent.getEntrustType())) {
b=false;
break;
}
}
List<String> wFParticipants2 = new ArrayList<String>();
//没有代办就添加此用户
if(b) {
wFParticipants.add(wfp.getUserCode());
}
//添加代理代办用户
for (ActExtendAgent actExtendAgent : actExtendAgents) {
wFParticipants.add(actExtendAgent.getAgentId());
}
}
}
if (EVENTNAME_CREATE.endsWith(eventName)) {
if(wFParticipants.size()>0) {
log.info("开始为任务id为:"+delegateTask.getId()+"的任务设置参与者。");
delegateTask.addCandidateUsers(wFParticipants);
}else{
//log.error("任务id为:"+delegateTask.getId()+"的参与者未获取到");
throw new NoParticipantException("下一步的参与者未获取到,请联系系统管理员!");
}
} else if (EVENTNAME_ASSIGNMENT.endsWith(eventName))
System.out.println("assignment===任务分配");
else if (EVENTNAME_COMPLETE.endsWith(eventName))
System.out.println("complete===任务完成");
else if (EVENTNAME_DELETE.endsWith(eventName))
System.out.println("delete===任务删除");
}
}
第五步、前端整合
前端整合,我这边儿是把流程统一发起、统一审核,这里面真核起来需要和流程图结合起来当然这需要许多扩展表和后台java代码的支撑。
我认为你只需要把握流程图中节点中配置的formkey这个关键属性以此和自己的前端代码结合使用就可整合。
基础支撑和扩展
下边儿是我集成的一些截图片段:
流程部署:主要是在线绘制流程发布后再设置相关配置。
设置流程图属性、或选中流程节点设置相关数据、操作等等
实例管理:针对所有的流程实例管理,可以再此处对流程进行前进后退等操作方便管理人员管理所有流程实例。
流程统一发起
流程统一审核: