版本:activiti7
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>7.0.0.Beta2</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--activiti与springboot整合包-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.0.0.Beta2</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
发布
流程图
会签
这里是将流程图通过bpmn.io插件设计好流程图,存放到服务器端
public AjaxResult publishBpm(BpmFormVo bpmFormVo) {
FileInputStream bpmnfileInputStream = null;
try{
BpmForm byId = getById(bpmFormVo.getId());
xml=BpmFormManager.xmlnsActivitiEmpty(xml);
xml=BpmFormManager.gatewaySupply(xml);
activitiFlowService.delFlowByDefKey(bpmFormVo.getCode());
repositoryService.createDeployment().addString(code+ ProcessConstants.SUFFIX, xml)
.name(code).key(code).deploy();
bpmFormVo.setPublishTime(new Date());
bpmFormVo.setState(1);
bpmFormMapper.publishBpm(bpmFormVo);
if("1".equals(byId.getCountersignature())){
bpmCountersignService.bpmPublishHandle(byId,xml);
}
}catch (Exception e){
e.printStackTrace();
return AjaxResult.error("部署失败");
}finally {
try {
if(bpmnfileInputStream!=null){
bpmnfileInputStream.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
return AjaxResult.success("部署成功");
}
重新发布需要做如下处理
@SneakyThrows
public void delFlowByDefKey(String code) {
List<String> deploymentIdByName = activitiFlowMapper.getDeploymentIdByName(code+ ProcessConstants.SUFFIX);
if(CollUtil.isEmpty(deploymentIdByName)){
return;
}
for(String did:deploymentIdByName){
repositoryService.deleteDeployment(did);
}
}
启动
@Override
public ProcessInstance startProcess(String definitionKey, String bussinessId,String userId,String userName, String projectId) {
String bpmCode=definitionKey;
Authentication.setAuthenticatedUserId(userId);
Map<String, Object> map=new HashMap<>();
map.put("state",ProcessConstants.AUDIT_YES);
map.put("assignee",userId);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(definitionKey, bussinessId, map);
Task task = taskService.createTaskQuery()
.processDefinitionKey(definitionKey)
.taskAssignee(userId) //设置任务的负责人
.orderByTaskCreateTime().desc().list().get(0);
if (task!=null){
taskService.addComment(task.getId(), task.getProcessInstanceId(), "流程提交","流程提交成功!");
startTaskComplete(bpmCode,projectId,task.getId(),null);
}
startLogProcessHandle(bpmCode,definitionKey,bussinessId,projectId,userId,userName,processInstance,task);
return processInstance;
}
将会签中的占位的数据赋值
@Override
public void startTaskComplete(String bpmCode,String projectId,String taskId, Map<String, Object> variables) {
BpmForm bpmForm = bpmFormService.queryInfoByCode(bpmCode, projectId);
if(variables==null){
variables=new HashMap<>();
}
if(bpmForm!=null&&"1".equals(bpmForm.getCountersignature())){
//会签模式
Object xmlByKey = activitiFlowMapper.getXmlByKey(bpmForm.getResCode() + ".bpmn");
Map<String, List<String>> stringListMap = bpmUtil.collectionUseMap(bpmForm, (byte[]) xmlByKey);
variables.putAll(stringListMap);
}
if(CollUtil.isEmpty(variables)){
taskService.complete(taskId);
}else{
taskService.complete(taskId,variables);
}
}
@SneakyThrows
public Map<String,List<String>> collectionUseMap(BpmForm bpmForm,byte[] resByte){
Map<String,List<String>> result=new HashMap<>();
if("0".equals(bpmForm.getCountersignature())){
return result;
}
String resStr = new String(resByte);
BpmnModel bpmnModel = ModelUtil.getBpmnModel(resStr);
Collection<UserTask> allUserTaskEvent = ModelUtil.getAllUserTaskEvent(bpmnModel);
if(CollUtil.isEmpty(allUserTaskEvent)){
return result;
}
SysRoleVo sysRoleVo=new SysRoleVo();
sysRoleVo.setProjectId(bpmForm.getProjectId());
List<SysRoleVo> sysRoleVos = sysRoleService.queryList(sysRoleVo);
SysDept sysDept=new SysDept();
sysDept.setProjectId(bpmForm.getProjectId());
List<SysDept> sysDepts = sysDeptService.queryList(sysDept);
for(UserTask userTask:allUserTaskEvent){
if(userTask.hasMultiInstanceLoopCharacteristics()){
MultiInstanceLoopCharacteristics loopCharacteristics = userTask.getLoopCharacteristics();
String inputDataItem = loopCharacteristics.getInputDataItem();
String coll = inputDataItem.replace("${", "").replace("}", "");
String[] groups = coll.split("_");
if("ROLE".equals(groups[0])){
List<String> idListByRoleId = getIdListByRoleId(groups[1], sysRoleVos).getUserIdList();
if(CollUtil.isNotEmpty(idListByRoleId)){
result.put(coll,idListByRoleId);
}
}else if("DEPT".equals(groups[0])){
List<String> idListByDeptId = getIdListByDeptId(groups[1], sysDepts).getUserIdList();
if(CollUtil.isNotEmpty(idListByDeptId)){
result.put(coll,idListByDeptId);
}
}
}
}
return result;
}
流程审批
@SneakyThrows
public CompletResultVo orSignCompletHandle(String taskId, String result, String remark, String definitionKey, Map<String, Object> variables, String userName){
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
taskService.addComment(taskId, task.getProcessInstanceId(), result,remark);
if(variables==null){
variables=new HashMap<>();
}
variables.put("state",result);
taskService.complete(taskId,variables);
CompletResultVo completResultVo=new CompletResultVo();
boolean afterHandle = afterHandle(task, userName, remark, result);
completResultVo.setAuditEndState(afterHandle);
return completResultVo;
}
boolean afterHandle(Task task,String userName,String remark,String result){
//存储审核日志信息
QueryWrapper queryWrapper=new QueryWrapper<>();
queryWrapper.eq("process_instance_id",task.getProcessInstanceId());
BussTask bussTask = bussTaskMapper.selectOne(queryWrapper);
//日志记录
completByTaskLogHandle(bussTask,task,task.getProcessInstanceId(),userName,remark, result);
//更新结束记录
return endHandle(task.getProcessInstanceId(),bussTask,userName);
}
判断流程结束后相关操作
boolean endHandle(String piid,BussTask bussTask,String userName){
List<Task> task1 = taskService.createTaskQuery().processInstanceId(piid).list();
bussTaskMapper.updateById(bussTask);
if(CollUtil.isEmpty(task1)){
bussTaskMapper.updateStatus(piid,ProcessConstants.AUDIT_NO);
endLogHandle(bussTask);
return true;
}
return false;
}
会签审批
@SneakyThrows
public CompletResultVo countersignCompletHandle(String taskId, String result, String remark,String definitionKey,String projectId,
String userName,Long userId,BpmForm bpmForm){
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
String taskDefinitionKey = task.getTaskDefinitionKey();
UserTask userTask = getUserTaskByNodeId(bpmForm.getResCode(), taskDefinitionKey);
if(userTask==null){
return orSignCompletHandle(taskId,result,remark,definitionKey,projectId,null,userName);
}
if(!userTask.hasMultiInstanceLoopCharacteristics()){
return orSignCompletHandle(taskId,result,remark,definitionKey,projectId,null,userName);
}
MultiInstanceLoopCharacteristics loopCharacteristics = userTask.getLoopCharacteristics();
String completionCondition = loopCharacteristics.getCompletionCondition();
String codeByInfo = BpmCountersignTypeEnum.getCodeByInfo(completionCondition);
Map<String,Object> variables=new HashMap<>();
QueryWrapper queryWrapper=new QueryWrapper<>();
queryWrapper.eq("process_instance_id",task.getProcessInstanceId());
BussTask bussTask = bussTaskMapper.selectOne(queryWrapper);
List<LogProcess> bussList = getLogs(bussTask.getBussId(), task.getProcessInstanceId());
List<BpmCountersignRecord> bpmCountersignRecords=new ArrayList<>();
if(CollUtil.isNotEmpty(bussList)){
BpmCountersignRecord bpmCountersignRecord=new BpmCountersignRecord();
bpmCountersignRecord.setExecutionId(bussList.get(0).getExecutionId());
bpmCountersignRecords= bpmCountersignService.queryListByExeId(bpmCountersignRecord);
}
BpmCountersign bpmCountersignByTaskInfo = bpmCountersignService.getBpmCountersignByTaskInfo(task.getTaskDefinitionKey(), bussTask.getDefinitionKey());
String[] user_split = bpmCountersignByTaskInfo.getUids().split(",");
if("all".equals(codeByInfo)){
if(ProcessConstants.AUDIT_NO.equals(result)){
variables.put("pass",true);
variables.put("state",ProcessConstants.AUDIT_NO);
taskService.complete(taskId,variables);
countersignRecordHandle(task,bpmForm,userName,userId,bpmCountersignByTaskInfo,result,remark,bussTask,false,true,bussList);
}else{
if(user_split.length<=(bpmCountersignRecords.size()+1)){
variables.put("pass",true);
variables.put("state",ProcessConstants.AUDIT_YES);
taskService.complete(taskId,variables);
countersignRecordHandle(task,bpmForm,userName,userId,bpmCountersignByTaskInfo,result,remark,bussTask,true,true,bussList);
}else{
variables.put("pass",false);
taskService.complete(taskId,variables);
countersignRecordHandle(task,bpmForm,userName,userId,bpmCountersignByTaskInfo,result,remark,bussTask,false,false,bussList);
}
}
}else if("half".equals(codeByInfo)){
if(user_split.length<=(bpmCountersignRecords.size()+1)){
int ok_users=0;
for(BpmCountersignRecord bpm:bpmCountersignRecords){
if(ProcessConstants.AUDIT_YES.equals(bpm.getAuditResult())){
ok_users+=1;
}
}
if(ProcessConstants.AUDIT_YES.equals(result)){
ok_users+=1;
}
String state=null;
BigDecimal divide = new BigDecimal(ok_users).divide(new BigDecimal(user_split.length), 2, BigDecimal.ROUND_HALF_UP)
double doubleValue = divide.doubleValue();
if(doubleValue>=0.5){
state=ProcessConstants.AUDIT_YES;
}else{
state=ProcessConstants.AUDIT_NO;
}
variables.put("state",state);
variables.put("pass",true);
taskService.complete(taskId,variables);
countersignRecordHandle(task,bpmForm,userName,userId,bpmCountersignByTaskInfo,result,remark,bussTask,state.equals(ProcessConstants.AUDIT_YES)?true:false,true,bussList);
}else{
variables.put("pass",false);
taskService.complete(taskId,variables);
countersignRecordHandle(task,bpmForm,userName,userId,bpmCountersignByTaskInfo,result,remark,bussTask,false,false,bussList);
}
}else if("single".equals(codeByInfo)){
if(ProcessConstants.AUDIT_YES.equals(result)) {
variables.put("pass",true);
variables.put("state",ProcessConstants.AUDIT_YES);
taskService.complete(taskId,variables);
countersignRecordHandle(task,bpmForm,userName,userId,bpmCountersignByTaskInfo,result,remark,bussTask,true,true,bussList);
}else{
if(user_split.length<=(bpmCountersignRecords.size()+1)){
variables.put("pass",true);
variables.put("state",ProcessConstants.AUDIT_NO);
taskService.complete(taskId,variables);
countersignRecordHandle(task,bpmForm,userName,userId,bpmCountersignByTaskInfo,result,remark,bussTask,false,true,bussList);
}else{
variables.put("pass",false);
taskService.complete(taskId,variables);
countersignRecordHandle(task,bpmForm,userName,userId,bpmCountersignByTaskInfo,result,remark,bussTask,false,false,bussList);
}
}
}
CompletResultVo completResultVo=new CompletResultVo();
List<LogProcess> bussList2 = getLogs(bussTask.getBussId(), task.getProcessInstanceId());
String aname="";
if(CollUtil.isNotEmpty(bussList2)){
aname=bussList2.get(0).getAssigneeName();
}else{
aname= bussList.get(0).getAssigneeName();
}
boolean endHandle = endHandle(task.getProcessInstanceId(), bussTask, aname);
completResultVo.setAuditEndState(endHandle);
return completResultVo;
}
//日志及操作记录
/**
*
* @param task
* @param bpmForm
* @param userName
* @param userId
* @param bpmCountersignByTaskInfo
* @param result 当前人的审核状态
* @param remark
* @param auditState 最终审核状态(会签)
* @param isEnd 当前节点是否全部完成
*/
void countersignRecordHandle(Task task,BpmForm bpmForm,String userName,Long userId,BpmCountersign bpmCountersignByTaskInfo,
String result,String remark,BussTask bussTask,boolean auditState,boolean isEnd,List<LogProcess> bussList){
LogProcess logRecord=null;
if(CollUtil.isEmpty(bussList)){
logRecord=new LogProcess();
logRecord.setProjectId(bpmForm.getProjectId());
String myExeId=UUID.randomUUID().toString();
logRecord.setExecutionId(myExeId);
logRecord.setBusinessId(bussTask.getBussId());
logRecord.setTaskId(task.getId());
logRecord.setAssigneeName(bpmCountersignByTaskInfo.getUnames());
logRecord.setAssignee(bpmCountersignByTaskInfo.getUids());
logRecord.setInstanceId(task.getProcessInstanceId());
logRecord.setDefinitionKey(bpmForm.getResCode());
if(isEnd){
logRecord.setStepType(auditState?ActivitiStepType.approved.getCode():ActivitiStepType.reject.getCode());
}else{
logRecord.setStepType(ActivitiStepType.unapproved.getCode());
}
logProcessMapper.insert(logRecord);
}else{
if(isEnd){
logRecord = bussList.get(0);
logRecord.setStepType(auditState?ActivitiStepType.approved.getCode():ActivitiStepType.reject.getCode());
logProcessMapper.updateById(logRecord);
}
}
BpmCountersignRecord bcr=new BpmCountersignRecord();
bcr.setNodeId(task.getTaskDefinitionKey());
bcr.setBpmCode(bpmForm.getCode());
bcr.setUserId(userId+"");
bcr.setUserName(userName);
if(logRecord!=null){
bcr.setLogId(logRecord.getId());
}else{
bcr.setLogId(bussList.get(0).getId());
}
bcr.setBusinessId(bussTask.getBussId());
bcr.setAuditRemark(remark);
bcr.setAuditResult(result);
bcr.setExecutionId(logRecord.getExecutionId());
bcr.setProcessInstanceId(task.getProcessInstanceId());
bpmCountersignRecordMapper.insert(bcr);
}
会签流程表达式枚举,全部通过即通过,半数通过即通过,一人通过即通过:
public enum BpmCountersignTypeEnum {
all("all", "${nrOfCompletedInstances==nrOfInstances||pass==true}"),
half("half", "${nrOfCompletedInstances/nrOfInstances>=0.5&&pass==true}"),
single("single", "${nrOfCompletedInstances>=1&&pass==true}");
private final String code;
private final String info;
BpmCountersignTypeEnum(String code, String info)
{
this.code = code;
this.info = info;
}
public static String getCodeByInfo(String info){
if(StringUtils.isBlank(info)){
return null;
}
for(BpmCountersignTypeEnum bct:BpmCountersignTypeEnum.values()){
String elEscape = EscapeUtil.elEscape(bct.info);
if(info.equals(elEscape)){
return bct.code;
}
}
return null;
}
public String getCode()
{
return code;
}
public String getInfo()
{
return info;
}
}
这里:
nrOfInstances:总实例数
nrOfCompletedInstances:已完成实例数
nrOfActiviteInstances:未完成的实例数
全部通过即通过和一人通过即通过直接处理即可,state是流线走向,按比例通过时,我这里用一张表记录了每次操作,即BpmCountersignRecord,将此次记录作为流程操作记录的子关联表,executionId作为关联键。
其他
每次流程启动表act_ge_bytearray中会出现如下图的数据,这就是会签流程里面的变量数据
当流程审批结束后,var-开头的数据就会消失,hist开头的是act_hi表中相关数据