yxk-20260409-集成流程引擎bpm代码

This commit is contained in:
ye1023
2026-04-09 15:55:31 +08:00
parent d94e9c9bc4
commit 9a65463105
582 changed files with 364516 additions and 0 deletions
@@ -0,0 +1,64 @@
///* Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//package org.activiti.spring.boot;
//
//import java.io.IOException;
//
//import javax.sql.DataSource;
//
//import org.activiti.spring.SpringAsyncExecutor;
//import org.activiti.spring.SpringProcessEngineConfiguration;
//import org.springframework.boot.autoconfigure.AutoConfigureAfter;
//import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
//import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
//import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
//import org.springframework.boot.context.properties.EnableConfigurationProperties;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.jdbc.datasource.DataSourceTransactionManager;
//import org.springframework.transaction.PlatformTransactionManager;
//
///**
// * 重构此类强制走Mybatis: 原来的逻辑是Activiti判断,如果项目使用了JPA,那走JPA,如果没有,则走Mybatis
// * @author scott
// * @参考链接:https://blog.csdn.net/jiaoshaoping/article/details/NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
// * @源码jar: activiti-spring-boot-starter-basic-5.22.0.jar
// * @date: 2019-04-13
// */
//@Configuration
//@AutoConfigureAfter(DataSourceAutoConfiguration.class)
//public class DataSourceProcessEngineAutoConfiguration {
//
// @Configuration
// //@ConditionalOnMissingClass(name= "javax.persistence.EntityManagerFactory")
// @EnableConfigurationProperties(ActivitiProperties.class)
// public static class DataSourceProcessEngineConfiguration extends AbstractProcessEngineAutoConfiguration {
//
// @Bean
// @ConditionalOnMissingBean
// public PlatformTransactionManager transactionManager(DataSource dataSource) {
// return new DataSourceTransactionManager(dataSource);
// }
//
// @Bean
// @ConditionalOnMissingBean
// public SpringProcessEngineConfiguration springProcessEngineConfiguration(
// DataSource dataSource,
// PlatformTransactionManager transactionManager,
// SpringAsyncExecutor springAsyncExecutor) throws IOException {
//
// return this.baseSpringProcessEngineConfiguration(dataSource, transactionManager, springAsyncExecutor);
// }
// }
//
//}
@@ -0,0 +1,72 @@
///* Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//package org.activiti.spring.boot;
//
//import java.io.IOException;
//
//import javax.persistence.EntityManagerFactory;
//import javax.sql.DataSource;
//
//import org.activiti.spring.SpringAsyncExecutor;
//import org.activiti.spring.SpringProcessEngineConfiguration;
//import org.springframework.boot.autoconfigure.AutoConfigureAfter;
//import org.springframework.boot.autoconfigure.AutoConfigureBefore;
//import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
//import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
//import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
//import org.springframework.boot.context.properties.EnableConfigurationProperties;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.orm.jpa.JpaTransactionManager;
//import org.springframework.transaction.PlatformTransactionManager;
//
///**
// * 重构此类强制走Mybatis: 原来的逻辑是Activiti判断,如果项目使用了JPA,那走JPA,如果没有,则走Mybatis
// * @author scott
// * @参考链接:https://blog.csdn.net/jiaoshaoping/article/details/NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
// * @源码jar: activiti-spring-boot-starter-basic-5.22.0.jar
// * @date: 2019-04-13
// */
//@Configuration
//@AutoConfigureBefore(DataSourceProcessEngineAutoConfiguration.class)
//@AutoConfigureAfter(DataSourceAutoConfiguration.class)
//public class JpaProcessEngineAutoConfiguration {
//
//// @Configuration
//// @ConditionalOnClass(name= "javax.persistence.EntityManagerFactory")
//// @EnableConfigurationProperties(ActivitiProperties.class)
//// public static class JpaConfiguration extends AbstractProcessEngineAutoConfiguration {
////
//// @Bean
//// @ConditionalOnMissingBean
//// public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
//// return new JpaTransactionManager(emf);
//// }
////
//// @Bean
//// @ConditionalOnMissingBean
//// public SpringProcessEngineConfiguration springProcessEngineConfiguration(
//// DataSource dataSource, EntityManagerFactory entityManagerFactory,
//// PlatformTransactionManager transactionManager, SpringAsyncExecutor springAsyncExecutor) throws IOException {
////
//// SpringProcessEngineConfiguration config = this.baseSpringProcessEngineConfiguration(dataSource,
//// transactionManager, springAsyncExecutor);
//// config.setJpaEntityManagerFactory(entityManagerFactory);
//// config.setTransactionManager(transactionManager);
//// config.setJpaHandleTransaction(false);
//// config.setJpaCloseEntityManager(false);
//// return config;
//// }
//// }
//
//}
@@ -0,0 +1,20 @@
//package org.jeecg;
//
//import org.springframework.boot.SpringApplication;
//import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
//import org.springframework.boot.autoconfigure.SpringBootApplication;
//
///**
// *
// * @author scott
// *
// */
//@SpringBootApplication
//@EnableAutoConfiguration(exclude={org.flowable.spring.boot.FlowableSecurityAutoConfiguration.class})
//public class JeecgBpmApplication {
//
// public static void main(String[] args) {
// System.setProperty("spring.devtools.restart.enabled", "false");
// SpringApplication.run(JeecgBpmApplication.class, args);
// }
//}
@@ -0,0 +1,41 @@
package org.jeecg.modules.aspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 拦截器——同步流程用户、角色、部门、职务
*/
@Configuration("synFlowUserPrincipalDataConfiguration")
public class SynFlowUserPrincipalDataConfiguration implements WebMvcConfigurer {
private static String[] URL_PATTERNS = new String[]{
"/sys/role/add",
"/sys/role/edit",
"/sys/role/delete",
"/sys/sysDepart/add",
"/sys/sysDepart/delete",
"/sys/user/doUpdateDepartInfo",
"/sys/user/deleteUserRoleBatch",
"/sys/tenant/invitationUser",
"/sys/user/userQuitAgent",
"/sys/user/batchEditUsers",
"/sys/user/addSysUserRole",
"/sys/position/removePositionUser",
"/sys/position/savePositionUser",
"/sys/position/add",
"/sys/position/edit",
"/sys/user/editTenantUser",
};
@Bean("synFlowUserPrincipalDataInterceptor")
public SynFlowUserPrincipalDataInterceptor dynFlowUserPrincipalDataInterceptor() {
return new SynFlowUserPrincipalDataInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(dynFlowUserPrincipalDataInterceptor()).addPathPatterns(URL_PATTERNS);
}
}
@@ -0,0 +1,54 @@
package org.jeecg.modules.aspect;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.modules.extbpm.process.controller.SyncTenantUserThreadTask;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* 注意:这里同步是同步用户下的所有数据(不区分租户)
*
* 针对零代码应用的操作请求,自动同步流程用户的 角色、部门、职务到流程中(用户下的所有租户数据一次性同步)
* @author zhang
*/
@Slf4j
public class SynFlowUserPrincipalDataInterceptor implements HandlerInterceptor {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
//触发同步流程主数据
log.info("===== 同步流程主数据 ===== URL:"+ request.getRequestURI());
String syncUserFlag = SyncTenantUserThreadTask.syncUserFlag;
if (syncUserFlag != null && "1".equals(syncUserFlag)) {
log.info("===== 正在同步中===== ");
} else {
SyncTenantUserThreadTask.syncUserFlag = "1";
String username = JwtUtil.getUserNameByToken(request);
//获取登录租户ID
String tenantId = TokenUtils.getTenantIdByRequest(request);
FutureTask<Boolean> futureTask = new FutureTask<Boolean>(new SyncTenantUserThreadTask(username, tenantId));
Thread newThread = new Thread(futureTask);
newThread.start();
try {
Boolean threadResult = futureTask.get(200, TimeUnit.MILLISECONDS);;
}catch (TimeoutException e) {
log.error("同步流程租户数据,等待数据200毫秒以上,异常信息!");
}catch (Exception e) {
e.printStackTrace();
log.error("fail:", e);
}
log.info("===== 同步流程主数据 完成 =====");
}
}
}
@@ -0,0 +1,250 @@
package org.jeecg.modules.aspect;
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.system.vo.UserAccountInfo;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.extbpm.process.pojo.DepInfo;
import org.jeecg.modules.extbpm.process.pojo.PositionInfo;
import org.jeecg.modules.extbpm.process.pojo.RoleInfo;
import org.jeecg.modules.extbpm.process.pojo.UserInfo;
import org.jeecg.modules.extbpm.process.service.IExtActProcessService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
/**
*
* 用户同步到工作流
* @author zhoujf
*
*/
@Aspect
@Slf4j
@Component("sysUserAspect")
public class SysUserAspect {
@Lazy
@Autowired
private ISysBaseAPI sysBaseAPI;
@Autowired
private IExtActProcessService extActProcessService;
@Pointcut("execution(public * org.jeecg.modules.*.controller.SysUserController.add(..))")
public void excudeServiceAdd() {
}
@Around("excudeServiceAdd()")
public Object doAroundAdd(ProceedingJoinPoint pjp) throws Throwable {
Object result = pjp.proceed();
try {
Object[] args = pjp.getArgs();
JSONObject param = (JSONObject)args[0];
String userName = param.getString("username");
log.info("------------SysUserAspect------add----"+userName);
LoginUser user = sysBaseAPI.getUserByName(userName);
if(user!=null) {
if(CommonConstant.ACT_SYNC_1.equals(user.getActivitiSync())) {
//同步则进行同步用户和角色信息
UserInfo userInfo = new UserInfo();
userInfo.setUuid(user.getId());
userInfo.setFirstName(user.getRealname());
userInfo.setEmail(user.getEmail());
userInfo.setId(user.getUsername());
//将用户的角色同步到流程
List<RoleInfo> roleList = extActProcessService.getSysRolesByUserId(userInfo.getUuid());
//将用户的部门同步到流程
List<DepInfo> depList = extActProcessService.getSysDepsByUserId(userInfo.getUuid());
//将用户的职务同步到流程
List<PositionInfo> positionList = extActProcessService.getSysPositionsByUserId(userInfo.getUuid());
extActProcessService.saveActIdMembership(userInfo, roleList,depList,positionList);
}
}
} catch (Exception e) {
}
return result;
}
@Pointcut("execution(public * org.jeecg.modules.*.controller.SysUserController.edit(..))")
public void excudeServiceEdit() {
}
@Around("excudeServiceEdit()")
public Object doAroundEdit(ProceedingJoinPoint pjp) throws Throwable {
Object result = pjp.proceed();
try {
Object[] args = pjp.getArgs();
JSONObject param = (JSONObject)args[0];
String userName = param.getString("username");
log.info("------------SysUserAspect------edit----"+userName);
LoginUser user = sysBaseAPI.getUserByName(userName);
if(user!=null) {
if(CommonConstant.ACT_SYNC_1.equals(user.getActivitiSync())) {
// NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
//同步则进行同步用户和角色信息
UserInfo userInfo = new UserInfo();
userInfo.setUuid(user.getId());
userInfo.setFirstName(user.getRealname());
userInfo.setEmail(user.getEmail());
userInfo.setId(user.getUsername());
//将用户的角色同步到流程
List<RoleInfo> roleList = extActProcessService.getSysRolesByUserId(userInfo.getUuid());
//将用户的部门同步到流程中
List<DepInfo> depList = extActProcessService.getSysDepsByUserId(userInfo.getUuid());
//将用户的职务同步到流程
List<PositionInfo> positionList = extActProcessService.getSysPositionsByUserId(userInfo.getUuid());
extActProcessService.saveActIdMembership(userInfo, roleList,depList,positionList);
}else {
//不同步删除用户信息
extActProcessService.deleteActIdMembership(userName);
}
}
} catch (Exception e) {
}
return result;
}
@Pointcut("execution(public * org.jeecg.modules.*.controller.SysUserController.putRecycleBin(..))")
public void excudeServicePutRecycleBin() {
}
@Around("excudeServicePutRecycleBin()")
public Object doAroundPutRecycleBin(ProceedingJoinPoint pjp) throws Throwable {
Object result = pjp.proceed();
try {
Object[] args = pjp.getArgs();
JSONObject param = (JSONObject) args[0];
String userIds = param.getString("userIds");
log.info("------------SysUserAspect------PutRecycleBin----" + userIds);
if (oConvertUtils.isNotEmpty(userIds)) {
List<UserAccountInfo> userList = sysBaseAPI.queryAllUserByIds((userIds.split(",")));
for (UserAccountInfo user : userList) {
if (CommonConstant.ACT_SYNC_1.equals(user.getActivitiSync())) {
//同步则进行同步用户和角色信息
UserInfo userInfo = new UserInfo();
userInfo.setUuid(user.getId());
userInfo.setFirstName(user.getRealname());
userInfo.setEmail(user.getEmail());
userInfo.setId(user.getUsername());
//将用户的角色同步到流程
List<RoleInfo> roleList = extActProcessService.getSysRolesByUserId(userInfo.getUuid());
//将用户的部门同步到流程
List<DepInfo> depList = extActProcessService.getSysDepsByUserId(userInfo.getUuid());
//将用户的职务同步到流程
List<PositionInfo> positionList = extActProcessService.getSysPositionsByUserId(userInfo.getUuid());
extActProcessService.saveActIdMembership(userInfo, roleList,depList,positionList);
}
}
}
} catch (Exception e) {
}
return result;
}
@Pointcut("execution(public * org.jeecg.modules.*.controller.SysUserController.delete(..))")
public void excudeServiceDelete() {
}
@Around("excudeServiceDelete()")
public Object doAroundDelete(ProceedingJoinPoint pjp) throws Throwable {
try {
Object[] args = pjp.getArgs();
String id = (String)args[0];
log.info("------------SysUserAspect------delete----"+id);
LoginUser user = sysBaseAPI.getUserById(id);
if(user!=null) {
//不同步删除用户信息
extActProcessService.deleteActIdMembership(user.getUsername());
}
} catch (Exception e) {
}
Object result = pjp.proceed();
return result;
}
@Pointcut("execution(public * org.jeecg.modules.*.controller.SysUserController.deleteBatch(..))")
public void excudeServiceDeleteBatch() {
}
@Around("excudeServiceDeleteBatch()")
public Object doAroundDeleteBatch(ProceedingJoinPoint pjp) throws Throwable {
try {
Object[] args = pjp.getArgs();
String ids = (String)args[0];
log.info("------------SysUserAspect------deleteBatch----"+ids);
String[] idArr = ids.split(",");
for(String id :idArr) {
LoginUser user = sysBaseAPI.getUserById(id);
if(user!=null) {
//不同步删除用户信息
extActProcessService.deleteActIdMembership(user.getUsername());
}
}
} catch (Exception e) {
}
Object result = pjp.proceed();
return result;
}
//----------------------------------------------------------------------------------------------------
/**
* 系统角色删除,同步删除流程中的角色
*/
@Pointcut("execution(public * org.jeecg.modules.*.controller.SysRoleController.delete(..))")
public void excudeServiceRoleDelete() {
}
@Pointcut("execution(public * org.jeecg.modules.*.controller.SysRoleController.deleteBatch(..))")
public void excudeServiceRoleDeleteBatch() {
}
@Around("excudeServiceRoleDelete()")
public Object doAroundRoleDelete(ProceedingJoinPoint pjp) throws Throwable {
try {
Object[] args = pjp.getArgs();
String id = (String) args[0];
log.info("------------SysUserAspect---Role---delete----" + id);
//同步删除流程中的角色
String roleCode = sysBaseAPI.getRoleCodeById(id);
extActProcessService.deleteFlowRoleSet(roleCode);
} catch (Exception e) {
}
Object result = pjp.proceed();
return result;
}
@Around("excudeServiceRoleDeleteBatch()")
public Object doAroundRoleDeleteBatch(ProceedingJoinPoint pjp) throws Throwable {
try {
Object[] args = pjp.getArgs();
String ids = (String) args[0];
log.info("------------SysUserAspect---Roles---deleteBatch----" + ids);
String[] idArr = ids.split(",");
for (String id : idArr) {
//同步删除流程中的角色
String roleCode = sysBaseAPI.getRoleCodeById(id);
extActProcessService.deleteFlowRoleSet(roleCode);
}
} catch (Exception e) {
}
Object result = pjp.proceed();
return result;
}
//----------------------------------------------------------------------------------------------------
}
@@ -0,0 +1,53 @@
package org.jeecg.modules.aspect.event;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.base.BaseMap;
import org.jeecg.common.config.mqtoken.UserTokenContext;
import org.jeecg.common.constant.FlowEventTypeConst;
import org.jeecg.common.modules.redis.listener.JeecgRedisListener;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.modules.extbpm.process.service.IExtActProcessService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
* 说明:系统角色删除,同步删除BPM中的角色
* <p>
*
* @author: scott
* @date: 2024-05-27
*/
@Slf4j
@Component(FlowEventTypeConst.DELETE_ROLE_2_BPM_REDIS_HANDLER)
@ConditionalOnMissingClass("org.jeecg.modules.system.controller.SysUserController") //与SysUserAspect互斥关系
public class DeleteRole2BpmRedisHandler implements JeecgRedisListener {
@Lazy
@Autowired
private ISysBaseAPI sysBaseAPI;
@Autowired
private IExtActProcessService extActProcessService;
@Override
public void onMessage(BaseMap map) {
//1.设置线程会话Token
UserTokenContext.setToken(UpdateUser2BpmRedisHandler.getTemporaryToken());
log.info("【同步删除流程角色】redis Listener: {},参数:{}", FlowEventTypeConst.DELETE_ROLE_2_BPM_REDIS_HANDLER, map.toString());
String ids = map.get("roleIds");
log.info("------------DeleteRole2BpmRedisHandler---Roles---deleteBatch----" + ids);
String[] idArr = ids.split(",");
for (String id : idArr) {
//同步删除流程中的角色
String roleCode = sysBaseAPI.getRoleCodeById(id);
extActProcessService.deleteFlowRoleSet(roleCode);
}
//2.使用完删除Token,避免性能(这一步可以不做,但是为了性能建议执行)
UserTokenContext.remove();
}
}
@@ -0,0 +1,141 @@
package org.jeecg.modules.aspect.event;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.base.BaseMap;
import org.jeecg.common.config.mqtoken.UserTokenContext;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.FlowEventTypeConst;
import org.jeecg.common.modules.redis.listener.JeecgRedisListener;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.extbpm.process.pojo.DepInfo;
import org.jeecg.modules.extbpm.process.pojo.PositionInfo;
import org.jeecg.modules.extbpm.process.pojo.RoleInfo;
import org.jeecg.modules.extbpm.process.pojo.UserInfo;
import org.jeecg.modules.extbpm.process.service.IExtActProcessService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 说明:BPM作为独立微服务模块允许,编辑用户,不会自动同步用户到工作流中
* <p>
* 此类解决:: 微服务情况下,同步用户信息到流程中的问题
*
* @author: scott
* @date: 2024-05-27
*/
@Slf4j
@Component(FlowEventTypeConst.UPDATE_USER_2_BPM_REDIS_HANDLER)
@ConditionalOnMissingClass("org.jeecg.modules.system.controller.SysUserController") //与SysUserAspect互斥关系
public class UpdateUser2BpmRedisHandler implements JeecgRedisListener {
@Lazy
@Autowired
private ISysBaseAPI sysBaseAPI;
@Autowired
private IExtActProcessService extActProcessService;
@Override
public void onMessage(BaseMap map) {
//1.设置线程会话Token
UserTokenContext.setToken(getTemporaryToken());
log.info("【同步流程用户】redis Listener: {},参数:{}", FlowEventTypeConst.UPDATE_USER_2_BPM_REDIS_HANDLER, map.toString());
String userNames = map.get("userNames");
if (StringUtils.isEmpty(userNames)) {
return;
}
if (userNames.contains(",")) {
// 判断传入的是多个用户
String[] userNamesArray = userNames.split(",");
for (String userName : userNamesArray) {
if (StringUtils.isEmpty(userName)) {
continue;
}
// 进行处理
synchronizeUser(userName);
}
} else {
// 判断传入的是单个用户
// 进行处理
synchronizeUser(userNames);
}
//2.使用完删除Token,避免性能(这一步可以不做,但是为了性能建议执行)
UserTokenContext.remove();
}
/**
* 同步单个用户
*
* @param userName
*/
private void synchronizeUser(String userName) {
log.info("------------SysUserAspect------Synch----userName=" + userName);
LoginUser user = sysBaseAPI.getUserByName(userName);
if (user != null) {
if (CommonConstant.ACT_SYNC_1.equals(user.getActivitiSync())) {
// NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
//同步则进行同步用户和角色信息
UserInfo userInfo = new UserInfo();
userInfo.setUuid(user.getId());
userInfo.setFirstName(user.getRealname());
userInfo.setEmail(user.getEmail());
userInfo.setId(user.getUsername());
//将用户的角色同步到流程
List<RoleInfo> roleList = extActProcessService.getSysRolesByUserId(userInfo.getUuid());
//将用户的部门同步到流程中
List<DepInfo> depList = extActProcessService.getSysDepsByUserId(userInfo.getUuid());
//将用户的职务同步到流程
List<PositionInfo> positionList = extActProcessService.getSysPositionsByUserId(userInfo.getUuid());
extActProcessService.saveActIdMembership(userInfo, roleList, depList, positionList);
} else {
// 从流程中删除用户信息(逻辑删除用户 或 用户变更为不同步流程走此逻辑)
extActProcessService.deleteActIdMembership(userName);
}
} else {
// 从流程中删除用户信息(逻辑删除用户 或 用户变更为不同步流程走此逻辑)
extActProcessService.deleteActIdMembership(userName);
}
}
//*********************************************************************************************************
/**
* TODO 有风险,考虑更好方案
* 模拟登陆临时账号密码
* _reserve_user_external
* jeecg@123
*/
private static final String EXTERNAL_USERNAME = "X3Jlc2VydmVfdXNlcl9leHRlcm5hbA==";
private static final String EXTERNAL_PASSWORD = "amVlY2dAMTIz";
/**
* 获取临时登录令牌
* <p>
* 模拟登陆接口,获取模拟 Token
*
* @return
*/
public static String getTemporaryToken() {
RedisUtil redisUtil = SpringContextUtils.getBean(RedisUtil.class);
//模拟登录生成临时Token
//参数说明:第一个参数是用户名、第二个参数是密码的加密串
String token = JwtUtil.sign(oConvertUtils.decodeBase64Str(EXTERNAL_USERNAME), oConvertUtils.decodeBase64Str(EXTERNAL_PASSWORD));
// 设置Token缓存有效时间为 5 分钟
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, 5 * 60 * 1000);
return token;
}
//*********************************************************************************************************
}
@@ -0,0 +1,100 @@
package org.jeecg.modules.bpm.cmd;
import org.apache.commons.lang.StringUtils;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.compatibility.Flowable5CompatibilityHandler;
import org.flowable.engine.impl.persistence.entity.CommentEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.Flowable5Util;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.task.Comment;
import org.flowable.engine.task.Event;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.jeecg.modules.bpm.constant.enums.CommentTypeEnum;
/**
* @Description:
* @Author: Bruce.liu
* @Since:19:01 2018/11/24
* 爱拼才会赢 2019 ~ 2030 版权所有
*/
public class AddCommentCmd implements Command<Comment> {
protected String taskId;
protected String userId;
protected String processInstanceId;
protected String type;
protected String message;
public AddCommentCmd(String userId, String processInstanceId, String type, String message) {
this.processInstanceId = processInstanceId;
this.message = message;
this.type = type;
this.userId = userId;
}
public AddCommentCmd(String taskId, String userId, String processInstanceId, String type, String message) {
this.taskId = taskId;
this.processInstanceId = processInstanceId;
this.type = type;
this.message = message;
this.userId = userId;
}
@Override
public Comment execute(CommandContext commandContext) {
TaskEntity task = null;
ExecutionEntity execution = null;
if (processInstanceId != null) {
execution = CommandContextUtil.getExecutionEntityManager(commandContext).findById(processInstanceId);
if (execution == null) {
throw new FlowableObjectNotFoundException("execution " + processInstanceId + " doesn't exist", Execution.class);
}
if (execution.isSuspended()) {
throw new FlowableException(getSuspendedExceptionMessage());
}
}
String processDefinitionId = null;
if (execution != null) {
processDefinitionId = execution.getProcessDefinitionId();
} else if (task != null) {
processDefinitionId = task.getProcessDefinitionId();
}
if (processDefinitionId != null && Flowable5Util.isFlowable5ProcessDefinitionId(commandContext, processDefinitionId)) {
Flowable5CompatibilityHandler compatibilityHandler = Flowable5Util.getFlowable5CompatibilityHandler();
if (org.apache.commons.lang.StringUtil.isBlank(message)){
message = "";
}
return compatibilityHandler.addComment(taskId, processInstanceId, type, message);
}
CommentEntity comment = CommandContextUtil.getCommentEntityManager(commandContext).create();
comment.setUserId(userId);
comment.setType((type == null) ? CommentTypeEnum.SP.toString() : type);
comment.setTime(CommandContextUtil.getProcessEngineConfiguration(commandContext).getClock().getCurrentTime());
comment.setTaskId(taskId);
comment.setProcessInstanceId(processInstanceId);
comment.setAction(Event.ACTION_ADD_COMMENT);
String eventMessage = org.apache.commons.lang.StringUtil.isNotBlank(message)?message.replaceAll("\\s+", " "):"";
if (eventMessage.length() > 3900) {
eventMessage = eventMessage.substring(0, 3900) + "...";
}
comment.setMessage(eventMessage);
comment.setFullMessage(message);
CommandContextUtil.getCommentEntityManager(commandContext).insert(comment);
return comment;
}
protected String getSuspendedTaskException() {
return "Cannot add a comment to a suspended task";
}
protected String getSuspendedExceptionMessage() {
return "Cannot add a comment to a suspended execution";
}
}
@@ -0,0 +1,71 @@
package org.jeecg.modules.bpm.cmd;
import org.apache.commons.lang.StringUtils;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.persistence.entity.CommentEntity;
import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.task.Comment;
import org.flowable.engine.task.Event;
import org.jeecg.modules.bpm.constant.enums.CommentTypeEnum;
/**
* 网上的参考代码(目前未使用)
*
* @Description: 添加历史审批记录
* @Author: scoot
* @Since:19:01 2022-12-06
*/
public class AddHisCommentCmd implements Command<Comment> {
protected String taskId;
protected String userId;
protected String processInstanceId;
protected String type;
protected String message;
public AddHisCommentCmd(String taskId, String userId, String processInstanceId, String type, String message) {
this.taskId = taskId;
this.userId = userId;
this.processInstanceId = processInstanceId;
this.type = type;
this.message = message;
}
@Override
public Comment execute(CommandContext commandContext) {
HistoricProcessInstanceEntity hisProIns = null;
if (processInstanceId != null) {
hisProIns = CommandContextUtil.getHistoricProcessInstanceEntityManager(commandContext).findById(processInstanceId);
if (hisProIns == null) {
throw new FlowableObjectNotFoundException("HistoricProcessInstance " + processInstanceId + " doesn't exist", HistoricProcessInstanceEntity.class);
}
}
CommentEntity comment = CommandContextUtil.getCommentEntityManager(commandContext).create();
comment.setUserId(userId);
comment.setType((type == null) ? CommentTypeEnum.SP.toString() : type);
comment.setTime(CommandContextUtil.getProcessEngineConfiguration(commandContext).getClock().getCurrentTime());
comment.setTaskId(taskId);
comment.setProcessInstanceId(processInstanceId);
comment.setAction(Event.ACTION_ADD_COMMENT);
String eventMessage = org.apache.commons.lang.StringUtil.isNotBlank(message) ? message.replaceAll("\\s+", " ") : "";
if (eventMessage.length() > 3900) {
eventMessage = eventMessage.substring(0, 3900) + "...";
}
comment.setMessage(eventMessage);
comment.setFullMessage(message);
CommandContextUtil.getCommentEntityManager(commandContext).insert(comment);
return comment;
}
protected String getSuspendedTaskException() {
return "Cannot add a comment to a suspended task";
}
protected String getSuspendedExceptionMessage() {
return "Cannot add a comment to a suspended execution";
}
}
@@ -0,0 +1,301 @@
package org.jeecg.modules.bpm.cmd;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.cmd.AbstractDynamicInjectionCmd;
import org.flowable.engine.impl.context.Context;
import org.flowable.engine.impl.dynamic.BaseDynamicSubProcessInjectUtil;
import org.flowable.engine.impl.dynamic.DynamicUserTaskBuilder;
import org.flowable.engine.impl.persistence.entity.DeploymentEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.task.service.TaskService;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.jeecg.modules.bpm.cmd.dto.TaskAddRequest;
import org.jeecg.modules.bpm.cmd.util.ApiAssert;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @Description: 动态添加用户节点(向后加签)
* @Author: zyf
* @Date: 2022-05-06
*/
public class AfterSignUserTaskCmd extends AbstractDynamicInjectionCmd implements Command<Void> {
protected String processInstanceId;
protected String currentTaskDefKey;
protected TaskAddRequest taskAddRequest;
private boolean autoLayout = true;
public AfterSignUserTaskCmd(TaskAddRequest taskAddRequest) {
this.processInstanceId = taskAddRequest.getProcInsId();
this.currentTaskDefKey = taskAddRequest.getCurrentTaskDefKey();
this.taskAddRequest = taskAddRequest;
}
@Override
public Void execute(CommandContext commandContext) {
createDerivedProcessDefinitionForProcessInstance(commandContext, processInstanceId);
return null;
}
@Override
protected void updateBpmnProcess(CommandContext commandContext, Process process, BpmnModel bpmnModel, ProcessDefinitionEntity originalProcessDefinitionEntity, DeploymentEntity newDeploymentEntity) {
String dynamicTaskId = "Task_" + RandomUtil.randomString(7);
DynamicUserTaskBuilder dynamicUserTaskBuilder = new DynamicUserTaskBuilder();
dynamicUserTaskBuilder.setId(dynamicTaskId);
dynamicUserTaskBuilder.setName(taskAddRequest.getTaskName());
dynamicUserTaskBuilder.setAssignee(taskAddRequest.getAssignee());
FlowElement currentFlowElement = process.getFlowElement(currentTaskDefKey);
if (ObjectUtil.isNotEmpty(currentFlowElement)) {
if (!(currentFlowElement instanceof Task)) {
ApiAssert.failure("非任务节点");
}
UserTask currentUserTask = (UserTask) currentFlowElement;
//定义新的节点
UserTask newUserTask = new UserTask();
if (dynamicUserTaskBuilder.getId() != null) {
newUserTask.setId(dynamicUserTaskBuilder.getId());
} else {
newUserTask.setId(dynamicUserTaskBuilder.nextTaskId(process.getFlowElementMap()));
}
dynamicUserTaskBuilder.setDynamicTaskId(newUserTask.getId());
newUserTask.setName(dynamicUserTaskBuilder.getName());
newUserTask.setAssignee(dynamicUserTaskBuilder.getAssignee());
List<SequenceFlow> outgoingFlows = new ArrayList<>();
//获取当前流程节点流出的sequence
List<SequenceFlow> currentOutgoingFlows = currentUserTask.getOutgoingFlows();
for (SequenceFlow sequenceFlow : currentOutgoingFlows) {
SequenceFlow outgoingFlow = new SequenceFlow(newUserTask.getId(), sequenceFlow.getTargetRef());
outgoingFlow.setSkipExpression(sequenceFlow.getSkipExpression());
outgoingFlow.setConditionExpression(sequenceFlow.getConditionExpression());
outgoingFlow.setExtensionElements(sequenceFlow.getExtensionElements());
outgoingFlow.setExecutionListeners(sequenceFlow.getExecutionListeners());
outgoingFlow.setName(sequenceFlow.getName());
outgoingFlow.setId("Flow_" + RandomUtil.randomString(7));
outgoingFlows.add(outgoingFlow);
//删除原先节点的出线
process.removeFlowElement(sequenceFlow.getId());
process.addFlowElement(outgoingFlow);
}
List<SequenceFlow> incomingFlows = new ArrayList<>();
SequenceFlow incomingFlow = new SequenceFlow(currentFlowElement.getId(), newUserTask.getId());
//设置唯一编
incomingFlow.setId("Flow_" + RandomUtil.randomString(7));
incomingFlows.add(incomingFlow);
process.addFlowElement(incomingFlow);
newUserTask.setOutgoingFlows(outgoingFlows);
newUserTask.setIncomingFlows(incomingFlows);
process.addFlowElement(newUserTask);
//新增坐标 点
GraphicInfo elementGraphicInfo = bpmnModel.getGraphicInfo(currentFlowElement.getId());
if (elementGraphicInfo != null) {
//自动排版
if (autoLayout) {
new BpmnAutoLayout(bpmnModel).execute();
} else {
drawNode(bpmnModel, elementGraphicInfo, newUserTask, outgoingFlows, incomingFlows, process);
}
}
} else {
drawNode(process, bpmnModel, dynamicUserTaskBuilder);
}
BaseDynamicSubProcessInjectUtil.processFlowElements(commandContext, process, bpmnModel, originalProcessDefinitionEntity, newDeploymentEntity);
}
@Override
protected void updateExecutions(CommandContext commandContext, ProcessDefinitionEntity processDefinitionEntity, ExecutionEntity processInstance, List<ExecutionEntity> childExecutions) {
}
private void drawNode(BpmnModel bpmnModel, GraphicInfo elementGraphicInfo, UserTask userTask, List<SequenceFlow> outgoingFlows, List<SequenceFlow> incomingFlows, Process process) {
double yDiff = 0;
double xDiff = 80;
if (elementGraphicInfo.getY() < 173) {
yDiff = 173 - elementGraphicInfo.getY();
elementGraphicInfo.setY(173);
}
StartEvent initialStartEvent = getStartEvent(process);
Map<String, GraphicInfo> locationMap = bpmnModel.getLocationMap();
for (String locationId : locationMap.keySet()) {
if (initialStartEvent.getId().equals(locationId)) {
continue;
}
GraphicInfo locationGraphicInfo = locationMap.get(locationId);
locationGraphicInfo.setX(locationGraphicInfo.getX() + xDiff);
locationGraphicInfo.setY(locationGraphicInfo.getY() + yDiff);
}
Map<String, List<GraphicInfo>> flowLocationMap = bpmnModel.getFlowLocationMap();
for (String flowId : flowLocationMap.keySet()) {
//if (flowFromStart.getId().equals(flowId)) {
//continue;
//}
List<GraphicInfo> flowGraphicInfoList = flowLocationMap.get(flowId);
for (GraphicInfo flowGraphicInfo : flowGraphicInfoList) {
flowGraphicInfo.setX(flowGraphicInfo.getX() + xDiff);
flowGraphicInfo.setY(flowGraphicInfo.getY() + yDiff);
}
}
//绘制节点
GraphicInfo newTaskGraphicInfo = new GraphicInfo(elementGraphicInfo.getX() + 185, elementGraphicInfo.getY() - 163, 80, 100);
bpmnModel.addGraphicInfo(userTask.getId(), newTaskGraphicInfo);
bpmnModel.addFlowGraphicInfoList(userTask.getId(), createWayPoints(elementGraphicInfo.getX() + 95, elementGraphicInfo.getY() - 5, elementGraphicInfo.getX() + 95, elementGraphicInfo.getY() - 123, elementGraphicInfo.getX() + 185, elementGraphicInfo.getY() - 123));
List<SequenceFlow> addFlows = new ArrayList<>();
addFlows.addAll(outgoingFlows);
addFlows.addAll(incomingFlows);
/* 绘制连线 */
for (SequenceFlow sequenceFlow1 : addFlows) {
bpmnModel.addFlowGraphicInfoList(sequenceFlow1.getId(), createWayPoints(elementGraphicInfo.getX() + 30, elementGraphicInfo.getY() + 15, elementGraphicInfo.getX() + 75, elementGraphicInfo.getY() + 15));
}
}
private void drawNode(Process process, BpmnModel bpmnModel, DynamicUserTaskBuilder dynamicUserTaskBuilder) {
StartEvent initialStartEvent = getStartEvent(process);
ParallelGateway parallelGateway = new ParallelGateway();
parallelGateway.setId(dynamicUserTaskBuilder.nextForkGatewayId(process.getFlowElementMap()));
process.addFlowElement(parallelGateway);
UserTask newUserTask = new UserTask();
if (dynamicUserTaskBuilder.getId() != null) {
newUserTask.setId(dynamicUserTaskBuilder.getId());
} else {
newUserTask.setId(dynamicUserTaskBuilder.nextTaskId(process.getFlowElementMap()));
}
dynamicUserTaskBuilder.setDynamicTaskId(newUserTask.getId());
newUserTask.setName(dynamicUserTaskBuilder.getName());
newUserTask.setAssignee(dynamicUserTaskBuilder.getAssignee());
process.addFlowElement(newUserTask);
EndEvent endEvent = new EndEvent();
endEvent.setId(dynamicUserTaskBuilder.nextEndEventId(process.getFlowElementMap()));
process.addFlowElement(endEvent);
SequenceFlow flowToUserTask = new SequenceFlow(parallelGateway.getId(), newUserTask.getId());
flowToUserTask.setId(dynamicUserTaskBuilder.nextFlowId(process.getFlowElementMap()));
process.addFlowElement(flowToUserTask);
SequenceFlow flowFromUserTask = new SequenceFlow(newUserTask.getId(), endEvent.getId());
flowFromUserTask.setId(dynamicUserTaskBuilder.nextFlowId(process.getFlowElementMap()));
process.addFlowElement(flowFromUserTask);
SequenceFlow initialFlow = initialStartEvent.getOutgoingFlows().get(0);
initialFlow.setSourceRef(parallelGateway.getId());
SequenceFlow flowFromStart = new SequenceFlow(initialStartEvent.getId(), parallelGateway.getId());
flowFromStart.setId(dynamicUserTaskBuilder.nextFlowId(process.getFlowElementMap()));
process.addFlowElement(flowFromStart);
//跳整节点的布局
GraphicInfo elementGraphicInfo = bpmnModel.getGraphicInfo(initialStartEvent.getId());
if (elementGraphicInfo != null) {
double yDiff = 0;
double xDiff = 80;
if (elementGraphicInfo.getY() < 173) {
yDiff = 173 - elementGraphicInfo.getY();
elementGraphicInfo.setY(173);
}
Map<String, GraphicInfo> locationMap = bpmnModel.getLocationMap();
for (String locationId : locationMap.keySet()) {
if (initialStartEvent.getId().equals(locationId)) {
continue;
}
GraphicInfo locationGraphicInfo = locationMap.get(locationId);
locationGraphicInfo.setX(locationGraphicInfo.getX() + xDiff);
locationGraphicInfo.setY(locationGraphicInfo.getY() + yDiff);
}
Map<String, List<GraphicInfo>> flowLocationMap = bpmnModel.getFlowLocationMap();
for (String flowId : flowLocationMap.keySet()) {
if (flowFromStart.getId().equals(flowId)) {
continue;
}
List<GraphicInfo> flowGraphicInfoList = flowLocationMap.get(flowId);
for (GraphicInfo flowGraphicInfo : flowGraphicInfoList) {
flowGraphicInfo.setX(flowGraphicInfo.getX() + xDiff);
flowGraphicInfo.setY(flowGraphicInfo.getY() + yDiff);
}
}
GraphicInfo forkGraphicInfo = new GraphicInfo(elementGraphicInfo.getX() + 75, elementGraphicInfo.getY() - 5, 40, 40);
bpmnModel.addGraphicInfo(parallelGateway.getId(), forkGraphicInfo);
bpmnModel.addFlowGraphicInfoList(flowFromStart.getId(), createWayPoints(elementGraphicInfo.getX() + 30, elementGraphicInfo.getY() + 15, elementGraphicInfo.getX() + 75, elementGraphicInfo.getY() + 15));
GraphicInfo newTaskGraphicInfo = new GraphicInfo(elementGraphicInfo.getX() + 185, elementGraphicInfo.getY() - 163, 80, 100);
bpmnModel.addGraphicInfo(newUserTask.getId(), newTaskGraphicInfo);
bpmnModel.addFlowGraphicInfoList(flowToUserTask.getId(), createWayPoints(elementGraphicInfo.getX() + 95, elementGraphicInfo.getY() - 5, elementGraphicInfo.getX() + 95, elementGraphicInfo.getY() - 123, elementGraphicInfo.getX() + 185, elementGraphicInfo.getY() - 123));
GraphicInfo endGraphicInfo = new GraphicInfo(elementGraphicInfo.getX() + 335, elementGraphicInfo.getY() - 137, 28, 28);
bpmnModel.addGraphicInfo(endEvent.getId(), endGraphicInfo);
bpmnModel.addFlowGraphicInfoList(flowFromUserTask.getId(), createWayPoints(elementGraphicInfo.getX() + 285, elementGraphicInfo.getY() - 123, elementGraphicInfo.getX() + 335, elementGraphicInfo.getY() - 123));
}
}
private void updateExecutions(CommandContext commandContext, ExecutionEntity processInstance, ProcessDefinitionEntity processDefinitionEntity, DynamicUserTaskBuilder dynamicUserTaskBuilder) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
List<ExecutionEntity> oldExecution = executionEntityManager.findChildExecutionsByProcessInstanceId(processInstance.getProcessInstanceId());
ExecutionEntity execution = executionEntityManager.createChildExecution(processInstance);
BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionEntity.getId());
TaskService taskService = CommandContextUtil.getTaskService(commandContext);
List<TaskEntity> taskEntities = taskService.findTasksByProcessInstanceId(processInstanceId);
// 删除当前活动任务
for (TaskEntity taskEntity : taskEntities) {
taskEntity.getIdentityLinks().stream().forEach(identityLinkEntity -> {
if (identityLinkEntity.isGroup()) {
taskEntity.deleteGroupIdentityLink(identityLinkEntity.getGroupId(), "candidate");
} else {
taskEntity.deleteUserIdentityLink(identityLinkEntity.getUserId(), "participant");
}
});
if (taskEntity.getTaskDefinitionKey().equals(currentTaskDefKey)) {
taskService.deleteTask(taskEntity, false);
}
}
//设置活动后的节点
UserTask userTask = (UserTask) bpmnModel.getProcessById(processDefinitionEntity.getKey()).getFlowElement(dynamicUserTaskBuilder.getId());
execution.setCurrentFlowElement(userTask);
Context.getAgenda().planContinueProcessOperation(execution);
}
private StartEvent getStartEvent(Process process) {
List<StartEvent> startEvents = process.findFlowElementsOfType(StartEvent.class);
StartEvent initialStartEvent = null;
for (StartEvent startEvent : startEvents) {
if (startEvent.getEventDefinitions().size() == 0) {
initialStartEvent = startEvent;
break;
} else if (initialStartEvent == null) {
initialStartEvent = startEvent;
}
}
return initialStartEvent;
}
}
@@ -0,0 +1,113 @@
package org.jeecg.modules.bpm.cmd;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.impl.calendar.CronExpression;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.common.engine.impl.util.DefaultClockImpl;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.job.api.Job;
import org.flowable.job.service.TimerJobService;
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.bpm.job.CustomTimeStartJobEventHandler;
import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
import java.io.Serializable;
import java.text.ParseException;
import java.util.Date;
/**
* 使用flowable内部Api手动创建作业(Job
*
* 参考博客
* https://blog.csdn.net/II_bat/article/details/51329399
* https://blog.csdn.net/oouuyy/article/details/118890552
*/
@Slf4j
public class CustomTimeStartJobCmd implements Command<Void>, Serializable {
//流程定义key
protected String processKey;
//表单编码或者表名
protected String formKey;
//表单数据id
protected String dataId;
//流程发起人
protected String applyUserId;
//定时开始时间
protected Date dueDate;
//触发字段
protected String triggerField;
//定时频率表达式
protected String repeat;
public CustomTimeStartJobCmd(String processKey, String formKey, String dataId, Date dueDate, String applyUserId, String triggerField) {
this.formKey = formKey;
this.dataId = dataId;
this.processKey = processKey;
this.applyUserId = applyUserId;
this.dueDate = dueDate;
this.triggerField = triggerField;
}
public CustomTimeStartJobCmd(String processKey, String formKey, String dataId, Date dueDate, String applyUserId,String repeat, String triggerField) {
this.formKey = formKey;
this.dataId = dataId;
this.processKey = processKey;
this.applyUserId = applyUserId;
this.dueDate = dueDate;
this.repeat = repeat;
this.triggerField = triggerField;
}
@Override
public Void execute(CommandContext commandContext) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("activityId", "start");
jsonObject.put(WorkFlowGlobals.BPM_DATA_ID, this.dataId);
jsonObject.put(WorkFlowGlobals.BPM_FORM_KEY, this.formKey);
jsonObject.put(WorkFlowGlobals.APPLY_USER_ID, this.applyUserId);
jsonObject.put(WorkFlowGlobals.TIME_TRIGGER_FIELD, this.triggerField);
TimerJobService timerJobService = CommandContextUtil.getTimerJobService(commandContext);
TimerJobEntity job = timerJobService.createTimerJob();
job.setJobType(Job.JOB_TYPE_TIMER);
job.setExclusive(true);
// 自定义作业处理器
//job.setJobHandlerType(TimerStartEventJobHandler.TYPE);
job.setJobHandlerType(CustomTimeStartJobEventHandler.TYPE);
// 处理时间
job.setDuedate(this.dueDate);
job.setExclusive(true);
if(oConvertUtils.isNotEmpty(this.repeat)){
job.setRepeat(this.repeat);
//--------------------------------------------------------------------------------
//测试发现flowable可以自动调整开始时间改为下一次有效日期,但是发现会发起一次流程,这个不应该发起,所以还是需要手工计算下次有效期
//判断开始时间 dueDate小于当前时间,则生成下次有效执行时间
try {
if (!this.dueDate.after(new Date())) {
CronExpression cronExpression = new CronExpression(this.repeat, new DefaultClockImpl());
Date nextDuteDate = cronExpression.getNextValidTimeAfter(new Date());
log.info("执行日期小于当前时间,重新计算下次执行时间!执行日期:{},通过计算获取下次执行有效期:{},循环表达式:{}",
DateUtils.datetimeFormat.get().format(this.dueDate), DateUtils.datetimeFormat.get().format(nextDuteDate), this.repeat);
job.setDuedate(nextDuteDate);
}
} catch (ParseException e) {
log.error("重新计算下次执行时间异常:" + e.getMessage());
}
//--------------------------------------------------------------------------------
}
//流程key
ProcessDefinitionEntity processDefinition = CommandContextUtil.getProcessDefinitionEntityManager().findLatestProcessDefinitionByKey(processKey);
String processDefinitionId = processDefinition.getId();
job.setProcessDefinitionId(processDefinitionId);
job.setJobHandlerConfiguration(jsonObject.toJSONString());
timerJobService.scheduleTimerJob(job);
return null;
}
}
@@ -0,0 +1,32 @@
package org.jeecg.modules.bpm.cmd;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import java.io.Serializable;
/**
* @Description: 删除执行数据命令
* @Author: zyf
* @Date: 2022-06-01
*/
public class DeleteExecutionCmd implements Command<String>, Serializable {
/**
* 执行id
*/
private String executionId;
public DeleteExecutionCmd(String executionId) {
this.executionId = executionId;
}
@Override
public String execute(CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
executionEntityManager.delete(executionId);
return null;
}
}
@@ -0,0 +1,30 @@
package org.jeecg.modules.bpm.cmd;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.cmd.NeedsActiveTaskCmd;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.task.service.TaskService;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
/**
* @Description: 删除任务命令
* @Author: zyf
* @Date: 2022-06-01
*/
public class DeleteTaskCmd extends NeedsActiveTaskCmd<String> {
/**
* 当前任务对应的 act_ru_task 任务id
*/
public DeleteTaskCmd(String taskId) {
super(taskId);
}
@Override
protected String execute(CommandContext commandContext, TaskEntity task) {
TaskService taskService = CommandContextUtil.getTaskService(commandContext);
taskService.deleteTask(task,true);
return null;
}
}
@@ -0,0 +1,91 @@
package org.jeecg.modules.bpm.cmd;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.repository.ProcessDefinition;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.bpm.cmd.util.ApiAssert;
import java.io.Serializable;
/**
* @Description: 获取流程定义
* @Author: zyf
* @Date: 2022-05-06
*/
@Slf4j
public class GetProcessDefinitionCmd implements Command<ProcessDefinition>, Serializable {
private static final long serialVersionUID = 1L;
/**
* 流程定义ID
*/
protected String processDefinitionId;
/**
* 流程KEY
*/
protected String processDefinitionKey;
/**
* 租户ID
*/
protected String tenantId;
public GetProcessDefinitionCmd(String processDefinitionId, String processDefinitionKey, String tenantId) {
this.processDefinitionId = processDefinitionId;
this.processDefinitionKey = processDefinitionKey;
this.tenantId = tenantId;
}
public GetProcessDefinitionCmd(String processDefinitionId) {
this.processDefinitionId = processDefinitionId;
}
public GetProcessDefinitionCmd(String processDefinitionId, String processDefinitionKey) {
this.processDefinitionId = processDefinitionId;
this.processDefinitionKey = processDefinitionKey;
}
@Override
public ProcessDefinition execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
//获取流程定义管理器
ProcessDefinitionEntityManager processDefinitionEntityManager = processEngineConfiguration.getProcessDefinitionEntityManager();
ProcessDefinition processDefinition = null;
if (processDefinitionId != null) {
processDefinition = processDefinitionEntityManager.findById(processDefinitionId);
log.error(StrFormatter.format("流程定义不存在,processDefinitionId:{}", processDefinitionId));
ApiAssert.isNotEmpty("流程定义不存在", processDefinition);
} else if (processDefinitionKey != null && ObjectUtil.isEmpty(tenantId)) {
processDefinition = processDefinitionEntityManager.findLatestProcessDefinitionByKey(processDefinitionKey);
log.error(StrFormatter.format("流程定义不存在,processDefinitionKey:{}", processDefinitionKey));
ApiAssert.isNotEmpty("流程定义不存在", processDefinition);
} else if (processDefinitionKey != null && oConvertUtils.isNotEmpty(tenantId)) {
processDefinition = processDefinitionEntityManager.findLatestProcessDefinitionByKeyAndTenantId(processDefinitionKey, tenantId);
if (processDefinition == null) {
if (processEngineConfiguration.isFallbackToDefaultTenant()) {
if (org.apache.commons.lang.StringUtil.isNotEmpty(processEngineConfiguration.getDefaultTenantValue())) {
processDefinition = processDefinitionEntityManager.findLatestProcessDefinitionByKeyAndTenantId(processDefinitionKey, processEngineConfiguration.getDefaultTenantValue());
} else {
processDefinition = processDefinitionEntityManager.findLatestProcessDefinitionByKey(processDefinitionKey);
}
log.error(StrFormatter.format("流程定义不存在,还应用了默认租户的回退,processDefinitionKey:{}", processDefinitionKey));
ApiAssert.isNotEmpty("流程定义不存在", processDefinition);
} else {
log.error(StrFormatter.format("流程定义不存在,processDefinitionKey:{},and 租户ID:{1}", processDefinitionKey, tenantId));
ApiAssert.failure("流程定义不存在");
}
}
} else {
ApiAssert.failure("参数异常");
}
return processDefinition;
}
}
@@ -0,0 +1,58 @@
package org.jeecg.modules.bpm.cmd;
import cn.hutool.core.util.ObjectUtil;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.jeecg.modules.bpm.cmd.util.ApiAssert;
import org.jeecg.modules.extbpm.process.entity.ExtActProcess;
import java.io.Serializable;
/**
* @Description: 流程级联删除命令
* @Author: zyf
* @Date: 2022-05-06
*/
public class ProcessDeleteCmd implements Command<Boolean>, Serializable {
private static final long serialVersionUID = 1L;
/**
* 管理控制部署与流程定义
*/
private RepositoryService repositoryService;
private ExtActProcess flowProcess;
public ProcessDeleteCmd(ExtActProcess flowProcess) {
this.flowProcess = flowProcess;
}
@Override
public Boolean execute(CommandContext commandContext) {
ProcessEngineConfiguration processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
this.repositoryService = processEngineConfiguration.getRepositoryService();
ProcessDefinitionQuery definitionQuery = repositoryService.createProcessDefinitionQuery();
String processKey = flowProcess.getProcessKey();
definitionQuery.processDefinitionKey(processKey);
long procDefCount = definitionQuery.count();
if (procDefCount > 0) {
ApiAssert.failure("流程已发布,禁止删除");
}
String procModelId = flowProcess.getProcessKey();
if (ObjectUtil.isNotEmpty(procModelId)) {
Model model = repositoryService.getModel(procModelId);
if (ObjectUtil.isNotEmpty(model)) {
repositoryService.deleteModel(procModelId);
}
}
return true;
}
}
@@ -0,0 +1,86 @@
package org.jeecg.modules.bpm.cmd;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.DeploymentBuilder;
import org.flowable.engine.repository.Model;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.bpm.cmd.util.ApiAssert;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
/**
* @Description: 流程部署命令
* @Author: zyf
* @Date: 2022-05-06
*/
public class ProcessDeployCmd implements Command<Deployment>, Serializable {
private static final long serialVersionUID = 1L;
/**
* 流程模型ID
*/
private String modelId;
private Model model;
public ProcessDeployCmd(String modelId, Model model) {
this.modelId = modelId;
this.model = model;
}
@Override
public Deployment execute(CommandContext commandContext) {
Deployment deployment = null;
RepositoryService repositoryService = CommandContextUtil.getProcessEngineConfiguration(commandContext).getRepositoryService();
if (model == null) {
ApiAssert.failure("流程模型不存在:" + modelId);
}
if (!model.hasEditorSource()) {
ApiAssert.failure("资源不存在,流程模型:" + modelId);
}
byte[] bpmnBytes = CommandContextUtil.getProcessEngineConfiguration(commandContext).getModelEntityManager().findEditorSourceByModelId(modelId);
if (bpmnBytes == null) {
ApiAssert.failure("资源不存在,流程模型:" + modelId);
}
try {
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
String fileName = model.getId() + ".bpmn20.xml";
ByteArrayInputStream bis = new ByteArrayInputStream(bpmnBytes);
deploymentBuilder.addInputStream(fileName, bis);
deploymentBuilder.name(fileName);
// modelId设置为部署的分类字段作为后续关联的需要
deploymentBuilder.category(model.getCategory());
XMLInputFactory xif = XMLInputFactory.newInstance();
InputStreamReader xmlIn = new InputStreamReader(new ByteArrayInputStream(bpmnBytes), "UTF-8");
XMLStreamReader xtr = xif.createXMLStreamReader(xmlIn);
BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
org.flowable.bpmn.model.Process process = bpmnModel.getMainProcess();
deploymentBuilder.key(process.getId());
//租户ID
String tenantId = model.getTenantId();
if (oConvertUtils.isNotEmpty(tenantId)) {
//设置租户ID
deploymentBuilder.tenantId(tenantId);
}
deployment = deploymentBuilder.deploy();
if (deployment != null) {
model.setDeploymentId(deployment.getId());
}
} catch (Exception e) {
ApiAssert.failure("发布失败");
}
return deployment;
}
}
@@ -0,0 +1,92 @@
package org.jeecg.modules.bpm.cmd;
import com.google.common.io.CharStreams;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.common.engine.impl.util.io.InputStreamSource;
import org.flowable.common.engine.impl.util.io.StreamSource;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.repository.DeploymentBuilder;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.bpm.cmd.util.ApiAssert;
import org.jeecg.modules.bpm.cmd.util.FlowableUtils;
import org.jeecg.modules.extbpm.process.entity.ExtActProcess;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
/**
* @Description: 根据导入文件构建FlowProcess
* @Author: zyf
* @Date: 2022-05-06
*/
@Slf4j
public class ProcessImportCmd implements Command<ExtActProcess>, Serializable {
private static final long serialVersionUID = 1L;
/**
* 文件名
*/
private String fileName;
/**
* 上传文件对象
*/
private MultipartFile file;
public ProcessImportCmd(HttpServletRequest request) {
if (!(request instanceof MultipartHttpServletRequest)) {
log.error("请求必须是MultipartHttpServletRequest的实例");
ApiAssert.failure("导入失败");
}
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
if (multipartRequest.getFileMap().size() == 0) {
log.error("请求文件为空");
ApiAssert.failure("导入失败");
}
this.file = multipartRequest.getFileMap().values().iterator().next();
this.fileName = this.file.getOriginalFilename();
//检测文件是否合法
FlowableUtils.checkImportFileName(this.fileName);
}
@Override
public ExtActProcess execute(CommandContext commandContext) {
ProcessEngineConfiguration processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
RepositoryService repositoryService = processEngineConfiguration.getRepositoryService();
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
boolean isBpmnFile = FlowableUtils.isBpmnFile(fileName);
ExtActProcess flowProcess = new ExtActProcess();
try {
if (isBpmnFile) {
deploymentBuilder.addInputStream(fileName, file.getInputStream());
StreamSource xmlSource = new InputStreamSource(file.getInputStream());
BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xmlSource, false, false, "UTF-8");
Process process = bpmnModel.getMainProcess();
if (oConvertUtils.isNotEmpty(process)) {
//添加自定义流程定义
String processXml = CharStreams.toString(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8));
flowProcess.setProcessXml(processXml.getBytes());
flowProcess.setProcessKey(process.getId());
flowProcess.setProcessName(process.getName());
flowProcess.setNote(process.getDocumentation());
}
} else {
}
} catch (Exception e) {
log.error("导入异常:", e.getMessage());
}
return flowProcess;
}
}
@@ -0,0 +1,151 @@
package org.jeecg.modules.bpm.cmd;
import cn.hutool.core.text.StrFormatter;
import com.google.common.collect.Sets;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.jeecg.modules.bpm.cmd.util.ApiAssert;
import org.jeecg.modules.bpm.cmd.util.FlowableUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @TODO 此方法暂时未用上,是从 yf 项目迁移过来
*
* @Description: 驳回任务(指定节点)
* @Author: zyf
* @Date: 2022-05-10
*/
public class RejectTaskCmd implements Command<String>, Serializable {
protected RuntimeService runtimeService;
/**
* 当前节点
*/
protected String taskId;
/**
* 跳转目标节点ID
*/
protected String targetActivityId;
public RejectTaskCmd(RuntimeService runtimeService, String taskId, String targetActivityId) {
this.runtimeService = runtimeService;
this.taskId = taskId;
this.targetActivityId = targetActivityId;
}
@Override
public String execute(CommandContext commandContext) {
// NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
ApiAssert.isNotEmpty("驳回节点ID不能为空", this.targetActivityId);
//当前任务节点对象
TaskEntity task = CommandContextUtil.getProcessEngineConfiguration().getTaskServiceConfiguration().getTaskService().getTask(taskId);
ApiAssert.isNotEmpty("任务不存在", task);
//流程定义ID
String processDefinitionId = task.getProcessDefinitionId();
//流程对象
Process process = ProcessDefinitionUtil.getProcess(processDefinitionId);
//当然任务节点key
String taskDefinitionKey = task.getTaskDefinitionKey();
//当前节点对象
FlowNode sourceFlowElement = (FlowNode) process.getFlowElement(taskDefinitionKey, true);
// 只支持从用户任务退回
if (!(sourceFlowElement instanceof UserTask)) {
ApiAssert.failure("当前节点不是UserTask对象");
}
//目标节点对象
FlowNode targetFlowElement = (FlowNode) process.getFlowElement(this.targetActivityId, true);
//检测目标节点是否可访问
if (!FlowableUtils.isReachable(process, targetFlowElement, sourceFlowElement)) {
ApiAssert.failure(StrFormatter.format("无法驳回到指定节点,targetActivityId:{}", this.targetActivityId));
}
//目标节点如果相对当前节点是在子流程内部,则无法直接退回,目前处理是只能退回到子流程开始节点
String[] sourceAndTargetRealActivityId = FlowableUtils.getSourceAndTargetRealActivityId(sourceFlowElement, targetFlowElement);
// 实际应操作的当前节点ID
String sourceRealActivityId = sourceAndTargetRealActivityId[0];
// 实际应操作的目标节点ID
String targetRealActivityId = sourceAndTargetRealActivityId[1];
//获取特殊网关节点
Map<String, Set<String>> specialGatewayNodes = FlowableUtils.getSpecialGatewayElements(process);
// 当前节点处在的并行网关list
List<String> sourceInSpecialGatewayList = new ArrayList<>();
// 目标节点处在的并行网关list
List<String> targetInSpecialGatewayList = new ArrayList<>();
FlowableUtils.setSpecialGatewayList(sourceRealActivityId, targetRealActivityId, specialGatewayNodes, sourceInSpecialGatewayList, targetInSpecialGatewayList);
// 实际应筛选的节点ID
Set<String> sourceRealAcitivtyIds = null;
// 若退回目标节点相对当前节点在并行网关中,则要找到相对当前节点最近的这个并行网关,后续做特殊处理
String targetRealSpecialGateway = null;
// 1.目标节点和当前节点都不在并行网关中
if (targetInSpecialGatewayList.isEmpty() && sourceInSpecialGatewayList.isEmpty()) {
sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
}
// 2.目标节点不在并行网关中、当前节点在并行网关中
else if (targetInSpecialGatewayList.isEmpty() && !sourceInSpecialGatewayList.isEmpty()) {
sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(0));
}
// 3.目标节点在并行网关中、当前节点不在并行网关中
else if (!targetInSpecialGatewayList.isEmpty() && sourceInSpecialGatewayList.isEmpty()) {
sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
targetRealSpecialGateway = targetInSpecialGatewayList.get(0);
}
// 4.目标节点和当前节点都在并行网关中
else {
int diffSpecialGatewayLevel = FlowableUtils.getDiffLevel(sourceInSpecialGatewayList,
targetInSpecialGatewayList);
// 在并行网关同一层且在同一分支
if (diffSpecialGatewayLevel == -1) {
sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
} else {
// 当前节点最内层并行网关不被目标节点最内层并行网关包含
// 或理解为当前节点相对目标节点在并行网关外
// 只筛选当前节点的execution
if (sourceInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
}
// 当前节点相对目标节点在并行网关内,应筛选相对目标节点最近的并行网关的所有节点的execution
else {
sourceRealAcitivtyIds = specialGatewayNodes
.get(sourceInSpecialGatewayList.get(diffSpecialGatewayLevel));
}
// 目标节点最内层并行网关包含当前节点最内层并行网关
// 或理解为目标节点相对当前节点在并行网关外
// 不做处理
if (targetInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
}
// 目标节点相对当前节点在并行网关内
else {
targetRealSpecialGateway = targetInSpecialGatewayList.get(diffSpecialGatewayLevel);
}
}
}
String processInstanceId = task.getProcessInstanceId();
// 筛选需要处理的execution
List<ExecutionEntity> realExecutions = FlowableUtils.getRealExecutions(commandContext, processInstanceId, task.getExecutionId(), sourceRealActivityId, sourceRealAcitivtyIds);
// 执行退回,直接跳转到实际的 targetRealActivityId
List<String> realExecutionIds = realExecutions.stream().map(ExecutionEntity::getId).collect(Collectors.toList());
runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId).moveExecutionsToSingleActivityId(realExecutionIds, targetRealActivityId).changeState();
// 目标节点相对当前节点处于并行网关内,需要特殊处理,需要手动生成并行网关汇聚节点(_end)的execution数据
if (targetRealSpecialGateway != null) {
FlowableUtils.createTargetInSpecialGatewayEndExecutions(commandContext, realExecutions, process, targetInSpecialGatewayList, targetRealSpecialGateway);
}
return targetRealActivityId;
}
}
@@ -0,0 +1,82 @@
package org.jeecg.modules.bpm.cmd;
import cn.hutool.core.util.ObjectUtil;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.jeecg.modules.bpm.constant.FlowableConstant;
import java.util.*;
/**
* @Description: 串行任务加签
* @Author: zyf NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
* @Date: 2022-05-24
*/
public class SequenceMultiInstanceAddCmd implements Command<Void> {
/**
* 会签人员集合KEY
*/
private String assigneeListKey;
/**
* 任务ID
*/
private String executionId;
/**
* 加签人员
*/
private List<String> assignees;
public SequenceMultiInstanceAddCmd(String executionId, String assigneeListKey, List<String> assignees) {
this.executionId = executionId;
this.assigneeListKey = assigneeListKey;
this.assignees = assignees;
}
@Override
public Void execute(CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
//会签参数是否是字符串类型(逗号分割)
boolean isStringVals = false;
// 查询执行实例
ExecutionEntity executionEntity = executionEntityManager.findById(executionId);
//多实例任务总数加一
Integer nrOfInstances = executionEntity.getVariable(FlowableConstant.NUMBER_OF_INSTANCES, Integer.class);
executionEntity.setVariable(FlowableConstant.NUMBER_OF_INSTANCES, nrOfInstances + assignees.size());
// 设置流程变量
List<String> userIds = null;
//兼容当前默认会签配置传递的是,逗号分割的用户账号字符串
Object userIdsStr = executionEntity.getVariable(assigneeListKey);
if(userIdsStr instanceof String){
isStringVals = true;
userIds = Arrays.asList(((String)userIdsStr).split(","));
}else{
userIds = executionEntity.getVariable(assigneeListKey, List.class);
isStringVals = false;
}
List<String> assigneeList = new ArrayList<>();
if (ObjectUtil.isNotEmpty(userIds)) {
assigneeList.addAll(userIds);
}
if (ObjectUtil.isNotEmpty(assignees)) {
assigneeList.addAll(assignees);
}
Map<String, Object> variables = new HashMap<>(5);
if(isStringVals){
variables.put(assigneeListKey, String.join(",", assigneeList));
}else{
variables.put(assigneeListKey, assigneeList);
}
executionEntity.setVariables(variables);
return null;
}
}
@@ -0,0 +1,97 @@
package org.jeecg.modules.bpm.cmd;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.jeecg.modules.bpm.constant.FlowableConstant;
import java.util.*;
/**
* @Description: 串行任务减签
* @Author: zyf NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
* @Date: 2022-05-24
*/
public class SequenceMultiInstanceDeleteCmd implements Command<Void> {
/**
* 当前节点审批人员id
*/
private String currentUserId;
/**
* 执行id
*/
private String executionId;
/**
* 会签人员集合KEY
*/
private String assigneeListKey;
/**
* 减签人员
*/
private List<String> assignees;
public SequenceMultiInstanceDeleteCmd(String currentUserId, String executionId, String assigneeListKey, List<String> assignees) {
this.currentUserId = currentUserId;
this.executionId = executionId;
this.assigneeListKey = assigneeListKey;
this.assignees = assignees;
}
@Override
public Void execute(CommandContext commandContext) {
//会签参数是否是字符串类型(逗号分割)
boolean isStringVals = false;
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
ExecutionEntity entity = executionEntityManager.findById(executionId);
//多实例任务总数加
Integer nrOfInstances = entity.getVariable(FlowableConstant.NUMBER_OF_INSTANCES, Integer.class);
entity.setVariable(FlowableConstant.NUMBER_OF_INSTANCES, nrOfInstances + assignees.size());
// 设置流程变量
List<String> userIds = null;
//兼容当前默认会签配置传递的是,逗号分割的用户账号字符串
Object userIdsStr = entity.getVariable(assigneeListKey);
if(userIdsStr instanceof String) {
isStringVals = true;
userIds = Arrays.asList(((String)userIdsStr).split(","));
}else{
isStringVals = false;
userIds = entity.getVariable(assigneeListKey,List.class);
}
List<String> userIdList = new ArrayList<>();
//遍历计算出剩余会签人
userIds.forEach(e -> {
String userId = assignees.stream().filter(id -> id.equals(e)).findFirst().orElse(null);
if (userId == null) {
userIdList.add(e);
}
});
//当前任务执行位置
int loopCounterIndex = -1;
for (int i = 0; i < userIdList.size(); i++) {
String userId = userIdList.get(i);
if (currentUserId.equals(userId)) {
loopCounterIndex = i;
}
}
Map<String, Object> variables = new HashMap<>(5);
variables.put(FlowableConstant.NUMBER_OF_INSTANCES, userIdList.size());
if(isStringVals){
variables.put(assigneeListKey, String.join(",", userIdList));
}else{
variables.put(assigneeListKey, userIdList);
}
variables.put(FlowableConstant.LOOP_COUNTER, loopCounterIndex);
entity.setVariables(variables);
return null;
}
}
@@ -0,0 +1,97 @@
package org.jeecg.modules.bpm.cmd;
import org.flowable.bpmn.model.Activity;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.FlowableEngineAgenda;
import org.flowable.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior;
import org.flowable.engine.impl.history.HistoryManager;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.task.service.TaskService;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.jeecg.common.util.DateUtils;
import org.jeecg.modules.bpm.cmd.dto.TaskJumpRequest;
import java.io.Serializable;
import java.util.List;
/**
* @Description: 任务跳转命令
* @Author: zyf
* @Date: 2022-05-19
*/
public class TaskJumpCmd implements Command<Void>, Serializable {
private TaskJumpRequest taskJumpRequest;
public TaskJumpCmd(TaskJumpRequest taskJumpRequest) {
super();
this.taskJumpRequest = taskJumpRequest;
}
@Override
public Void execute(CommandContext commandContext) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
// 查询当前任务节点
TaskService taskService = CommandContextUtil.getProcessEngineConfiguration().getTaskServiceConfiguration().getTaskService();
TaskEntity taskEntity = taskService.getTask(taskJumpRequest.getTaskId());
// 查询执行实例
ExecutionEntity executionEntity = executionEntityManager.findById(taskEntity.getExecutionId());
// 查询流程
Process process = ProcessDefinitionUtil.getProcess(taskEntity.getProcessDefinitionId());
// 当前任务的key
String currentTaskDefinitionKey = taskEntity.getTaskDefinitionKey();
// 当前任务
Activity currentActivity = (Activity) process.getFlowElement(currentTaskDefinitionKey);
Object currentBehavior = currentActivity.getBehavior();
// 父执行实例
ExecutionEntity parentExecutionEntity = executionEntity.getParent();
//是否是会签节点
if (currentBehavior instanceof MultiInstanceActivityBehavior) {
executionEntityManager.deleteChildExecutions(parentExecutionEntity, "", false);
// 修改is_mi_root_字段
parentExecutionEntity.setMultiInstanceRoot(false);
// 修改is_active_
parentExecutionEntity.setActive(true);
// 需要更新数据
executionEntityManager.update(parentExecutionEntity);
} else {
// 删除跳转前的任务节点
taskService.deleteTask(taskEntity, true);
HistoryManager historyManager = CommandContextUtil.getHistoryManager();
historyManager.recordTaskEnd(taskEntity, executionEntity, "", DateUtils.getDate());
historyManager.recordActivityEnd(parentExecutionEntity, "", DateUtils.getDate());
}
//----------------------------执行节点跳转-------------------------------
// 获取目标节点
FlowElement targetFlowElement = process.getFlowElement(taskJumpRequest.getTargetNode());
// 指定当前节点为目标节点
executionEntity.setCurrentFlowElement(targetFlowElement);
FlowableEngineAgenda flowableEngineAgenda = CommandContextUtil.getAgenda();
if (!(currentBehavior instanceof MultiInstanceActivityBehavior)) {
flowableEngineAgenda.planContinueProcessInCompensation(executionEntity);
} else {
// 触发一下,让执行实例往下运行 NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
ExecutionEntity rootExecutionEntity = parentExecutionEntity.getParent();
List<ExecutionEntity> executionEntities = executionEntityManager.findChildExecutionsByParentExecutionId(rootExecutionEntity.getId());
if (executionEntities.size() > 0) {
ExecutionEntity oneExecutionEntity = executionEntities.get(0);
oneExecutionEntity.setCurrentFlowElement(targetFlowElement);
// 让流程实例运转
flowableEngineAgenda.planContinueProcessInCompensation(oneExecutionEntity);
}
}
return null;
}
}
@@ -0,0 +1,29 @@
package org.jeecg.modules.bpm.cmd.dto;
import lombok.Data;
/**
* @Description: 动态添加节点
* @Author: zyf
* @Date: 2022-05-26
*/
@Data
public class TaskAddRequest {
/**
* 流程实例ID
*/
private String procInsId;
/**
* 当前任务节点
*/
private String currentTaskDefKey;
/**
* 添加节点的名字
*/
private String taskName;
/**
* 添加节点处理人
*/
private String assignee;
}
@@ -0,0 +1,29 @@
package org.jeecg.modules.bpm.cmd.dto;
import lombok.Data;
/**
* @Description: 任务办理请求参数对象
* @Author: zyf
* @Date: 2022-05-10
*/
@Data
public class TaskJumpRequest {
/**
* 流程实例ID
*/
private String procInsId;
/**
* 当前任务ID
*/
private String taskId;
/**
* 目标任务ID
*/
private String targetNode;
}
@@ -0,0 +1,64 @@
package org.jeecg.modules.bpm.cmd.util;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.jeecg.common.exception.JeecgBootException;
import java.util.Objects;
/**
* @Description: API断言工具类
* @Author: zyf
* @Date: 2022-05-07
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ApiAssert {
/**
* obj1 eq obj2
*
* @param obj1
* @param obj2
* @param info
*/
public static void eq(String info, Object obj1, Object obj2) {
if (!Objects.equals(obj1, obj2)) {
failure(info);
}
}
public static void isTrue(String info, boolean condition) {
if (!condition) {
failure(info);
}
}
public static void isFalse(String info, boolean condition) {
if (condition) {
failure(info);
}
}
public static void isEmpty(String info, Object obj) {
if (ObjectUtils.isNotEmpty(obj)) {
failure(info);
}
}
public static void isNotEmpty(String info, Object obj) {
if (ObjectUtils.isEmpty(obj)) {
failure(info);
}
}
/**
* 失败结果
*
* @param info 错误信息
*/
public static void failure(String info) {
throw new JeecgBootException(info);
}
}
@@ -0,0 +1,461 @@
package org.jeecg.modules.bpm.cmd.util;
import cn.hutool.core.util.ObjectUtil;
import com.google.common.collect.Sets;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.delegate.ActivityBehavior;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.bpm.constant.FlowableConstant;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @Description: 流程工具类
* @Author: zyf
* @Date: 2022-05-07
*/
public class FlowableUtils {
/**
* 检测导入文件名是否符合flowable导入规范
*
* @param fileName
* @return
*/
public static boolean checkImportFileName(String fileName) {
boolean check = oConvertUtils.isNotEmpty(fileName) && (fileName.endsWith(".bpmn20.xml") || fileName.endsWith(".bpmn") || fileName.toLowerCase().endsWith(".bar") || fileName.toLowerCase().endsWith(".zip"));
if (!check) {
ApiAssert.failure("请求文件必须以.bpmn20.xml,.bpmn,.bar,.zip结尾");
}
return true;
}
/**
* 检测文件是否是BpmnFile
*
* @param fileName
* @return
*/
public static boolean isBpmnFile(String fileName) {
return fileName.endsWith(".bpmn20.xml") || fileName.endsWith(".bpmn");
}
/**
* 是否可访问
*
* @param process
* @param sourceElement
* @param targetElement
* @return
*/
public static boolean isReachable(Process process, FlowNode sourceElement, FlowNode targetElement) {
return isReachable(process, sourceElement, targetElement, Sets.newHashSet());
}
/**
* 是否可访问
*
* @param process
* @param sourceElement
* @param targetElement
* @param visitedElements
* @return
*/
public static boolean isReachable(Process process, FlowNode sourceElement, FlowNode targetElement,
Set<String> visitedElements) {
//特殊情况:事件子流程中的启动事件可能作为执行存在,并且最有可能达到目标
//当目标位于事件子流程中,但应忽略,因为它们不是“真正的”运行时执行,(而是在等待/触发器)
if (sourceElement instanceof StartEvent && isInEventSubprocess(sourceElement)) {
return false;
}
// 没有传出序列流:可能是eg的结束。进程或嵌入式子进程
if (sourceElement.getOutgoingFlows().size() == 0) {
visitedElements.add(sourceElement.getId());
FlowElementsContainer parentElement = process.findParent(sourceElement);
if (parentElement instanceof SubProcess) {
sourceElement = (SubProcess) parentElement;
// 子流程的结束节点,若目标节点在该子流程中,说明无法到达,返回false
if (((SubProcess) sourceElement).getFlowElement(targetElement.getId()) != null) {
return false;
}
} else {
return false;
}
}
if (sourceElement.getId().equals(targetElement.getId())) {
return true;
}
// 为了避免无限循环,我们必须捕获我们访问的每个节点
visitedElements.add(sourceElement.getId());
// 当前节点能够到达子流程,且目标节点在子流程中,说明可以到达,返回true
if (sourceElement instanceof SubProcess
&& ((SubProcess) sourceElement).getFlowElement(targetElement.getId()) != null) {
return true;
}
List<SequenceFlow> sequenceFlows = sourceElement.getOutgoingFlows();
if (sequenceFlows != null && sequenceFlows.size() > 0) {
for (SequenceFlow sequenceFlow : sequenceFlows) {
String targetRef = sequenceFlow.getTargetRef();
FlowNode sequenceFlowTarget = (FlowNode) process.getFlowElement(targetRef, true);
if (sequenceFlowTarget != null && !visitedElements.contains(sequenceFlowTarget.getId())) {
boolean reachable = isReachable(process, sequenceFlowTarget, targetElement, visitedElements);
if (reachable) {
return true;
}
}
}
}
return false;
}
/**
* 是否是在事件子进程中
*
* @param flowNode
* @return
*/
protected static boolean isInEventSubprocess(FlowNode flowNode) {
FlowElementsContainer flowElementsContainer = flowNode.getParentContainer();
while (flowElementsContainer != null) {
if (flowElementsContainer instanceof EventSubProcess) {
return true;
}
if (flowElementsContainer instanceof FlowElement) {
flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer();
} else {
flowElementsContainer = null;
}
}
return false;
}
public static List<String> getParentProcessIds(FlowNode flowNode) {
List<String> result = new ArrayList<>();
FlowElementsContainer flowElementsContainer = flowNode.getParentContainer();
while (flowElementsContainer != null) {
if (flowElementsContainer instanceof SubProcess) {
SubProcess flowElement = (SubProcess) flowElementsContainer;
result.add(flowElement.getId());
flowElementsContainer = flowElement.getParentContainer();
} else if (flowElementsContainer instanceof Process) {
Process flowElement = (Process) flowElementsContainer;
result.add(flowElement.getId());
flowElementsContainer = null;
}
}
// 第一层Process为第0个
Collections.reverse(result);
return result;
}
/**
* 查询不同层级
*
* @param sourceList
* @param targetList
* @return 返回不同的层级,如果其中一个层级较深,则返回层级小的+1,从第0层开始,请注意判断是否会出现下标越界异常;返回 -1 表示在同一层
*/
public static Integer getDiffLevel(List<String> sourceList, List<String> targetList) {
if (sourceList == null || sourceList.isEmpty() || targetList == null || targetList.isEmpty()) {
throw new FlowableException("sourceList and targetList cannot be empty");
}
if (sourceList.size() == 1 && targetList.size() == 1) {
// 都在第0层且不相等
if (!sourceList.get(0).equals(targetList.get(0))) {
return 0;
} else {// 都在第0层且相等
return -1;
}
}
int minSize = sourceList.size() < targetList.size() ? sourceList.size() : targetList.size();
Integer targetLevel = null;
for (int i = 0; i < minSize; i++) {
if (!sourceList.get(i).equals(targetList.get(i))) {
targetLevel = i;
break;
}
}
if (targetLevel == null) {
if (sourceList.size() == targetList.size()) {
targetLevel = -1;
} else {
targetLevel = minSize;
}
}
return targetLevel;
}
public static Map<String, Set<String>> getSpecialGatewayElements(FlowElementsContainer container) {
return getSpecialGatewayElements(container, null);
}
public static Map<String, Set<String>> getSpecialGatewayElements(FlowElementsContainer container, Map<String, Set<String>> specialGatewayElements) {
if (specialGatewayElements == null) {
specialGatewayElements = new HashMap<>(16);
}
Collection<FlowElement> flowelements = container.getFlowElements();
for (FlowElement flowElement : flowelements) {
boolean isBeginSpecialGateway = flowElement.getId().endsWith(FlowableConstant.SPECIAL_GATEWAY_BEGIN_SUFFIX)
&& (flowElement instanceof ParallelGateway || flowElement instanceof InclusiveGateway
|| flowElement instanceof ComplexGateway);
if (isBeginSpecialGateway) {
String gatewayBeginRealId = flowElement.getId();
String gatewayId = gatewayBeginRealId.substring(0, gatewayBeginRealId.length() - 6);
Set<String> gatewayIdContainFlowelements = specialGatewayElements.computeIfAbsent(gatewayId,
k -> new HashSet<>());
findElementsBetweenSpecialGateway(flowElement, gatewayId + FlowableConstant.SPECIAL_GATEWAY_END_SUFFIX,
gatewayIdContainFlowelements);
} else if (flowElement instanceof SubProcess) {
getSpecialGatewayElements((SubProcess) flowElement, specialGatewayElements);
}
}
// 外层到里层排序
Map<String, Set<String>> specialGatewayNodesSort = new LinkedHashMap<>();
specialGatewayElements.entrySet().stream().sorted((o1, o2) -> o2.getValue().size() - o1.getValue().size())
.forEach(entry -> specialGatewayNodesSort.put(entry.getKey(), entry.getValue()));
return specialGatewayNodesSort;
}
public static void findElementsBetweenSpecialGateway(FlowElement specialGatewayBegin, String specialGatewayEndId,
Set<String> elements) {
elements.add(specialGatewayBegin.getId());
List<SequenceFlow> sequenceFlows = ((FlowNode) specialGatewayBegin).getOutgoingFlows();
if (sequenceFlows != null && sequenceFlows.size() > 0) {
for (SequenceFlow sequenceFlow : sequenceFlows) {
FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
String targetFlowElementId = targetFlowElement.getId();
elements.add(specialGatewayEndId);
if (targetFlowElementId.equals(specialGatewayEndId)) {
continue;
} else {
findElementsBetweenSpecialGateway(targetFlowElement, specialGatewayEndId, elements);
}
}
}
}
public static String[] getSourceAndTargetRealActivityId(FlowNode sourceFlowElement, FlowNode targetFlowElement) {
// 实际应操作的当前节点ID
String sourceRealActivityId = sourceFlowElement.getId();
// 实际应操作的目标节点ID
String targetRealActivityId = targetFlowElement.getId();
List<String> sourceParentProcesss = FlowableUtils.getParentProcessIds(sourceFlowElement);
List<String> targetParentProcesss = getParentProcessIds(targetFlowElement);
int diffParentLevel = getDiffLevel(sourceParentProcesss, targetParentProcesss);
if (diffParentLevel != -1) {
sourceRealActivityId = sourceParentProcesss.size() == diffParentLevel ? sourceRealActivityId : sourceParentProcesss.get(diffParentLevel);
targetRealActivityId = targetParentProcesss.size() == diffParentLevel ? targetRealActivityId : targetParentProcesss.get(diffParentLevel);
}
return new String[]{sourceRealActivityId, targetRealActivityId};
}
public static void setSpecialGatewayList(String sourceActivityId, String targetActivityId, Map<String, Set<String>> specialGatewayNodes, List<String> sourceInSpecialGatewayList, List<String> targetInSpecialGatewayList) {
for (Map.Entry<String, Set<String>> entry : specialGatewayNodes.entrySet()) {
if (entry.getValue().contains(sourceActivityId)) {
sourceInSpecialGatewayList.add(entry.getKey());
}
if (entry.getValue().contains(targetActivityId)) {
targetInSpecialGatewayList.add(entry.getKey());
}
}
}
public static boolean isFlowElementMultiInstance(FlowElement flowElement) {
if (flowElement instanceof Activity) {
return ((Activity) flowElement).getLoopCharacteristics() != null;
}
return false;
}
public static Optional<String> getFlowElementMultiInstanceParentId(FlowElement flowElement) {
FlowElementsContainer parentContainer = flowElement.getParentContainer();
while (parentContainer instanceof Activity) {
if (isFlowElementMultiInstance((Activity) parentContainer)) {
return Optional.of(((Activity) parentContainer).getId());
}
parentContainer = ((Activity) parentContainer).getParentContainer();
}
return Optional.empty();
}
public static boolean isExecutionInsideMultiInstance(ExecutionEntity execution) {
return getFlowElementMultiInstanceParentId(execution.getCurrentFlowElement()).isPresent();
}
public static Set<String> getParentExecutionIdsByActivityId(List<ExecutionEntity> executions, String activityId) {
List<ExecutionEntity> activityIdExecutions = executions.stream()
.filter(e -> activityId.equals(e.getActivityId())).collect(Collectors.toList());
if (activityIdExecutions.isEmpty()) {
throw new FlowableException("Active execution could not be found with activity id " + activityId);
}
// check for a multi instance root execution
ExecutionEntity miExecution = null;
boolean isInsideMultiInstance = false;
for (ExecutionEntity possibleMiExecution : activityIdExecutions) {
if (possibleMiExecution.isMultiInstanceRoot()) {
miExecution = possibleMiExecution;
isInsideMultiInstance = true;
break;
}
if (isExecutionInsideMultiInstance(possibleMiExecution)) {
isInsideMultiInstance = true;
}
}
Set<String> parentExecutionIds = new HashSet<>();
if (isInsideMultiInstance) {
Stream<ExecutionEntity> executionEntitiesStream = activityIdExecutions.stream();
if (miExecution != null) {
executionEntitiesStream = executionEntitiesStream.filter(ExecutionEntity::isMultiInstanceRoot);
}
executionEntitiesStream.forEach(childExecution -> {
parentExecutionIds.add(childExecution.getParentId());
});
} else {
ExecutionEntity execution = activityIdExecutions.iterator().next();
parentExecutionIds.add(execution.getParentId());
}
return parentExecutionIds;
}
public static String getParentExecutionIdFromParentIds(ExecutionEntity execution, Set<String> parentExecutionIds) {
ExecutionEntity taskParentExecution = execution.getParent();
String realParentExecutionId = null;
while (taskParentExecution != null) {
if (parentExecutionIds.contains(taskParentExecution.getId())) {
realParentExecutionId = taskParentExecution.getId();
break;
}
taskParentExecution = taskParentExecution.getParent();
}
if (realParentExecutionId == null || realParentExecutionId.length() == 0) {
throw new FlowableException("Parent execution could not be found with executionId id " + execution.getId());
}
return realParentExecutionId;
}
public static List<ExecutionEntity> getRealExecutions(CommandContext commandContext, String processInstanceId,
String taskExecutionId, String sourceRealActivityId, Set<String> activityIds) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
ExecutionEntity taskExecution = executionEntityManager.findById(taskExecutionId);
List<ExecutionEntity> executions = executionEntityManager.findChildExecutionsByProcessInstanceId(processInstanceId);
Set<String> parentExecutionIds = getParentExecutionIdsByActivityId(executions, sourceRealActivityId);
String realParentExecutionId = getParentExecutionIdFromParentIds(taskExecution,
parentExecutionIds);
List<ExecutionEntity> childExecutions = executionEntityManager.findExecutionsByParentExecutionAndActivityIds(realParentExecutionId, activityIds);
return childExecutions;
}
public static void createTargetInSpecialGatewayEndExecutions(CommandContext commandContext, List<ExecutionEntity> excutionEntitys, Process process, List<String> targetInSpecialGatewayList, String targetRealSpecialGateway) {
// 目标节点相对当前节点处于并行网关,需要手动生成并行网关汇聚节点(_end)的execution数据
String parentExecutionId = excutionEntitys.iterator().next().getParentId();
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
ExecutionEntity parentExecutionEntity = executionEntityManager.findById(parentExecutionId);
int index = targetInSpecialGatewayList.indexOf(targetRealSpecialGateway);
for (; index < targetInSpecialGatewayList.size(); index++) {
String targetInSpecialGateway = targetInSpecialGatewayList.get(index);
String targetInSpecialGatewayEndId = targetInSpecialGateway + FlowableConstant.SPECIAL_GATEWAY_END_SUFFIX;
FlowNode targetInSpecialGatewayEnd = (FlowNode) process.getFlowElement(targetInSpecialGatewayEndId, true);
int nbrOfExecutionsToJoin = targetInSpecialGatewayEnd.getIncomingFlows().size();
// 处理目标节点所处的分支以外的分支,即 总分枝数-1 = nbrOfExecutionsToJoin - 1
for (int i = 0; i < nbrOfExecutionsToJoin - 1; i++) {
ExecutionEntity childExecution = executionEntityManager.createChildExecution(parentExecutionEntity);
childExecution.setCurrentFlowElement(targetInSpecialGatewayEnd);
ActivityBehavior activityBehavior = (ActivityBehavior) targetInSpecialGatewayEnd.getBehavior();
activityBehavior.execute(childExecution);
}
}
}
/**
* long类型日期转换为x天x时x分格式
*
* @param startTime
* @param endTime
* @return
*/
public static String longTimeToStr(Date startTime, Date endTime) {
long start = startTime.getTime();
long end = DateUtils.getDate().getTime();
if (ObjectUtil.isNotEmpty(endTime)) {
end = endTime.getTime();
}
long mu=end-start;
return longTimeToStr(mu);
}
/**
* long类型日期转换为x天x时x分格式
*
* @param time
* @return
*/
public static String longTimeToStr(Long time) {
StringBuilder builder = new StringBuilder();
if (ObjectUtil.isNotEmpty(time)) {
try {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
format.setTimeZone(TimeZone.getTimeZone("GMT+00:00"));
String dateTime = format.format(time);
Date date = format.parse(dateTime);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
Calendar orgin = Calendar.getInstance();
orgin.setTimeInMillis(0);
int year = calendar.get(Calendar.YEAR) - orgin.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) - orgin.get(Calendar.MONTH);
int day = calendar.get(Calendar.DATE) - orgin.get(Calendar.DATE);
int hour = calendar.get(Calendar.HOUR_OF_DAY) - orgin.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE) - orgin.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND) - orgin.get(Calendar.SECOND);
int millisecond = calendar.get(Calendar.MILLISECOND) - orgin.get(Calendar.MILLISECOND);
if (year != 0) {
builder.append(year).append("");
}
if (month != 0) {
builder.append(month).append("");
}
if (day != 0) {
builder.append(day).append("");
}
if (hour != 0) {
builder.append(Math.abs(hour)).append("小时");
}
if (minute != 0) {
builder.append(minute).append("");
}
if (second != 0 && day <= 0) {
builder.append(second).append("");
}
if (millisecond != 0 && second <= 0) {
builder.append(millisecond).append("毫秒");
}
} catch (Exception e) {
ApiAssert.failure("格式化日期出错");
}
}
return builder.toString();
}
}
@@ -0,0 +1,22 @@
package org.jeecg.modules.bpm.config;
/**
* 项目名称:jeecg
* 类名称:Globals
* 类描述: 全局变量定义
* 创建人:scott
* @version
*
*/
public final class BpmGlobals {
/**
* 我发起流程: 根据不同关键词查询不同状态流程
* isUnfinished/查询未完成流程
* isFinished/查询已完成流程
*/
public static final String IS_UN_FINISHED = "isUnfinished";
public static final String IS_FINISHED = "isFinished";
public static final String XX_SIGN_GG = "NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=";
}
@@ -0,0 +1,46 @@
//package org.jeecg.modules.bpm.config;
//
//import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
//import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
//import org.flowable.spring.SpringProcessEngineConfiguration;
//import org.jeecg.modules.bpm.config.flowable.GlobalProcessStartedListener;
//import org.jeecg.modules.bpm.config.flowable.GlobalProcistEndListener;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.ApplicationListener;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.context.event.ContextRefreshedEvent;
//
///**
// * @TODO 待实现未用上1
// *
// * Flowable全局监听配置
// * 用途:在任务特殊节点或者流程的特殊节点做一些自定义操作
// *
// * @author gblfy
// * @Date 2022-06-21
// **/
//@Configuration("flowableGlobListenerConfig")
//public class FlowableGlobListenerConfig implements ApplicationListener<ContextRefreshedEvent> {
// @Autowired
// private SpringProcessEngineConfiguration configuration;
// @Autowired
// private GlobalProcessStartedListener globalProcessStartedListener;
// @Autowired
// private GlobalProcistEndListener globalProcistEndListener;
//
// @Override
// public void onApplicationEvent(ContextRefreshedEvent event) {
// FlowableEventDispatcher dispatcher = configuration.getEventDispatcher();
//
// //流程开始全局监听
// dispatcher.addEventListener(globalProcessStartedListener, FlowableEngineEventType.PROCESS_STARTED);
//
// //流程结束全局监听
// dispatcher.addEventListener(globalProcistEndListener, FlowableEngineEventType.PROCESS_COMPLETED,
// FlowableEngineEventType.PROCESS_CANCELLED,
// FlowableEngineEventType.PROCESS_COMPLETED_WITH_ERROR_END_EVENT,
// FlowableEngineEventType.PROCESS_COMPLETED_WITH_TERMINATE_END_EVENT);
// }
//
//}
//
@@ -0,0 +1,122 @@
package org.jeecg.modules.bpm.config;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
import org.flowable.common.engine.impl.cfg.IdGenerator;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.jeecg.modules.bpm.job.CustomTimeStartJobEventHandler;
import org.jeecg.modules.bpm.constant.FlowableConstant;
import org.jeecg.modules.bpm.service.impl.image.CustomProcessDiagramGenerator;
import org.jeecg.modules.extbpm.listener.global.GlobalEventListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 流程配置信息
* @date 2019/03/06
* @author scott
*/
@Slf4j
@Configuration("flowableEngineConfiguration")
public class ProcessEngineConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
@Autowired
public DataSource dataSource;
@Override
public void configure(SpringProcessEngineConfiguration springProcessEngineConfiguration) {
//------------------------------------------------------------------------
// 1.流程图渲染设置
springProcessEngineConfiguration.setProcessDiagramGenerator(processDiagramGenerator());
//设置流程图字体(防止乱码)
springProcessEngineConfiguration.setActivityFontName(FlowableConstant.FONT_NAME);
//设置label字体
springProcessEngineConfiguration.setLabelFontName(FlowableConstant.FONT_NAME);
//设置注释字体
springProcessEngineConfiguration.setAnnotationFontName(FlowableConstant.FONT_NAME);
//关闭定时任务,有需要再开启 NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
//springProcessEngineConfiguration.setAsyncExecutorActivate(false);
//------------------------------------------------------------------------
// 2.自定义id生成规则
springProcessEngineConfiguration.setIdGenerator(new IdGenerator() {
@Override
public String getNextId() {
try {
return IdWorker.getIdStr(); //id生成方法
} catch (Exception e) {
}
return null;
}
});
//------------------------------------------------------------------------
// 3.国产化数据库适配
this.setFlowableDataSource(springProcessEngineConfiguration);
//------------------------------------------------------------------------
// 4.全局监听器
Map<String, String> handlers = new HashMap<String, String>();
List<FlowableEventListener> eventListeners = new ArrayList<FlowableEventListener>();
GlobalEventListener global = new GlobalEventListener();
global.setHandlers(handlers);
eventListeners.add(global);
springProcessEngineConfiguration.setEventListeners(eventListeners);
//5.注册自定义作业处理器
springProcessEngineConfiguration.addCustomJobHandler(new CustomTimeStartJobEventHandler());
}
/**
* 国产化数据库适配兼容
* @param springProcessEngineConfiguration
*/
private void setFlowableDataSource(SpringProcessEngineConfiguration springProcessEngineConfiguration){
long time1=System.currentTimeMillis();
try {
Connection connection = dataSource.getConnection();
DatabaseMetaData md = connection.getMetaData();
String dbType = md.getDatabaseProductName();
//log.info("DataSource Customize DbType = " + dbType);
//log.info("ProcessEngine databaseType = " + springProcessEngineConfiguration.getDatabaseType());
//达梦数据库兼容,采用oracle类型
if (dbType.toLowerCase().indexOf("dm") >= 0) {
springProcessEngineConfiguration.setDataSource(dataSource);
springProcessEngineConfiguration.setDatabaseType("oracle");
long time2=System.currentTimeMillis();
log.info("ProcessEngine 兼容达梦逻辑耗时:"+(time2-time1)+"ms");
}
//人大金仓数据库兼容,采用PostgreSQL类型
if (dbType.toLowerCase().indexOf("kingbase") >= 0) {
springProcessEngineConfiguration.setDataSource(dataSource);
springProcessEngineConfiguration.setDatabaseType("postgres");
long time2=System.currentTimeMillis();
log.info("ProcessEngine 兼容人大金仓逻辑耗时:"+(time2-time1)+"ms");
}
} catch (SQLException e) {
log.error(e.getMessage(), e);
}
}
/**
* 自定义流程进度图
*
* @return
*/
@Bean("customProcessDiagramGenerator")
public ProcessDiagramGenerator processDiagramGenerator() {
return new CustomProcessDiagramGenerator();
}
}
@@ -0,0 +1,49 @@
package org.jeecg.modules.bpm.config.flowable;
import org.flowable.engine.delegate.event.impl.FlowableEntityEventImpl;
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
import org.flowable.engine.delegate.event.FlowableProcessStartedEvent;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* 全局的流程启动的监听器(有效,但未写逻辑)
*
* @author: gblfy
* @date 2022-06-21
*/
@Component("globalProcessStartedListener")
public class GlobalProcessStartedListener extends AbstractFlowableEngineEventListener {
protected Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
protected void processStarted(FlowableProcessStartedEvent event) {
// logger.info("进入流程开始监听器------------------------Start---------------------->");
String eventName = event.getType().name();
FlowableEntityEventImpl flowableEntityEvent = (FlowableEntityEventImpl) event;
ExecutionEntityImpl processInstance = (ExecutionEntityImpl) flowableEntityEvent.getEntity();
Date startTime = processInstance.getStartTime();
String processDefinitionKey = processInstance.getProcessDefinitionKey();
String processInstanceId = processInstance.getProcessInstanceId();
String processInstanceBusinessKey = processInstance.getProcessInstanceBusinessKey();
int suspensionState = processInstance.getSuspensionState();
// logger.info("流程事件类型->{}", eventName);
// logger.info("流程开始时间->{}", startTime);
// logger.info("流程定义Key->{}", processDefinitionKey);
// logger.info("流程实例ID->{}", processInstanceId);
// logger.info("流程业务key->{}", processInstanceBusinessKey);
// logger.info("流程是否挂起标志->{}", suspensionState);
// logger.info("流程开始监听器------------------------End---------------------->");
}
}
@@ -0,0 +1,49 @@
package org.jeecg.modules.bpm.config.flowable;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
import org.flowable.engine.delegate.event.impl.FlowableEntityEventImpl;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* 流程结束全局监听器(有效,但未写逻辑)
*
* @author: gblfy
* @date 2022-06-21
*/
@Component("globalProcistEndListener")
public class GlobalProcistEndListener extends AbstractFlowableEngineEventListener {
protected Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
protected void processCompleted(FlowableEngineEntityEvent event) {
// logger.info("进入流程完成监听器------------------------Start---------------------->");
String eventName = event.getType().name();
FlowableEntityEventImpl flowableEntityEvent = (FlowableEntityEventImpl) event;
ExecutionEntityImpl processInstance = (ExecutionEntityImpl) flowableEntityEvent.getEntity();
Date startTime = processInstance.getStartTime();
String processDefinitionKey = processInstance.getProcessDefinitionKey();
String processInstanceId = processInstance.getProcessInstanceId();
String processInstanceBusinessKey = processInstance.getProcessInstanceBusinessKey();
int suspensionState = processInstance.getSuspensionState();
// logger.info("流程事件类型->{}", eventName);
// logger.info("流程开始时间->{}", startTime);
// logger.info("流程定义Key->{}", processDefinitionKey);
// logger.info("流程实例ID->{}", processInstanceId);
// logger.info("流程业务key->{}", processInstanceBusinessKey);
// logger.info("流程是否挂起标志->{}", suspensionState);
// logger.info("流程完成监听器------------------------End---------------------->");
}
}
@@ -0,0 +1,52 @@
package org.jeecg.modules.bpm.constant;
/**
* @Description: 流程常量定义
* @Author: zyf
* @Date: 2022-05-05
*/
public final class FlowableConstant {
//TODO 待测试
public final static String SPECIAL_GATEWAY_BEGIN_SUFFIX = "_begin";
//TODO 待测试
public final static String SPECIAL_GATEWAY_END_SUFFIX = "_end";
/**
* 流程图字体
*/
public static final String FONT_NAME = "宋体";
/**
* 会签任务总数
*/
public static final String NUMBER_OF_INSTANCES = "nrOfInstances";
/**
* 【自定义变量:串行会签人员集合】
* 简流模式支持选固定会签人员,进行加减签
*/
public static final String LOOP_ASSIGNEE_COLLECTION = "loopAssigneeCollection";
/**
* 正在执行的会签总数
*/
public static final String NUMBER_OF_ACTIVE_INSTANCES = "nrOfActiveInstances";
/**
* 已完成的会签任务总数
*/
public static final String NUMBER_OF_COMPLETED_INSTANCES = "nrOfCompletedInstances";
/**
* 循环的索引值,可以使用elementIndexVariable属性修改loopCounter的变量名 NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
*/
public static final String LOOP_COUNTER = "loopCounter";
//后加签
public static final String AFTER_ADDSIGN = "after";
//前加签
public static final String BEFORE_ADDSIGN = "before";
}
@@ -0,0 +1,44 @@
package org.jeecg.modules.bpm.constant.enums;
/**
* @TODO 待实现未用上1
*
* @Description: 业务状态
* @Author: zyf
* @Date: 2022-06-31
*/
public enum BusinessStatusEnum {
/**
* 枚举字段
*/
NOTSUBMIT("1", "待提交"), PROCESSING("2", "处理中"), FINISH("3", "已完成"), DELETE("4", "已作废"), SUSPEND("5", "已挂起");
/**
* 名称
*/
private String status;
/**
* 名称
*/
private String statusName;
public static String getStatusName(String status) {
for (BusinessStatusEnum e : BusinessStatusEnum.values()) {
if (e.status.equals(status)) {
return e.statusName;
}
}
return "";
}
BusinessStatusEnum(String status, String statusName) {
this.status = status;
this.statusName = statusName;
}
public String getStatus() {
return status;
}
}
@@ -0,0 +1,60 @@
package org.jeecg.modules.bpm.constant.enums;
/**
* @TODO 待实现未用上1
*
* @author : scott
* @title: : CommentTypeEnum
* @projectName : flowable
* @description: 审批意见的类型
* @date : 2019/11/2412:53
*/
public enum CommentTypeEnum {
SP("审批"),
BH("驳回"),
CH("撤回"),
ZC("暂存"),
QS("签收"),
WP("委派"),
ZH("知会"),
ZY("转阅"),
YY("已阅"),
ZB("转办"),
QJQ("前加签"),
HJQ("后加签"),
XTZX("系统执行"),
TJ("提交"),
CXTJ("重新提交"),
SPJS("审批结束"),
LCZZ("流程终止"),
SQ("授权"),
CFTG("重复跳过"),
XT("协同"),
PS("评审");
private String name;//名称
/**
* 通过type获取Msg
*
* @param type
* @return
* @Description:
*/
public static String getEnumMsgByType(String type) {
for (CommentTypeEnum e : CommentTypeEnum.values()) {
if (e.toString().equals(type)) {
return e.name;
}
}
return "";
}
private CommentTypeEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@@ -0,0 +1,120 @@
//package org.jeecg.modules.bpm.controller;
//
//import java.util.Arrays;
//import java.util.List;
//import java.util.Map;
//
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//
//import lombok.extern.slf4j.Slf4j;
//import org.activiti.engine.repository.Model;
//import org.jeecg.common.api.vo.Result;
//import org.jeecg.common.util.oConvertUtils;
//import org.jeecg.modules.bpm.dto.ModelDTO;
//import org.jeecg.modules.bpm.service.impl.ActModelService;
//import org.jeecg.modules.extbpm.util.CommonRandomUtil;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.web.bind.annotation.DeleteMapping;
//import org.springframework.web.bind.annotation.GetMapping;
//import org.springframework.web.bind.annotation.PathVariable;
//import org.springframework.web.bind.annotation.RequestBody;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RequestParam;
//import org.springframework.web.bind.annotation.RestController;
//
//import com.baomidou.mybatisplus.core.metadata.IPage;
//import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
//
//import springfox.documentation.annotations.ApiIgnore;
//
///**
// * 流程设计管理
// *
// * @author scott
// * @date 2019/03/06
// * @desc
// */
//@Slf4j
//@RestController("act/model")
//@RequestMapping("/act/model")
//public class ActModelController {
// @Autowired
// private ActModelService actModelService;
//
// /**
// * 流程设计模型列表
// * @param params
// * @return
// */
// @GetMapping("list")
// public Result<IPage<Model>> list(@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
// Result<IPage<Model>> result = new Result<IPage<Model>>();
// IPage<Model> pageList = new Page<Model>();
// List<Model> records = actModelService.page(params);
// pageList.setRecords(records);
// result.setSuccess(true);
// result.setResult(pageList);
// return result;
// }
//
// /**
// * 创建模型
// */
// @RequestMapping("/create")
// public Result<String> create(@RequestBody ModelDTO dto, HttpServletRequest request, HttpServletResponse response) {
// Result<String> result = new Result<String>();
// try {
// String modelId = actModelService.save(dto.getName(), dto.getKey(), dto.getDescription());
// result.setResult(modelId);
// //response.sendRedirect(request.getContextPath() + "/modeler.html?modelId=" + modelId);
// } catch (Exception e) {
// e.printStackTrace();
// log.error("新建流程失败!");
// }
// return result;
// }
//
//
// /**
// * 流程发布
// * @param id
// * @return
// */
// @GetMapping("deploy/{id}")
// public Result deploy(@PathVariable("id") String id) {
// actModelService.deploy(id);
// return Result.ok("流程发布成功");
// }
//
// /**
// * 流程导出
// * @param id
// * @param response
// * @return
// */
// @GetMapping("export/{id}")
// public Result<Object> export(@PathVariable("id") String id, @Parameter(hidden = true) HttpServletResponse response) {
// try {
// actModelService.export(id, response);
// return Result.ok("导出成功!");
// } catch (Exception e) {
// return Result.error("导出失败:"+e.toString());
// }
// }
//
// /**
// * 删除流程设计模型
// * @param ids
// * @return
// */
// @DeleteMapping(value = "/delete")
// public Result delete(@RequestParam(name = "ids", required = true) String ids) {
// if(CommonRandomUtil.isNotEmpty(ids)) {
// for (String id : Arrays.asList(ids.split(","))) {
// actModelService.delete(id);
// }
// }
// return Result.ok("流程删除成功");
// }
//}
@@ -0,0 +1,279 @@
package org.jeecg.modules.bpm.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.engine.RepositoryService;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.bpm.service.impl.ActProcessService;
import org.jeecg.modules.extbpm.process.service.IExtActProcessService;
import org.jeecg.modules.extbpm.util.CommonRandomUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;
/**
* 流程管理功能
*
* @author scott
*/
@Slf4j
@Tag(name="流程实例")
@RestController("actProcessController")
@RequestMapping("/act/process")
public class ActProcessController {
@Autowired
private RepositoryService repositoryService;
@Autowired
private ActProcessService actProcessService;
@Autowired
private IExtActProcessService extActProcessService;
@Lazy
@Autowired
private ISysBaseAPI sysBaseAPI;
@Autowired
@Lazy
private RedisUtil redisUtil;
/**
* 查询所有版本的流程
* @param params
* @return
*/
@GetMapping("list")
public Result<IPage<Map<String, Object>>> list(@RequestParam Map<String, Object> params) {
Result<IPage<Map<String, Object>>> result = new Result<IPage<Map<String, Object>>>();
IPage<Map<String, Object>> pageList = new Page<Map<String, Object>>();
List<Map<String, Object>> records = actProcessService.page(params);
pageList.setRecords(records);
result.setSuccess(true);
result.setResult(pageList);
return result;
}
@PostMapping("deploy")
@Operation(summary = "部署流程文件")
public Result deploy(@RequestParam("processFile") MultipartFile file) throws IOException {
if (file.isEmpty()) {
return Result.error("发布流程失败!");
}
try {
actProcessService.deploy(file);
return Result.ok("流程上传成功!");
} catch (Exception e) {
return Result.error("发布流程失败: "+ e.getMessage());
}
}
/**
* 激活流程
* @param id
* @return
*/
@GetMapping("active/{id}")
public Result active(@PathVariable("id") String id) {
actProcessService.active(id);
return new Result();
}
/**
* 挂起流程
* @param id
* @return
*/
@GetMapping("suspend/{id}")
@Operation(summary="挂起流程")
@Parameter(name = "id", description = "流程ID")
public Result suspend(@PathVariable("id") String id) {
actProcessService.suspend(id);
return new Result();
}
/**
* 关闭流程
* @param key
* @return
*/
@GetMapping("close/{key}")
@Operation(summary="关闭流程")
@Parameter(name = "key", description = "流程key")
public Result close(@PathVariable("key") String key) {
actProcessService.closeProcess(key);
return new Result();
}
/**
* 启动流程
* @param key
* @return
*/
@GetMapping("open/{key}")
@Operation(summary="启动流程")
@Parameter(name = "key", description = "流程key")
public Result open(@PathVariable("key") String key) {
actProcessService.openProcess(key);
return new Result();
}
/**
* 删除
* @param deploymentIds
* @return
*/
@DeleteMapping(value = "/delete")
public Result delete(@RequestParam(name = "ids", required = true) String ids) {
if(CommonRandomUtil.isNotEmpty(ids)) {
for (String id : Arrays.asList(ids.split(","))) {
actProcessService.deleteDeployment(id);
}
}
return new Result();
}
/**
* 显示静态资源(图片/XML)
* @param deploymentId
* @param resourceName
* @param response
* @throws Exception
*/
@GetMapping(value = "resource")
public void resource(@RequestParam(value="deploymentId")String deploymentId, @RequestParam(value="resourceName")String resourceName, @Parameter(hidden = true) HttpServletResponse response) throws Exception {
log.debug("-------------deploymentId-----",deploymentId);
log.debug("-------------resourceName-----",resourceName);
InputStream resourceAsStream = actProcessService.getResourceAsStream(deploymentId, resourceName);
IOUtils.copy(resourceAsStream, response.getOutputStream());
}
/**
* 获取流程进度图
* @param processInstanceId
* @param response
* @throws Exception
*/
@GetMapping(value = "processPic")
public void processPic(@RequestParam(value="processInstanceId")String processInstanceId, HttpServletResponse response) throws Exception {
actProcessService.genProcessDiagram(response, processInstanceId);
}
/**
* 读取资源,通过部署ID
*
* @param deploymentId 流程部署的ID
* @param resourceName 资源名称(foo.xml|foo.png)
* @param response
* @throws Exception
*/
@RequestMapping(value = "downProcessXml")
public void downProcessXmlByDeploy(HttpServletRequest request, HttpServletResponse response) throws Exception {
InputStream resourceAsStream = null;
try {
String deploymentId = oConvertUtils.getString(request.getParameter("deploymentId"));
String resourceName = oConvertUtils.getString(request.getParameter("resourceName"));
String token = oConvertUtils.getString(request.getParameter("token"));
//校验 token 登录有效性
TokenUtils.verifyToken(request, sysBaseAPI, redisUtil);
resourceAsStream = repositoryService.getResourceAsStream(deploymentId, resourceName);
// 文件下载
File file = new File(resourceName);
String filename = file.getName();
// 设置response的Header
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition", "attachment;filename="+ new String(filename.getBytes("utf-8"), "ISO8859-1"));
IOUtils.copy(resourceAsStream, response.getOutputStream());
response.flushBuffer();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (resourceAsStream != null) {
resourceAsStream.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* @TODO 未实现
* 通过上传流程文件,发布流程
*
* @param deploymentId 流程部署的ID
* @param resourceName 资源名称(foo.xml|foo.png)
* @param response
* @throws Exception
*/
@RequestMapping(value = "deployProcess")
@ResponseBody
public void deployProcess(HttpServletRequest request) {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
Map<String, MultipartFile> files = multipartRequest.getFileMap();
List<String> urls = new ArrayList<>();
for (Map.Entry<String, MultipartFile> entry : files.entrySet()) {
MultipartFile file = entry.getValue();// 获取上传文件对象
String fileName = file.getOriginalFilename();
InputStream fileInputStream = null;
try {
fileInputStream = file.getInputStream();
String extension = FilenameUtils.getExtension(fileName);
if (extension.equals("zip") || extension.equals("bar")) {
ZipInputStream zip = new ZipInputStream(fileInputStream);
repositoryService.createDeployment().addZipInputStream(zip).deploy();
zip.close();
} else if (extension.equals("png")) {
repositoryService.createDeployment().addInputStream(fileName, fileInputStream).deploy();
} else if (extension.indexOf("bpmn20.xml") != -1) {
repositoryService.createDeployment().addInputStream(fileName, fileInputStream).deploy();
} else if (extension.equals("bpmn")) {
/*
* bpmn扩展名特殊处理,转换为bpmn20.xml
*/
String baseName = FilenameUtils.getBaseName(fileName);
repositoryService.createDeployment().addInputStream(baseName + ".bpmn20.xml", fileInputStream).deploy();
} else {
throw new FlowableException("错误信息:不支改文件类型" + extension);
}
} catch (Exception e) {
log.error("错误信息:在部署过程中,文件输入流异常" + e.toString());
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
}
} catch (Exception e) {
}
}
}
}
}
@@ -0,0 +1,476 @@
package org.jeecg.modules.bpm.controller;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.shiro.SecurityUtils;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.bpm.dto.BatchSuspendInfo;
import org.jeecg.modules.bpm.dto.ProcessInstHisDTO;
import org.jeecg.modules.bpm.dto.SuspendInfo;
import org.jeecg.modules.bpm.service.ActivitiService;
import org.jeecg.modules.bpm.service.impl.SuperTaskService;
import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
import org.jeecg.modules.extbpm.process.entity.ExtActFlowData;
import org.jeecg.modules.extbpm.process.service.IExtActFlowDataService;
import org.jeecg.modules.extbpm.util.CommonRandomUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
/**
* 流程实例管理功能
*
* @author scott
*/
@Slf4j
@RestController("actProcessInstanceController")
@RequestMapping("/act/processInstance")
public class ActProcessInstanceController {
private final String START_USER_ID = CommonRandomUtil.trim("startUserId");
private final String PROCESS_INSTANCE_ID = CommonRandomUtil.trim("processInstanceId");
@Autowired
protected RepositoryService repositoryService;
@Autowired
private HistoryService historyService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private ActivitiService activitiService;
@Autowired
private SuperTaskService superTaskService;
@Autowired
private IExtActFlowDataService extActFlowDataService;
@Lazy
@Autowired
private ISysBaseAPI sysBaseAPI;
/**
* 运行中流程实例数据
*
* @param request
* @return
*/
@GetMapping(value = "/list")
public Result<IPage<ProcessInstHisDTO>> listRunning(HttpServletRequest request) {
Result<IPage<ProcessInstHisDTO>> result = new Result<IPage<ProcessInstHisDTO>>();
IPage<ProcessInstHisDTO> pageList = new Page<ProcessInstHisDTO>();
HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();
if (CommonRandomUtil.isNotEmpty(request.getParameter(START_USER_ID))) {
historicProcessInstanceQuery = historicProcessInstanceQuery.startedBy(request.getParameter(START_USER_ID));
}
if (CommonRandomUtil.isNotEmpty(request.getParameter(PROCESS_INSTANCE_ID))) {
historicProcessInstanceQuery = historicProcessInstanceQuery.processInstanceId(request.getParameter(PROCESS_INSTANCE_ID));
}
String startTimeBegin = request.getParameter("startTime_begin");// 时间开始
String startTimeEnd = request.getParameter("startTime_end");// 时间结束
if (CommonRandomUtil.isNotEmpty(startTimeBegin)) {
try {
historicProcessInstanceQuery.startedAfter(DateUtils.parseDate(startTimeBegin, "yyyy-MM-dd"));
} catch (ParseException e) {
e.printStackTrace();
}
}
if (CommonRandomUtil.isNotEmpty(startTimeEnd)) {
try {
historicProcessInstanceQuery.startedBefore(DateUtils.parseDate(startTimeEnd, "yyyy-MM-dd"));
} catch (ParseException e) {
e.printStackTrace();
}
}
String endtimeBegin = request.getParameter("endTime_begin");// 时间开始
String endtimeEnd = request.getParameter("endTime_end");// 时间结束
if (CommonRandomUtil.isNotEmpty(endtimeBegin)) {
try {
historicProcessInstanceQuery.finishedAfter(DateUtils.parseDate(endtimeBegin, "yyyy-MM-dd"));
} catch (ParseException e) {
e.printStackTrace();
}
}
if (CommonRandomUtil.isNotEmpty(endtimeEnd)) {
try {
historicProcessInstanceQuery.finishedBefore(DateUtils.parseDate(endtimeEnd, "yyyy-MM-dd"));
} catch (ParseException e) {
e.printStackTrace();
}
}
//改造treegrid
// 分页参数
Integer pageNo = oConvertUtils.getInt(request.getParameter("pageNo"),1);
Integer pageSize = oConvertUtils.getInt(request.getParameter("pageSize"),10);
Integer start = (pageNo - 1) * pageSize;
//Integer end = page * rows - 1;
List<HistoricProcessInstance> list = historicProcessInstanceQuery.unfinished().orderByProcessInstanceStartTime().desc().listPage(start, pageSize);
long count = historicProcessInstanceQuery.unfinished().count();
List<ProcessInstHisDTO> records = new ArrayList<ProcessInstHisDTO>();
ProcessInstHisDTO processInstHisDTO = null;
for (HistoricProcessInstance hi : list) {
String starttime = DateFormatUtils.format(hi.getStartTime(), "yyyy-MM-dd HH:mm:ss");
String endtime = hi.getEndTime() == null ? "" : DateFormatUtils.format(hi.getEndTime(), "yyyy-MM-dd HH:mm:ss");
long totalTimes = hi.getEndTime() == null ? (Calendar.getInstance().getTimeInMillis() - hi.getStartTime().getTime()) : (hi.getEndTime().getTime() - hi.getStartTime().getTime());
long dayCount = totalTimes / (1000 * 60 * 60 * 24);// 计算天
long restTimes = totalTimes % (1000 * 60 * 60 * 24);// 剩下的时间用于计于小时
long hourCount = restTimes / (1000 * 60 * 60);// 小时
restTimes = restTimes % (1000 * 60 * 60);
long minuteCount = restTimes / (1000 * 60);
String spendTimes = dayCount + "" + hourCount + "小时" + minuteCount + "";
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(hi.getProcessDefinitionId()).singleResult();
log.debug("spendTimes" + spendTimes);
String isSuspended = "finished";
String activityName = "";// 当前任务名称
String activityUser = "";// 当前任务签收人
String taskId = "";// 任务ID
if (hi.getEndTime() == null) {
// endtime为空表示流程实例未结束
// 增加业务标题
String bpmBizTitle = this.activitiService.getHisVarinst(WorkFlowGlobals.BPM_BIZ_TITLE, hi.getId());
bpmBizTitle = oConvertUtils.isEmpty(bpmBizTitle)?processDefinition.getName():bpmBizTitle;
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(hi.getId()).singleResult();
List<Task> tasks = taskService.createTaskQuery().processInstanceId(hi.getId()).list();
if (tasks.size() > 1) {
// 针对会签或者一个流程多个分支任务(一个流程实例,同时存在多个任务情况),流程实例报错处理--------------------
isSuspended = "" + pi.isSuspended();
activityName = CommonRandomUtil.trimToEmpty(tasks.get(0).getName());
activityUser = CommonRandomUtil.trimToEmpty(tasks.get(0).getAssignee());
taskId = oConvertUtils.getString(tasks.get(0).getId());
processInstHisDTO = new ProcessInstHisDTO();
processInstHisDTO.setId(hi.getId());
processInstHisDTO.setBpmBizTitle(bpmBizTitle);
processInstHisDTO.setParentId("");
processInstHisDTO.setTaskId(taskId);
processInstHisDTO.setName(activityName);
processInstHisDTO.setAssignee("");
processInstHisDTO.setPrcocessDefinitionName(processDefinition.getName());
processInstHisDTO.setStartUserId(hi.getStartUserId());
if(oConvertUtils.isNotEmpty(processInstHisDTO.getStartUserId())) {
LoginUser sysUser = sysBaseAPI.getUserByName(processInstHisDTO.getStartUserId());
processInstHisDTO.setStartUserName(sysUser.getRealname());
}
processInstHisDTO.setStartTime(starttime);
processInstHisDTO.setEndTime(endtime);
processInstHisDTO.setSpendTimes(spendTimes);
processInstHisDTO.setIsSuspended(isSuspended);
processInstHisDTO.setProcessDefinitionId(hi.getProcessDefinitionId());
processInstHisDTO.setProcessInstanceId(hi.getId());
int i = 1;
List<ProcessInstHisDTO> childrens = new ArrayList<ProcessInstHisDTO>();
ProcessInstHisDTO children = null;
for (Task task : tasks) {
isSuspended = "" + pi.isSuspended();
activityName = CommonRandomUtil.trimToEmpty(task.getName());
activityUser = CommonRandomUtil.trimToEmpty(task.getAssignee());
taskId = oConvertUtils.getString(task.getId());
children = new ProcessInstHisDTO();
children.setId(hi.getId() + ":" + (i++));
children.setBpmBizTitle(bpmBizTitle);
children.setParentId(hi.getId());
children.setTaskId(taskId);
children.setName(activityName);
children.setAssignee(activityUser);
if(oConvertUtils.isNotEmpty(children.getAssignee())) {
LoginUser sysUser = sysBaseAPI.getUserByName(children.getAssignee());
children.setAssigneeName(sysUser!=null?sysUser.getRealname():children.getAssignee());
}
children.setPrcocessDefinitionName(processDefinition.getName());
children.setStartUserId(hi.getStartUserId());
if(oConvertUtils.isNotEmpty(children.getStartUserId())) {
LoginUser sysUser = sysBaseAPI.getUserByName(children.getStartUserId());
children.setStartUserName(sysUser.getRealname());
}
children.setStartTime(starttime);
children.setEndTime(endtime);
children.setSpendTimes(spendTimes);
children.setIsSuspended(isSuspended);
children.setProcessDefinitionId(hi.getProcessDefinitionId());
children.setProcessInstanceId(hi.getId());
childrens.add(children);
}
processInstHisDTO.setChildren(childrens);
records.add(processInstHisDTO);
} else {
for (Task task : tasks) {
isSuspended = "" + pi.isSuspended();
activityName = CommonRandomUtil.trimToEmpty(task.getName());
activityUser = CommonRandomUtil.trimToEmpty(task.getAssignee());
taskId = oConvertUtils.getString(task.getId());
processInstHisDTO = new ProcessInstHisDTO();
processInstHisDTO.setId(hi.getId());
processInstHisDTO.setBpmBizTitle(bpmBizTitle);
processInstHisDTO.setParentId("");
processInstHisDTO.setTaskId(taskId);
processInstHisDTO.setName(activityName);
// NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
processInstHisDTO.setAssignee(activityUser);
if(oConvertUtils.isNotEmpty(processInstHisDTO.getAssignee())) {
LoginUser sysUser = sysBaseAPI.getUserByName(processInstHisDTO.getAssignee());
processInstHisDTO.setAssigneeName(sysUser!=null?sysUser.getRealname():processInstHisDTO.getAssignee());
}
processInstHisDTO.setPrcocessDefinitionName(processDefinition.getName());
processInstHisDTO.setStartUserId(hi.getStartUserId());
if(oConvertUtils.isNotEmpty(processInstHisDTO.getStartUserId())) {
LoginUser sysUser = sysBaseAPI.getUserByName(processInstHisDTO.getStartUserId());
processInstHisDTO.setStartUserName(sysUser!=null?sysUser.getRealname():processInstHisDTO.getStartUserId());
}
processInstHisDTO.setStartTime(starttime);
processInstHisDTO.setEndTime(endtime);
processInstHisDTO.setSpendTimes(spendTimes);
processInstHisDTO.setIsSuspended(isSuspended);
processInstHisDTO.setProcessDefinitionId(hi.getProcessDefinitionId());
processInstHisDTO.setProcessInstanceId(hi.getId());
records.add(processInstHisDTO);
}
}
}
}
pageList.setRecords(records);
log.debug(" count : " + count);
pageList.setTotal(count);
result.setSuccess(true);
result.setResult(pageList);
return result;
}
/**
* 进行委派操作
* @param processInstanceId 流程部署ID
*/
@GetMapping(value = "reassign")
public Result reassign(@RequestParam("taskId") String taskId, @RequestParam("userName") String assignUserId,HttpServletRequest request) {
Task task = activitiService.getTask(taskId);
//去掉最后面的逗号
if(oConvertUtils.isNotEmpty(assignUserId) && assignUserId.endsWith(",")) {
assignUserId = assignUserId.substring(0, assignUserId.length() -1);
}
LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal();
taskService.setOwner(task.getId(), sysUser.getUsername());
//TODO 委派给多个人好使吗?
taskService.setAssignee(task.getId(), assignUserId);
return Result.ok("委派成功!");
}
/**
* 暂停正在运行的流程实例
* @param processInstanceId 流程实例ID
*/
@GetMapping(value = "suspend")
public Result suspend(@RequestParam("processInstanceId") String processInstanceId, HttpServletRequest request) {
activitiService.suspend(processInstanceId);
return Result.ok("挂起成功!");
}
/**
* 启动暂停的流程实例
* @param processInstanceId 流程实例ID
*/
@GetMapping(value = "restart")
public Result restart(@RequestParam("processInstanceId") String processInstanceId, HttpServletRequest request) {
activitiService.restart(processInstanceId);
return Result.ok("启动成功!");
}
/**
* 暂停正在运行的流程实例(批量处理)
*/
@PostMapping(value = "batchSuspend")
public Result<BatchSuspendInfo> batchSuspend(@RequestBody BatchSuspendInfo batchSuspendInfo, HttpServletRequest request) {
Result<BatchSuspendInfo> result = new Result<BatchSuspendInfo>();
List<SuspendInfo> data = batchSuspendInfo.getData();
if(data==null||data.size()<=0) {
result.error500("请选择数据");
return result;
}
int sucessCount = 0;
for(SuspendInfo info :data) {
try {
LambdaQueryWrapper<ExtActFlowData> queryWrapper = new LambdaQueryWrapper<ExtActFlowData>();
queryWrapper.eq(ExtActFlowData::getFormDataId, info.getDataId());
queryWrapper.eq(ExtActFlowData::getRelationCode, info.getFlowCode());
ExtActFlowData extActFlowData = extActFlowDataService.getOne(queryWrapper);
if(extActFlowData!=null) {
info.setProcInstId(extActFlowData.getProcessInstId());
activitiService.suspend(extActFlowData.getProcessInstId());
info.setStatus("1");//处理成功
sucessCount++;
continue;
}
} catch (Exception e) {
log.error(e.getMessage(),e);
}
info.setStatus("0");//处理失败
}
batchSuspendInfo.setData(data);
batchSuspendInfo.setSucessCount(sucessCount);
result.setResult(batchSuspendInfo);
result.success("处理完成!");
return result;
}
/**
* 启动暂停的流程实例(批量处理)
*/
@PostMapping(value = "batchRestart")
public Result<BatchSuspendInfo> batchRestart(@RequestBody BatchSuspendInfo batchSuspendInfo, HttpServletRequest request) {
Result<BatchSuspendInfo> result = new Result<BatchSuspendInfo>();
List<SuspendInfo> data = batchSuspendInfo.getData();
if(data==null||data.size()<=0) {
result.error500("请选择数据");
return result;
}
int sucessCount = 0;
for(SuspendInfo info :data) {
try {
LambdaQueryWrapper<ExtActFlowData> queryWrapper = new LambdaQueryWrapper<ExtActFlowData>();
queryWrapper.eq(ExtActFlowData::getFormDataId, info.getDataId());
queryWrapper.eq(ExtActFlowData::getRelationCode, info.getFlowCode());
ExtActFlowData extActFlowData = extActFlowDataService.getOne(queryWrapper);
if(extActFlowData!=null) {
info.setProcInstId(extActFlowData.getProcessInstId());
activitiService.restart(extActFlowData.getProcessInstId());
info.setStatus("1");//处理成功
sucessCount++;
continue;
}
} catch (Exception e) {
log.error(e.getMessage(),e);
}
info.setStatus("0");//处理失败
}
batchSuspendInfo.setData(data);
batchSuspendInfo.setSucessCount(sucessCount);
result.setResult(batchSuspendInfo);
result.success("处理完成!");
return result;
}
/**
* 作废运行中的流程实例
* @param processInstanceId 流程实例ID
*/
@GetMapping(value = "close")
public Result close(@RequestParam("processInstanceId") String processInstanceId, HttpServletRequest request) {
//JTC-853 流程实例中被关闭,提示作废成功,但是我的工单列表显示状态是 完成
runtimeService.setVariable(processInstanceId, WorkFlowGlobals.BPM_STATUS, WorkFlowGlobals.PROCESS_INVALIDPROCESS_STATUS);
runtimeService.deleteProcessInstance(processInstanceId, "作废流程");
return Result.ok("作废成功!");
}
/**
* 跳转到某个节点
* @param taskId 当前节点
* @param skipNode 跳转到那个节点
* @param request
* @return
* @throws Exception
*/
@GetMapping(value = "skipNode")
public Result skipNode(@RequestParam("taskId") String taskId, @RequestParam("skipTaskNode") String skipNode,HttpServletRequest request) throws Exception {
//TODO 流程变量参数
//Map<String, Object> map = var.getVariableMap(processHandle.getTpProcesspros());
//判断是否是委托任务,被委托人先解决委托
Task task=taskService.createTaskQuery().taskId(taskId).active().singleResult();
if (org.apache.commons.lang.StringUtil.isNotBlank(task.getOwner())) {
DelegationState delegationState = task.getDelegationState();
switch (delegationState) {
case PENDING:
taskService.resolveTask(taskId);
break;
case RESOLVED:
//委托任务已经完成
break;
default:
//不是委托任务
break;
}
}
if("end".equals(skipNode)){
superTaskService.endProcess(taskId);
}else{
superTaskService.goProcessTaskNode(taskId,skipNode);
}
return Result.ok("跳转成功!");
}
/**
* 通过当前taskId获取所有的流程任务节点
* @param taskId 当前节点
* @param request
* @return
* @throws Exception
*/
@GetMapping(value = "getAllTask")
public Result getAllTask(@RequestParam("taskId") String taskId,HttpServletRequest request) throws Exception {
List<Map> ls = activitiService.getAllTaskNode(taskId);
List<Map> list = new ArrayList<Map>();
for(Map map :ls) {
String taskKey = (String)map.get("taskKey");
String name = (String)map.get("name");
//author:scott date:20220118 for:JTC-1044【工作流】流程实例 跳转 有空节点
if(taskKey==null
||name == null
||taskKey.startsWith("exclusiveGateway")
||taskKey.startsWith("parallelGateway")
||taskKey.startsWith("callSubProcess")
||taskKey.startsWith("Gateway_")
||taskKey.startsWith("Start_")
||taskKey.startsWith("End_")
||taskKey.startsWith("Start")
||taskKey.startsWith("end")
||taskKey.startsWith("start1")) {
continue;
}
list.add(map);
}
return Result.ok(list);
}
}
@@ -0,0 +1 @@
package org.jeecg.modules.bpm.dto;import java.util.Date;import lombok.Data;@Datapublic class ActHiActinstDTO implements java.io.Serializable { private static final long serialVersionUID = 1L; /**␍ *␍ */ private String id; /**␍ *␍ */ private String procDefId; /**␍ *␍ */ private String procInstId; /**␍ *␍ */ private String executionId; /**␍ *␍ */ private String actId; /**␍ *␍ */ private String taskId; /**␍ *␍ */ private String callProcInstId; /**␍ *␍ */ private String actName; /**␍ *␍ */ private String actType; /**␍ *␍ */ private String assignee; /**␍ *␍ */ private Date startTime; /**␍ *␍ */ private Date endTime; /**␍ *␍ */ private Long duration; /**␍ *␍ */ private String tenantId; }
@@ -0,0 +1,22 @@
package org.jeecg.modules.bpm.dto;
import lombok.Data;
import java.util.Date;
/**
* @Description: 运行时超时任务
* @author: scott
* @date: 2020年04月21日 19:59
*/
@Data
public class ActRuTaskDTO {
private Date createTime;
private String id;
private String name;
private String assignee;
private String owner;
private String procInstId;
private String executionId;
private Integer nodeTimeout;
}
@@ -0,0 +1,17 @@
package org.jeecg.modules.bpm.dto;
import lombok.Data;
/**
* 抄送任务
*/
@Data
public class ActTaskCcDTO {
private String procDefId;
private String taskDefKey;
private String procInstId;
private String executionId;
private String taskName;
}
@@ -0,0 +1,63 @@
package org.jeecg.modules.bpm.dto;
import java.util.List;
/**
* 动态加任务节点用(流程图跟踪变)
*
* @author : scott
* @title: : AddSignTaskVo
* @projectName : flowable
* @description: 加签Vo
* @date : 2022-12-02
*/
public class AddSignTaskVo {
/**
* 任务id 必填
*/
private String currentTaskId;
/**
* 被加签人
*/
private List<String> signUserIds;
/**
* 操作人code 必填
*/
private String userCode;
/**
* 意见
*/
private String reason;
public String getCurrentTaskId() {
return currentTaskId;
}
public void setCurrentTaskId(String currentTaskId) {
this.currentTaskId = currentTaskId;
}
public List<String> getSignUserIds() {
return signUserIds;
}
public void setSignUserIds(List<String> signUserIds) {
this.signUserIds = signUserIds;
}
public String getUserCode() {
return userCode;
}
public void setUserCode(String userCode) {
this.userCode = userCode;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
}
@@ -0,0 +1,22 @@
package org.jeecg.modules.bpm.dto;
import java.util.List;
import lombok.Data;
@Data
public class BatchSuspendInfo implements java.io.Serializable{
private static final long serialVersionUID = 1L;
/**
* 批量挂起业务参数
*/
private List<SuspendInfo> data;
/**
* 处理成功的个数
*/
private int sucessCount;
}
@@ -0,0 +1,162 @@
package org.jeecg.modules.bpm.dto;
import org.jeecg.modules.bpm.constant.enums.CommentTypeEnum;
import java.io.Serializable;
import java.util.Date;
/**
* @Description:
* @Author: Bruce.liu
* @Since:19:24 2019/11/24
* 爱拼才会赢 2019 ~ 2030 版权所有
*/
public class CommentVo implements Serializable {
/**
* 任务id
*/
protected String taskId;
/**
* 添加人
*/
protected String userId;
/**
* 用户的名称
*/
protected String userName;
/**
* 用户的头像链接
*/
protected String userUrl;
/**
* 流程实例id
*/
protected String processInstanceId;
/**
* 意见信息
*/
protected String message;
/**
* 时间
*/
protected Date time;
/**
* @see org.jeecg.modules.bpm.constant.enums.CommentTypeEnum
*/
private String type;
/**
* 类型名称
*/
private String typeName;
/**
* 任务名称
*/
private String taskName;
/**
* 评论全信息
*/
private String fullMsg;
public CommentVo(){}
public CommentVo(String userId, String processInstanceId, String type, String message) {
this.userId = userId;
this.processInstanceId = processInstanceId;
this.message = message;
this.type = type;
}
public CommentVo(String taskId, String userId, String processInstanceId, String type, String message) {
this.taskId = taskId;
this.userId = userId;
this.processInstanceId = processInstanceId;
this.message = message;
this.type = type;
}
public String getTaskId() {
return taskId;
}
public void setTaskId(String taskId) {
this.taskId = taskId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserUrl() {
return userUrl;
}
public void setUserUrl(String userUrl) {
this.userUrl = userUrl;
}
public String getProcessInstanceId() {
return processInstanceId;
}
public void setProcessInstanceId(String processInstanceId) {
this.processInstanceId = processInstanceId;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
this.typeName = CommentTypeEnum.getEnumMsgByType(type);
}
public String getTypeName() {
return typeName;
}
public void setTypeName(String typeName) {
this.typeName = typeName;
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
public String getFullMsg() {
return fullMsg;
}
public void setFullMsg(String fullMsg) {
this.fullMsg = fullMsg;
}
}
@@ -0,0 +1,36 @@
package org.jeecg.modules.bpm.dto;
import lombok.Data;
import java.util.Date;
/**
* @Description: 流程节点信息
* @Author: zyf
* @Date: 2022-05-05
*/
@Data
public class FlowNodeDto {
/**
* 节点id
*/
private String nodeId;
/**
* 节点名称
*/
private String nodeName;
/**
* 执行人的id
*/
private String userId;
/**
* 执行人姓名
*/
private String userName;
/**
* 任务节点结束时间
*/
private Date endTime;
}
@@ -0,0 +1,31 @@
package org.jeecg.modules.bpm.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* 模型
*
* @author scott
* @since 1.0.0
*/
@Data
@Schema(description="模型")
public class ModelDTO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "模型名称")
@NotBlank(message="{model.name.require}")
private String name;
@Schema(description = "模型标识")
@NotBlank(message="{model.key.require}")
private String key;
@Schema(description = "模型描述")
private String description;
}
@@ -0,0 +1,32 @@
package org.jeecg.modules.bpm.dto;
import lombok.Data;
import java.util.List;
/**
* @Description: 多实例加签减签操作请求参数
* @Author: zyf
* @Date: 2022-05-24
*/
@Data
public class MultiInstanceRequestDto {
/**
* 任务节点id
*/
private String taskId;
/**
* 会签赋值表达式 KEY
*
* - 简流模式参数值:loopAssigneeCollection —— FlowableConstant.LOOP_ASSIGNEE_COLLECTION
* - 低代码模式参数值:assigneeUserIdList —— WorkFlowGlobals.ASSIGNEE_USER_ID_LIST
*/
private String assigneeListKey;
/**
* 加签/减签 人员
*/
private List<String> assignees;
}
@@ -0,0 +1,32 @@
package org.jeecg.modules.bpm.dto;
import lombok.Data;
/**
* 会签节点信息
*
* @Author: zyf
* @Date: 2022-05-25
*/
@Data
public class MultiInstanceResponseDto {
/**
* 会签类型
*/
private Object type;
/**
* 是否串行
*/
private Boolean isSequential;
/**
* 会签人员KEY
*/
private String assignee;
/**
* 会签人员集合KEY
*/
private String assigneeList;
}
@@ -0,0 +1,88 @@
package org.jeecg.modules.bpm.dto;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Data;
import org.jeecg.modules.bpm.util.BpmUtil;
@Data
public class ProcessHisDTO implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private String id;
/** 名称*/
private String name;
/** 流程实例ID */
private String processInstanceId;
/** 流程定义id */
private String processDefinitionId;
/** 流程定义名称 */
private String prcocessDefinitionName;
/** 开始时间 */
private String startTime;
/** 结束时间 */
private String endTime;
/** 正在处理的节点 —— 负责人 */
private String owner;
private String assignee;
/** 正在处理的节点 —— 负责人昵称 */
private String assigneeName;
/** 正在处理的节点 —— 负责人头像 */
private String assigneeAvatar;
/** 正在处理的节点 */
private String currentTaskKey;
private String currentTaskName;
/** 处理结果 */
private String deleteReason;
/** 流程发起人 */
private String startUserId;
private String startUserName;
/** 流程发起人 - 头像*/
private String startUserAvatar;
/** 耗时 */
private String spendTimes;
/** 标题 */
private String bpmBizTitle;
/** 流程状态 */
private String bpmStatus;
//--update--begin------author:scott-----date:20220705-----for:【VUEN-1334】流程高级配置支持通知设置和控制撤回、催办权限-----
/**是否允许催办1是0否*/
private String urgeStatus;
/**是否允许取回流程1是0否*/
private String backStatus;
/**是否允许查看流程图1是0否*/
private String graphicStatus;
/**是否启用自动提交流程1是0否*/
private String autoSubmitStatus;
/**通知类型(多个会以逗号隔开)*/
private String notifyWay;
//--update--end------author:scott-----date:20220705-----for:【VUEN-1334】流程高级配置支持通知设置和控制撤回、催办权限-----
//--update--begin------author:scott-----date:20230107-----for:【QQYUN-3618】待办流程列表增加字段【应用名称】【表单名称】---
/** 应用名称*/
private String lowAppName;
/** 应用下表单名称*/
private String lowFormName;
//--update--end------author:scott-----date:20230107-----for:【QQYUN-3618】待办流程列表增加字段【应用名称】【表单名称】---
/** 审批意见*/
private String remarks;
/** 任务耗时*/
private Long taskDueTime;
public String getDurationStr(){
try {
if(this.taskDueTime == null) {
return null;
}
return BpmUtil.dealTimeFromNum(this.taskDueTime);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
@@ -0,0 +1,49 @@
package org.jeecg.modules.bpm.dto;
import java.util.List;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Data;
@Data
public class ProcessInstHisDTO implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private String id;
/** 名称*/
private String name;
/** 父id*/
private String parentId;
/** 流程实例ID */
private String processInstanceId;
/** 流程定义id */
private String processDefinitionId;
/** 流程定义名称 */
private String prcocessDefinitionName;
/** 开始时间 */
private String startTime;
/** 结束时间 */
private String endTime;
/** 任务id */
private String taskId;
/** 负责人 */
private String assignee;
private String assigneeName;
/** 处理结果 */
private String deleteReason;
/** 流程发起人 */
private String startUserId;
private String startUserName;
/** 耗时 */
private String spendTimes;
/** 标题 */
private String bpmBizTitle;
/** 状态 */
private String isSuspended;
/** 子节点 */
private List<ProcessInstHisDTO> children;
}
@@ -0,0 +1,20 @@
package org.jeecg.modules.bpm.dto;
import lombok.Data;
/**
* @Description: 统计流程的每个类型的数量
* @author: scott
* @date: 2023年01月09日 12:06
*/
@Data
public class StartTypeStatisticsDto {
/**
* 流程开始节点,启动类型
*/
private String startType;
/**
* 个数
*/
private Integer count;
}
@@ -0,0 +1,29 @@
package org.jeecg.modules.bpm.dto;
import lombok.Data;
@Data
public class SuspendInfo implements java.io.Serializable{
private static final long serialVersionUID = 1L;
/**
* 业务单据id
*/
private String dataId;
/**
* 流程关联唯一编码
*/
private String flowCode;
/**
* 业务标题
*/
private String bizTitle;
/**
* 流程实例id
*/
private String procInstId;
/**
* 处理状态
*/
private String status;
}
@@ -0,0 +1,91 @@
package org.jeecg.modules.bpm.dto;
import java.util.Date;
import org.jeecg.modules.bpm.util.BpmUtil;
import org.springframework.format.annotation.DateTimeFormat;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
@Data
public class TaskDTO implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private String id;
/** 任务名*/
private String taskName;
/** 任务ID*/
private String taskId;
/** 任务办理人 - 用户名*/
private String taskAssigneeName;
/** 任务办理人 - 用户ID*/
private String taskAssigneeId;
/** 任务办理人 - 头像*/
private String taskAssigneeAvatar;
/** 任务开始时间*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date taskBeginTime;
/** 任务结束时间*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date taskEndTime;
/** 任务耗时*/
private Long taskDueTime;
/** 流程发起人,用户名*/
private String processApplyUserName;
/** 流程发起人,头像*/
private String processApplyUserAvatar;
/** 流程发起人,用户ID*/
private String processApplyUserId;
/** 流程编号*/
private String processDefinitionId;
/** 流程名称*/
private String processDefinitionName;
/** 流程实例ID*/
private String processInstanceId;
/** 流程结束时间*/
private Date processEndTime;
/** 流程取消原因*/
private String processDeleteReason;
/** 业务标题*/
private String bpmBizTitle;
/** 流程状态 未发起、处理中、处理完成*/
private String bpmStatus;
/** 超时提醒*/
private Boolean taskTimeoutWarn;
/** 是否催办*/
private Boolean taskUrge;
/** 催办说明*/
private String taskUrgeRemark;
/** 意见*/
private String remarks;
//--update--begin------author:scott-----date:20230107-----for:【QQYUN-3618】待办流程列表增加字段【应用名称】【表单名称】---
/** 应用名称*/
private String lowAppName;
/** 应用下表单名称*/
private String lowFormName;
//--update--end------author:scott-----date:20230107-----for:【QQYUN-3618】待办流程列表增加字段【应用名称】【表单名称】---
public String getDurationStr(){
try {
if(this.taskDueTime == null) {
return null;
}
return BpmUtil.dealTimeFromNum(this.taskDueTime);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
@@ -0,0 +1,73 @@
package org.jeecg.modules.bpm.dto;
import java.io.Serializable;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
/**
* @Description: 用户代理人设置
* @Author: jeecg-boot
* @Date: 2019-04-17
* @Version: V1.0
*/
@Data
public class UserAgentDTO implements Serializable {
private static final long serialVersionUID = 1L;
/**序号*/
@TableId(type = IdType.ASSIGN_ID)
private java.lang.String id;
/**用户名*/
@Excel(name = "用户名", width = 15)
private java.lang.String userName;
/**代理人用户名*/
@Excel(name = "代理人用户名", width = 15)
private java.lang.String agentUserName;
/**代理开始时间*/
@Excel(name = "代理开始时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private java.util.Date startTime;
/**代理结束时间*/
@Excel(name = "代理结束时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private java.util.Date endTime;
/**状态0无效1有效*/
@Excel(name = "状态0无效1有效", width = 15)
private java.lang.String status;
/**创建人名称*/
@Excel(name = "创建人名称", width = 15)
private java.lang.String createName;
/**创建人登录名称*/
@Excel(name = "创建人登录名称", width = 15)
private java.lang.String createBy;
/**创建日期*/
@Excel(name = "创建日期", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private java.util.Date createTime;
/**更新人名称*/
@Excel(name = "更新人名称", width = 15)
private java.lang.String updateName;
/**更新人登录名称*/
@Excel(name = "更新人登录名称", width = 15)
private java.lang.String updateBy;
/**更新日期*/
@Excel(name = "更新日期", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private java.util.Date updateTime;
/**所属部门*/
@Excel(name = "所属部门", width = 15)
private java.lang.String sysOrgCode;
/**所属公司*/
@Excel(name = "所属公司", width = 15)
private java.lang.String sysCompanyCode;
}
@@ -0,0 +1,123 @@
package org.jeecg.modules.bpm.job;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.delegate.event.impl.FlowableEventBuilder;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.cmd.StartProcessInstanceCmd;
import org.flowable.engine.impl.jobexecutor.TimerEventHandler;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.impl.util.ProcessInstanceHelper;
import org.flowable.job.service.JobHandler;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.variable.api.delegate.VariableScope;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
import org.jeecg.modules.extbpm.process.service.IExtActFormDataService;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义定时作业的个性化处理器
*/
@Slf4j
@Component("customTimeStartJobEventHandler")
public class CustomTimeStartJobEventHandler extends TimerEventHandler implements JobHandler {
public static final String TYPE = "timer-start-event-custom";
private IExtActFormDataService extActFormDataService;
@Override
public String getType() {
return "timer-start-event-custom";
}
@Override
public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
log.info("============执行自定义 定时启动流程 处理器============");
log.info("定时任务详情 = {}", JSON.toJSONString(job));
if (extActFormDataService == null) {
extActFormDataService = SpringContextUtils.getBean(IExtActFormDataService.class);
}
String jobHandlerConfigStr = job.getJobHandlerConfiguration();
//获取自定义的定时启动,流程参数
JSONObject jobHandlerConfig = JSONObject.parseObject(jobHandlerConfigStr);
ProcessDefinitionEntity processDefinitionEntity = ProcessDefinitionUtil.getProcessDefinitionFromDatabase(job.getProcessDefinitionId());
if (processDefinitionEntity == null) {
throw new FlowableException("Could not find process definition needed for timer start event");
} else {
try {
if (!processDefinitionEntity.isSuspended()) {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher();
if (eventDispatcher != null && eventDispatcher.isEnabled()) {
eventDispatcher.dispatchEvent(FlowableEventBuilder.createEntityEvent(FlowableEngineEventType.TIMER_FIRED, job), processEngineConfiguration.getEngineCfgKey());
}
Process process = ProcessDefinitionUtil.getProcess(job.getProcessDefinitionId());
String activityId = TimerEventHandler.getActivityIdFromConfiguration(configuration);
if (activityId != null) {
FlowElement flowElement = process.getFlowElement(activityId, true);
if (flowElement == null) {
throw new FlowableException("Could not find matching FlowElement for activityId " + activityId);
}
ProcessInstanceHelper processInstanceHelper = processEngineConfiguration.getProcessInstanceHelper();
//----------------------------------------------------------------------------------------------
//设置流程发起所需要的流程变量
String formKey = oConvertUtils.getString(jobHandlerConfig.get(WorkFlowGlobals.BPM_FORM_KEY));
String dataId = oConvertUtils.getString(jobHandlerConfig.get(WorkFlowGlobals.BPM_DATA_ID));
Map<String, Object> params = extActFormDataService.getJsonDataById(formKey, dataId);
log.debug("---日期定时启动器,加载初始化数据:{}", params);
if (params == null) {
params = new HashMap<String, Object>();
params.put(WorkFlowGlobals.BPM_DES_DATA_ID, dataId);
params.put(WorkFlowGlobals.BPM_DES_FORM_CODE, formKey);
}
params.put(WorkFlowGlobals.BPM_DES_DATA_ID, dataId);
params.put(WorkFlowGlobals.BPM_DES_FORM_CODE, formKey);
params.put(WorkFlowGlobals.BPM_DATA_ID, dataId);
params.put(WorkFlowGlobals.BPM_FORM_TYPE, "2");
params.put(WorkFlowGlobals.BPM_FORM_KEY, formKey);
String url = "{{DOMAIN_URL}}/desform/detail/" + formKey + "/"+dataId+"?token={{TOKEN}}&taskId={{TASKID}}";
String mobileUrl = "{{DOMAIN_URL}}/desform/detail/" + formKey + "/"+dataId+"?token={{TOKEN}}&taskId={{TASKID}}";
params.put(WorkFlowGlobals.BPM_FORM_CONTENT_URL, url);
params.put(WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE, mobileUrl);
params.put(WorkFlowGlobals.APPLY_USER_ID, jobHandlerConfig.get(WorkFlowGlobals.APPLY_USER_ID));
//----------------------------------------------------------------------------------------------
//flowable升级 6.7.2 => 6.8.0
//processInstanceHelper.createAndStartProcessInstanceWithInitialFlowElement(processDefinitionEntity, (String) null, (String) null, (String) null, flowElement, process, params, (Map) null , true);
processInstanceHelper.createAndStartProcessInstanceWithInitialFlowElement(processDefinitionEntity, (String) null, (String) null, (String) null, flowElement, process, params, (Map) null, null, null, true);
} else {
(new StartProcessInstanceCmd(processDefinitionEntity.getKey(), (String) null, (String) null, (Map) null, job.getTenantId())).execute(commandContext);
}
} else {
log.debug("ignoring timer of suspended process definition {}", processDefinitionEntity.getName());
}
} catch (RuntimeException var12) {
log.error("exception during timer execution", var12);
throw var12;
} catch (Exception var13) {
log.error("exception during timer execution", var13);
throw new FlowableException("exception during timer execution: " + var13.getMessage(), var13);
}
}
}
}
@@ -0,0 +1,216 @@
package org.jeecg.modules.bpm.mapper;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.flowable.idm.api.User;
import org.jeecg.modules.bpm.dto.*;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
public interface ActivitiMapper extends BaseMapper {
/**
* 根据历史执行ID,查询变量内容
* @param proid EXECUTION_ID_
* @param name NAME_
* @return
*/
@Select("select TEXT_ from act_hi_varinst where NAME_= #{name} and EXECUTION_ID_= #{proid}")
String getHisVarinst(@Param("name")String name,@Param("proid") String proid);
// /**
// * 通过业务标题,去流程变量里模糊匹配出 流程实例list
// * @param text text
// * @return
// */
// List<String> getHisVarinstProids(@Param("text") String text);
/**
* 通过业务标题,去流程变量里模糊匹配出 流程实例list
* @param text text
* @return
*/
List<String> getHisVarinstProidsWithIn(@Param("text") String text, @Param("procInstIdList") List<String> getHisVarinstProidsWithIn);
/**
* 获取流程的启动和结束时间
* @param name
* @param proid
* @return
*/
/*
* @Select("select * from act_hi_actinst where EXECUTION_ID_=#{proid} and ACT_TYPE_ in ('startEvent','endEvent')"
* )
*
* @ResultType(Map.class) List<Map<String,Object>> getActHiActinst(String
* proid);
*/
/**
* 指定下一步操作人
* @param proInsId
* @param taskDefKey
* @param nextUser
*/
@Select("select ID_ from act_ru_task where PROC_INST_ID_= #{proInsId} and TASK_DEF_KEY_= #{taskDefKey} order by CREATE_TIME_")
List<String> getTaskIdByProins(@Param("proInsId")String proInsId,@Param("taskDefKey")String taskDefKey);
List<TaskDTO> getHistoryTasks(Page page,@Param("userName") String userName,
@Param("processDefinitionId") String processDefinitionId,
@Param("processDefinitionName") String processDefinitionName,
@Param("procInstIdList") List<String> procInstIdList,
@Param("processKeyList") List<String> processKeyList);
List<TaskDTO> getAllHistoryTasks(Page page,@Param("processDefinitionId") String processDefinitionId,
@Param("processDefinitionName") String processDefinitionName);
List<TaskDTO> getAllCcHistoryTasks(Page page,@Param("ccUserName") String ccUserName,
@Param("processDefinitionId") String processDefinitionId,
@Param("processDefinitionName") String processDefinitionName,
@Param("executionIdList") List<String> executionIdList,
@Param("processInstanceIds") List<String> processInstanceIds,
@Param("processKeyList") List<String> processKeyList);
/**
* 查询流程的历史任务节点
* @param proceInsId
* @return
*/
//@Select("select DISTINCT TASK_DEF_KEY_,NAME_ from act_hi_taskinst where PROC_INST_ID_= #{proceInsId} and DELETE_REASON_ ='completed'")
@Select("select DISTINCT TASK_DEF_KEY_,NAME_ from act_hi_taskinst where PROC_INST_ID_= #{proceInsId} AND END_TIME_ is not null")
List<Map<String,Object>> getHistTaskNodeList(@Param("proceInsId") String proceInsId);
/**
* 获取流程的启动和结束时间
* @param proceInsId
* @return
*/
List<ActHiActinstDTO> getActHiActinstStartAndEnd(@Param("proceInsId") String proceInsId);
/**
* 根据流程实例ID,获取流程的发起人
* @param userId
* @return
*/
@Select("select START_USER_ID_ from act_hi_procinst where ID_ = #{proceInsId}")
String getProcessStartUserId(@Param("proceInsId") String proceInsId);
/**
* 根据流程实例ID,获取结束节点处理人
* @param userId
* @return
*/
@Select("select ASSIGNEE_ from act_hi_taskinst where PROC_INST_ID_ = #{proceInsId} order by END_TIME_ DESC")
List<Map<String,Object>> getProcessEndUserId(@Param("proceInsId") String proceInsId);
List<UserAgentDTO> getUserAgent(@Param("currDate") Date currDate);
/**
* 查询抄送的流程实例ID
* @param ccUserName
* @return
*/
List<ActTaskCcDTO> getUserCcHistoryTask(@Param("ccUserName") String ccUserName);
//
//
// /**
// * 根据流程ID,获取流程的发起人
// * @param ID_ 流程ID
// */
// @Select("select START_USER_ID_ from act_hi_procinst where id_ = :id")
// String getProcessStartUserId(String id);
//
// /**
// * 根据流程ID,流程最后一个处理人
// * @param ID_ 流程ID
// */
// @Select("select ASSIGNEE_ from act_hi_taskinst where proc_inst_id_ = :id order by end_time_ desc")
// List<Map<String,Object>> getProcessEndUserId(String id,int page, int rows);
//
//
// /**
// * 获取流程发起节点的表单
// * @param processid 流程ID
// */
// @Select("select modelandview from t_p_processnode where processid = :processid and processnodecode = 'start'")
// String getProcessStartNodeView(String processid);
//
//
// /**
// * 获取流程发起节点的表单
// * @param processid 流程ID
// */
// @Select("select modelandview_mobile from t_p_processnode where processid = :processid and processnodecode = 'start'")
// String getProcessStartNodeViewMobile(String processid);
//
// /**
// * 获取流程发起节点的表单
// * @param processid 流程ID
// */
// @Select("select id from t_p_process where processkey = :processkey ")
// String getProcessidByProcesskey(String processkey);
/**
* 获取超时任务节点
* @return
*/
List<ActRuTaskDTO> getTimeoutTask();
/**
* 查询用户列表
*
* @param wrapper
* @return
*/
List<User> selectUserList(@Param(Constants.WRAPPER) Wrapper wrapper);
/**
* 删除节点信息
* @param ids ids
*/
public void deleteHisActinstsByIds(List<String> ids) ;
/**
* 删除节点信息
* @param ids ids
*/
public void deleteRunActinstsByIds(List<String> ids) ;
/**
* 查询流程设计的不同类型下的数量
* @return
*/
public List<StartTypeStatisticsDto> countStartTypeNum(String lowAppId);
/**
* 批量删除流程的所有自定义定时器
*
* @param processKey 流程KEY
* @return boolean
*/
boolean deleteAllTimerJobByProcessKey(@Param("processKey") String processKey);
/**
* QQYUN-4415
*
* 查询低代码应用名称
* @param id
* @return
*/
@Select("select app_name from low_app where id = #{id}")
public String getLowAppName(@Param("id") String id);
}
@@ -0,0 +1,26 @@
package org.jeecg.modules.bpm.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.bpm.dto.CommentVo;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @author : scott
* @projectName : flowable
* @description: 流程备注Dao
* @date : 2022-12-06
*/
@Mapper
@Repository
public interface FlowableCommentMapper {
/**
* 通过流程实例id获取审批意见列表
* @param ProcessInstanceId 流程实例id
* @return
*/
public List<CommentVo> getFlowCommentVosByProcessInstanceId(String ProcessInstanceId);
}
@@ -0,0 +1,332 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.bpm.mapper.ActivitiMapper">
<resultMap id="TaskDTO" type="org.jeecg.modules.bpm.dto.TaskDTO" >
<result column="id" property="id" jdbcType="VARCHAR"/>
<result column="processDefinitionId" property="processDefinitionId" jdbcType="VARCHAR"/>
<result column="processDefinitionName" property="processDefinitionName" jdbcType="VARCHAR"/>
<result column="taskName" property="taskName" jdbcType="VARCHAR"/>
<result column="processInstanceId" property="processInstanceId" jdbcType="VARCHAR"/>
<result column="processApplyUserId" property="processApplyUserId" jdbcType="VARCHAR"/>
<result column="taskAssigneeId" property="taskAssigneeId" jdbcType="VARCHAR"/>
<result column="taskBeginTime" property="taskBeginTime" jdbcType="TIMESTAMP"/>
<result column="taskEndTime" property="taskEndTime" jdbcType="TIMESTAMP"/>
<result column="taskDueTime" property="taskDueTime" jdbcType="BIGINT"/>
<result column="taskId" property="taskId" jdbcType="VARCHAR"/>
<result column="processEndTime" property="processEndTime" jdbcType="TIMESTAMP"/>
<result column="processDeleteReason" property="processDeleteReason" jdbcType="VARCHAR"/>
</resultMap>
<resultMap id="ActTaskCcDTO" type="org.jeecg.modules.bpm.dto.ActTaskCcDTO" >
<result column="procDefId" property="procDefId" jdbcType="VARCHAR"/>
<result column="taskDefKey" property="taskDefKey" jdbcType="VARCHAR"/>
<result column="procInstId" property="procInstId" jdbcType="VARCHAR"/>
<result column="executionId" property="executionId" jdbcType="VARCHAR"/>
<result column="taskName" property="taskName" jdbcType="VARCHAR"/>
</resultMap>
<resultMap id="UserAgentDTO" type="org.jeecg.modules.bpm.dto.UserAgentDTO" >
<result column="id" property="id" jdbcType="VARCHAR"/>
<result column="userName" property="userName" jdbcType="VARCHAR"/>
<result column="agentUserName" property="agentUserName" jdbcType="VARCHAR"/>
<result column="startTime" property="startTime" jdbcType="TIMESTAMP"/>
<result column="endTime" property="endTime" jdbcType="TIMESTAMP"/>
<result column="status" property="status" jdbcType="VARCHAR"/>
<result column="createName" property="createName" jdbcType="VARCHAR"/>
<result column="createBy" property="createBy" jdbcType="VARCHAR"/>
<result column="createTime" property="createTime" jdbcType="TIMESTAMP"/>
<result column="updateName" property="updateName" jdbcType="VARCHAR"/>
<result column="updateBy" property="updateBy" jdbcType="VARCHAR"/>
<result column="updateTime" property="updateTime" jdbcType="TIMESTAMP"/>
<result column="sysOrgCode" property="sysOrgCode" jdbcType="VARCHAR"/>
<result column="sysCompanyCode" property="sysCompanyCode" jdbcType="VARCHAR"/>
</resultMap>
<resultMap id="ActHiActinstDTO" type="org.jeecg.modules.bpm.dto.ActHiActinstDTO" >
<result column="ID_" property="id" jdbcType="VARCHAR"/>
<result column="PROC_DEF_ID_" property="procDefId" jdbcType="VARCHAR"/>
<result column="PROC_INST_ID_" property="procInstId" jdbcType="VARCHAR"/>
<result column="EXECUTION_ID_" property="executionId" jdbcType="VARCHAR"/>
<result column="ACT_ID_" property="actId" jdbcType="VARCHAR"/>
<result column="TASK_ID_" property="taskId" jdbcType="VARCHAR"/>
<result column="CALL_PROC_INST_ID_" property="callProcInstId" jdbcType="VARCHAR"/>
<result column="ACT_NAME_" property="actName" jdbcType="VARCHAR"/>
<result column="ACT_TYPE_" property="actType" jdbcType="VARCHAR"/>
<result column="ASSIGNEE_" property="assignee" jdbcType="VARCHAR"/>
<result column="START_TIME_" property="startTime" jdbcType="TIMESTAMP"/>
<result column="END_TIME_" property="endTime" jdbcType="TIMESTAMP"/>
<result column="DURATION_" property="duration" jdbcType="INTEGER"/>
<result column="TENANT_ID_" property="tenantId" jdbcType="VARCHAR"/>
</resultMap>
<resultMap id="ActRuTaskDTO" type="org.jeecg.modules.bpm.dto.ActRuTaskDTO" >
<result column="ID_" property="id" jdbcType="VARCHAR"/>
<result column="CREATE_TIME_" property="createTime" jdbcType="TIMESTAMP"/>
<result column="NAME_" property="name" jdbcType="VARCHAR"/>
<result column="ASSIGNEE_" property="assignee" jdbcType="VARCHAR"/>
<result column="OWNER_" property="owner" jdbcType="VARCHAR"/>
<result column="PROC_INST_ID_" property="procInstId" jdbcType="VARCHAR"/>
<result column="EXECUTION_ID_" property="executionId" jdbcType="VARCHAR"/>
<result column="NODE_TIMEOUT" property="nodeTimeout" jdbcType="INTEGER"/>
</resultMap>
<!-- 查询我的历史参与任务 -->
<select id="getHistoryTasks" resultMap="TaskDTO">
SELECT
aht.ID_ AS id
,aht.PROC_DEF_ID_ AS processDefinitionId
,arp.NAME_ AS processDefinitionName
,aht.NAME_ AS taskName
,ahp.PROC_INST_ID_ AS processInstanceId
,ahp.START_USER_ID_ AS processApplyUserId
,aht.ASSIGNEE_ AS taskAssigneeId
,aht.START_TIME_ AS taskBeginTime
,aht.END_TIME_ AS taskEndTime
,aht.DURATION_ AS taskDueTime
,aht.TASK_DEF_KEY_ AS taskId
FROM ACT_HI_TASKINST aht,
ACT_RE_PROCDEF arp,
ACT_HI_PROCINST ahp
WHERE aht.PROC_DEF_ID_ = arp.id_
and aht.PROC_INST_ID_ = ahp.id_
and aht.DURATION_>0
and aht.ASSIGNEE_ = #{userName}
<if test="processKeyList!= null and processKeyList.size()>0">
AND arp.KEY_ IN
<foreach item="item" collection="processKeyList" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="procInstIdList!= null and procInstIdList.size()>0">
and aht.PROC_INST_ID_ IN
<foreach item="item" collection="procInstIdList" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="processDefinitionId != null and processDefinitionId !=''">
and aht.PROC_DEF_ID_ like concat(concat('%',#{processDefinitionId}),'%')
</if>
<if test="processDefinitionName != null and processDefinitionName !=''">
and arp.NAME_ like concat(concat('%',#{processDefinitionName}),'%')
</if>
order by aht.START_TIME_ desc
</select>
<!-- 查询我的抄送任务列表 -->
<select id="getAllHistoryTasks" resultMap="TaskDTO">
SELECT
aht.ID_ AS id
,aht.PROC_DEF_ID_ AS processDefinitionId
,arp.NAME_ AS processDefinitionName
,aht.NAME_ AS taskName
,ahp.PROC_INST_ID_ AS processInstanceId
,ahp.START_USER_ID_ AS processApplyUserId
,aht.ASSIGNEE_ AS taskAssigneeId
,aht.START_TIME_ AS taskBeginTime
,aht.END_TIME_ AS taskEndTime
,aht.DURATION_ AS taskDueTime
,aht.TASK_DEF_KEY_ AS taskId
FROM ACT_HI_TASKINST aht,
ACT_RE_PROCDEF arp,
ACT_HI_PROCINST ahp
WHERE aht.PROC_DEF_ID_ = arp.id_
and aht.PROC_INST_ID_ = ahp.id_
and aht.DURATION_>0
<if test="processDefinitionId != null and processDefinitionId !=''">
and aht.PROC_DEF_ID_ like concat(concat('%',#{processDefinitionId}),'%')
</if>
<if test="processDefinitionName != null and processDefinitionName !=''">
and arp.NAME_ like concat(concat('%',#{processDefinitionName}),'%')
</if>
order by aht.START_TIME_ desc
</select>
<!-- 查询我的抄送任务列表 -->
<select id="getAllCcHistoryTasks" resultMap="TaskDTO">
SELECT
aht.ID_ AS id
,aht.PROC_DEF_ID_ AS processDefinitionId
,arp.NAME_ AS processDefinitionName
,aht.NAME_ AS taskName
,ahp.PROC_INST_ID_ AS processInstanceId
,ahp.START_USER_ID_ AS processApplyUserId
,ahp.END_TIME_ as processEndTime
,ahp.DELETE_REASON_ as processDeleteReason
,aht.ASSIGNEE_ AS taskAssigneeId
,aht.START_TIME_ AS taskBeginTime
,aht.END_TIME_ AS taskEndTime
,aht.DURATION_ AS taskDueTime
,aht.TASK_DEF_KEY_ AS taskId
FROM ACT_HI_TASKINST aht,
EXT_ACT_TASK_CC cc,
ACT_RE_PROCDEF arp,
ACT_HI_PROCINST ahp
WHERE aht.ID_ = cc.TASK_ID
and aht.PROC_INST_ID_ = cc.proc_inst_id
and aht.EXECUTION_ID_ = cc.execution_id
and aht.PROC_DEF_ID_ = arp.id_
and aht.PROC_INST_ID_ = ahp.id_
and aht.DURATION_ >0
and cc.CC_USER_NAME = #{ccUserName}
<if test="processKeyList!= null and processKeyList.size()>0">
AND arp.KEY_ IN
<foreach item="item" collection="processKeyList" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="executionIdList!= null and executionIdList.size()>0">
and aht.EXECUTION_ID_ IN
<foreach item="item" collection="executionIdList" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="processInstanceIds!= null and processInstanceIds.size()>0">
and aht.PROC_INST_ID_ IN
<foreach item="item" collection="processInstanceIds" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="processDefinitionId != null and processDefinitionId !=''">
and aht.PROC_DEF_ID_ like concat(concat('%',#{processDefinitionId}),'%')
</if>
<if test="processDefinitionName != null and processDefinitionName !=''">
and arp.NAME_ like concat(concat('%',#{processDefinitionName}),'%')
</if>
ORDER BY aht.START_TIME_ DESC
</select>
<!-- 查询我的抄送任务实例ID -->
<select id="getUserCcHistoryTask" resultMap="ActTaskCcDTO">
SELECT
proc_def_id AS procDefId,
proc_inst_id AS procInstId
FROM EXT_ACT_TASK_CC
WHERE CC_USER_NAME = #{ccUserName}
</select>
<select id="getUserAgent" resultMap="UserAgentDTO">
SELECT
sua.id AS id
,sua.user_name AS userName
,sua.agent_user_name AS agentUserName
,sua.start_time AS startTime
,sua.end_time AS endTime
,sua.status AS status
,sua.create_name AS createName
,sua.create_by AS createBy
,sua.create_time AS createTime
,sua.update_name AS updateName
,sua.update_by AS updateBy
,sua.update_time AS updateTime
,sua.sys_org_code AS sysOrgCode
,sua.sys_company_code AS sysCompanyCode
FROM sys_user_agent sua
WHERE sua.status = '1'
<![CDATA[
and sua.start_time <= #{currDate}
and sua.end_time >= #{currDate}
]]>
</select>
<!-- update-begin author:scott date:2022-12-06 for:【升级flowable】流程历史跟踪查不到开始和结束节点【原因: 老版activiti字段 PROC_INST_ID_、EXECUTION_ID_ 值一样的,换成flowable后变的不一样了】-->
<select id="getActHiActinstStartAndEnd" resultMap="ActHiActinstDTO">
SELECT * FROM ACT_HI_ACTINST WHERE PROC_INST_ID_= #{proceInsId} and ACT_TYPE_ in ('startEvent','endEvent')
</select>
<!-- update-end-author:scott date:2022-12-06 for:【升级flowable】流程历史跟踪查不到开始和结束节点【原因: 老版activiti字段 PROC_INST_ID_、EXECUTION_ID_ 值一样的,换成flowable后变的不一样了】-->
<!-- 查询超时任务 TODO 节点key重名有问题 -->
<select id="getTimeoutTask" resultMap="ActRuTaskDTO">
SELECT
RES.CREATE_TIME_,
RES.ID_,
RES.NAME_,
RES.ASSIGNEE_,
RES.OWNER_,
RES.PROC_INST_ID_,
RES.EXECUTION_ID_,
t.NODE_TIMEOUT
FROM
ACT_RU_TASK RES
LEFT JOIN EXT_ACT_PROCESS_NODE t ON t.PROCESS_NODE_CODE = RES.TASK_DEF_KEY_
WHERE
t.NODE_TIMEOUT > 0
AND ( RES.ASSIGNEE_ IS NOT NULL OR RES.ASSIGNEE_ != '' )
UNION ALL
SELECT
RES.CREATE_TIME_,
RES.ID_,
RES.NAME_,
I.USER_ID_ as ASSIGNEE_,
RES.OWNER_,
RES.PROC_INST_ID_,
RES.EXECUTION_ID_,
t.NODE_TIMEOUT
FROM
ACT_RU_TASK RES
LEFT JOIN EXT_ACT_PROCESS_NODE t ON t.PROCESS_NODE_CODE = RES.TASK_DEF_KEY_
LEFT JOIN ACT_RU_IDENTITYLINK I ON I.TASK_ID_ = RES.ID_
WHERE
t.NODE_TIMEOUT > 0
AND RES.ASSIGNEE_ IS NULL
AND I.USER_ID_ IS NOT NULL
AND I.TYPE_ = 'candidate'
</select>
<!-- &lt;!&ndash; 通过业务标题,去流程变量里模糊匹配出 流程实例list &ndash;&gt;-->
<!-- <select id="getHisVarinstProids" resultType="java.lang.String" parameterType="java.lang.String">-->
<!-- <bind name="bindText" value="'%'+text+'%'"/>-->
<!-- select PROC_INST_ID_ from act_hi_varinst where NAME_= 'bpm_biz_title' and TEXT_ like #{bindText}-->
<!-- </select>-->
<!-- 通过业务标题,去流程变量里模糊匹配出 流程实例list -->
<select id="getHisVarinstProidsWithIn" resultType="java.lang.String" parameterType="java.lang.String">
<bind name="bindText" value="'%'+text+'%'"/>
select PROC_INST_ID_ from act_hi_varinst where NAME_= 'bpm_biz_title' and TEXT_ like #{bindText}
<if test="procInstIdList!= null and procInstIdList.size()>0">
AND PROC_INST_ID_ in
<foreach item="item" index="index" collection="procInstIdList" open="(" separator="," close=")">
#{item}
</foreach>
</if>
</select>
<select id="selectUserList" resultType="org.flowable.idm.engine.impl.persistence.entity.UserEntityImpl">
SELECT username id, realname displayName, email
FROM sys_user ${ew.customSqlSegment}
</select>
<select id="countStartTypeNum" resultType="org.jeecg.modules.bpm.dto.StartTypeStatisticsDto">
SELECT
start_type,
count(*) AS count
FROM
ext_act_process
WHERE
low_app_id = #{lowAppId}
GROUP BY
start_type
</select>
<delete id="deleteRunActinstsByIds" parameterType="java.util.List">
delete from act_ru_actinst where ID_ in
<foreach item="item" index="index" collection="list" open="(" separator="," close=")">
#{item}
</foreach>
</delete>
<delete id="deleteHisActinstsByIds" parameterType="java.util.List">
delete from act_hi_actinst where ID_ in
<foreach item="item" index="index" collection="list" open="(" separator="," close=")">
#{item}
</foreach>
</delete>
<!--批量删除流程的所有自定义定时器-->
<delete id="deleteAllTimerJobByProcessKey" parameterType="string">
delete from act_ru_timer_job where HANDLER_TYPE_ ='timer-start-event-custom' and PROC_DEF_ID_ like CONCAT(#{processKey}, '%')
</delete>
</mapper>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.jeecg.modules.bpm.mapper.FlowableCommentMapper">
<select id="getFlowCommentVosByProcessInstanceId" parameterType="java.lang.String" resultType="org.jeecg.modules.bpm.dto.CommentVo">
SELECT
t1.ID_,
t1.TYPE_ AS type,
t1.TIME_ AS time,
t1.USER_ID_ AS userId,
t2.FIRST_ AS userName,
t1.TASK_ID_ AS taskId,
t1.PROC_INST_ID_ AS processInstanceId,
t1.MESSAGE_ AS message,
CONVERT (FULL_MSG_ USING utf8) AS fullMsg
FROM
act_hi_comment t1
INNER JOIN act_id_user t2 ON t1.USER_ID_ = t2.ID_
WHERE
t1.PROC_INST_ID_ = #{value} and ACTION_='AddComment'
ORDER BY
t1.TIME_ ASC
</select>
</mapper>
@@ -0,0 +1,20 @@
package org.jeecg.modules.bpm.service;
import java.util.Map;
/**
* Title: activiti流程图工具类 <br>
* @author scott
* 日期:2021/11/19
*/
public interface ActImageService {
byte[] getFlowImgByProcInstId(String procInstId) throws Exception;
/**
* 新版前端渲染流程图,进度效果
* @param procInstId
* @return
* @throws Exception
*/
Map getFlowMsgByProcInstId(String procInstId) throws Exception;
}
@@ -0,0 +1,262 @@
package org.jeecg.modules.bpm.service;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.ibatis.annotations.Param;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.bpm.cmd.dto.TaskAddRequest;
import org.jeecg.modules.bpm.dto.*;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
public interface ActivitiService{
/**
* 查询待办任务-只查询用户自己的 (统计总数)
* @param userName
* @param procInstIds
* @param request
* @return
*/
public Long countPriTodaoTask(String userName,String procInstIds, HttpServletRequest request);
/**
* 待办任务列表-组任务(统计总数)
* @param roles
* @param request
* @return
*/
public Long countGroupTodoTasks(List<String> roles, HttpServletRequest request);
/**
* 查询待办任务-只查询用户自己的
* @param userId
* @param procInstIds 流程实例Ids
* @param request
* @return
*/
public List<TaskDTO> findPriTodoTasks(String userId,String procInstIds, HttpServletRequest request);
/**
* 加载我的任务查询条件,返回inProcInstIdsStr
*
* @param request
* @return
*/
public String initQueryProcInstIds(HttpServletRequest request);
/**
* 待办任务列表-组任务
* @param roles
* @param request
* @return
*/
public List<TaskDTO> findGroupTodoTasks(List<String> roles,HttpServletRequest request);
/**
* 查询历史任务
* @param userName
* @param request
* @return
*/
public Page<TaskDTO> findHistoryTasks(Page page,String userName, HttpServletRequest request);
/**
* 查询所有的流程历史
* @param request
* @return
*/
public Page<TaskDTO> findAllHistoryTasks(Page page,HttpServletRequest request);
/**
* 查询抄送给我的历史任务
* @param request
* @return
*/
public Page<TaskDTO> findAllCcHistoryTasks(Page page,String username,HttpServletRequest request);
/**
* 根据任务id查询任务信息
* @param taskId
* @return
*/
public Task getTask(String taskId);
/**
* 查询流程的所有节点
* @param taskId
* @return
*/
public List<Map> getAllTaskNode(String taskId);
/**
* 根据流程processkey获取该流程下已经发布的流程
* @param processkey
* @return
*/
public List<ProcessDefinition> processDefinitionListByProcesskey(String processkey);
/**
* 通过任务节点ID,获取当前节点 分支
*/
public List getOutTransitions(String taskId) ;
/**
* 查询流程的历史任务节点
*/
public List<Map<String,Object>> getHistTaskNodeList(String proceInsId);
/**
* 获取流程的启动和结束时间
* @param proid
* @return
*/
public List<ActHiActinstDTO> getActHiActinstStartAndEnd(String proceInsId);
/**
* 根据流程实例ID,获取流程的发起人
* @param proceInsId
* @return
*/
public String getProcessStartUserId(String proceInsId);
/**
* 根据流程实例ID,获取结束节点处理人
* @param proceInsId
* @return
*/
public List<Map<String,Object>> getProcessEndUserId(String proceInsId);
/**
* 走向流程 【带参数】
* @param taskId
* @param nextTaskId
* @param variables
* @throws Exception
*/
public void goProcessTaskNode(String taskId, String nextTaskId, Map<String, Object> variables) throws Exception;
/**
* 指定下一步操作人
* @param proInsId
* @param taskDefKey
*/
public String getTaskIdByProins(String proInsId,String taskDefKey);
/**
* 根据历史执行流程实例ID,查询变量内容
* @param varName
* @param proInsId
* @return
*/
public String getHisVarinst(String varName,String proInsId);
/**
* 查询有效期的代理设置
* @return
*/
public List<UserAgentDTO> getUserAgent(Date currDate);
/**
* 更新流程实例关联业务单据的流程状态
* @param procInstId 流程实例id
* @param bpmStatus 流程状态
*/
public void updateBpmStatus(String procInstId,String bpmStatus);
/**
* 流程挂起
* @param procInstId
*/
public void suspend(String procInstId);
/**
* 流程解挂启动
* @param procInstId
*/
public void restart(String procInstId);
/**
* 多实例加签
* @param multiInstanceRequest
* @return
*/
Result addMultiInstanceExecution(MultiInstanceRequestDto multiInstanceRequest);
/**
* 多实例减签
* @param multiInstanceRequest
* @return
*/
Result deleteMultiInstanceExecution(MultiInstanceRequestDto multiInstanceRequest);
/**
* 查询会签人员
* @param multiInstanceRequest
* @return
*/
Result selectMultiUserList(MultiInstanceRequestDto multiInstanceRequest);
/**
* 动态添加用户节点
* @param taskAddRequest
* @return
*/
Result addUserTask(TaskAddRequest taskAddRequest);
/**
* 任务前加签 (如果多次加签只能显示第一次前加签的处理人来处理任务)
* 多个加签人处理完毕任务之后又流到自己这里
*
* @param addSignTaskVo 参数
* @return
*/
public Result<String> beforeAddSignTask(AddSignTaskVo addSignTaskVo);
/**
* 任务后加签(加签人自己自动审批完毕加签多个人处理任务)
*
* @param addSignTaskVo 参数
* @return
*/
public Result<String> afterAddSignTask(AddSignTaskVo addSignTaskVo);
/**
* 任务加签
*
* @param addSignTaskVo 参数
* @param flag true向后加签 false向前加签
* @return
*/
public Result<String> addSignTask(AddSignTaskVo addSignTaskVo, Boolean flag);
/**
* 创建虚拟子任务
*
* @param ptask
* @param assignee
* @return
*/
public TaskEntity createSubTask(TaskEntity ptask, String assignee);
/**
* 批量删除流程的所有自定义定时器
*
* @param processKey 流程KEY
* @return boolean
*/
boolean deleteAllTimerJobByProcessKey(@Param("processKey") String processKey);
}
@@ -0,0 +1,43 @@
package org.jeecg.modules.bpm.service;
import org.jeecg.modules.bpm.dto.CommentVo;
import java.util.List;
/**
* 网上的参考代码(目前未使用)
* 说明: 这个使用的是flowable官方表【act_hi_comment】【act_hi_attachment】
* 目前jeecgboot自定义了两个表【ext_act_bpm_log】【ext_act_bpm_file】,未使用官方表
*
* @author : scott
* @projectName : flowable
* @description: 审批意见service
* @date : 2022-12-06
*/
public interface IFlowableCommentService {
/**
* 添加备注
* @param comment 参数
*/
public void addComment(CommentVo comment) ;
/**
* 通过流程实例id获取审批意见列表
* @param processInstanceId 流程实例id
* @return
*/
public List<CommentVo> getFlowCommentVosByProcessInstanceId(String processInstanceId) ;
/**
* 添加审批意见
*
* @param taskId
* @param userCode
* @param processInstanceId
* @param type
* @param comment
*/
public void addComment(String taskId, String userCode, String processInstanceId, String type, String comment);
}
@@ -0,0 +1,81 @@
package org.jeecg.modules.bpm.service;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.task.api.Task;
/**
* @Description: 流程校验接口
* @Author: zyf
* @Date: 2022-05-07
*/
public interface IPermissionService {
/**
* 校验用户是否是任务的拥有者或者代理人
*
* @param userName
* @param task
* @return
*/
boolean checkUserIsTaskOwnerOrAssignee(String userName, Task task);
/**
* 校验用户是否是发起人是否可以完成任务
*
* @param userName
* @param task
* @return
*/
boolean checkUserIsInitiatorAndCanCompleteTask(String userName, Task task);
/**
* 判断是否是协办完成还是正常流转
*
* @param task
* @return
*/
boolean isTaskPending(Task task);
/**
* 验证任务的执行权限
*
* @param taskId
* @param userId
* @return
*/
Task checkPermissionOnTask(String taskId, String userId);
/**
* 是否可以转办任务
* 1.任务所有人可以转办
* 2.任务执行人可以转办,但要求任务非委派状态
* 3.被转办人不能是当前任务执行人
*
* @param taskId
* @param userId
* @param assignee
* @return
*/
Task checkPermissionOnTask(String taskId, String userId, String assignee);
/**
* 校验用户是否有权限读取该流程定义
*
* @param userId
* @param processDefinitionId
*/
void checkPermissionOnProcessDefinition(String processDefinitionId, String userId);
/**
* 校验用户是否有权限读取该流程实例
*
* @param userId
* @param processInstanceId
* @return
*/
HistoricProcessInstance checkPermissionOnProcessInstance(String processInstanceId, String userId);
}
@@ -0,0 +1,19 @@
package org.jeecg.modules.bpm.service;
/**
* 流程追踪图生成类
*
* @author liuxz
*/
public interface ImageService {
/**
* 根据流程实例标识,生成流程跟踪图示(高亮)
*
* @param procInstId 流程实例标识
* @return
* @throws Exception
*/
byte[] generateImageByProcInstId(String procInstId) throws Exception;
}
@@ -0,0 +1,345 @@
package org.jeecg.modules.bpm.service.impl;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.SequenceFlow;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.image.ProcessDiagramGenerator;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.modules.bpm.service.ActImageService;
import org.jeecg.modules.bpm.service.impl.image.CustomProcessDiagramGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Title: activiti流程图工具类 <br>
* @author scott
* 日期:2021/11/19
*/
@Service("actImageServiceImpl")
public class ActImageServiceImpl implements ActImageService {
protected static Logger logger = LoggerFactory.getLogger(ActImageServiceImpl.class);
@Autowired
private RepositoryService repositoryService;
@Autowired
private HistoryService historyService;
@Autowired
private RuntimeService runtimeService;
/**
* Desc: 通过流程实例ID获取历史流程实例
*
* @param procInstId
* @return
* @author liuxz
*/
public HistoricProcessInstance getHistoricProcInst(String procInstId) {
return historyService.createHistoricProcessInstanceQuery().processInstanceId(procInstId).singleResult();
}
/**
* Desc: 通过流程实例ID获取流程中已经执行的节点,按照执行先后顺序排序
*
* @param procInstId
* @return
* @author liuxz
*/
public List<HistoricActivityInstance> getHistoricActivityInstAsc(String procInstId) {
return historyService.createHistoricActivityInstanceQuery().processInstanceId(procInstId)
.orderByHistoricActivityInstanceId().asc().list();
}
/**
* Desc: 通过流程实例ID获取流程中正在执行的节点
*
* @param procInstId
* @return
* @author liuxz
*/
public List<Execution> getRunningActivityInst(String procInstId) {
return runtimeService.createExecutionQuery().processInstanceId(procInstId).list();
}
/**
* Desc: 通过流程实例ID获取已经完成的历史流程实例
*
* @param procInstId
* @return
* @author liuxz
*/
public List<HistoricProcessInstance> getHistoricFinishedProcInst(String procInstId) {
return historyService.createHistoricProcessInstanceQuery().processInstanceId(procInstId).finished().list();
}
/**
* 获取已流经的流程线,需要高亮显示高亮流程已发生流转的线id集合
*
* @param bpmnModel
* @param historicActivityInstanceList
* @return
*/
public List<String> getHighLightedFlows(BpmnModel bpmnModel,
List<HistoricActivityInstance> historicActivityInstanceList) {
// 已流经的流程线,需要高亮显示
List<String> highLightedFlowIdList = new ArrayList<>();
// 全部活动节点
List<FlowNode> allHistoricActivityNodeList = new ArrayList<>();
// 已完成的历史活动节点
List<HistoricActivityInstance> finishedActivityInstanceList = new ArrayList<>();
for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
// 获取流程节点
// org.flowable.bpmn.model.SequenceFlow cannot be cast to org.flowable.bpmn.model.FlowNode
FlowNode flowNode = null;
FlowElement flowElement = bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId());
if(flowElement instanceof FlowNode){
flowNode = (FlowNode)flowElement;
allHistoricActivityNodeList.add(flowNode);
}
// 结束时间不为空,当前节点则已经完成
if (historicActivityInstance.getEndTime() != null) {
finishedActivityInstanceList.add(historicActivityInstance);
}
}
FlowNode currentFlowNode = null;
FlowNode targetFlowNode = null;
HistoricActivityInstance currentActivityInstance;
// 遍历已完成的活动实例,从每个实例的outgoingFlows中找到已执行的
for (int k = 0; k < finishedActivityInstanceList.size(); k++) {
currentActivityInstance = finishedActivityInstanceList.get(k);
// 获得当前活动对应的节点信息及outgoingFlows信息
FlowElement currentFlowElement = bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId());
if(currentFlowElement==null || !(currentFlowElement instanceof FlowNode)){
continue;
}
currentFlowNode = (FlowNode)currentFlowElement;
// 当前节点的所有流出线
List<SequenceFlow> outgoingFlowList = currentFlowNode.getOutgoingFlows();
/**
* 遍历outgoingFlows并找到已流转的 满足如下条件认为已流转
* 1.当前节点是并行网关或兼容网关,则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
* 2.当前节点是以上两种类型之外的,通过outgoingFlows查找到的时间最早的流转节点视为有效流转 (第2点有问题,有过驳回的,会只绘制驳回的流程线,通过走向下一级的流程线没有高亮显示)
*/
if ("parallelGateway".equals(currentActivityInstance.getActivityType()) || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {
// 遍历历史活动节点,找到匹配流程目标节点的
for (SequenceFlow outgoingFlow : outgoingFlowList) {
// 获取当前节点流程线对应的下级节点
targetFlowNode = (FlowNode)bpmnModel.getMainProcess().getFlowElement(outgoingFlow.getTargetRef());
// 如果下级节点包含在所有历史节点中,则将当前节点的流出线高亮显示
if (allHistoricActivityNodeList.contains(targetFlowNode)) {
highLightedFlowIdList.add(outgoingFlow.getId());
}
}
} else {
/**
* 2、当前节点不是并行网关或兼容网关 【已解决-问题】如果当前节点有驳回功能,驳回到申请节点, 则因为申请节点在历史节点中,导致当前节点驳回到申请节点的流程线被高亮显示,但实际并没有进行驳回操作
*/
List<Map<String, Object>> tempMapList = new ArrayList<>();
// 当前节点ID
String currentActivityId = currentActivityInstance.getActivityId();
int size = historicActivityInstanceList.size();
boolean ifStartFind = false;
boolean ifFinded = false;
HistoricActivityInstance historicActivityInstance;
// 循环当前节点的所有流出线
// 循环所有历史节点
logger.debug("【开始】-匹配当前节点-ActivityId=【{}】需要高亮显示的流出线", currentActivityId);
logger.debug("循环历史节点");
for (int i = 0; i < historicActivityInstanceList.size(); i++) {
// 历史节点
historicActivityInstance = historicActivityInstanceList.get(i);
logger.debug("第【{}/{}】个历史节点-ActivityId=[{}]", i + 1, size, historicActivityInstance.getActivityId());
// 如果循环历史节点中的id等于当前节点id,从当前历史节点继续先后查找是否有当前节点流程线等于的节点
// 历史节点的序号需要大于等于已完成历史节点的序号,防止驳回重审一个节点经过两次是只取第一次的流出线高亮显示,第二次的不显示
if (i >= k && historicActivityInstance.getActivityId().equals(currentActivityId)) {
logger.debug("第[{}]个历史节点和当前节点一致-ActivityId=[{}]", i + 1,historicActivityInstance.getActivityId());
ifStartFind = true;
// 跳过当前节点继续查找下一个节点
continue;
}
if (ifStartFind) {
logger.debug("[开始]-循环当前节点-ActivityId=【{}】的所有流出线", currentActivityId);
ifFinded = false;
for (SequenceFlow sequenceFlow : outgoingFlowList) {
// 如果当前节点流程线对应的下级节点在其后面的历史节点中,则该条流程线进行高亮显示
logger.debug("当前流出线的下级节点=[{}]", sequenceFlow.getTargetRef());
if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
logger.debug("当前节点[{}]需高亮显示的流出线=[{}]", currentActivityId, sequenceFlow.getId());
highLightedFlowIdList.add(sequenceFlow.getId());
// 暂时默认找到离当前节点最近的下一级节点即退出循环,否则有多条流出线时将全部被高亮显示
ifFinded = true;
break;
}
}
logger.debug("[完成]-循环当前节点-ActivityId=【{}】的所有流出线", currentActivityId);
}
if (ifFinded) {
// 暂时默认找到离当前节点最近的下一级节点即退出历史节点循环,否则有多条流出线时将全部被高亮显示
break;
}
}
logger.debug("【完成】-匹配当前节点-ActivityId=【{}】需要高亮显示的流出线", currentActivityId);
}
}
return highLightedFlowIdList;
}
/**
* 根据流程实例Id,获取实时流程图片
*
* @param procInstId
* @return
* @throws Exception
*/
@Override
public byte[] getFlowImgByProcInstId(String procInstId) throws Exception {
if (org.apache.commons.lang.StringUtil.isEmpty(procInstId)) {
logger.error("[错误]-传入的参数procInstId为空!");
throw new Exception("[异常]-传入的参数procInstId为空!");
}
InputStream imageStream = null;
try {
// 通过流程实例ID获取历史流程实例
HistoricProcessInstance historicProcessInstance = getHistoricProcInst(procInstId);
// 通过流程实例ID获取流程中已经执行的节点,按照执行先后顺序排序
List<HistoricActivityInstance> historicActivityInstanceList = getHistoricActivityInstAsc(procInstId);
// 将已经执行的节点ID放入高亮显示节点集合
List<String> highLightedActivitiIdList = new ArrayList<>();
for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
highLightedActivitiIdList.add(historicActivityInstance.getActivityId());
logger.debug("已执行的节点[{}-{}-{}-{}]", historicActivityInstance.getId(),
historicActivityInstance.getActivityId(), historicActivityInstance.getActivityName(),
historicActivityInstance.getAssignee());
}
// 通过流程实例ID获取流程中正在执行的节点
List<Execution> runningActivityInstanceList = getRunningActivityInst(procInstId);
List<String> runningActivitiIdList = new ArrayList<String>();
for (Execution execution : runningActivityInstanceList) {
if (org.apache.commons.lang.StringUtil.isNotEmpty(execution.getActivityId())) {
runningActivitiIdList.add(execution.getActivityId());
logger.debug("执行中的节点[{}-{}-{}]", execution.getId(), execution.getActivityId(), execution.getName());
}
}
// 通过流程实例ID获取已经完成的历史流程实例
List<HistoricProcessInstance> historicFinishedProcessInstanceList = getHistoricFinishedProcInst(procInstId);
// 定义流程画布生成器
ProcessDiagramGenerator processDiagramGenerator = null;
// 如果还没完成,流程图高亮颜色为绿色,如果已经完成为红色
// if (!CollectionUtils.isEmpty(historicFinishedProcessInstanceList)) {
// // 如果不为空,说明已经完成
// processDiagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
// } else {
processDiagramGenerator = new CustomProcessDiagramGenerator();
// }
// 获取流程定义Model对象
BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
// 获取已流经的流程线,需要高亮显示高亮流程已发生流转的线id集合
List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, historicActivityInstanceList);
// 使用默认配置获得流程图表生成器,并生成追踪图片字符流
imageStream = ((CustomProcessDiagramGenerator)processDiagramGenerator).generateDiagramCustom(bpmnModel,
"png", highLightedActivitiIdList, runningActivitiIdList, highLightedFlowIds, "宋体",
"微软雅黑", "黑体", null,
2.0);
// 将InputStream数据流转换为byte[]
byte[] buffer = new byte[imageStream.available()];
imageStream.read(buffer);
return buffer;
} catch (Exception e) {
logger.error("通过流程实例ID[{}]获取流程图时出现异常!", e);
throw new Exception("通过流程实例ID" + procInstId + "获取流程图时出现异常!", e);
} finally {
if (imageStream != null) {
imageStream.close();
}
}
}
/**
* 根据流程实例Id,获取实时流程图片 【新版npm.js渲染图片进度使用接口】
*
* @param procInstId
* @return
* @throws Exception
*/
@Override
public Map<String, List<String>> getFlowMsgByProcInstId(String procInstId) throws Exception {
if (org.apache.commons.lang.StringUtil.isEmpty(procInstId)) {
logger.error("[错误]-传入的参数procInstId为空!");
throw new Exception("[异常]-传入的参数procInstId为空!");
}
try {
Map map = new HashMap();
// 通过流程实例ID获取历史流程实例
HistoricProcessInstance historicProcessInstance = getHistoricProcInst(procInstId);
// 通过流程实例ID获取流程中已经执行的节点,按照执行先后顺序排序
List<HistoricActivityInstance> historicActivityInstanceList = getHistoricActivityInstAsc(procInstId);
// 将已经执行的节点ID放入高亮显示节点集合
List<String> highLightedActivitiIdList = new ArrayList<>();
for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
highLightedActivitiIdList.add(historicActivityInstance.getActivityId());
logger.debug("已执行的节点[{}-{}-{}-{}]", historicActivityInstance.getId(),
historicActivityInstance.getActivityId(), historicActivityInstance.getActivityName(),
historicActivityInstance.getAssignee());
}
//已执行过节点ID
map.put("highLightedActivitiIdList", highLightedActivitiIdList);
// 通过流程实例ID获取流程中正在执行的节点
List<Execution> runningActivityInstanceList = getRunningActivityInst(procInstId);
List<String> runningActivitiIdList = new ArrayList<String>();
for (Execution execution : runningActivityInstanceList) {
if (org.apache.commons.lang.StringUtil.isNotEmpty(execution.getActivityId())) {
runningActivitiIdList.add(execution.getActivityId());
logger.debug("执行中的节点[{}-{}-{}]", execution.getId(), execution.getActivityId(), execution.getName());
}
}
//正在执行的节点ID
map.put("runningActivitiIdList", runningActivitiIdList);
// 通过流程实例ID获取已经完成的历史流程实例
List<HistoricProcessInstance> historicFinishedProcessInstanceList = getHistoricFinishedProcInst(procInstId);
// 获取流程定义Model对象
BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
// 获取已流经的流程线,需要高亮显示高亮流程已发生流转的线id集合
List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, historicActivityInstanceList);
//获取已流经的流程线ID
map.put("highLightedFlowIds", highLightedFlowIds);
return map;
} catch (Exception e) {
logger.error("通过流程实例ID[{}]获取流程图时出现异常!", e);
throw new Exception("通过流程实例ID" + procInstId + "获取流程图时出现异常!", e);
}
}
}
@@ -0,0 +1,138 @@
//package org.jeecg.modules.bpm.service.impl;
//
//import java.io.ByteArrayInputStream;
//import java.io.UnsupportedEncodingException;
//import java.util.List;
//import java.util.Map;
//
//import javax.servlet.http.HttpServletResponse;
//import org.activiti.bpmn.converter.BpmnXMLConverter;
//import org.activiti.bpmn.model.BpmnModel;
//import org.activiti.editor.constants.ModelDataJsonConstants;
//import org.activiti.editor.language.json.converter.BpmnJsonConverter;
//import org.activiti.engine.RepositoryService;
//import org.activiti.engine.repository.Deployment;
//import org.activiti.engine.repository.Model;
//import org.activiti.engine.repository.ModelQuery;
//import org.activiti.engine.repository.ProcessDefinition;
//import org.apache.commons.io.IOUtils;
//import org.apache.commons.lang3.StringUtils;
//import org.jeecg.common.exception.JeecgBootException;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.stereotype.Service;
//import com.fasterxml.jackson.databind.JsonNode;
//import com.fasterxml.jackson.databind.ObjectMapper;
//import com.fasterxml.jackson.databind.node.ObjectNode;
//
///**
// * 流程设计管理
// *
// * @author scott
// */
//@Service("actModelService")
//public class ActModelService {
// @Autowired
// protected RepositoryService repositoryService;
// @Autowired
// private ObjectMapper objectMapper;
//
// public List<Model> page(Map<String, Object> params) {
// String key = (String)params.get("key");
// String name = (String)params.get("name");
//
// ModelQuery modelQuery = repositoryService.createModelQuery().latestVersion().orderByLastUpdateTime().desc();
//
// if(org.apache.commons.lang.StringUtil.isNotEmpty(key)){
// modelQuery.modelKey(key);
// }
// if(org.apache.commons.lang.StringUtil.isNotEmpty(name)){
// modelQuery.modelName(name);
// }
//
// List<Model> list = modelQuery.list();
//// List<Model> list = modelQuery.listPage((curPage - 1) * limit, limit);
// return list;
// }
//
// public String save(String name, String key, String description) throws UnsupportedEncodingException {
// //新建一个空模型
// Model model = repositoryService.newModel();
//
// //metaInfo信息
// ObjectNode metaInfo = objectMapper.createObjectNode();
// metaInfo.put(ModelDataJsonConstants.MODEL_NAME, name);
// metaInfo.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
// metaInfo.put(ModelDataJsonConstants.MODEL_REVISION, model.getVersion());
//
// model.setKey(key);
// model.setName(name);
// model.setMetaInfo(metaInfo.toString());
//
// repositoryService.saveModel(model);
//
// ObjectNode editorNode = objectMapper.createObjectNode();
// editorNode.put("id", "canvas");
// editorNode.put("resourceId", "canvas");
// ObjectNode stencilset = objectMapper.createObjectNode();
// stencilset.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
// editorNode.set("stencilset", stencilset);
// repositoryService.addModelEditorSource(model.getId(), editorNode.toString().getBytes("utf-8"));
// return model.getId();
// }
//
// public void deploy(String id) {
// try {
// Model model = repositoryService.getModel(id);
// BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
// JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(model.getId()));
// BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
// BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
// byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);
//
// String processName = model.getName();
// if (!org.apache.commons.lang.StringUtil.endsWith(processName, ".bpmn20.xml")){
// processName += ".bpmn20.xml";
// }
//
// ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
// Deployment deployment = repositoryService.createDeployment().name(model.getName()).addInputStream(processName, in).deploy();
//
// List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
// if (list.size() == 0){
// throw new JeecgBootException("流程定义为空");
// }
// } catch (Exception e) {
// throw new JeecgBootException(e);
// }
// }
//
// /**
// * 导出模型
// */
// public void export(String id, HttpServletResponse response) {
// try {
// Model model = repositoryService.getModel(id);
// BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
// JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(model.getId()));
// BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
// BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
// byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);
//
// ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
// IOUtils.copy(in, response.getOutputStream());
// String filename = bpmnModel.getMainProcess().getId() + ".bpmn20.xml";
// response.setHeader("Content-Disposition", "attachment; filename=" + filename);
// response.flushBuffer();
// } catch (Exception e) {
// throw new JeecgBootException(e);
// }
// }
//
// /**
// * 删除模型
// * @param id 模型ID
// */
// public void delete(String id) {
// repositoryService.deleteModel(id);
// }
//}
@@ -0,0 +1,310 @@
package org.jeecg.modules.bpm.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.modules.bpm.dto.StartTypeStatisticsDto;
import org.jeecg.modules.bpm.mapper.ActivitiMapper;
import org.jeecg.modules.extbpm.process.entity.ExtActProcess;
import org.jeecg.modules.extbpm.process.mapper.ExtActProcessMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;
/**
* 流程管理
*
* @author scott
*/
@Slf4j
@Service("actProcessService")
public class ActProcessService {
@Autowired
protected RepositoryService repositoryService;
@Autowired
protected HistoryService historyService;
@Autowired
protected RuntimeService runtimeService;
@Autowired
protected ProcessEngine processEngine;
@Autowired
protected ActivitiMapper activitiMapper;
@Autowired
protected ExtActProcessMapper extActProcessMapper;
/**
* 流程列表
*/
public List<Map<String, Object>> page(Map<String, Object> params) {
String key = (String) params.get("key");
String processName = (String) params.get("processName");
//ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery().latestVersion().orderByProcessDefinitionId().desc().orderByProcessDefinitionKey().desc();
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery().orderByProcessDefinitionId().desc().orderByProcessDefinitionKey().desc();
if (org.apache.commons.lang.StringUtil.isNotEmpty(key)) {
processDefinitionQuery.processDefinitionKeyLike(key);
}
if (org.apache.commons.lang.StringUtil.isNotEmpty(processName)) {
processDefinitionQuery.processDefinitionNameLike(processName);
}
List<ProcessDefinition> processDefinitionList = processDefinitionQuery.list();
// List<ProcessInstance> processInstanceList = processInstanceQuery.listPage((curPage - 1) * limit, limit);
List<Map<String, Object>> objectList = new ArrayList<>();
for (ProcessDefinition processDefinition : processDefinitionList) {
objectList.add(processDefinitionConvert(processDefinition));
}
return objectList;
}
/**
* 流程定义信息
*/
private Map<String, Object> processDefinitionConvert(ProcessDefinition processDefinition) {
String deploymentId = processDefinition.getDeploymentId();
Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
Map<String, Object> map = new HashMap<>(9);
map.put("suspended", processDefinition.isSuspended());
map.put("id", processDefinition.getId());
map.put("deploymentId", processDefinition.getDeploymentId());
map.put("name", deployment.getName());
map.put("key", processDefinition.getKey());
map.put("version", processDefinition.getVersion());
map.put("resourceName", processDefinition.getResourceName());
map.put("diagramResourceName", processDefinition.getDiagramResourceName());
map.put("deploymentTime", deployment.getDeploymentTime());
return map;
}
/**
* 部署
*
* @param file 文件
*/
public void deploy(MultipartFile file) throws IOException {
String fileName = file.getOriginalFilename();
String extension = FilenameUtils.getExtension(fileName);
if ("zip".equalsIgnoreCase(extension) || "bar".equalsIgnoreCase(extension)) {
ZipInputStream zip = new ZipInputStream(file.getInputStream());
repositoryService.createDeployment().addZipInputStream(zip).deploy();
} else if (fileName.indexOf("bpmn20.xml") != -1) {
repositoryService.createDeployment().addInputStream(fileName, file.getInputStream()).deploy();
} else if ("bpmn".equalsIgnoreCase(extension)) {
repositoryService.createDeployment().addInputStream(fileName, file.getInputStream()).deploy();
} else {
throw new JeecgBootException("部署失败!");
}
}
/**
* 激活流程
*
* @param id 流程ID
*/
public void active(String id) {
repositoryService.activateProcessDefinitionById(id, true, null);
}
/**
* 挂起流程
*
* @param id 流程ID
*/
public void suspend(String id) {
repositoryService.suspendProcessDefinitionById(id, true, null);
}
/**
* 关闭流程(挂起流程最后一个版本)
*
* @param key 流程key
*/
public void closeProcess(String key) {
//修改流程状态为 “关闭”
ExtActProcess extActProcess = new ExtActProcess();
extActProcess.setOpenStatus(Integer.parseInt(CommonConstant.STATUS_0));
LambdaQueryWrapper<ExtActProcess> queryWrapper = new LambdaQueryWrapper<ExtActProcess>();
queryWrapper.eq(ExtActProcess::getProcessKey,key);
extActProcessMapper.update(extActProcess, queryWrapper);
//获取最新发布的流程定义(挂起)
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).latestVersion().singleResult();
if (processDefinition != null) {
boolean suspended = repositoryService.isProcessDefinitionSuspended(processDefinition.getId());
if (!suspended) {
repositoryService.suspendProcessDefinitionById(processDefinition.getId(), true, null);
}
}
}
/**
* 开启流程(激活流程最后一个版本)
*
* @param key 流程key
*/
public void openProcess(String key) {
//修改流程状态为 “打开”
ExtActProcess extActProcess = new ExtActProcess();
extActProcess.setOpenStatus(Integer.parseInt(CommonConstant.STATUS_1));
LambdaQueryWrapper<ExtActProcess> queryWrapper = new LambdaQueryWrapper<ExtActProcess>();
queryWrapper.eq(ExtActProcess::getProcessKey,key);
extActProcessMapper.update(extActProcess, queryWrapper);
//获取最新发布的流程定义(激活)
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).latestVersion().singleResult();
if (processDefinition != null) {
boolean suspended = repositoryService.isProcessDefinitionSuspended(processDefinition.getId());
if (suspended) {
repositoryService.activateProcessDefinitionById(processDefinition.getId(), true, null);
}
}
}
/**
* 删除部署
*
* @param deploymentId 部署ID
*/
public void deleteDeployment(String deploymentId) {
repositoryService.deleteDeployment(deploymentId, true);
}
/**
* 获取资源文件
*
* @param deploymentId 部署ID
* @param resourceName 资源名称
*/
public InputStream getResourceAsStream(String deploymentId, String resourceName) {
InputStream resourceAsStream = repositoryService.getResourceAsStream(deploymentId, resourceName);
return resourceAsStream;
}
/**
* @TODO 待测试1
*
* 获取流程图显示
* @param httpServletResponse
* @param processId
*/
public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) {
/**
* 获得当前活动的节点
*/
String processDefinitionId = "";
if (this.isFinished(processId)) {
// 如果流程已经结束,则得到结束节点
HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult();
processDefinitionId=pi.getProcessDefinitionId();
} else {
// 如果流程没有结束,则取当前活动节点
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
processDefinitionId=pi.getProcessDefinitionId();
}
List<String> highLightedActivitis = new ArrayList<String>();
/**
* 获得活动的节点
*/
List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list();
for(HistoricActivityInstance tempActivity : highLightedActivitList){
String activityId = tempActivity.getActivityId();
highLightedActivitis.add(activityId);
}
List<String> flows = new ArrayList<>();
//获取流程图
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
InputStream in = diagramGenerator.generateDiagram(bpmnModel, "bmp", highLightedActivitis, flows, engconf.getActivityFontName(),engconf.getLabelFontName(),
engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, true);
OutputStream out = null;
byte[] buf = new byte[1024];
int legth = 0;
try {
out = httpServletResponse.getOutputStream();
while ((legth = in.read(buf)) != -1) {
out.write(buf, 0, legth);
}
} catch (IOException e) {
log.error("操作异常",e);
} finally {
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(in);
}
}
public boolean isFinished(String processInstanceId) {
return historyService.createHistoricProcessInstanceQuery().finished()
.processInstanceId(processInstanceId).count() > 0;
}
/**
* 中终止流程
*
* @param processInstanceId
*/
public void stopProcessInstanceById(String processInstanceId) {
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
if (processInstance != null) {
//一、获取終止节点
List<EndEvent> endNodes = findEndFlowElement(processInstance.getProcessDefinitionId());
String endId = endNodes.get(0).getId();
//二、执行終止
List<Execution> executions = runtimeService.createExecutionQuery().parentId(processInstanceId).list();
List<String> executionIds = new ArrayList<>();
executions.forEach(execution -> executionIds.add(execution.getId()));
runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId).changeState();
log.info("終止processInstanceId:{}成功", processInstanceId);
} else {
log.info("不存在运行的流程实例processInstanceId:{},请确认!", processInstanceId);
}
}
public List findEndFlowElement(String processDefId) {
Process mainProcess = repositoryService.getBpmnModel(processDefId).getMainProcess();
Collection<FlowElement> list = mainProcess.getFlowElements();
if (CollectionUtils.isEmpty(list)) {
return Collections.EMPTY_LIST;
}
return list.stream().filter(f -> f instanceof EndEvent).collect(Collectors.toList());
}
/**
* 查询流程设计的不同类型下的数量
* @return
*/
public List<StartTypeStatisticsDto> countStartTypeNum(String lowAppId){
return this.activitiMapper.countStartTypeNum(lowAppId);
}
}
@@ -0,0 +1,49 @@
package org.jeecg.modules.bpm.service.impl;
import org.flowable.engine.ManagementService;
import org.jeecg.modules.bpm.cmd.AddHisCommentCmd;
import org.jeecg.modules.bpm.dto.CommentVo;
import org.jeecg.modules.bpm.mapper.FlowableCommentMapper;
import org.jeecg.modules.bpm.service.IFlowableCommentService;
import org.jeecg.modules.bpm.constant.enums.CommentTypeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 网上的参考代码(目前未使用)
* 说明: 这个使用的是flowable官方表【act_hi_comment】【act_hi_attachment】
* 目前jeecgboot自定义了两个表【ext_act_bpm_log】【ext_act_bpm_file】,未使用官方表
*
* @description: 流程日志记录 (待实现未用上)
* @author : scott
* @date :2022-12-06
*/
@Service("flowableCommentService")
public class FlowableCommentServiceImpl implements IFlowableCommentService {
@Autowired
ManagementService managementService;
@Autowired
FlowableCommentMapper flowableCommentMapper;
@Override
public void addComment(CommentVo comment) {
managementService.executeCommand(new AddHisCommentCmd(comment.getTaskId(), comment.getUserId(), comment.getProcessInstanceId(),
comment.getType(), comment.getMessage()));
}
@Override
public List<CommentVo> getFlowCommentVosByProcessInstanceId(String processInstanceId) {
List<CommentVo> datas = flowableCommentMapper.getFlowCommentVosByProcessInstanceId(processInstanceId);
datas.forEach(commentVo -> {
commentVo.setTypeName(CommentTypeEnum.getEnumMsgByType(commentVo.getType()));
});
return datas;
}
@Override
public void addComment(String taskId, String userCode, String processInstanceId, String type, String comment) {
managementService.executeCommand(new AddHisCommentCmd(taskId, userCode, processInstanceId,
type, comment));
}
}
@@ -0,0 +1,306 @@
package org.jeecg.modules.bpm.service.impl;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.SequenceFlow;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.image.ProcessDiagramGenerator;
import org.jeecg.modules.bpm.service.ImageService;
import org.jeecg.modules.bpm.service.impl.image.CustomProcessDiagramGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @TODO 待实现未用上1
*
* 流程追踪图生成类
* @author liuxz
*/
@Service("imageServiceImpl")
public class ImageServiceImpl implements ImageService {
protected static Logger logger = LoggerFactory.getLogger(ImageServiceImpl.class);
@Autowired
private RepositoryService repositoryService;
@Autowired
private HistoryService historyService;
@Autowired
private RuntimeService runtimeService;
/**
* 通过流程实例ID获取历史流程实例
*
* @param procInstId
* @return
*/
public HistoricProcessInstance getHistoricProcInst(String procInstId) {
return historyService.createHistoricProcessInstanceQuery().processInstanceId(procInstId).singleResult();
}
/**
* 通过流程实例ID获取流程中已经执行的节点,按照执行先后顺序排序
*
* @param procInstId
* @return
*/
public List<HistoricActivityInstance> getHistoricActivityInstAsc(String procInstId) {
return historyService.createHistoricActivityInstanceQuery().processInstanceId(procInstId)
.orderByHistoricActivityInstanceId().asc().list();
}
/**
* 通过流程实例ID获取流程中正在执行的节点
*
* @param procInstId
* @return
*/
public List<Execution> getRunningActivityInst(String procInstId) {
return runtimeService.createExecutionQuery().processInstanceId(procInstId).list();
}
/**
* 通过流程实例ID获取已经完成的历史流程实例
*
* @param procInstId
* @return
*/
public List<HistoricProcessInstance> getHistoricFinishedProcInst(String procInstId) {
return historyService.createHistoricProcessInstanceQuery().processInstanceId(procInstId).finished().list();
}
/**
* 获取已流经的流程线,需要高亮显示高亮流程已发生流转的线id集合
*
* @param bpmnModel
* @param historicActivityInstanceList
* @return
*/
public List<String> getHighLightedFlows(BpmnModel bpmnModel,
List<HistoricActivityInstance> historicActivityInstanceList) {
// 已流经的流程线,需要高亮显示
List<String> highLightedFlowIdList = new ArrayList<>();
// 全部活动节点
List<FlowNode> allHistoricActivityNodeList = new ArrayList<>();
// 已完成的历史活动节点
List<HistoricActivityInstance> finishedActivityInstanceList = new ArrayList<>();
for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
// 获取流程节点
FlowElement fe = bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true);
if(fe instanceof FlowNode){
FlowNode flowNode = (FlowNode) fe;
allHistoricActivityNodeList.add(flowNode);
}
// 结束时间不为空,当前节点则已经完成
if (historicActivityInstance.getEndTime() != null) {
finishedActivityInstanceList.add(historicActivityInstance);
}
}
FlowNode currentFlowNode = null;
FlowNode targetFlowNode = null;
HistoricActivityInstance currentActivityInstance;
// 遍历已完成的活动实例,从每个实例的outgoingFlows中找到已执行的
for (int k = 0; k < finishedActivityInstanceList.size(); k++) {
currentActivityInstance = finishedActivityInstanceList.get(k);
// 获得当前活动对应的节点信息及outgoingFlows信息
FlowElement fe = bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
if(fe instanceof FlowNode){
currentFlowNode = (FlowNode)fe;
}else{
break;
}
// 当前节点的所有流出线
List<SequenceFlow> outgoingFlowList = currentFlowNode.getOutgoingFlows();
/**
* 遍历outgoingFlows并找到已流转的 满足如下条件认为已流转:
* 1.当前节点是并行网关或兼容网关,则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
* 2.当前节点是以上两种类型之外的,通过outgoingFlows查找到的时间最早的流转节点视为有效流转
* (第2点有问题,有过驳回的,会只绘制驳回的流程线,通过走向下一级的流程线没有高亮显示)
*/
if ("parallelGateway".equals(currentActivityInstance.getActivityType()) || "inclusiveGateway".equals(
currentActivityInstance.getActivityType())) {
// 遍历历史活动节点,找到匹配流程目标节点的
for (SequenceFlow outgoingFlow : outgoingFlowList) {
// 获取当前节点流程线对应的下级节点
targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(outgoingFlow.getTargetRef(),
true);
// 如果下级节点包含在所有历史节点中,则将当前节点的流出线高亮显示
if (allHistoricActivityNodeList.contains(targetFlowNode)) {
highLightedFlowIdList.add(outgoingFlow.getId());
}
}
} else {
/**
* 2、当前节点不是并行网关或兼容网关
* 【已解决-问题】如果当前节点有驳回功能,驳回到申请节点,
* 则因为申请节点在历史节点中,导致当前节点驳回到申请节点的流程线被高亮显示,但实际并没有进行驳回操作
*/
List<Map<String, Object>> tempMapList = new ArrayList<>();
// 当前节点ID
String currentActivityId = currentActivityInstance.getActivityId();
int size = historicActivityInstanceList.size();
boolean ifStartFind = false;
boolean ifFinded = false;
HistoricActivityInstance historicActivityInstance;
// 循环当前节点的所有流出线
// 循环所有历史节点
logger.debug("【开始】-匹配当前节点-ActivityId=【{}】需要高亮显示的流出线", currentActivityId);
logger.debug("循环历史节点");
for (int i = 0; i < historicActivityInstanceList.size(); i++) {
// // 如果当前节点流程线对应的下级节点在历史节点中,则该条流程线进行高亮显示(【问题】有驳回流程线时,即使没有进行驳回操作,因为申请节点在历史节点中,也会将驳回流程线高亮显示-_-||)
// if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
// Map<String, Object> map = new HashMap<>();
// map.put("highLightedFlowId", sequenceFlow.getId());
// map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime());
// tempMapList.add(map);
// // highLightedFlowIdList.add(sequenceFlow.getId());
// }
// 历史节点
historicActivityInstance = historicActivityInstanceList.get(i);
logger.debug("第【{}/{}】个历史节点-ActivityId=[{}]", i + 1, size, historicActivityInstance.getActivityId());
// 如果循环历史节点中的id等于当前节点id,从当前历史节点继续先后查找是否有当前节点流程线等于的节点
// 历史节点的序号需要大于等于已完成历史节点的序号,防止驳回重审一个节点经过两次是只取第一次的流出线高亮显示,第二次的不显示
if (i >= k && historicActivityInstance.getActivityId().equals(currentActivityId)) {
logger.debug("第[{}]个历史节点和当前节点一致-ActivityId=[{}]", i + 1, historicActivityInstance
.getActivityId());
ifStartFind = true;
// 跳过当前节点继续查找下一个节点
continue;
}
if (ifStartFind) {
logger.debug("[开始]-循环当前节点-ActivityId=【{}】的所有流出线", currentActivityId);
ifFinded = false;
for (SequenceFlow sequenceFlow : outgoingFlowList) {
// 如果当前节点流程线对应的下级节点在其后面的历史节点中,则该条流程线进行高亮显示
// 【问题】
logger.debug("当前流出线的下级节点=[{}]", sequenceFlow.getTargetRef());
if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
logger.debug("当前节点[{}]需高亮显示的流出线=[{}]", currentActivityId, sequenceFlow.getId());
highLightedFlowIdList.add(sequenceFlow.getId());
// 暂时默认找到离当前节点最近的下一级节点即退出循环,否则有多条流出线时将全部被高亮显示
ifFinded = true;
break;
}
}
logger.debug("[完成]-循环当前节点-ActivityId=【{}】的所有流出线", currentActivityId);
}
if (ifFinded) {
// 暂时默认找到离当前节点最近的下一级节点即退出历史节点循环,否则有多条流出线时将全部被高亮显示
break;
}
}
logger.debug("【完成】-匹配当前节点-ActivityId=【{}】需要高亮显示的流出线", currentActivityId);
// if (!CollectionUtils.isEmpty(tempMapList)) {
// // 遍历匹配的集合,取得开始时间最早的一个
// long earliestStamp = 0L;
// String highLightedFlowId = null;
// for (Map<String, Object> map : tempMapList) {
// long highLightedFlowStartTime = Long.valueOf(map.get("highLightedFlowStartTime").toString());
// if (earliestStamp == 0 || earliestStamp <= highLightedFlowStartTime) {
// highLightedFlowId = map.get("highLightedFlowId").toString();
// earliestStamp = highLightedFlowStartTime;
// }
// }
// highLightedFlowIdList.add(highLightedFlowId);
// }
}
}
return highLightedFlowIdList;
}
/**
* 根据流程实例Id,获取实时流程图片
*
* @param procInstId
* @return
* @throws Exception
*/
@Override
public byte[] generateImageByProcInstId(String procInstId) throws Exception {
if (org.apache.commons.lang.StringUtil.isEmpty(procInstId)) {
logger.error("[错误]-传入的参数procInstId为空!");
throw new Exception("[异常]-传入的参数procInstId为空!");
}
InputStream imageStream = null;
try {
// 通过流程实例ID获取历史流程实例
HistoricProcessInstance historicProcessInstance = getHistoricProcInst(procInstId);
// 通过流程实例ID获取流程中已经执行的节点,按照执行先后顺序排序
List<HistoricActivityInstance> historicActivityInstanceList = getHistoricActivityInstAsc(procInstId);
// 将已经执行的节点ID放入高亮显示节点集合
List<String> highLightedActivitiIdList = new ArrayList<>();
for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
highLightedActivitiIdList.add(historicActivityInstance.getActivityId());
logger.debug("已执行的节点[{}-{}-{}-{}]", historicActivityInstance.getId(), historicActivityInstance
.getActivityId(), historicActivityInstance.getActivityName(), historicActivityInstance
.getAssignee());
}
// 通过流程实例ID获取流程中正在执行的节点
List<Execution> runningActivityInstanceList = getRunningActivityInst(procInstId);
List<String> runningActivitiIdList = new ArrayList<String>();
for (Execution execution : runningActivityInstanceList) {
if (org.apache.commons.lang.StringUtil.isNotEmpty(execution.getActivityId())) {
runningActivitiIdList.add(execution.getActivityId());
logger.debug("执行中的节点[{}-{}-{}]", execution.getId(), execution.getActivityId(), execution.getName());
}
}
// 通过流程实例ID获取已经完成的历史流程实例
List<HistoricProcessInstance> historicFinishedProcessInstanceList = getHistoricFinishedProcInst(procInstId);
// 定义流程画布生成器
ProcessDiagramGenerator processDiagramGenerator = new CustomProcessDiagramGenerator();
// 获取流程定义Model对象
BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
// 获取已流经的流程线,需要高亮显示高亮流程已发生流转的线id集合
List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, historicActivityInstanceList);
// 使用默认配置获得流程图表生成器,并生成追踪图片字符流
imageStream = ((CustomProcessDiagramGenerator) processDiagramGenerator).generateDiagramCustom(bpmnModel, "png",
highLightedActivitiIdList, runningActivitiIdList, highLightedFlowIds,
"宋体", "微软雅黑", "黑体",
null, 2.0);
// 将InputStream数据流转换为byte[]
byte[] buffer = new byte[imageStream.available()];
imageStream.read(buffer);
return buffer;
} catch (Exception e) {
logger.error("通过流程实例ID[{}]获取流程图时出现异常!", e);
throw new Exception("通过流程实例ID" + procInstId + "获取流程图时出现异常!", e);
} finally {
if (imageStream != null) {
imageStream.close();
}
}
}
}
@@ -0,0 +1,269 @@
package org.jeecg.modules.bpm.service.impl;
import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.ExtensionElement;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.editor.language.json.converter.util.CollectionUtils;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.idm.api.Group;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.jeecg.modules.bpm.cmd.util.ApiAssert;
import org.jeecg.modules.bpm.service.IPermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: 流程校验Service
* @Author: zyf
* @Date: 2022-05-10
*/
@Service("permissionServiceImpl")
@AllArgsConstructor
@Slf4j
public class PermissionServiceImpl implements IPermissionService {
/**
* 任务处理Service
* 1查询分配给某人或一组的任务 2产生独立的任务,不依赖于任何流程实例的任务 3操纵用户分配任务 4完成任务。
*/
@Autowired
protected TaskService taskService;
/**
* 查询各种历史数据
*/
@Autowired
protected HistoryService historyService;
/**
* 流程运行时Service
* 1:用来获取和保存流程变量 2: 查询流程实例和当前执行位置 3:流程实例需要等待一个外部触发,从而继续进行。
*/
@Autowired
protected RuntimeService runtimeService;
/**
* 用来执行flowable自定义命令
*/
@Autowired
protected ManagementService managementService;
@Autowired
protected IdentityService identityService;
@Autowired
protected FormService formService;
/**
* 管理控制部署与流程定义
*/
@Autowired
protected RepositoryService repositoryService;
@Override
public boolean checkUserIsTaskOwnerOrAssignee(String userName, Task task) {
return userName.equals(task.getOwner()) || userName.equals(task.getAssignee());
}
@Override
public boolean checkUserIsInitiatorAndCanCompleteTask(String userName, Task task) {
boolean result = false;
String processInstanceId = task.getProcessInstanceId();
if (ObjectUtil.isNotEmpty(processInstanceId)) {
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
String startUserId = historicProcessInstance.getStartUserId();
if (ObjectUtil.isNotEmpty(historicProcessInstance) && ObjectUtil.isNotEmpty(startUserId)) {
if (userName.equals(startUserId) && checkIfInitiatorCanCompleteTask(task)) {
return true;
}
}
}
return result;
}
/**
* 验证发起人是否可以完成任务
*
* @param task
* @return
*/
private boolean checkIfInitiatorCanCompleteTask(Task task) {
boolean result = false;
BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
if (flowElement instanceof UserTask) {
UserTask userTask = (UserTask) flowElement;
List<ExtensionElement> extensionElements = userTask.getExtensionElements().get("initiator-can-complete");
if (CollectionUtils.isNotEmpty(extensionElements)) {
String value = extensionElements.get(0).getElementText();
if (org.apache.commons.lang.StringUtil.isNotEmpty(value) && Boolean.valueOf(value)) {
result = true;
}
}
}
return result;
}
@Override
public boolean isTaskPending(Task task) {
return DelegationState.PENDING.equals(task.getDelegationState());
}
@Override
public Task checkPermissionOnTask(String taskId, String userId) {
ApiAssert.isNotEmpty("taskId不能为空", taskId);
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
ApiAssert.isNotEmpty("任务不存在", task);
if (isTaskOwnerOrAssignee(userId, taskId)) {
return task;
}
ApiAssert.failure("用户无权限");
return null;
}
@Override
public Task checkPermissionOnTask(String taskId, String userId, String assignee) {
ApiAssert.isNotEmpty("taskId不能为空", taskId);
ApiAssert.isNotEmpty("办理人不能为空", assignee);
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
ApiAssert.isNotEmpty("任务不存在", task);
if (isTaskOwnerOrAssignee(userId, task)) {
String owner = task.getOwner();
String oldAssignee = task.getAssignee();
if (assignee.equals(oldAssignee)) {
ApiAssert.failure("不能委派给当前办理人");
}
if (assignee.equals(owner)) {
ApiAssert.failure("不能委派给任务拥有者");
}
return task;
}
return task;
}
@Override
public void checkPermissionOnProcessDefinition(String processDefinitionId, String userId) {
List<IdentityLink> identityLinks = repositoryService.getIdentityLinksForProcessDefinition(processDefinitionId);
List<String> startUserIds = getPotentialStarterUserIds(identityLinks);
List<String> startGroupIds = getPotentialStarterGroupIds(identityLinks);
if (startUserIds.isEmpty() && startGroupIds.isEmpty()) {
return;
}
if (startUserIds.contains(userId)) {
return;
}
List<String> groupsIds = getGroupIdsForUser(userId);
for (String startGroupId : startGroupIds) {
if (groupsIds.contains(startGroupId)) {
return;
}
}
ApiAssert.failure("您没有执行流程的权限,请联系管理员");
}
@Override
public HistoricProcessInstance checkPermissionOnProcessInstance(String processInstanceId, String userId) {
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
boolean hasPermission = hasReadPermissionOnProcessInstance(userId, historicProcessInstance, processInstanceId);
if (!hasPermission) {
}
return historicProcessInstance;
}
/**
* Check if the given user is allowed to read the process instance.
*/
public boolean hasReadPermissionOnProcessInstance(String userId, HistoricProcessInstance historicProcessInstance,
String processInstanceId) {
if (historicProcessInstance == null) {
throw new FlowableObjectNotFoundException(
"ProcessInstance with id: " + processInstanceId + " does not exist");
}
// Start user check
if (historicProcessInstance.getStartUserId() != null && historicProcessInstance.getStartUserId().equals(userId)) {
return true;
}
// check if the user is involved in the task
HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();
historicProcessInstanceQuery.processInstanceId(processInstanceId);
historicProcessInstanceQuery.involvedUser(userId);
if (historicProcessInstanceQuery.count() > 0) {
return true;
}
// Visibility: check if there are any tasks for the current user
HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery();
historicTaskInstanceQuery.processInstanceId(processInstanceId);
historicTaskInstanceQuery.taskInvolvedUser(userId);
if (historicTaskInstanceQuery.count() > 0) {
return true;
}
List<String> groupIds = getGroupIdsForUser(userId);
if (!groupIds.isEmpty()) {
historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery();
historicTaskInstanceQuery.processInstanceId(processInstanceId).taskCandidateGroupIn(groupIds);
return historicTaskInstanceQuery.count() > 0;
}
return false;
}
private boolean isTaskOwnerOrAssignee(String currentUser, String taskId) {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
return isTaskOwnerOrAssignee(currentUser, task);
}
private boolean isTaskOwnerOrAssignee(String currentUser, Task task) {
return task.getAssignee()==null || currentUser.equals(task.getOwner()) || currentUser.equals(task.getAssignee());
}
public List<String> getPotentialStarterUserIds(List<IdentityLink> identityLinks) {
List<String> userIds = new ArrayList<>();
for (IdentityLink identityLink : identityLinks) {
if (identityLink.getUserId() != null && identityLink.getUserId().length() > 0) {
if (!userIds.contains(identityLink.getUserId())) {
userIds.add(identityLink.getUserId());
}
}
}
return userIds;
}
public List<String> getPotentialStarterGroupIds(List<IdentityLink> identityLinks) {
List<String> groupIds = new ArrayList<>();
for (IdentityLink identityLink : identityLinks) {
if (identityLink.getGroupId() != null && identityLink.getGroupId().length() > 0) {
if (!groupIds.contains(identityLink.getGroupId())) {
groupIds.add(identityLink.getGroupId());
}
}
}
return groupIds;
}
private List<String> getGroupIdsForUser(String userId) {
List<String> groupIds = new ArrayList<>();
List<Group> userGroups = identityService.createGroupQuery().groupMember(userId).list();
for (Group group : userGroups) {
groupIds.add(String.valueOf(group.getId()));
}
return groupIds;
}
}
@@ -0,0 +1,262 @@
package org.jeecg.modules.bpm.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtil;
import org.apache.commons.lang.StringUtils;
import org.flowable.engine.*;
import org.flowable.engine.impl.RepositoryServiceImpl;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.bpm.mapper.ActivitiMapper;
import org.jeecg.modules.bpm.service.IPermissionService;
import org.jeecg.modules.bpm.util.XmlActivitiUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
/**
* 类名:SuperTaskService
* 功能: 工作流核心处理类
* 详细:
* 作者:jeecg
* 版本:1.0
* 日期:2013-7-25 上午10:19:16
*/
@Slf4j
@Service("superTaskService")
public class SuperTaskService {
@Autowired
protected RepositoryService repositoryService; //部署流程服务
@Autowired
protected RuntimeService runtimeService; //流程执行服务
@Autowired
protected TaskService taskService; //任务服务
@Autowired
protected HistoryService historyService; //历史服务
@Autowired
protected ActivitiMapper activitiMapper;
@Autowired
protected IPermissionService permissionService;
/**
* 走向流程 【带参数】
*
* @param taskId 当前任务节点 ID
* @param nextTaskId 下个任务节点ID
* @param variables 流程存储参数
* @throws Exception
*/
public synchronized void goProcessTaskNode(String taskId, String nextTaskId, Map<String, Object> variables) throws Exception {
// try {
// 查找所有并行任务节点,同时驳回
List<Task> taskList = findTaskListByKey(findProcessInstanceByTaskId(taskId).getId(), findTaskById(taskId).getTaskDefinitionKey());
for (Task task : taskList) {
if (taskId.equals(task.getId())) {
commitProcess(task.getId(), variables, nextTaskId);
}
}
}
/**
* 走向流程 【不带参数】
*
* @param taskId 当前任务节点
* @param nextTaskId 下个任务节点
* @throws Exception
*/
public void goProcessTaskNode(String taskId, String nextTaskId) throws Exception {
if (org.apache.commons.lang.StringUtil.isEmpty(nextTaskId)) {
throw new Exception("目标节点ID为空!");
}
// 查找所有并行任务节点,同时取回
List<Task> taskList = findTaskListByKey(findProcessInstanceByTaskId(taskId).getId(), findTaskById(taskId).getTaskDefinitionKey());
for (Task task : taskList) {
commitProcess(task.getId(), null, nextTaskId);
}
}
/**
* @param taskId 当前任务ID
* @param variables 流程变量
* @param activityId 流程转向执行任务节点ID<br>
* 此参数为空,默认为提交操作
* @throws Exception
*/
private void commitProcess(String taskId, Map<String, Object> variables, String activityId) throws Exception {
// 跳转节点为空,默认提交操作
if (oConvertUtils.isEmpty(activityId)) {
taskService.complete(taskId, variables);
} else {// 流程转向操作
turnTransition(taskId, activityId, variables);
}
}
/**
* 流程转向操作
*
* @param taskId 当前任务ID
* @param activityId 目标节点任务ID
* @param variables 流程变量
* @throws Exception
*/
private void turnTransition(String taskId, String activityId, Map<String, Object> variables) throws Exception {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId())
.moveActivityIdTo(task.getTaskDefinitionKey(), activityId)
.changeState();
}
/**
* 中止流程(特权人直接审批通过等)
*
* @param taskId
*/
public void endProcess(String taskId) throws Exception {
commitProcess(taskId, null, "end");
}
static {
StringUtil.lastIndexOf();
}
/**
* 根据任务ID获取流程定义
*
* @param taskId 任务ID
* @return
* @throws Exception
*/
public ProcessDefinitionEntity findProcessDefinitionEntityByTaskId(
String taskId) throws Exception {
// 取得流程定义
ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
.getDeployedProcessDefinition(findTaskById(taskId)
.getProcessDefinitionId());
if (processDefinition == null) {
throw new Exception("流程定义未找到!");
}
return processDefinition;
}
/**
* 根据任务ID获取对应的流程实例
*
* @param taskId 任务ID
* @return
* @throws Exception
*/
public ProcessInstance findProcessInstanceByTaskId(String taskId)
throws Exception {
// 找到流程实例
ProcessInstance processInstance = runtimeService
.createProcessInstanceQuery().processInstanceId(
findTaskById(taskId).getProcessInstanceId())
.singleResult();
if (processInstance == null) {
throw new Exception("流程实例未找到!");
}
return processInstance;
}
/**
* 根据任务ID获得任务实例
*
* @param taskId 任务ID
* @return
* @throws Exception
*/
private TaskEntity findTaskById(String taskId) throws Exception {
TaskEntity task = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
if (task == null) {
throw new Exception("任务实例未找到!");
}
return task;
}
/**
* 根据流程实例ID和任务key值查询所有同级任务集合
*
* @param processInstanceId
* @param key
* @return
*/
private List<Task> findTaskListByKey(String processInstanceId, String key) {
return taskService.createTaskQuery().processInstanceId(
processInstanceId).taskDefinitionKey(key).list();
}
/**
* 转办流程
*
* @param taskId 当前任务节点ID
* @param userCode 被转办人Code
*/
public void transferAssignee(String taskId, String userCode) {
taskService.setAssignee(taskId, userCode);
}
/**
* 指定下一步操作人
*
* @param proInsId
* @param taskDefKey
*/
public String getTaskIdByProins(String proInsId, String taskDefKey) {
List<String> list = activitiMapper.getTaskIdByProins(proInsId, taskDefKey);
if (list != null && list.size() > 0) {
return list.get(0);
}
return null;
}
/**
* 通过流程定义key和任务,判断任务是否是会签节点
* 处理方法:通过解析xml文件
*
* @param taskId
* @return
*/
//TODO
public boolean checkUserTaskIsHuiQian(String taskId, String taskkey) {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
//String taskkey = task.getTaskDefinitionKey();
String processkey = task.getProcessDefinitionId();
ProcessDefinition pd = repositoryService.getProcessDefinition(processkey);
InputStream resourceAsStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), pd.getResourceName());
String xmlString = null;
try {
xmlString = IOUtils.toString(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}
return XmlActivitiUtil.parseXml(xmlString, taskkey);
}
}
@@ -0,0 +1,735 @@
//package org.jeecg.modules.bpm.service.impl.image;
//
//import com.sun.corba.se.impl.orbutil.graph.Graph;
//import com.sun.javafx.geom.Edge;
//import org.apache.commons.io.FilenameUtils;
//import org.flowable.bpmn.constants.BpmnXMLConstants;
//import org.flowable.bpmn.model.*;
//import org.flowable.bpmn.model.Process;
//import org.flowable.common.engine.impl.Page;
//import org.flowable.common.engine.impl.context.Context;
//import org.flowable.engine.history.HistoricActivityInstance;
//import org.flowable.engine.history.HistoricProcessInstance;
//import org.flowable.engine.impl.HistoricActivityInstanceQueryImpl;
//import org.flowable.engine.impl.cmd.GetBpmnModelCmd;
//import org.flowable.engine.impl.cmd.GetDeploymentProcessDefinitionCmd;
//import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
//
//import javax.imageio.ImageIO;
//import java.awt.*;
//import java.awt.geom.AffineTransform;
//import java.awt.geom.Ellipse2D.Double;
//import java.awt.geom.Line2D;
//import java.awt.geom.Path2D;
//import java.awt.geom.RoundRectangle2D;
//import java.awt.image.BufferedImage;
//import java.io.ByteArrayInputStream;
//import java.io.ByteArrayOutputStream;
//import java.io.IOException;
//import java.io.InputStream;
//import java.util.ArrayList;
//import java.util.List;
//
///**
// * 原来有用的类
// *
// * 流程图绘制工具
// */
//public class OriginalCustomProcessDiagramGenerator {
// //update-begin 5.22流程图矩形圆角问题
// //public static final int OFFSET_SUBPROCESS = 5;
// //public static final int OFFSET_TASK = 20;
// public static final int OFFSET_SUBPROCESS = 0;
// public static final int OFFSET_TASK = 0;
// //update-end 5.22流程图矩形圆角问题
//
// private static List<String> taskType = new ArrayList<String>();
// private static List<String> eventType = new ArrayList<String>();
// private static List<String> gatewayType = new ArrayList<String>();
// private static List<String> subProcessType = new ArrayList<String>();
// private static Color RUNNING_COLOR = Color.RED;
// private static Color HISTORY_COLOR = Color.GREEN;
// private static Color SKIP_COLOR = Color.GRAY;
// private static Stroke THICK_BORDER_STROKE = new BasicStroke(3.0f);
// private int minX;
// private int minY;
//
// public CustomProcessDiagramGenerator() {
// init();
// }
//
// protected static void init() {
// taskType.add(BpmnXMLConstants.ELEMENT_TASK_MANUAL);
// taskType.add(BpmnXMLConstants.ELEMENT_TASK_RECEIVE);
// taskType.add(BpmnXMLConstants.ELEMENT_TASK_SCRIPT);
// taskType.add(BpmnXMLConstants.ELEMENT_TASK_SEND);
// taskType.add(BpmnXMLConstants.ELEMENT_TASK_SERVICE);
// taskType.add(BpmnXMLConstants.ELEMENT_TASK_USER);
//
// gatewayType.add(BpmnXMLConstants.ELEMENT_GATEWAY_EXCLUSIVE);
// gatewayType.add(BpmnXMLConstants.ELEMENT_GATEWAY_INCLUSIVE);
// gatewayType.add(BpmnXMLConstants.ELEMENT_GATEWAY_EVENT);
// gatewayType.add(BpmnXMLConstants.ELEMENT_GATEWAY_PARALLEL);
//
// eventType.add("intermediateTimer");
// eventType.add("intermediateMessageCatch");
// eventType.add("intermediateSignalCatch");
// eventType.add("intermediateSignalThrow");
// eventType.add("messageStartEvent");
// eventType.add("startTimerEvent");
// eventType.add(BpmnXMLConstants.ELEMENT_ERROR);
// eventType.add(BpmnXMLConstants.ELEMENT_EVENT_START);
// eventType.add("errorEndEvent");
// eventType.add(BpmnXMLConstants.ELEMENT_EVENT_END);
//
// subProcessType.add(BpmnXMLConstants.ELEMENT_SUBPROCESS);
// subProcessType.add(BpmnXMLConstants.ELEMENT_CALL_ACTIVITY);
// }
//
// public InputStream generateDiagram(String processInstanceId)
// throws IOException {
// HistoricProcessInstance historicProcessInstance = Context
// .getCommandContext().getHistoricProcessInstanceEntityManager()
// .findHistoricProcessInstance(processInstanceId);
// String processDefinitionId = historicProcessInstance
// .getProcessDefinitionId();
// GetBpmnModelCmd getBpmnModelCmd = new GetBpmnModelCmd(
// processDefinitionId);
// BpmnModel bpmnModel = getBpmnModelCmd.execute(Context
// .getCommandContext());
// Point point = getMinXAndMinY(bpmnModel);
// //5.22流程图跟踪错位问题解决start
//
// //update-begin--Author:scott Date:20220112 forLOWCOD-2229【新版流程】流程设计时上移流程后,图标注错乱---------
// this.minX = point.x;
// if (this.minX >= 0) {
// this.minX = 0;
// this.minX = (this.minX <= 5) ? 5 : this.minX;
// this.minX -= 5;
// }
//
// this.minY = point.y;
// if (this.minY >= 0) {
// this.minY = 0;
// this.minY = (this.minY <= 5) ? 5 : this.minY;
// this.minY -= 5;
// }
// //update-end--Author:scott Date:20220112 forLOWCOD-2229【新版流程】流程设计时上移流程后,图标注错乱---------
//
// ByteArrayInputStream byteArrayInputStream = null;
// InputStream originDiagram =null;
// ByteArrayOutputStream out=null;
// try {
// ProcessDefinitionEntity definition = new GetDeploymentProcessDefinitionCmd(
// processDefinitionId).execute(Context.getCommandContext());
// String diagramResourceName = definition.getDiagramResourceName();
// String deploymentId = definition.getDeploymentId();
// byte[] bytes = Context
// .getCommandContext()
// .getResourceEntityManager()
// .findResourceByDeploymentIdAndResourceName(deploymentId,
// diagramResourceName).getBytes();
// originDiagram = new ByteArrayInputStream(bytes);
// BufferedImage image = ImageIO.read(originDiagram);
//
// HistoricActivityInstanceQueryImpl historicActivityInstanceQueryImpl = new HistoricActivityInstanceQueryImpl();
// historicActivityInstanceQueryImpl.processInstanceId(processInstanceId)
// .orderByHistoricActivityInstanceStartTime().asc();
//
// Page page = new Page(0, 100);
// List<HistoricActivityInstance> activityInstances = Context
// .getCommandContext()
// .getHistoricActivityInstanceEntityManager()
// .findHistoricActivityInstancesByQueryCriteria(
// historicActivityInstanceQueryImpl, page);
//
// this.drawHistoryFlow(image, processInstanceId);
//
// for (HistoricActivityInstance historicActivityInstance : activityInstances) {
// String historicActivityId = historicActivityInstance
// .getActivityId();
// ActivityImpl activity = definition.findActivity(historicActivityId);
//
// if (activity != null) {
// if (historicActivityInstance.getEndTime() == null) {
// // 节点正在运行中
// signRunningNode(image, activity.getX() - this.minX,
// activity.getY() - this.minY, activity.getWidth(),
// activity.getHeight(),
// historicActivityInstance.getActivityType());
// } else {
// String deleteReason = null;
//
// if (historicActivityInstance.getTaskId() != null) {
// deleteReason = Context
// .getCommandContext()
// .getHistoricTaskInstanceEntityManager()
// .findHistoricTaskInstanceById(
// historicActivityInstance.getTaskId())
// .getDeleteReason();
// }
//
// // 节点已经结束
// if ("跳过".equals(deleteReason)) {
// signSkipNode(image, activity.getX() - this.minX,
// activity.getY() - this.minY,
// activity.getWidth(), activity.getHeight(),
// historicActivityInstance.getActivityType());
// } else {
// signHistoryNode(image, activity.getX() - this.minX,
// activity.getY() - this.minY,
// activity.getWidth(), activity.getHeight(),
// historicActivityInstance.getActivityType());
// }
// }
// }
// }
//
// out = new ByteArrayOutputStream();
// String formatName = getDiagramExtension(diagramResourceName);
// ImageIO.write(image, formatName, out);
// byteArrayInputStream = new ByteArrayInputStream(out.toByteArray());
// } catch (Exception e) {
// e.printStackTrace();
// }finally{
// try {
// if(out!=null){
// out.close();
// }
// if(originDiagram!=null){
// originDiagram.close();
// }
// } catch (Exception e) {
// }
//
//
// }
//
// return byteArrayInputStream;
// }
//
// private static String getDiagramExtension(String diagramResourceName) {
// return FilenameUtils.getExtension(diagramResourceName);
// }
//
// /**
// * 标记运行节点
// *
// * @param image
// * 原始图片
// * @param x
// * 左上角节点坐在X位置
// * @param y
// * 左上角节点坐在Y位置
// * @param width
// * 宽
// * @param height
// * 高
// * @param activityType
// * 节点类型
// */
// private static void signRunningNode(BufferedImage image, int x, int y,
// int width, int height, String activityType) {
// Color nodeColor = RUNNING_COLOR;
// Graphics2D graphics = image.createGraphics();
//
// try {
// drawNodeBorder(x, y, width, height, graphics, nodeColor,
// activityType);
// } finally {
// graphics.dispose();
// }
// }
//
// /**
// * 标记历史节点
// *
// * @param image
// * 原始图片
// * @param x
// * 左上角节点坐在X位置
// * @param y
// * 左上角节点坐在Y位置
// * @param width
// * 宽
// * @param height
// * 高
// * @param activityType
// * 节点类型
// */
// private static void signHistoryNode(BufferedImage image, int x, int y,
// int width, int height, String activityType) {
// Color nodeColor = HISTORY_COLOR;
// Graphics2D graphics = image.createGraphics();
//
// try {
// drawNodeBorder(x, y, width, height, graphics, nodeColor,
// activityType);
// } finally {
// graphics.dispose();
// }
// }
//
// private static void signSkipNode(BufferedImage image, int x, int y,
// int width, int height, String activityType) {
// Color nodeColor = SKIP_COLOR;
// Graphics2D graphics = image.createGraphics();
//
// try {
// drawNodeBorder(x, y, width, height, graphics, nodeColor,
// activityType);
// } finally {
// graphics.dispose();
// }
// }
//
// /**
// * 绘制节点边框
// *
// * @param x
// * 左上角节点坐在X位置
// * @param y
// * 左上角节点坐在Y位置
// * @param width
// * 宽
// * @param height
// * 高
// * @param graphics
// * 绘图对象
// * @param color
// * 节点边框颜色
// * @param activityType
// * 节点类型
// */
// protected static void drawNodeBorder(int x, int y, int width, int height,
// Graphics2D graphics, Color color, String activityType) {
// graphics.setPaint(color);
// graphics.setStroke(THICK_BORDER_STROKE);
//
// if (taskType.contains(activityType)) {
// drawTask(x, y, width, height, graphics);
// } else if (gatewayType.contains(activityType)) {
// drawGateway(x, y, width, height, graphics);
// } else if (eventType.contains(activityType)) {
// drawEvent(x, y, width, height, graphics);
// } else if (subProcessType.contains(activityType)) {
// drawSubProcess(x, y, width, height, graphics);
// }
// }
//
// /**
// * 绘制任务
// */
// protected static void drawTask(int x, int y, int width, int height,
// Graphics2D graphics) {
// RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width,
// height, OFFSET_TASK, OFFSET_TASK);
// graphics.draw(rect);
// }
//
// /**
// * 绘制网关
// */
// protected static void drawGateway(int x, int y, int width, int height,
// Graphics2D graphics) {
// Polygon rhombus = new Polygon();
// rhombus.addPoint(x, y + (height / 2));
// rhombus.addPoint(x + (width / 2), y + height);
// rhombus.addPoint(x + width, y + (height / 2));
// rhombus.addPoint(x + (width / 2), y);
// graphics.draw(rhombus);
// }
//
// /**
// * 绘制任务
// */
// protected static void drawEvent(int x, int y, int width, int height,
// Graphics2D graphics) {
// Double circle = new Double(x, y, width, height);
// graphics.draw(circle);
// }
//
// /**
// * 绘制子流程
// */
// protected static void drawSubProcess(int x, int y, int width, int height,
// Graphics2D graphics) {
// RoundRectangle2D rect = new RoundRectangle2D.Double(x + 1, y + 1,
// width - 2, height - 2, OFFSET_SUBPROCESS, OFFSET_SUBPROCESS);
// graphics.draw(rect);
// }
//
// protected Point getMinXAndMinY(BpmnModel bpmnModel) {
// // We need to calculate maximum values to know how big the image will be in its entirety
// double theMinX = java.lang.Double.MAX_VALUE;
// double theMaxX = 0;
// double theMinY = java.lang.Double.MAX_VALUE;
// double theMaxY = 0;
//
// for (Pool pool : bpmnModel.getPools()) {
// GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
// theMinX = graphicInfo.getX();
// theMaxX = graphicInfo.getX() + graphicInfo.getWidth();
// theMinY = graphicInfo.getY();
// theMaxY = graphicInfo.getY() + graphicInfo.getHeight();
// }
//
// List<FlowNode> flowNodes = gatherAllFlowNodes(bpmnModel);
//
// for (FlowNode flowNode : flowNodes) {
// GraphicInfo flowNodeGraphicInfo = bpmnModel.getGraphicInfo(flowNode
// .getId());
//
// // width
// if ((flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth()) > theMaxX) {
// theMaxX = flowNodeGraphicInfo.getX()
// + flowNodeGraphicInfo.getWidth();
// }
//
// if (flowNodeGraphicInfo.getX() < theMinX) {
// theMinX = flowNodeGraphicInfo.getX();
// }
//
// // height
// if ((flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight()) > theMaxY) {
// theMaxY = flowNodeGraphicInfo.getY()
// + flowNodeGraphicInfo.getHeight();
// }
//
// if (flowNodeGraphicInfo.getY() < theMinY) {
// theMinY = flowNodeGraphicInfo.getY();
// }
//
// for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
// List<GraphicInfo> graphicInfoList = bpmnModel
// .getFlowLocationGraphicInfo(sequenceFlow.getId());
//
// for (GraphicInfo graphicInfo : graphicInfoList) {
// // width
// if (graphicInfo.getX() > theMaxX) {
// theMaxX = graphicInfo.getX();
// }
//
// if (graphicInfo.getX() < theMinX) {
// theMinX = graphicInfo.getX();
// }
//
// // height
// if (graphicInfo.getY() > theMaxY) {
// theMaxY = graphicInfo.getY();
// }
//
// if (graphicInfo.getY() < theMinY) {
// theMinY = graphicInfo.getY();
// }
// }
// }
// }
//
// List<Artifact> artifacts = gatherAllArtifacts(bpmnModel);
//
// for (Artifact artifact : artifacts) {
// GraphicInfo artifactGraphicInfo = bpmnModel.getGraphicInfo(artifact
// .getId());
//
// if (artifactGraphicInfo != null) {
// // width
// if ((artifactGraphicInfo.getX() + artifactGraphicInfo
// .getWidth()) > theMaxX) {
// theMaxX = artifactGraphicInfo.getX()
// + artifactGraphicInfo.getWidth();
// }
//
// if (artifactGraphicInfo.getX() < theMinX) {
// theMinX = artifactGraphicInfo.getX();
// }
//
// // height
// if ((artifactGraphicInfo.getY() + artifactGraphicInfo
// .getHeight()) > theMaxY) {
// theMaxY = artifactGraphicInfo.getY()
// + artifactGraphicInfo.getHeight();
// }
//
// if (artifactGraphicInfo.getY() < theMinY) {
// theMinY = artifactGraphicInfo.getY();
// }
// }
//
// List<GraphicInfo> graphicInfoList = bpmnModel
// .getFlowLocationGraphicInfo(artifact.getId());
//
// if (graphicInfoList != null) {
// for (GraphicInfo graphicInfo : graphicInfoList) {
// // width
// if (graphicInfo.getX() > theMaxX) {
// theMaxX = graphicInfo.getX();
// }
//
// if (graphicInfo.getX() < theMinX) {
// theMinX = graphicInfo.getX();
// }
//
// // height
// if (graphicInfo.getY() > theMaxY) {
// theMaxY = graphicInfo.getY();
// }
//
// if (graphicInfo.getY() < theMinY) {
// theMinY = graphicInfo.getY();
// }
// }
// }
// }
//
// int nrOfLanes = 0;
//
// for (Process process : bpmnModel.getProcesses()) {
// for (Lane l : process.getLanes()) {
// nrOfLanes++;
//
// GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId());
//
// // // width
// if ((graphicInfo.getX() + graphicInfo.getWidth()) > theMaxX) {
// theMaxX = graphicInfo.getX() + graphicInfo.getWidth();
// }
//
// if (graphicInfo.getX() < theMinX) {
// theMinX = graphicInfo.getX();
// }
//
// // height
// if ((graphicInfo.getY() + graphicInfo.getHeight()) > theMaxY) {
// theMaxY = graphicInfo.getY() + graphicInfo.getHeight();
// }
//
// if (graphicInfo.getY() < theMinY) {
// theMinY = graphicInfo.getY();
// }
// }
// }
//
// // Special case, see http://jira.codehaus.org/browse/ACT-1431
// if ((flowNodes.size() == 0) && (bpmnModel.getPools().size() == 0)
// && (nrOfLanes == 0)) {
// // Nothing to show
// theMinX = 0;
// theMinY = 0;
// }
//
// return new Point((int) theMinX, (int) theMinY);
// }
//
// protected static List<Artifact> gatherAllArtifacts(BpmnModel bpmnModel) {
// List<Artifact> artifacts = new ArrayList<Artifact>();
//
// for (Process process : bpmnModel.getProcesses()) {
// artifacts.addAll(process.getArtifacts());
// }
//
// return artifacts;
// }
//
// protected static List<FlowNode> gatherAllFlowNodes(BpmnModel bpmnModel) {
// List<FlowNode> flowNodes = new ArrayList<FlowNode>();
//
// for (Process process : bpmnModel.getProcesses()) {
// flowNodes.addAll(gatherAllFlowNodes(process));
// }
//
// return flowNodes;
// }
//
// protected static List<FlowNode> gatherAllFlowNodes(
// FlowElementsContainer flowElementsContainer) {
// List<FlowNode> flowNodes = new ArrayList<FlowNode>();
//
// for (FlowElement flowElement : flowElementsContainer.getFlowElements()) {
// if (flowElement instanceof FlowNode) {
// flowNodes.add((FlowNode) flowElement);
// }
//
// if (flowElement instanceof FlowElementsContainer) {
// flowNodes
// .addAll(gatherAllFlowNodes((FlowElementsContainer) flowElement));
// }
// }
//
// return flowNodes;
// }
//
// public void drawHistoryFlow(BufferedImage image, String processInstanceId) {
// HistoricProcessInstance historicProcessInstance = Context
// .getCommandContext().getHistoricProcessInstanceEntityManager()
// .findHistoricProcessInstance(processInstanceId);
// String processDefinitionId = historicProcessInstance
// .getProcessDefinitionId();
// Graph graph = new FlowableHistoryGraphBuilder(processInstanceId)
// .build();
//
// for (Edge edge : graph.getEdges()) {
// drawSequenceFlow(image, processDefinitionId, edge.getName());
// }
// }
//
// /**
// * 画线条
// * @param image
// * @param processDefinitionId
// * @param sequenceFlowId
// */
// private void drawSequenceFlow(BufferedImage image,String processDefinitionId, String sequenceFlowId) {
// GetBpmnModelCmd getBpmnModelCmd = new GetBpmnModelCmd(processDefinitionId);
// BpmnModel bpmnModel = getBpmnModelCmd.execute(Context.getCommandContext());
//
// Graphics2D graphics = image.createGraphics();
// graphics.setPaint(HISTORY_COLOR);
// graphics.setStroke(new BasicStroke(2f));
//
// try {
// //TODO 【JTC-1177】流程画线有斜度,流转的进度线就对不上。问题出在这里,返回的Y轴是一样高度
// List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlowId);
//
// int[] xPoints = new int[graphicInfoList.size()];
// int[] yPoints = new int[graphicInfoList.size()];
//
// for (int i = 1; i < graphicInfoList.size(); i++) {
// GraphicInfo graphicInfo = graphicInfoList.get(i);
//
// //System.out.println("GraphicInfo x: " + graphicInfo.getX());
// //System.out.println("GraphicInfo y: " + graphicInfo.getY());
//
// GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);
//
// if (i == 1) {
// xPoints[0] = (int) previousGraphicInfo.getX() - minX;
// yPoints[0] = (int) previousGraphicInfo.getY() - minY;
// }
//
// xPoints[i] = (int) graphicInfo.getX() - minX;
// yPoints[i] = (int) graphicInfo.getY() - minY;
// }
//
// //5.22流程图路径线圆角问题start
// //int radius = 15;
// int radius = 0;
// //5.22流程图路径线圆角问题end
//
// //画线
// Path2D path = new Path2D.Double();
//
// for (int i = 0; i < xPoints.length; i++) {
// Integer anchorX = xPoints[i];
// Integer anchorY = yPoints[i];
//
// double targetX = anchorX;
// double targetY = anchorY;
//
// double ax = 0;
// double ay = 0;
// double bx = 0;
// double by = 0;
// double zx = 0;
// double zy = 0;
//
// if ((i > 0) && (i < (xPoints.length - 1))) {
// Integer cx = anchorX;
// Integer cy = anchorY;
//
// // pivot point of prev line
// double lineLengthY = yPoints[i] - yPoints[i - 1];
//
// // pivot point of prev line
// double lineLengthX = xPoints[i] - xPoints[i - 1];
// double lineLength = Math.sqrt(Math.pow(lineLengthY, 2)
// + Math.pow(lineLengthX, 2));
// double dx = (lineLengthX * radius) / lineLength;
// double dy = (lineLengthY * radius) / lineLength;
// targetX = targetX - dx;
// targetY = targetY - dy;
//
// // isDefaultConditionAvailable = isDefault && i == 1 && lineLength > 10;
// if ((lineLength < (2 * radius)) && (i > 1)) {
// targetX = xPoints[i] - (lineLengthX / 2);
// targetY = yPoints[i] - (lineLengthY / 2);
// }
//
// // pivot point of next line
// lineLengthY = yPoints[i + 1] - yPoints[i];
// lineLengthX = xPoints[i + 1] - xPoints[i];
// lineLength = Math.sqrt(Math.pow(lineLengthY, 2)
// + Math.pow(lineLengthX, 2));
//
// if (lineLength < radius) {
// lineLength = radius;
// }
//
// dx = (lineLengthX * radius) / lineLength;
// dy = (lineLengthY * radius) / lineLength;
//
// double nextSrcX = xPoints[i] + dx;
// double nextSrcY = yPoints[i] + dy;
//
// if ((lineLength < (2 * radius))
// && (i < (xPoints.length - 2))) {
// nextSrcX = xPoints[i] + (lineLengthX / 2);
// nextSrcY = yPoints[i] + (lineLengthY / 2);
// }
//
// double dx0 = (cx - targetX) / 3;
// double dy0 = (cy - targetY) / 3;
// ax = cx - dx0;
// ay = cy - dy0;
//
// double dx1 = (cx - nextSrcX) / 3;
// double dy1 = (cy - nextSrcY) / 3;
// bx = cx - dx1;
// by = cy - dy1;
//
// zx = nextSrcX;
// zy = nextSrcY;
// }
//
// if (i == 0) {
// path.moveTo(targetX, targetY);
// } else {
// path.lineTo(targetX, targetY);
// }
//
// if ((i > 0) && (i < (xPoints.length - 1))) {
// // add curve 添加曲线
// path.curveTo(ax, ay, bx, by, zx, zy);
// }
// }
//
// graphics.draw(path);
//
// //画箭头 draw arrow
// Line2D.Double line = new Line2D.Double(xPoints[xPoints.length - 2],
// yPoints[xPoints.length - 2], xPoints[xPoints.length - 1],
// yPoints[xPoints.length - 1]);
//
// int ARROW_WIDTH = 5;
// int doubleArrowWidth = 2 * ARROW_WIDTH;
// Polygon arrowHead = new Polygon();
// arrowHead.addPoint(0, 0);
// arrowHead.addPoint(-ARROW_WIDTH, -doubleArrowWidth);
// arrowHead.addPoint(ARROW_WIDTH, -doubleArrowWidth);
//
// AffineTransform transformation = new AffineTransform();
// transformation.setToIdentity();
//
// double angle = Math.atan2(line.y2 - line.y1, line.x2 - line.x1);
// transformation.translate(line.x2, line.y2);
// transformation.rotate((angle - (Math.PI / 2d)));
//
// AffineTransform originalTransformation = graphics.getTransform();
// graphics.setTransform(transformation);
// graphics.fill(arrowHead);
// graphics.setTransform(originalTransformation);
// } finally {
// graphics.dispose();
// }
// }
//}
@@ -0,0 +1,103 @@
package org.jeecg.modules.bpm.util;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.ReflectHelper;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.pagination.DialectFactory;
import com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;
@Slf4j
public class BpmSqlUtil {
// /**
// * 分页SQL
// */
// public static final String MYSQL_SQL = "select * from ( {0}) sel_tab00 limit {1},{2}"; // mysql
// public static final String POSTGRE_SQL = "select * from ( {0}) sel_tab00 limit {2} offset {1}";// postgresql
// public static final String ORACLE_SQL = "select * from (select row_.*,rownum rownum_ from ({0}) row_ where rownum <= {1}) where rownum_>{2}"; // oracle
// public static final String SQLSERVER_SQL = "select * from ( select row_number() over(order by tempColumn) tempRowNumber, * from (select top {1} tempColumn = 0, {0}) t ) tt where tempRowNumber > {2}"; // sqlserver
//
//
//
// /**
// * 按照数据库类型,封装SQL
// *
// * @param dbType
// * 数据库类型
// * @param sql
// * @param pageNo
// * @param pageSize
// * @return
// */
// public static String createPageSql(String dbType, String sql, int pageNo, int pageSize) {
// int beginNum = (pageNo - 1) * pageSize;
// String[] sqlParam = new String[3];
// sqlParam[0] = sql;
// sqlParam[1] = beginNum + "";
// sqlParam[2] = pageSize + "";
// if (dbType == null || "".equals(dbType)) {
// throw new RuntimeException("SqlUtil:(数据库类型:dbType)没有设置,请检查配置文件");
// }
//
// if (DataBaseConstant.DB_TYPE_MYSQL.equalsIgnoreCase(dbType) || DataBaseConstant.DB_TYPE_MARIADB.equalsIgnoreCase(dbType)) {
// sql = MessageFormat.format(MYSQL_SQL, sqlParam);
// } else if (DataBaseConstant.DB_TYPE_POSTGRESQL.equalsIgnoreCase(dbType)) {
// sql = MessageFormat.format(POSTGRE_SQL, sqlParam);
// } else {
// int beginIndex = (pageNo - 1) * pageSize;
// int endIndex = beginIndex + pageSize;
// sqlParam[2] = Integer.toString(beginIndex);
// sqlParam[1] = Integer.toString(endIndex);
// if (DataBaseConstant.DB_TYPE_ORACLE.equalsIgnoreCase(dbType)) {
// sql = MessageFormat.format(ORACLE_SQL, sqlParam);
// } else if (DataBaseConstant.DB_TYPE_SQLSERVER.equalsIgnoreCase(dbType)) {
// sqlParam[0] = sql.substring(getAfterSelectInsertPoint(sql));
// sql = MessageFormat.format(SQLSERVER_SQL, sqlParam);
// }
// }
// return sql;
// }
//
//
// /**
// *
// * @param sql
// * @return
// */
// private static int getAfterSelectInsertPoint(String sql) {
// int selectIndex = sql.toLowerCase().indexOf("select");
// int selectDistinctIndex = sql.toLowerCase().indexOf("select distinct");
// return selectIndex + (selectDistinctIndex == selectIndex ? 15 : 6);
// }
/**
* 按照数据库类型,封装SQL
*
* @param sql
* @param pageNo
* @param pageSize
* @return
*/
public static String createPageSql(String sql, int pageNo, int pageSize) {
DbType dbType = CommonUtils.getDatabaseTypeEnum();
log.debug(" 当前环境 DbType"+dbType.getDb());
IDialect dialect = DialectFactory.getDialect(dbType);
Page page = new Page<>(pageNo, pageSize);
DialectModel model = dialect.buildPaginationSql(sql, page.offset(), page.getSize());
String pageSQL = model.getDialectSql();
// 设置分页参数
if (model.getDialectSql().contains("?")) {
long firstParam = (long)ReflectHelper.getFieldVal("firstParam", model);
long secondParam = (long)ReflectHelper.getFieldVal("secondParam", model);
pageSQL = pageSQL.replaceFirst("\\?", firstParam + "");
pageSQL = pageSQL.replace("?", secondParam + "");
}
return pageSQL;
}
}
@@ -0,0 +1,190 @@
package org.jeecg.modules.bpm.util;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.oConvertUtils;
public class BpmUtil {
public static String getNodeUrl(Map<String, Object> variables,String formUrl,String dataId) {
if(oConvertUtils.isEmpty(formUrl)) {
return null;
}
Set<String> varParams = getVarParams(formUrl);
if(varParams!=null&&varParams.size()>0) {
formUrl = getContent(variables, formUrl);
}else {
if(formUrl.indexOf("/online/df/")>=0||formUrl.indexOf("/online/dfview/")>=0) {
if(formUrl.endsWith("/")) {
formUrl +=dataId;
}else {
formUrl +="/"+dataId;
}
}
}
return formUrl;
}
/**
* 获取标题表达式 ,翻译后的值文本
*
* @param variables
* @param exp
* @return
*/
public static String getContent(Map<String, Object> variables, String exp){
try {
if(oConvertUtils.isEmpty(exp)) {
return null;
}
Set<String> varParams = getVarParams(exp);
for(String key: varParams){
//update-begin---author:scott ---date:2023-08-29 for:【简流】标题字段是关联记录时 流程标题显示id---
//取值表临时变量 key
String var = key;
//【QQYUN-6347】判断标题字段类型是关联记录
if(var.startsWith("link_record_")) {
var = var + "_dicttext";
}
//update-end---author:scott ---date::2023-08-29 for:【简流】标题字段是关联记录时 流程标题显示id-
Object obj = variables.get(var);
//update-begin---author:scott ---date:20231016 for:【QQYUN-6760 QQYUN-6812】标题字段是人员、部门、日期时,任务处理中显示的标题未翻译---
//翻译值
Object objDictText = variables.get((var + CommonConstant.DICT_TEXT_SUFFIX).toLowerCase());
if (oConvertUtils.isNotEmpty(objDictText)) {
exp = exp.replace("${"+key+"}", objDictText.toString());
return exp;
}
//update-end---author:scott ---date::20231016 for:【QQYUN-6760 QQYUN-6812】标题字段是人员、部门、日期时,任务处理中显示的标题未翻译---
if(obj==null){
obj = variables.get(var.toUpperCase());
if(obj==null) {
obj = "";
}
}else if(obj instanceof Date){
obj = DateUtils.formatDate((Date)obj);
}else if (obj instanceof JSONArray) {
//【QQYUN-4094】标题显示问题,表单设计器中使用用户组件
JSONArray array = (JSONArray) obj;
if (array.size() > 1) {
obj = StringUtils.join(array.toArray(), ",");
} else {
obj = array.size() == 1 ? array.get(0) : "";
}
//update-begin---author:scott ---date:2023-11-20 for:【QQYUN-7077】设计器表单,出差申请/请假申请/离职申请/晋级申请 名称为 java.lang.Object----
} else if (obj instanceof Object[]) {
Object[] vals = (Object[]) obj;
if (vals.length == 1) {
obj = vals[0];
} else {
obj = StringUtils.join(vals, ",");
}
}
//update-end---author:scott ---date::2023-11-20 for:【QQYUN-7077】设计器表单,出差申请/请假申请/离职申请/晋级申请 名称为 java.lang.Object--
exp = exp.replace("${"+key+"}", obj.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
return exp;
}
public static Set<String> getVarParams(String exp) {
if(oConvertUtils.isEmpty(exp)){
return null;
}
Set<String> varParams = new HashSet<String>();
String regex = "\\$\\{\\w+\\}";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(exp);
while(m.find()){
String var = m.group();
varParams.add(var.substring(var.indexOf("{")+1,var.indexOf("}")));
}
return varParams;
}
/**
* 时间的处理
* @param time
* @return
*/
public static String dealTimeFromNum(long time) {
StringBuilder result = new StringBuilder();
long interval = time / 1000;
final long day = 24 * 60 * 60;
final long hour = 60 * 60;
final long minute = 60;
int detailDay = 0;
int detailHour = 0;
int detailMinute = 0;
int detailSecond = 0;
if (interval >= day) {
detailDay = (int) (interval / day);
interval = interval - detailDay * day;
}
if (interval >= hour) {
detailHour = (int) (interval / hour);
interval = interval - hour * detailHour;
}
if (interval >= minute) {
detailMinute = (int) (interval / minute);
interval = interval - detailMinute * minute;
}
if (interval > 0) {
detailSecond = (int) interval;
}
result.setLength(0);
if (detailDay > 0) {
result.append(detailDay);
result.append("");
}
if (detailHour > 0) {
result.append(detailHour);
result.append("小时");
}
if (detailMinute > 0) {
result.append(detailMinute);
result.append("");
}
if (detailSecond > 0) {
result.append(detailSecond);
result.append("");
}else if (detailSecond == 0) {
result.append(detailSecond);
result.append("");
}
return result.toString();
}
/**
*
* @param str 要处理的字符串
* @param substring 要移除的子字符串
* @return
*/
public static String removeSubstring(String str, String substring) {
int index = str.indexOf(substring);
if (index != -1) { // 如果找到了指定的子字符串
return str.substring(0, index); // 截取前面部分并返回
} else {
return str; // 如果没有找到指定的子字符串,则返回原字符串
}
}
}
@@ -0,0 +1,170 @@
//package org.jeecg.modules.bpm.util;
//
//import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
//
//public class JoaUtil {
//
// /**
// * 查询待审批的单据
// * @param bizTaskType 业务类型1待我审批2我发起的申请
// * @param queryWrapper 查询器
// * @param userName 用户账号
// * @param flowCode 流程唯一编码
// */
// @SuppressWarnings("rawtypes")
// public static void filterRunningTask(String bizTaskType,QueryWrapper queryWrapper,String userName,String flowCode) {
// StringBuilder sb = new StringBuilder(" (");
// if("1".equals(bizTaskType)) {
// sb.append(" ID ").append(" in ").append("(");
// sb.append(" select fd.FORM_DATA_ID from EXT_ACT_FLOW_DATA fd ");
// sb.append(" RIGHT JOIN (");
// sb.append(" select * from (");
// sb.append(" (select distinct RES.* from ACT_RU_TASK RES inner join ACT_RU_IDENTITYLINK I on I.TASK_ID_ = RES.ID_ ");
// sb.append(" WHERE RES.ASSIGNEE_ is null and I.TYPE_ = 'candidate' ");
// sb.append(" and ( I.USER_ID_ = {0} or I.GROUP_ID_ IN ( select g.GROUP_ID_ from ACT_ID_MEMBERSHIP g where g.USER_ID_ = {0} ) ) ");
// sb.append(" and RES.SUSPENSION_STATE_ = 1 ");
// sb.append(" ) union (select distinct RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ = {0} ");
// sb.append(" )) v ");
// sb.append(" ) art ");
// sb.append(" on fd.PROCESS_INST_ID = art.PROC_INST_ID_ ");
// sb.append(" where fd.RELATION_CODE = {1} ");
// sb.append(" )");
// sb.append(" )");
// queryWrapper.apply(sb.toString(), userName,flowCode);
// }else {
// sb.append(" CREATE_BY = {0}");
// sb.append(" )");
// queryWrapper.apply(sb.toString(), userName);
// }
//
// }
//
//
// /**
// * 查询待审批的单据
// * @param bizTaskType 业务类型1待我审批2我发起的申请
// * @param queryWrapper 查询器
// * @param userName 用户账号
// * @param flowCode 流程唯一编码
// * @param taskDefKey 任务key
// */
// @SuppressWarnings("rawtypes")
// public static void filterRunningTask(String bizTaskType,QueryWrapper queryWrapper,String userName,String flowCode,String taskDefKey) {
// StringBuilder sb = new StringBuilder(" (");
// if("1".equals(bizTaskType)) {
// sb.append(" ID ").append(" in ").append("(");
// sb.append(" select fd.FORM_DATA_ID from EXT_ACT_FLOW_DATA fd ");
// sb.append(" RIGHT JOIN (");
// sb.append(" select * from (");
// sb.append(" (select distinct RES.* from ACT_RU_TASK RES inner join ACT_RU_IDENTITYLINK I on I.TASK_ID_ = RES.ID_ ");
// sb.append(" WHERE RES.ASSIGNEE_ is null and I.TYPE_ = 'candidate' ");
// sb.append(" and ( I.USER_ID_ = {0} or I.GROUP_ID_ IN ( select g.GROUP_ID_ from ACT_ID_MEMBERSHIP g where g.USER_ID_ = {0} ) ) ");
// sb.append(" and RES.SUSPENSION_STATE_ = 1 ");
// sb.append(" ) union (select distinct RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ = {0} ");
// sb.append(" )) v ");
// sb.append(" ) art ");
// sb.append(" on fd.PROCESS_INST_ID = art.PROC_INST_ID_ ");
// sb.append(" where fd.RELATION_CODE = {1} ");
// sb.append(" and art.TASK_DEF_KEY_ = {2} ");
// sb.append(" )");
// sb.append(" )");
// queryWrapper.apply(sb.toString(), userName,flowCode,taskDefKey);
// }else {
// sb.append(" CREATE_BY = {0}");
// sb.append(" )");
// queryWrapper.apply(sb.toString(), userName);
// }
// }
//
// /**
// * 查询待审批的单据
// * @param userName 用户账号
// * @param flowCode 流程唯一编码
// * @param taskDefKey 任务key
// */
// public static String getMyTaskSql(String userName,String flowCode) {
// StringBuilder sb = new StringBuilder(" ");
// sb.append(" select fd.FORM_DATA_ID from EXT_ACT_FLOW_DATA fd ");
// sb.append(" RIGHT JOIN (");
// sb.append(" select * from (");
// sb.append(" (select distinct RES.* from ACT_RU_TASK RES inner join ACT_RU_IDENTITYLINK I on I.TASK_ID_ = RES.ID_ ");
// sb.append(" WHERE RES.ASSIGNEE_ is null and I.TYPE_ = 'candidate' ");
// sb.append(" and ( I.USER_ID_ = '"+userName+"' or I.GROUP_ID_ IN ( select g.GROUP_ID_ from ACT_ID_MEMBERSHIP g where g.USER_ID_ = '"+userName+"' ) ) ");
// sb.append(" and RES.SUSPENSION_STATE_ = 1 ");
// sb.append(" ) union (select distinct RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ = '"+userName+"' ");
// sb.append(" )) v ");
// sb.append(" ) art ");
// sb.append(" on fd.PROCESS_INST_ID = art.PROC_INST_ID_ ");
// sb.append(" where fd.RELATION_CODE = '"+flowCode+"' ");
// return sb.toString();
// }
//
// /**
// * 查询待审批的单据
// * @param userName 用户账号 NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
// * @param flowCode 流程唯一编码
// * @param taskDefKey 任务key
// */
// public static String getMyTaskSql(String userName,String flowCode,String taskDefKey) {
// StringBuilder sb = new StringBuilder(" ");
// sb.append(" select fd.FORM_DATA_ID from EXT_ACT_FLOW_DATA fd ");
// sb.append(" RIGHT JOIN (");
// sb.append(" select * from (");
// sb.append(" (select distinct RES.* from ACT_RU_TASK RES inner join ACT_RU_IDENTITYLINK I on I.TASK_ID_ = RES.ID_ ");
// sb.append(" WHERE RES.ASSIGNEE_ is null and I.TYPE_ = 'candidate' ");
// sb.append(" and ( I.USER_ID_ = '"+userName+"' or I.GROUP_ID_ IN ( select g.GROUP_ID_ from ACT_ID_MEMBERSHIP g where g.USER_ID_ = '"+userName+"' ) ) ");
// sb.append(" and RES.SUSPENSION_STATE_ = 1 ");
// sb.append(" ) union (select distinct RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ = '"+userName+"' ");
// sb.append(" )) v ");
// sb.append(" ) art ");
// sb.append(" on fd.PROCESS_INST_ID = art.PROC_INST_ID_ ");
// sb.append(" where fd.RELATION_CODE = '"+flowCode+"' ");
// sb.append(" and art.TASK_DEF_KEY_ = '"+taskDefKey+"' ");
// return sb.toString();
// }
//
//
// /**
// * 查询我的审批的单据
// * @param userName 用户账号
// * @param flowCode 流程唯一编码
// * @param taskDefKey 任务key
// */
// public static String getMyRunningTaskSql(String userName,String flowCode) {
// StringBuilder sb = new StringBuilder(" ");
// sb.append(" select fd.FORM_DATA_ID from EXT_ACT_FLOW_DATA fd ");
// sb.append(" RIGHT JOIN (");
// sb.append(" select * from (");
// sb.append(" (select distinct RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ = '"+userName+"' ");
// sb.append(" )) v ");
// sb.append(" ) art ");
// sb.append(" on fd.PROCESS_INST_ID = art.PROC_INST_ID_ ");
// sb.append(" where fd.RELATION_CODE = '"+flowCode+"' ");
// return sb.toString();
// }
//
// /**
// * 查询待我签收的审批的单据
// * @param userName 用户账号
// * @param flowCode 流程唯一编码
// * @param taskDefKey 任务key
// */
// public static String getMyClaimTaskSql(String userName,String flowCode) {
// StringBuilder sb = new StringBuilder(" ");
// sb.append(" select fd.FORM_DATA_ID from EXT_ACT_FLOW_DATA fd ");
// sb.append(" RIGHT JOIN (");
// sb.append(" select * from (");
// sb.append(" (select distinct RES.* from ACT_RU_TASK RES inner join ACT_RU_IDENTITYLINK I on I.TASK_ID_ = RES.ID_ ");
// sb.append(" WHERE RES.ASSIGNEE_ is null and I.TYPE_ = 'candidate' ");
// sb.append(" and ( I.USER_ID_ = '"+userName+"' or I.GROUP_ID_ IN ( select g.GROUP_ID_ from ACT_ID_MEMBERSHIP g where g.USER_ID_ = '"+userName+"' ) ) ");
// sb.append(" and RES.SUSPENSION_STATE_ = 1 ");
// sb.append(" )");
// sb.append(" ) v ");
// sb.append(" ) art ");
// sb.append(" on fd.PROCESS_INST_ID = art.PROC_INST_ID_ ");
// sb.append(" where fd.RELATION_CODE = '"+flowCode+"' ");
// return sb.toString();
// }
//
//
//}
@@ -0,0 +1,135 @@
package org.jeecg.modules.bpm.util;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Iterator;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class XmlActivitiUtil {
/**
* 通过解析xml文件,判断该节点是否是会签节点
*
* @param resourceAsStream
* @param taskkey
* @throws Exception
*/
public static boolean parseXml(String doc, String taskkey) {
Element root = null;
try {
Document document = null;
try {
SAXReader reader = new SAXReader();
//解决报错:Dom4j中文异常处理:Invalid byte 2 of 2-byte UTF-8 sequence
reader.setEncoding("GBK");
InputStream inputStream = new ByteArrayInputStream(doc.toString().getBytes());
document = reader.read(inputStream);
} catch (Exception e) {
SAXReader reader = new SAXReader();
InputStream inputStream = new ByteArrayInputStream(doc.toString().getBytes());
document = reader.read(inputStream);
}
root = document.getRootElement();
for (Iterator iter = root.elementIterator(); iter.hasNext(); ) {
Element element = (Element) iter.next();
for (Iterator iterInner = element.elementIterator(); iterInner.hasNext(); ) {
Element elementInner = (Element) iterInner.next();
if ("userTask".equals(elementInner.getName())) {
//获取节点的id属性的值
Attribute leaderAttr = elementInner.attribute("id");
if (leaderAttr != null) {
if (leaderAttr.getValue().equals(taskkey)) {
Element e_adds = elementInner.element("multiInstanceLoopCharacteristics");
if (e_adds != null) {
return true;
}
}
}
}
//---update-begin-----autor:zhoujf------date:20190916-------for:子流程多实例判断--------
if ("callActivity".equals(elementInner.getName())) {
//获取节点的id属性的值
Attribute leaderAttr = elementInner.attribute("id");
if (leaderAttr != null) {
if (leaderAttr.getValue().equals(taskkey)) {
Element e_adds = elementInner.element("multiInstanceLoopCharacteristics");
if (e_adds != null) {
return true;
}
}
}
}
if ("subProcess".equals(elementInner.getName())) {
//获取节点的id属性的值
Attribute leaderAttr = elementInner.attribute("id");
if (leaderAttr != null) {
if (leaderAttr.getValue().equals(taskkey)) {
Element e_adds = elementInner.element("multiInstanceLoopCharacteristics");
if (e_adds != null) {
return true;
}
}
}
}
//---update-end-----autor:zhoujf------date:20190916-------for:子流程多实例判断--------
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 通过解析xml文件,判断该节点是否是并行网关
*
* @param doc 流程设计 XML
* @param taskkey 任务id
* @throws Exception
*/
public static boolean parseParallelGatewayFromXml(String doc, String taskkey) {
Element root = null;
try {
Document document = null;
try {
SAXReader reader = new SAXReader();
//解决报错:Dom4j中文异常处理:Invalid byte 2 of 2-byte UTF-8 sequence NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
reader.setEncoding("GBK");
InputStream inputStream = new ByteArrayInputStream(doc.toString().getBytes());
document = reader.read(inputStream);
} catch (Exception e) {
SAXReader reader = new SAXReader();
InputStream inputStream = new ByteArrayInputStream(doc.toString().getBytes());
document = reader.read(inputStream);
}
root = document.getRootElement();
for (Iterator iter = root.elementIterator(); iter.hasNext(); ) {
Element element = (Element) iter.next();
for (Iterator iterInner = element.elementIterator(); iterInner.hasNext(); ) {
Element elementInner = (Element) iterInner.next();
if ("parallelGateway".equals(elementInner.getName())) {
//update-begin---author:scott ---date:20220118 for[JTC-20、JTC-1229]【流程】多分支和同步网关同时配置时,选择其中一个分支,所有分支均执行-----
//获取节点的id属性的值
Attribute leaderAttr = elementInner.attribute("id");
if (leaderAttr != null) {
if (leaderAttr.getValue().equals(taskkey)) {
return true;
}
}
//update-end---author:scott ---date:20220118 for[JTC-20、JTC-1229]【流程】多分支和同步网关同时配置时,选择其中一个分支,所有分支均执行-----
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
@@ -0,0 +1,144 @@
//package org.jeecg.modules.designer.config;
//
//import org.springframework.boot.web.servlet.ServletRegistrationBean;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//
///**
// * 流程设计器JSP配置
// * @author scott
// *
// */
//@Configuration("moduleDesigerConfig")
//public class ModuleDesigerConfig {
//
// @Bean
// public ServletRegistrationBean eventProperties() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.eventProperties_jsp(), "/designer/eventProperties.jsp");
// }
// @Bean
// public ServletRegistrationBean addCandidateGroup() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.addCandidateGroup_jsp(), "/designer/addCandidateGroup.jsp");
// }
// @Bean
// public ServletRegistrationBean addCandidateUser() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.addCandidateUser_jsp(), "/designer/addCandidateUser.jsp");
// }
// @Bean
// public ServletRegistrationBean boundaryEventProperties() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.boundaryEventProperties_jsp(), "/designer/boundaryEventProperties.jsp");
// }
// @Bean
// public ServletRegistrationBean businessRuleTaskProperties() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.businessRuleTaskProperties_jsp(), "/designer/businessRuleTaskProperties.jsp");
// }
// @Bean
// public ServletRegistrationBean candidateGroupsConfig() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.candidateGroupsConfig_jsp(), "/designer/candidateGroupsConfig.jsp");
// }
// @Bean
// public ServletRegistrationBean candidateUsersConfig() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.candidateUsersConfig_jsp(), "/designer/candidateUsersConfig.jsp");
// }
// @Bean
// public ServletRegistrationBean errorBoundaryEventProperties() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.errorBoundaryEventProperties_jsp(), "/designer/errorBoundaryEventProperties.jsp");
// }
// @Bean
// public ServletRegistrationBean flowListenerConfig() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.flowListenerConfig_jsp(), "/designer/flowListenerConfig.jsp");
// }
// @Bean
// public ServletRegistrationBean flowProperties() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.flowProperties_jsp(), "/designer/flowProperties.jsp");
// }
// @Bean
// public ServletRegistrationBean gatewayProperties() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.gatewayProperties_jsp(), "/designer/gatewayProperties.jsp");
// }
// @Bean
// public ServletRegistrationBean index() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.index_jsp(), "/designer/index.jsp");
// }
// @Bean
// public ServletRegistrationBean listenerList() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.listenerList_jsp(), "/designer/listenerList.jsp");
// }
// @Bean
// public ServletRegistrationBean mailTaskProperties() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.mailTaskProperties_jsp(), "/designer/mailTaskProperties.jsp");
// }
// @Bean
// public ServletRegistrationBean processListenerConfig() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.processListenerConfig_jsp(), "/designer/processListenerConfig.jsp");
// }
// @Bean
// public ServletRegistrationBean processpro() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.processpro_jsp(), "/designer/processpro.jsp");
// }
// @Bean
// public ServletRegistrationBean processProperties() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.processProperties_jsp(), "/designer/processProperties.jsp");
// }
// @Bean
// public ServletRegistrationBean receiveTaskProperties() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.receiveTaskProperties_jsp(), "/designer/receiveTaskProperties.jsp");
// }
// @Bean
// public ServletRegistrationBean scriptTaskProperties() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.scriptTaskProperties_jsp(), "/designer/scriptTaskProperties.jsp");
// }
// @Bean
// public ServletRegistrationBean selectExpressionList() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.selectExpressionList_jsp(), "/designer/selectExpressionList.jsp");
// }
// @Bean
// public ServletRegistrationBean selectProcessListenerList() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.selectProcessListenerList_jsp(), "/designer/selectProcessListenerList.jsp");
// }
// @Bean
// public ServletRegistrationBean serviceTaskProperties() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.serviceTaskProperties_jsp(), "/designer/serviceTaskProperties.jsp");
// }
// @Bean
// public ServletRegistrationBean serviceTaskssdkes() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.taskMyProperties_jsp(), "/designer/taskMyProperties.jsp");
// }
// @Bean
// public ServletRegistrationBean subProcessProperties() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.subProcessProperties_jsp(), "/designer/subProcessProperties.jsp");
// }
// @Bean
// public ServletRegistrationBean taskFormConfig() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.taskFormConfig_jsp(), "/designer/taskFormConfig.jsp");
// }
// @Bean
// public ServletRegistrationBean taskListenerConfig() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.taskListenerConfig_jsp(), "/designer/taskListenerConfig.jsp");
// }
// @Bean
// public ServletRegistrationBean taskProperties() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.taskProperties_jsp(), "/designer/taskProperties.jsp");
// }
// @Bean
// public ServletRegistrationBean taskVariableConfig() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.taskVariableConfig_jsp(), "/designer/taskVariableConfig.jsp");
// }
// @Bean
// public ServletRegistrationBean subParameterList_jsp() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.subParameterList_jsp(), "/designer/subParameterList.jsp");
// }
// @Bean
// public ServletRegistrationBean searchSelectUserList_jsp() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.searchSelectUserList_jsp(), "/designer/searchSelectUserList.jsp");
// }
// @Bean
// public ServletRegistrationBean mytags() {
// return new ServletRegistrationBean(new org.apache.jsp.context.mytags_jsp(), "/context/mytags.jsp");
// }
// @Bean
// public ServletRegistrationBean flowMyProperties() {
// return new ServletRegistrationBean(new org.apache.jsp.designer.flowMyProperties_jsp(), "/designer/flowMyProperties.jsp");
// }
//
//}
@@ -0,0 +1,466 @@
package org.jeecg.modules.designer.controller;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StreamUtils;
import org.apache.commons.lang.StringUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.bpm.service.impl.ActProcessService;
import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
import org.jeecg.modules.extbpm.process.entity.*;
import org.jeecg.modules.extbpm.process.mapper.ExtActProcessMapper;
import org.jeecg.modules.extbpm.process.pojo.ListenerInfo;
import org.jeecg.modules.extbpm.process.pojo.RoleInfo;
import org.jeecg.modules.extbpm.process.pojo.UserInfo;
import org.jeecg.modules.extbpm.process.service.IExtActExpressionService;
import org.jeecg.modules.extbpm.process.service.IExtActListenerService;
import org.jeecg.modules.extbpm.process.service.IExtActProcessNodeService;
import org.jeecg.modules.extbpm.process.service.IExtActProcessService;
import org.jeecgframework.designer.util.DesUtils;
import org.jeecgframework.designer.vo.AjaxJson;
import org.jeecgframework.designer.vo.ProcessTypeVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
/**
* @ClassName: MockAPIController
* @Description: 流程设计器(设计器首页所需数据API接口、接受设计器请求数据)
* NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
* @author scott
* @date 2010-03-12
*
*/
@RestController("designerApiController")
@RequestMapping("/act/designer/api")
@Slf4j
//@RequestMapping("/designer/mock")
public class DesignerApiController {
@Autowired
private HistoryService historyService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private RepositoryService repositoryService;
@Autowired
private IExtActProcessService extActProcessService;
@Autowired
private IExtActProcessNodeService extActProcessNodeService;
@Autowired
private IExtActListenerService extActListenerService;
@Autowired
private IExtActExpressionService extActExpressionService;
@Autowired
private ExtActProcessMapper extActProcessMapper;
@Lazy
@Autowired
private ISysBaseAPI sysBaseAPI;
@Autowired
@Lazy
private RedisUtil redisUtil;
/**
* 流程编辑
*
* @param processId 流程ID
* @return
*/
@RequestMapping("/getProcessXml")
@ResponseBody
public void getProcessXml(HttpServletRequest request, HttpServletResponse response) {
response.setContentType("text/xml;charset=UTF-8");
String processId = oConvertUtils.getString(request.getParameter("processId"));
//String token = oConvertUtils.getString(request.getParameter("token"));
//log.info(" getProcessXml 登录令牌token "+token);
//校验 token 登录有效性
TokenUtils.verifyToken(request, sysBaseAPI, redisUtil);
ExtActProcess process = extActProcessService.getById(processId);
String retstr = null;
try {
retstr = StreamUtils.InputStreamTOString(StreamUtils.byteTOInputStream(process.getProcessXml()));
response.getWriter().write(retstr);
} catch (Exception e1) {
e1.printStackTrace();
}
}
/**
* 【新版流程预览】
* 通过流程实例ID获取流程设计xml(用于流程预览)
*
* @param processInstanceId 流程ID
* @return
*/
@RequestMapping("/getProcessXmlByInstanceId")
@ResponseBody
public void getProcessXmlByInstanceId(HttpServletRequest request, HttpServletResponse response) {
response.setContentType("text/xml;charset=UTF-8");
String processInstanceId = oConvertUtils.getString(request.getParameter("processInstanceId"));
//String token = oConvertUtils.getString(request.getParameter("token"));
//log.info(" getProcessXml 登录令牌token "+token);
//校验 token 登录有效性
TokenUtils.verifyToken(request, sysBaseAPI, redisUtil);
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
//ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processInstance.getProcessDefinitionId()).singleResult();
//获取BpmnModel对象
BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
//创建转换对象
BpmnXMLConverter converter = new BpmnXMLConverter();
//把bpmnModel对象转换成字符
byte[] bytes = converter.convertToXML(bpmnModel);
String xmlContenxt = new String(bytes, StandardCharsets.UTF_8);
// LambdaQueryWrapper<ExtActProcess> queryWrapper = new LambdaQueryWrapper<ExtActProcess>();
// queryWrapper.eq(ExtActProcess::getProcessKey, processDefinition.getKey());
// ExtActProcess process = extActProcessService.getOne(queryWrapper);
// String retstr = null;
try {
//retstr = StreamUtils.InputStreamTOString(StreamUtils.byteTOInputStream(process.getProcessXml()));
response.getWriter().write(xmlContenxt);
} catch (Exception e1) {
e1.printStackTrace();
}
}
/**
* 获取流程表达式列表
*
* @return
*/
@RequestMapping(value = "/getExpressions", method = { RequestMethod.POST, RequestMethod.GET })
public String getExpressions(HttpServletRequest request) {
String token = oConvertUtils.getString(request.getParameter("token"));
//表达式业务类型
String bizType = oConvertUtils.getString(request.getParameter("bizType"));
log.debug(" getExpressions 登录令牌token "+token);
// String jsonpath = "classpath:org/jeecgframework/designer/mock/getExpressions.json";
// return StringUtil.readJson(jsonpath);
//update-begin---author:scott ---date:2023-11-20 for:【QQYUN-7066】根据类型“指定人公式” ”候选人公式“,返回单人表达式或者多人表达式,以便于区分
LambdaQueryWrapper<ExtActExpression> queryWrapper = new LambdaQueryWrapper<ExtActExpression>();
if(oConvertUtils.isNotEmpty(bizType)){
queryWrapper.eq(ExtActExpression::getBizType, bizType);
}
List<ExtActExpression> list = extActExpressionService.list(queryWrapper);
//update-end---author:scott ---date:2023-11-20 for:【QQYUN-7066】根据类型“指定人公式” ”候选人公式“,返回单人表达式或者多人表达式,以便于区分
Map<String,Object> map = new HashMap<String,Object>();
map.put("total", list.size());
map.put("rows", list);
JSONObject json = new JSONObject(map);
log.debug(json.toJSONString());
return json.toJSONString();
}
/**
* 获取默认流程监听
*
* @return
*/
@RequestMapping(value = "/getDefaultListener", method = { RequestMethod.POST, RequestMethod.GET })
public String getDefaultListener(HttpServletRequest request) {
String token = oConvertUtils.getString(request.getParameter("token"));
log.debug(" getDefaultListener 登录令牌token "+token);
Map<String,Object> map = new HashMap<String,Object>();
Map<String,Object> listenerObj = new HashMap<String,Object>();
ExtActListener extActListener = extActListenerService.getById("402880e54803a496014805e5d9190012");
if(extActListener!=null) {
map.put("success", true);
map.put("msg", "成功获取默认监听器!");
listenerObj.put("id",extActListener.getId());
listenerObj.put("listenername",extActListener.getListenerName());
listenerObj.put("listenereven", extActListener.getListenerEvent());
listenerObj.put("listenertype", extActListener.getListenerValueType());
listenerObj.put("listenervalue", extActListener.getListenerValue());
}else {
map.put("success", false);
map.put("msg", "获取默认监听器失败");
}
map.put("obj", listenerObj);
JSONObject json = new JSONObject(map);
log.debug(json.toJSONString());
return json.toJSONString();
// String jsonpath = "classpath:org/jeecgframework/designer/mock/getDefaultListener.json";
// return StringUtil.readJson(jsonpath);
}
/**
* 获取流程角色组列表(也叫用户组列表)
*
* @return
*/
@RequestMapping(value = "/getGroups", method = { RequestMethod.POST, RequestMethod.GET })
public String getGroups(HttpServletRequest request) {
String token = oConvertUtils.getString(request.getParameter("token"));
log.debug(" getGroups 登录令牌token "+token);
//TODO 有token风险
//------------------------------------------------------------------------------------------------
Integer tenantId = null;
//是否开启系统管理模块的多租户数据隔离【SAAS多租户模式】
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL){
tenantId = oConvertUtils.getInt(TenantContext.getTenant(), 0);
}
//------------------------------------------------------------------------------------------------
List<RoleInfo> list = extActProcessService.getBpmRoles(tenantId);
Map<String,Object> map = new HashMap<String,Object>();
map.put("total", list.size());
map.put("rows", list);
JSONObject json = new JSONObject(map);
log.debug(json.toJSONString());
return json.toJSONString();
// String jsonpath = "classpath:org/jeecgframework/designer/mock/getGroups.json";
// return StringUtil.readJson(jsonpath);
}
/**
* 获取流程用户列表
*
* @return
*/
@RequestMapping(value = "/getUsers", method = { RequestMethod.POST, RequestMethod.GET })
public String getUsers(HttpServletRequest request) {
String token = oConvertUtils.getString(request.getParameter("token"));
log.debug(" getUsers 登录令牌token "+token);
//------------------------------------------------------------------------------------------------
Integer tenantId = null;
//是否开启系统管理模块的多租户数据隔离【SAAS多租户模式】
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL){
tenantId = oConvertUtils.getInt(TenantContext.getTenant(), 0);
}
//------------------------------------------------------------------------------------------------
//--update--begin------author:scott-----date:20210423-----for:【流程】用户数据多了以后就出现界面卡顿 LOWCOD-1488---------
//List<UserInfo> list = extActProcessService.getBpmUsers();
Page<UserInfo> page = new Page<UserInfo>(oConvertUtils.getInt(1), oConvertUtils.getInt(30));
Page<UserInfo> list = extActProcessService.getPageBpmUsers(null,null,page,tenantId);
Map<String,Object> map = new HashMap<String,Object>();
map.put("total", list.getTotal());
map.put("rows", list.getRecords());
//--update--end------author:scott-----date:20210423-----for:【流程】用户数据多了以后就出现界面卡顿 LOWCOD-1488-----------
JSONObject json = new JSONObject(map);
log.debug(json.toJSONString());
return json.toJSONString();
//String jsonpath = "classpath:org/jeecgframework/designer/mock/getUsers.json";
//return StringUtil.readJson(jsonpath);
}
/**
* 分页获取流程用户列表(popup分页列表)
*
* @return
*/
@RequestMapping(value = "/getPageUsers", method = { RequestMethod.POST, RequestMethod.GET })
public String getPageUsers(HttpServletRequest request) {
String token = oConvertUtils.getString(request.getParameter("token"));
log.debug(" getPageUsers 登录令牌token "+token);
//TODO 有token风险
Object pageNumber = request.getParameter("page"); //获取当前页码,easyui默认传到后台
Object pageSize = request.getParameter("rows"); //获取每页显示多少行,easyui默认传到后台
String account = request.getParameter("account"); //用户账号
String name = request.getParameter("name"); //用户昵称
//------------------------------------------------------------------------------------------------
Integer tenantId = null;
//是否开启系统管理模块的多租户数据隔离【SAAS多租户模式】
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL){
tenantId = oConvertUtils.getInt(TenantContext.getTenant(), 0);
}
//------------------------------------------------------------------------------------------------
log.debug("pageNumber: "+ oConvertUtils.getInt(pageNumber,1));
log.debug("pageSize: "+ oConvertUtils.getInt(pageSize,10));
log.debug("account: "+ account);
log.debug("name: "+ name);
Page<UserInfo> page = new Page<UserInfo>(oConvertUtils.getInt(pageNumber,1), oConvertUtils.getInt(pageSize,10));
Page<UserInfo> list = extActProcessService.getPageBpmUsers(account,name,page,tenantId);
Map<String,Object> map = new HashMap<String,Object>();
map.put("total", list.getTotal());
map.put("rows", list.getRecords());
JSONObject json = new JSONObject(map);
log.debug(json.toJSONString());
return json.toJSONString();
}
/**
* 目前流程监听数据,不能通过解析xml直接获取数据,所以需要同步维护xml和表关系 1. 目前可以通过流程解析,获取到流程监听的 ids 2.
* 然后通过ids ,再获取到流程监听数据列表
*
* @param id
* @return
* @return
*/
@RequestMapping("/getListenersByIds")
public String getRealListenerGrid(HttpServletRequest request, HttpServletResponse response) {
String token = oConvertUtils.getString(request.getParameter("token"));
log.debug(" getRealListenerGrid 登录令牌token "+token);
//TODO 有token风险
// 获取流程监听IDS
String ids = oConvertUtils.getString(request.getParameter("ids"));
String[] idsArr = ids.split(",");
List<ListenerInfo> list = extActListenerService.getListenerInfoByIds(idsArr);
Map<String,Object> map = new HashMap<String,Object>();
map.put("total", list.size());
map.put("rows", list);
JSONObject json = new JSONObject(map);
log.debug(json.toJSONString());
return json.toJSONString();
// return DesUtils.getMockDataRealListeners(ids);
}
/**
* 根据流程监听类型返回不同监听列表
*
* @param id
* @return
* @return
*/
@RequestMapping("/getListenersByType")
public String getListenersByType(HttpServletRequest request, HttpServletResponse response) {
String token = oConvertUtils.getString(request.getParameter("token"));
log.debug(" getListenersByType 登录令牌token "+token);
//TODO 有token风险
// 获取流程监听IDS
String typeid = oConvertUtils.getString(request.getParameter("typeid"));
log.debug(" 流程监听类型 typeid " + typeid);
List<ListenerInfo> list = extActListenerService.getListenerInfoByType(typeid);
Map<String,Object> map = new HashMap<String,Object>();
map.put("total", list.size());
map.put("rows", list);
JSONObject json = new JSONObject(map);
log.debug(json.toJSONString());
return json.toJSONString();
// TODO
// String jsonpath = "classpath:org/jeecgframework/designer/mock/listenersByType.json";
// return StringUtil.readJson(jsonpath);
}
/**
* 获取流程类型
*
* @return
*/
@RequestMapping(value = "/getTypes", method = { RequestMethod.POST, RequestMethod.GET })
public String getTypes(HttpServletRequest request) {
List<ProcessTypeVo> ls = extActProcessMapper.getDictItems("bpm_process_type");
List<Map> mapResult = ls.stream().map(p -> {
Map mp = new HashMap();
mp.put("value",p.getId());
mp.put("label",p.getTypename());
return mp;
}).collect(Collectors.toList());
Map<String,Object> map = new HashMap<String,Object>();
map.put("total", mapResult.size());
map.put("rows", mapResult);
JSONObject json = new JSONObject(map);
log.debug(json.toJSONString());
return json.toJSONString();
}
/**
* @return 导出流程
*/
@RequestMapping("/exportProcessDef")
@ResponseBody
public AjaxJson exportProcessDef(HttpServletRequest request, HttpServletResponse response) {
String token = oConvertUtils.getString(request.getParameter("token"));
log.debug(" exportProcessDef 登录令牌token "+token);
//TODO 有token风险
AjaxJson j = new AjaxJson();
String procdefId = oConvertUtils.getString(request.getParameter("procdefId"));
String processName = oConvertUtils.getString(request.getParameter("processName"));
LambdaQueryWrapper<ExtActProcess> queryWrapper = new LambdaQueryWrapper<ExtActProcess>();
queryWrapper.eq(ExtActProcess::getProcessKey, procdefId);
ExtActProcess tProcess = extActProcessService.getOne(queryWrapper);
// String token = oConvertUtils.getString(request.getParameter("token"),"NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=");
// //校验 token 登录有效性
// TokenUtils.verifyToken(request, sysBaseAPI, redisUtil);
try {
ByteArrayInputStream in = new ByteArrayInputStream(tProcess.getProcessXml());
String filename = procdefId + ".bpmn20.xml";
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + filename);
IOUtils.copy(in, response.getOutputStream());
response.flushBuffer();
j.setMsg("导出成功!");
in.close();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* @return 保存流程
*/
@RequiresPermissions("act:des:process:save")
@RequestMapping("/saveProcess")
@ResponseBody
public AjaxJson saveProcess(HttpServletRequest request) {
String token = oConvertUtils.getString(request.getParameter("token"));
log.debug(" exportProcessDef 登录令牌token "+token);
//TODO 有token风险
AjaxJson j = new AjaxJson();
try {
j = extActProcessService.saveProcess(request);
} catch (Exception e) {
e.printStackTrace();
j.setMsg(e.getMessage());
j.setSuccess(false);
}
return j;
}
}
@@ -0,0 +1,184 @@
package org.jeecg.modules.designer.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.extbpm.process.entity.ExtActProcess;
import org.jeecg.modules.extbpm.process.mapper.ExtActProcessMapper;
import org.jeecg.modules.extbpm.process.service.IExtActProcessService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
*
* @ClassName: DesignerController
* @Description: 流程设计器(负责页面跳转)
* @author scott
* @date 2010-03-12
*
*/
@Controller("designerController")
@RequestMapping("/act/designer")
public class DesignerController {
private static final Log logger = LogFactory.getLog(DesignerController.class);
@Autowired
private IExtActProcessService extActProcessService;
@Autowired
private ExtActProcessMapper extActProcessMapper;
@Lazy
@Autowired
private ISysBaseAPI sysBaseAPI;
@Autowired
@Lazy
private RedisUtil redisUtil;
/**
* 流程设计主页
*
* @return
*/
@RequestMapping
public void index(HttpServletRequest request,HttpServletResponse response) {
String processid = oConvertUtils.getString(request.getParameter("id"), "0");
String token = oConvertUtils.getString(request.getParameter("token"));
logger.info(" index 登录令牌token "+token);
//校验 token 登录有效性
TokenUtils.verifyToken(request, sysBaseAPI, redisUtil);
request.setAttribute("processid", processid);// 流程ID
request.setAttribute("token", token);// 登录人token令牌 NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
try {
request.getRequestDispatcher("/designer/index.jsp").forward(request,response);
}catch (Exception e) {
e.printStackTrace();
}
}
/**
* 流程设计属性页面跳转
*
* @return
* @throws IOException
* @throws ServletException
*/
@RequestMapping("processProperties")
public void processProperties(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
String turn = oConvertUtils.getString(request.getParameter("turn"));
String id = oConvertUtils.getString(request.getParameter("id"));// 流程当前节点Id
String checkbox = oConvertUtils.getString(request.getParameter("checkbox"));// 设置单选多选
String processId = oConvertUtils.getString(request.getParameter("processId"));// 流程ID
//TODO 查看流程属性是请求中流程id传参传给该属性了 后期需要优化
String processDefinitionId = oConvertUtils.getString(request.getParameter("processDefinitionId"));// 流程ID
logger.info(" 流程设计属性页面 turn " + turn + ",节点ID: " + id + ",checkbox: " + checkbox + ",processId: " + processId);
//设置流程类型
ExtActProcess process = extActProcessService.getById(processId);
if(process!=null) {
request.setAttribute("typeId", process.getProcessType());// 流程ID
}else {
process = extActProcessService.getById(processDefinitionId);
if(process!=null) {
request.setAttribute("typeId", process.getProcessType());// 流程ID
}else{
//默认测试流程
request.setAttribute("typeId", "test");
}
}
request.setAttribute("checkbox", checkbox);
request.setAttribute("id", id);
//流程设计器流程类型设置
request.setAttribute("proTypeList", extActProcessMapper.getDictItems("bpm_process_type"));
request.setAttribute("processId", processId);
request.getRequestDispatcher("/designer/" + turn+".jsp").forward(request,response);
}
/**
* 选择表达式页面
*
* @return
* @throws IOException
* @throws ServletException
*/
@RequestMapping("goExpression")
public void goExpression(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
String id = oConvertUtils.getString(request.getParameter("id"));// 流程当前节点Id
String checkbox = oConvertUtils.getString(request.getParameter("checkbox"));// 设置单选多选
String processId = oConvertUtils.getString(request.getParameter("processId"));// 流程ID
request.setAttribute("checkbox", checkbox);
request.setAttribute("id", id);
request.setAttribute("processId", processId);
String token = oConvertUtils.getString(request.getParameter("token"));
logger.debug(" index 登录令牌token "+token);
request.setAttribute("token", token);// 登录人token令牌
request.getRequestDispatcher("/designer/selectExpressionList.jsp").forward(request,response);
}
/**
* popup选择用户列表页面
*
* @return
*/
@RequestMapping("goSearchSelectUser")
public void goSearchSelectUser(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
String id = oConvertUtils.getString(request.getParameter("id"));// 流程当前节点Id
String checkbox = oConvertUtils.getString(request.getParameter("checkbox"));// 设置单选多选
String processId = oConvertUtils.getString(request.getParameter("processId"));// 流程ID
request.setAttribute("checkbox", checkbox);
request.setAttribute("id", id);
request.setAttribute("processId", processId);
String token = oConvertUtils.getString(request.getParameter("token"));
logger.debug(" index 登录令牌token "+token);
request.setAttribute("token", token);// 登录人token令牌
request.getRequestDispatcher("/designer/searchSelectUserList.jsp").forward(request,response);
}
/**
* 跳转到子流程参数配置页面
*
* @return
*/
@RequestMapping("goSubParameterList")
public void subParameterList(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
String id = oConvertUtils.getString(request.getParameter("id"));// 流程当前节点Id
String checkbox = oConvertUtils.getString(request.getParameter("checkbox"));// 设置单选多选
String processId = oConvertUtils.getString(request.getParameter("processId"));// 流程ID
request.setAttribute("checkbox", checkbox);
request.setAttribute("id", id);
request.setAttribute("processId", processId);
request.getRequestDispatcher("/designer/subParameterList.jsp").forward(request,response);
}
/**
* 跳转到监听列表页面
*
* @return
* @throws IOException
* @throws ServletException
*/
@RequestMapping("goListeners")
public void goListeners(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
// 获取流程监听IDS
String typeid = oConvertUtils.getString(request.getParameter("typeid"));
logger.info(" 流程监听类型 typeid " + typeid);
request.setAttribute("typeid", typeid);
String token = oConvertUtils.getString(request.getParameter("token"));
logger.debug(" index 登录令牌token "+token);
request.setAttribute("token", token);// 登录人token令牌
request.getRequestDispatcher("/designer/selectProcessListenerList.jsp").forward(request,response);
}
}
@@ -0,0 +1,126 @@
package org.jeecg.modules.designer.controller;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jeecg.common.constant.TenantConstant;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.extbpm.process.entity.ExtActProcess;
import org.jeecg.modules.extbpm.process.service.IExtActProcessService;
import org.jeecg.modules.extbpm.util.ViewFreemarker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* 流程设计器 NEW
* @Author jeecg
* @since 2021-10-27
*/
@Controller("designerNewController")
@RequestMapping("/act/designer")
@Slf4j
public class DesignerNewController {
private static final Log logger = LogFactory.getLog(DesignerNewController.class);
@Lazy
@Autowired
private ISysBaseAPI sysBaseAPI;
@Autowired
@Lazy
private RedisUtil redisUtil;
@Autowired
private IExtActProcessService extActProcessService;
/**
* 打开流程设计界面
*
* @param request
* @param response
* @param id
* @param token
* @throws Exception
*/
@RequestMapping("/index")
public void designer(HttpServletRequest request, HttpServletResponse response,
@RequestParam(name="id",required = false,defaultValue = "") String id,
@RequestParam(name="token",required = false,defaultValue = "") String token) throws Exception{
Map content = new HashMap<String,Object>(5);
content.put("id",id);
content.put("token",token);
//支持应用ID的设置 20220908
String lowAppId = request.getParameter(TenantConstant.FIELD_LOW_APP_ID);
if(oConvertUtils.isNotEmpty(lowAppId)){
content.put(TenantConstant.FIELD_LOW_APP_ID,lowAppId);
}
//支持租户ID的设置 20221229
String tenantId = TokenUtils.getTenantIdByRequest(request);
content.put(TenantConstant.TENANT_ID, tenantId);
//base url
String domianURL= this.getBaseUrl(request);
content.put("domianURL",domianURL);
if(oConvertUtils.isNotEmpty(id)){
//流程类型
ExtActProcess process = extActProcessService.getById(id);
content.put("typeid", oConvertUtils.getString(process.getProcessType(),"test"));
}else{
content.put("typeid","test");
}
logger.info(" index 登录令牌token "+token);
//校验 token 登录有效性
TokenUtils.verifyToken(request, sysBaseAPI, redisUtil);
//跳转 ftl页面
ViewFreemarker.view(request, response, "templates/desflow/designer.ftl", content);
}
/**
* 获取服务器地址
*
* @param request
* @return
*/
private String getBaseUrl(HttpServletRequest request) {
//1.【微服务环境下】用户、部门等接口无法获取数据问题--------
String x_gateway_base_path = request.getHeader("X_GATEWAY_BASE_PATH");
if(oConvertUtils.isNotEmpty(x_gateway_base_path)){
log.info("x_gateway_base_path = "+ x_gateway_base_path);
return x_gateway_base_path;
}
//2. SSL认证之后,request.getScheme()获取不到https的问题记录
// https://blog.csdn.net/weixin_34376986/article/details/NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
String scheme = request.getHeader("X-Forwarded-Scheme");
if(oConvertUtils.isEmpty(scheme)){
scheme = request.getScheme();
}
//3.常规操作
String serverName = request.getServerName();
int serverPort = request.getServerPort();
String contextPath = request.getContextPath();
//返回 host domain
String baseDomainPath = null;
if(80 == serverPort){
baseDomainPath = scheme + "://" + serverName + contextPath ;
}else{
baseDomainPath = scheme + "://" + serverName + ":" + serverPort + contextPath ;
}
log.info("================getBaseUrl==============: " + baseDomainPath);
return baseDomainPath;
}
}
@@ -0,0 +1,54 @@
package org.jeecg.modules.extbpm.enums;
/**
* 流程节点消息类型
* @author: jeecg-boot
*/
public enum TaskTipTypeEnum {
XT("system", "bpm_system"),
YJ("email", "bpm_email"),
DD("dingtalk", "bpm_dingtalk"),
QYWX("wechat_enterprise", "bpm_wechat_enterprise");
TaskTipTypeEnum(String type, String code){
this.type = type;
this.code = code;
}
/**
* 消息类型
*/
String type;
/**
* 模板编码
*/
String code;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public static String getCodeByType(String type) {
for (TaskTipTypeEnum e : TaskTipTypeEnum.values()) {
if (type.startsWith(e.getType())) {
return e.getCode();
}
}
return null;
}
}
@@ -0,0 +1,105 @@
package org.jeecg.modules.extbpm.job;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.TaskService;
import org.jeecg.common.api.dto.message.BusTemplateMessageDTO;
import org.jeecg.common.constant.CommonSendStatus;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.constant.enums.SysAnnmentTypeEnum;
import org.jeecg.modules.bpm.dto.ActRuTaskDTO;
import org.jeecg.modules.bpm.mapper.ActivitiMapper;
import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
import org.jeecgframework.core.util.ApplicationContextUtil;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.*;
/**
* @Author scott
* @Date 2019/11/7 18:30
* @Description:
*/
@Slf4j
public class TimeOutTask implements Job {
private static TaskService taskService;
private static ISysBaseAPI sysBaseAPI;
private static ActivitiMapper activitiMapper;
static {
taskService = ApplicationContextUtil.getContext().getBean(TaskService.class);
sysBaseAPI = ApplicationContextUtil.getContext().getBean(ISysBaseAPI.class);
activitiMapper = ApplicationContextUtil.getContext().getBean(ActivitiMapper.class);
}
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
try {
log.info("----------【定时任务】--任务超时提醒开始--------------");
List<ActRuTaskDTO> list = activitiMapper.getTimeoutTask();
if (list != null && list.size() > 0) {
taskDeal(list);
}
log.info("-----------【定时任务】--任务超时提醒结束---------------");
} catch (Exception e) {
e.printStackTrace();
}
}
private void taskDeal(List<ActRuTaskDTO> list) {
try {
for (ActRuTaskDTO actRuTaskDTO : list) {
//超时时间
int nodeTimeout = actRuTaskDTO.getNodeTimeout();
//流程发起时间
Date startTime = actRuTaskDTO.getCreateTime();
if (checkTimeOut(startTime, nodeTimeout)) {
log.info("-------------任务超时提醒-----startTime=" + startTime.toString()+",nodeTimeout="+nodeTimeout);
String taskId = actRuTaskDTO.getId();
String bpmBizTitle = (String) taskService.getVariable(taskId, WorkFlowGlobals.BPM_BIZ_TITLE);
Map<String, String> mp = new HashMap<>();
mp.put("title",bpmBizTitle);
mp.put("task",actRuTaskDTO.getName());
mp.put("user",actRuTaskDTO.getAssignee());
mp.put("time",DateUtils.formatDateTime());
//系统通知
BusTemplateMessageDTO msg = new BusTemplateMessageDTO("流程通知", actRuTaskDTO.getAssignee(),"任务超时提醒",mp,CommonSendStatus.TZMB_BPM_CHAOSHI_TIP, SysAnnmentTypeEnum.BPM.getType(),taskId);
sysBaseAPI.sendBusTemplateAnnouncement(msg);
//sysBaseAPI.sendSysAnnouncement("系统", (String) map.get("ASSIGNEE_"), "任务超时提醒", sb.toString());
//短信通知接口
//邮件通知接口
//TODO
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 比较时间大小
* @param startTime
* @param timeout
* @return
*/
private boolean checkTimeOut(Date startTime, Integer timeout) {
boolean flag = false;
try {
if (timeout == null || startTime == null) {
return flag;
}
Calendar calSrc = Calendar.getInstance();
Calendar calDes = Calendar.getInstance();
calDes.setTime(startTime);
int diff = DateUtils.dateDiff('h', calSrc, calDes);
if (diff >= timeout) {
flag = true;
}
} catch (Exception e) {
}
return flag;
}
}
@@ -0,0 +1,147 @@
package org.jeecg.modules.extbpm.job;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.NativeTaskQuery;
import org.flowable.task.api.Task;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.modules.bpm.dto.UserAgentDTO;
import org.jeecg.modules.bpm.service.ActivitiService;
import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
import org.jeecg.modules.extbpm.process.entity.ExtActBpmLog;
import org.jeecg.modules.extbpm.process.service.IExtActBpmLogService;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
import java.util.List;
/**
* 用户代理任务
*
* @author zhoujf
*/
@Slf4j
public class UserAgentJob implements Job {
private static TaskService taskService;
private static ActivitiService activitiService;
private static IExtActBpmLogService extActBpmLogService;
protected static RuntimeService runtimeService;
static {
taskService = SpringContextUtils.getBean(TaskService.class);
activitiService = SpringContextUtils.getBean(ActivitiService.class);
extActBpmLogService = SpringContextUtils.getBean(IExtActBpmLogService.class);
runtimeService = SpringContextUtils.getBean(RuntimeService.class);
}
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info(String.format(" Jeecg-Boot 用户代理任务 UserAgentJob ! 时间:" + DateUtils.getTimestamp()));
try {
//查询有代理设置的用户
String currDateStr = DateUtils.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss");
Date currDate = DateUtils.parseDate(currDateStr, "yyyy-MM-dd HH:mm:ss");
List<UserAgentDTO> userAgentList = activitiService.getUserAgent(currDate);
for(UserAgentDTO agent:userAgentList) {
//log.info("--------任务处理人:"+agent.getUserName()+",代办人:"+agent.getAgentUserName());
taskDeal(agent);
}
} catch (Exception e) {
log.error("Jeecg-Boot 用户代理任务 UserAgentJob 异常"+e.getMessage(),e);
}
}
private void taskDeal(UserAgentDTO agent){
//1、查用户的待处理的任务
StringBuilder sb = new StringBuilder("");
sb.append("select * ").append("from (");
//---update-begin-----autor:zhoujf------date:20190916-------for:未签收的任务不进行委派--------
//sb.append("(select distinct RES.* ");
//sb.append(" from ACT_RU_TASK RES inner join ACT_RU_IDENTITYLINK I on I.TASK_ID_ = RES.ID_ ");
//sb.append("WHERE RES.ASSIGNEE_ is null and I.TYPE_ = 'candidate' ");
//sb.append(" and ( I.USER_ID_ = #{userid} or I.GROUP_ID_ IN ( select g.GROUP_ID_ from ACT_ID_MEMBERSHIP g where g.USER_ID_ = #{userid} ) ");
//sb.append(" ) ").append(" and RES.SUSPENSION_STATE_ = 1 ");
//sb.append(") union ");
//---update-end-----autor:zhoujf------date:20190916-------for:未签收的任务不进行委派--------
sb.append("(select distinct RES.* ");
sb.append(" from ACT_RU_TASK RES ");
sb.append("WHERE RES.ASSIGNEE_ = #{userid} ");
sb.append(" )) v ");
// sb.append(" order by v.CREATE_TIME_ desc, v.PRIORITY_ desc ");
NativeTaskQuery query = taskService.createNativeTaskQuery()
.sql(sb.toString())
.parameter("userid", agent.getUserName());
List<Task> pretasks = query.list();
//2、任务委派给代理人
for(Task task:pretasks){
log.info("-------------任务id:"+task.getId()+",taskName:"+task.getName());
try{
//---update-begin-----autor:zhoujf------date:20190916-------for:未签收的任务不进行委派--------
//判断任务是否签收没有签收需要进行签收
//if(oConvertUtils.isEmpty(task.getAssignee())) {
// taskService.claim(task.getId(), agent.getAgentUserName());
//}
//---update-end-----autor:zhoujf------date:20190916-------for:未签收的任务不进行委派--------
this.trunTask(task.getId(), agent.getAgentUserName());
String description = ""+task.getAssignee()==null?"系统":task.getAssignee()+" 转办任务【"+task.getName()+"】给 "+agent.getAgentUserName()+" 办理";
String processInstanceId = task.getProcessInstanceId();
//保存审批意见
this.saveBpmLog(processInstanceId, description, task, agent);
}catch(Exception e){
log.error("Jeecg-Boot 用户代理任务 UserAgentJob 异常"+e.getMessage(),e);
}
}
}
/**
* 转办
*
* @param taskId
* @param assignee
* @return
*/
public boolean trunTask(String taskId, String assignee) {
taskService.setAssignee(taskId, assignee);
return true;
}
/**
* 保存审批日志
*
* @param processInstanceId
* @param description
* @param task
* @param agent
*/
private void saveBpmLog(String processInstanceId,String description,Task task,UserAgentDTO agent){
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
ExtActBpmLog bpmlog = new ExtActBpmLog();
if(processInstance!=null) {
bpmlog.setBusinessKey(processInstance.getBusinessKey());
bpmlog.setProcName(processInstance.getName());
}
bpmlog.setOpTime(new Date());
bpmlog.setOpUserId(WorkFlowGlobals.SYS_AGENT_JOB_OPUSER_ID);
bpmlog.setOpUserName("系统任务");
bpmlog.setProcInstId(processInstanceId);
bpmlog.setRemarks(description);
bpmlog.setTaskDefKey(task.getTaskDefinitionKey());
bpmlog.setTaskId(task.getId());
bpmlog.setTaskName(task.getName());
extActBpmLogService.save(bpmlog);
String msg = "用户"+task.getName()+"[任务,id:"+task.getId()+",taskName:"+task.getName()+"]委派给"+ agent.getAgentUserName();
log.info(msg);
}
}
@@ -0,0 +1,162 @@
/**
* @Project: jeecg
* @Title: ProcessEndListener.java
* @Package com.oa.manager.workFlow.listener.execution
* NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
* @date 2013-8-16 下午2:04:12
* @Copyright: 2013
*/
package org.jeecg.modules.extbpm.listener.execution;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
import org.jeecg.modules.extbpm.process.entity.ExtActFlowData;
import org.jeecg.modules.extbpm.process.mapper.ExtActFlowDataMapper;
import org.jeecg.modules.extbpm.process.mapper.ExtActProcessMapper;
import org.jeecg.modules.extbpm.process.service.IExtActFormDataService;
/**
* 类名:ProcessEndListener
* 功能:流程实例结束监听器
* @author zhoujf
*
*/
@Slf4j
public class ProcessEndListener implements ExecutionListener{
private static final long serialVersionUID = 1L;
@Override
public void notify(DelegateExecution execution) {
//修改流程状态
doUpdateStatus(execution);
// //清空流程实例所有历史流程变量,任务变量
// HistoryService historyService = SpringContextUtils.getBean(HistoryService.class);
// List<HistoricVariableInstance> hvis = historyService.createHistoricVariableInstanceQuery().processInstanceId(execution.getProcessInstanceId()).list();
//
// for(HistoricVariableInstance h:hvis){
// //log.info(h.getVariableName());
// //流程对应的表单业务标题,不清空
// if(WorkFlowGlobals.BPM_DATA_ID.equals(h.getVariableName())){
// continue;
// }
// if(WorkFlowGlobals.BPM_FORM_KEY.equals(h.getVariableName())){
// continue;
// }
// if(WorkFlowGlobals.BPM_BIZ_TITLE.equals(h.getVariableName())){
// continue;
// }
// //流程办理风格,不清空
// if(WorkFlowGlobals.BPM_PROC_DEAL_STYLE.equals(h.getVariableName())){
// continue;
// }
// //流程对应的表单页面,不清空
// if(!WorkFlowGlobals.BPM_FORM_CONTENT_URL.equals(h.getVariableName()) && !WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE.equals(h.getVariableName())){
// //TODO 待测试
// ((HistoricVariableInstanceEntity)h).setDeleted(true);
// }
// }
log.info(" -- 流程结束监听执行完成 -- ");
}
private void doUpdateStatus(DelegateExecution execution){
String businessKey = execution.getProcessInstanceBusinessKey();
log.info("流程结束监听,处理businessKey = {}",businessKey );
if(oConvertUtils.isEmpty(businessKey)) {
return;
}
ExtActProcessMapper extActProcessMapper= SpringContextUtils.getBean(ExtActProcessMapper.class);
ExtActFlowDataMapper extActFlowDataMapper= SpringContextUtils.getBean(ExtActFlowDataMapper.class);
IExtActFormDataService extActFormDataService= SpringContextUtils.getBean(IExtActFormDataService.class);
String desFormCode = oConvertUtils.getString(execution.getVariable(WorkFlowGlobals.BPM_DES_FORM_CODE));
String bpmDesDataId = oConvertUtils.getString(execution.getVariable(WorkFlowGlobals.BPM_DES_DATA_ID));
String bpmStatus=(String)execution.getVariable(WorkFlowGlobals.BPM_STATUS);//获取流程状态
// String bpm_form_key = (String)execution.getVariable(WorkFlowGlobals.BPM_FORM_KEY);//获取流程对应的表单
// String id = (String)execution.getVariable(WorkFlowGlobals.BPM_DATA_ID);//获取表单数据id
// String formType = (String)execution.getVariable(WorkFlowGlobals.BPM_FORM_TYPE);//获取表单类型
if(WorkFlowGlobals.PROCESS_CALLBACKPROCESS_STATUS.equals(bpmStatus)){
//流程追回逻辑
LambdaQueryWrapper<ExtActFlowData> queryWrapper = new LambdaQueryWrapper<ExtActFlowData>();
queryWrapper.eq(ExtActFlowData::getFormDataId, businessKey);
queryWrapper.eq(ExtActFlowData::getProcessInstId, execution.getProcessInstanceId());
ExtActFlowData extActFlowData = extActFlowDataMapper.selectOne(queryWrapper);
//更新表单状态
if(extActFlowData!=null) {
String tableName = extActFlowData.getFormTableName().toUpperCase();
if(oConvertUtils.isEmpty(desFormCode) || WorkFlowGlobals.DESIGN_FORM_DRAFT_TABLE.equals(tableName)){
//变更物理表 流程状态
String col = extActFlowData.getBpmStatusField();
if(oConvertUtils.isEmpty(col)) {
col = "BPM_STATUS";
}
extActProcessMapper.updateBpmStatusById(tableName, businessKey, col.toUpperCase(), WorkFlowGlobals.BPM_BUS_STATUS_1);
}
if(oConvertUtils.isNotEmpty(desFormCode)){
//变更设计器表单和映射的,online表单的流程状态
extActFormDataService.updateDesformAndOnlineDataStatus(desFormCode,bpmDesDataId,extActFlowData.getBpmStatusField(), WorkFlowGlobals.BPM_BUS_STATUS_1);
}
extActFlowDataMapper.deleteById(extActFlowData.getId());
}
}else if(WorkFlowGlobals.PROCESS_INVALIDPROCESS_STATUS.equals(bpmStatus)){
LambdaQueryWrapper<ExtActFlowData> queryWrapper = new LambdaQueryWrapper<ExtActFlowData>();
queryWrapper.eq(ExtActFlowData::getFormDataId, businessKey);
queryWrapper.eq(ExtActFlowData::getProcessInstId, execution.getProcessInstanceId());
ExtActFlowData extActFlowData = extActFlowDataMapper.selectOne(queryWrapper);
//更新表单状态
if(extActFlowData!=null) {
String tableName = extActFlowData.getFormTableName().toUpperCase();
log.debug("-------tableName--------"+ tableName);
if(oConvertUtils.isEmpty(desFormCode) || WorkFlowGlobals.DESIGN_FORM_DRAFT_TABLE.equals(tableName)){
//变更物理表 流程状态
String col = extActFlowData.getBpmStatusField().toUpperCase();
if(oConvertUtils.isEmpty(col)) {
col = "BPM_STATUS";
}
extActProcessMapper.updateBpmStatusById(tableName, businessKey, col, WorkFlowGlobals.BPM_BUS_STATUS_4);
}
if(oConvertUtils.isNotEmpty(desFormCode)){
//变更设计器表单和映射的,online表单的流程状态
extActFormDataService.updateDesformAndOnlineDataStatus(desFormCode,bpmDesDataId,extActFlowData.getBpmStatusField(), WorkFlowGlobals.BPM_BUS_STATUS_4);
}
extActFlowData.setBpmStatus(WorkFlowGlobals.BPM_BUS_STATUS_4);
extActFlowDataMapper.updateById(extActFlowData);
}
}else {
LambdaQueryWrapper<ExtActFlowData> queryWrapper = new LambdaQueryWrapper<ExtActFlowData>();
queryWrapper.eq(ExtActFlowData::getFormDataId, businessKey);
queryWrapper.eq(ExtActFlowData::getProcessInstId, execution.getProcessInstanceId());
ExtActFlowData extActFlowData = extActFlowDataMapper.selectOne(queryWrapper);
//1.更新表单状态
if(extActFlowData!=null) {
String tableName = extActFlowData.getFormTableName().toUpperCase();
if(oConvertUtils.isEmpty(desFormCode) || WorkFlowGlobals.DESIGN_FORM_DRAFT_TABLE.equals(tableName)){
//变更物理表 流程状态
String col = extActFlowData.getBpmStatusField().toUpperCase();
if(oConvertUtils.isEmpty(col)) {
col = "BPM_STATUS";
}
extActProcessMapper.updateBpmStatusById(tableName, businessKey, col, WorkFlowGlobals.BPM_BUS_STATUS_3);
}
if(oConvertUtils.isNotEmpty(desFormCode)){
//变更设计器表单和映射的,online表单的流程状态
extActFormDataService.updateDesformAndOnlineDataStatus(desFormCode,bpmDesDataId,extActFlowData.getBpmStatusField(),WorkFlowGlobals.BPM_BUS_STATUS_3);
}
extActFlowData.setBpmStatus(WorkFlowGlobals.BPM_BUS_STATUS_3);
extActFlowDataMapper.updateById(extActFlowData);
}
//2.修改流程中的流程变量为已完成
execution.setVariable(WorkFlowGlobals.BPM_STATUS,WorkFlowGlobals.BPM_BUS_STATUS_3);
}
}
}
@@ -0,0 +1,195 @@
/**
* @Project: jeecg
* @Title: ProcessEndListener.java
* @Package com.oa.manager.workFlow.listener.execution
* @date 2013-8-16 下午2:04:12
* @Copyright: 2013
*/
package org.jeecg.modules.extbpm.listener.execution;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import liquibase.pro.packaged.U;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceQuery;
import org.jeecg.common.constant.DataBaseConstant;
import org.jeecg.common.desform.api.IDesformBaseApi;
import org.jeecg.common.desform.vo.DesignFormDataVo;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.bpm.service.ActivitiService;
import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
import org.jeecg.modules.extbpm.process.entity.ExtActProcess;
import org.jeecg.modules.extbpm.process.entity.ExtActProcessForm;
import org.jeecg.modules.extbpm.process.mapper.ExtActProcessFormMapper;
import org.jeecg.modules.extbpm.process.mapper.ExtActProcessMapper;
import org.jeecg.modules.extbpm.process.service.IExtActProcessService;
import org.springframework.beans.factory.annotation.Autowired;
import java.sql.Blob;
import java.sql.Clob;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 【简流】
* 类名:SignalProcessStartListener
* 功能:信号启动流程监听
*
* @author scott
*/
@Slf4j
public class SignalProcessStartListener implements ExecutionListener {
private static final long serialVersionUID = 1L;
private static ExtActProcessMapper extActProcessMapper;
private static ExtActProcessFormMapper extActProcessFormMapper;
private static IDesformBaseApi desformBaseApi;
private static IExtActProcessService extActProcessService;
private static RuntimeService runtimeService;
static {
extActProcessMapper = SpringContextUtils.getBean(ExtActProcessMapper.class);
extActProcessFormMapper = SpringContextUtils.getBean(ExtActProcessFormMapper.class);
desformBaseApi = SpringContextUtils.getBean(IDesformBaseApi.class);
extActProcessService = SpringContextUtils.getBean(IExtActProcessService.class);
runtimeService = SpringContextUtils.getBean(RuntimeService.class);
}
/**
* 这里可以加逻辑,定义给子流程传递变量
*
* @param execution
*/
@Override
public void notify(DelegateExecution execution) {
//接口参数
String id = oConvertUtils.getString(execution.getVariable(WorkFlowGlobals.BPM_DATA_ID));
String tableName = oConvertUtils.getString(execution.getVariable(WorkFlowGlobals.BPM_FORM_KEY));
//表单类型 —— 1:Online表单,2:设计器表单,3:自定义开发
String bpmFormType = oConvertUtils.getString(execution.getVariable(WorkFlowGlobals.BPM_FORM_TYPE));
String formUrl = oConvertUtils.getString(execution.getVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL));
String formUrlMobile = oConvertUtils.getString(execution.getVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE));
String username = oConvertUtils.getString(execution.getVariable(WorkFlowGlobals.APPLY_USER_ID));
String processInstanceId = execution.getProcessInstanceId();
//更新业务data id
runtimeService.updateBusinessKey(processInstanceId,id);
String processDefinitionKey = null;
String processDefinitionName = null;
log.info("data id = {}", id);
log.info("tableName = {}", tableName);
log.info("bpmFormType = {}", bpmFormType);
log.info("processInstanceId = {}", processInstanceId);
try {
ProcessInstance processInstance = (ProcessInstance)execution;
processDefinitionKey = processInstance.getProcessDefinitionKey();
processDefinitionName = processInstance.getProcessDefinitionName();
log.info("getProcessDefinitionKey = {}", processDefinitionKey);
log.info("getProcessDefinitionName = {}", processDefinitionName);
} catch (Exception e) {
e.printStackTrace();
}
//获取流程设计配置
LambdaQueryWrapper<ExtActProcess> queryWrapper = new LambdaQueryWrapper<ExtActProcess>();
queryWrapper.eq(ExtActProcess::getProcessKey, processDefinitionKey);
ExtActProcess extActProcess = extActProcessMapper.selectOne(queryWrapper);
log.info("process Des Id = {}", extActProcess.getId());
//查询流程业务关联配置
ExtActProcessForm extActProcessForm = null;
LambdaQueryWrapper<ExtActProcessForm> extActProcessFormQueryWrapper = new LambdaQueryWrapper<ExtActProcessForm>();
extActProcessFormQueryWrapper.eq(ExtActProcessForm::getFormTableName, tableName);
extActProcessFormQueryWrapper.eq(ExtActProcessForm::getProcessId, extActProcess.getId());
extActProcessForm = extActProcessFormMapper.selectOne(extActProcessFormQueryWrapper);
Map<String, Object> dataForm = null;
if (WorkFlowGlobals.BPM_FORM_TYPE_2.equals(bpmFormType)) {
dataForm = this.getJsonDataById(tableName, id, execution);
dataForm.put(WorkFlowGlobals.BPM_DES_DATA_ID, id);
dataForm.put(WorkFlowGlobals.BPM_DES_FORM_CODE, tableName);
} else {
dataForm = this.getDataById(tableName, id);
}
if (dataForm == null) {
dataForm = new HashMap<String, Object>();
}
//获取数据创建人
if(oConvertUtils.isEmpty(username)){
username = oConvertUtils.getString(dataForm != null ? dataForm.get(DataBaseConstant.CREATE_BY_TABLE) : null);
}
log.info("获取登录人: {}" , username);
dataForm.put(WorkFlowGlobals.BPM_DATA_ID, id);
dataForm.put(WorkFlowGlobals.BPM_FORM_CONTENT_URL, formUrl);
if (oConvertUtils.isNotEmpty(formUrlMobile)) {
dataForm.put(WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE, formUrlMobile);
} else {
//没有传移动端表单,默认用PC端
dataForm.put(WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE, formUrl);
}
dataForm.put(WorkFlowGlobals.BPM_FORM_KEY, tableName);
extActProcessService.startSignalMutilProcess(username, id, dataForm, extActProcessForm, execution);
}
public Map<String, Object> getJsonDataById(String desFormCode, String id, DelegateExecution execution) {
Map<String, Object> result = new HashMap<>();
//TODO 设计器表单改成 mongdb后,查询不到数据报错临时方案
//Map<String, Object> desformData = extActProcessMapper.getJsonDataById(id);
DesignFormDataVo designFormDataVo = desformBaseApi.getTranslateDataById(desFormCode, id);
// 如果是删除数据走流程,从mongo查不到数据,再从流程变量中查询一下
if (designFormDataVo == null || designFormDataVo.getDesformData() == null) {
JSONObject bpmDeleteData = execution.getVariable(WorkFlowGlobals.BPM_DELETE_DATA, JSONObject.class);
if (oConvertUtils.isNotEmpty(bpmDeleteData)) {
designFormDataVo = JSONObject.toJavaObject(bpmDeleteData , DesignFormDataVo.class);
}
}
if (designFormDataVo != null && designFormDataVo.getDesformData()!=null) {
for (Map.Entry<String, Object> entry : designFormDataVo.getDesformData().entrySet()) {
//update-begin--- author:scott---date:20201227------for:JT-250【流程】特殊字段 发起流程失败------
//大字段处理
if (entry.getValue() != null && (entry.getValue() instanceof Clob || entry.getValue() instanceof Blob)) {
continue;
}
//update-end--- author:scott---date:20201227------for:JT-250【流程】特殊字段 发起流程失败------
result.put(entry.getKey().toLowerCase(), entry.getValue());
}
}else{
//删除走流程此实体会为空
result.put("id", id);
}
return result;
}
public Map<String, Object> getDataById(String tableName, String id) {
Map<String, Object> result = new HashMap<>();
Map<String, Object> data = extActProcessMapper.getDataById(tableName, id);
for (Map.Entry<String, Object> entry : data.entrySet()) {
//update-begin--- author:scott---date:20201227------for:JT-250【流程】特殊字段 发起流程失败------
//大字段处理
if (entry.getValue() != null && (entry.getValue() instanceof Clob || entry.getValue() instanceof Blob)) {
continue;
}
//update-end--- author:scott---date:20201227------for:JT-250【流程】特殊字段 发起流程失败------
result.put(entry.getKey().toLowerCase(), entry.getValue());
}
return result;
}
}
@@ -0,0 +1,80 @@
/**
* @Project: jeecg
* @Title: ProcessEndListener.java
* @Package com.oa.manager.workFlow.listener.execution
* @date 2013-8-16 下午2:04:12
* @Copyright: 2013
*/
package org.jeecg.modules.extbpm.listener.execution;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
/**
* 类名:SubProcessStartListener
* 功能:会签子流程开始监听器
* @author zhoujf
*
*/
@Slf4j
public class SubProcessHqStartListener implements ExecutionListener{
private static final long serialVersionUID = 1L;
@Override
public void notify(DelegateExecution execution) {
RuntimeService runtimeService = SpringContextUtils.getBean(RuntimeService.class);
String mainProcessId = (String)execution.getVariable(WorkFlowGlobals.JG_SUB_MAIN_PROCESS_ID);
String url = (String)execution.getVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL);
String bizTitle = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_BIZ_TITLE);
if(org.apache.commons.lang.StringUtil.isEmpty(url)) {
url = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_FORM_CONTENT_URL);
String mobileUrl = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE);
execution.setVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL, url);
execution.setVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE, mobileUrl);
}
execution.setVariable(WorkFlowGlobals.BPM_BIZ_TITLE, bizTitle);
//获取主表数据id
String businessKey = (String)runtimeService.getVariable(mainProcessId, WorkFlowGlobals.BPM_DATA_ID);
//获取主表的设计表单数据ID
String BPM_DES_DATA_ID = oConvertUtils.getString(runtimeService.getVariable(mainProcessId, WorkFlowGlobals.BPM_DES_DATA_ID));
//获取主表表名
String tableName = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_FORM_KEY);
execution.setVariable(WorkFlowGlobals.BPM_FORM_KEY, tableName);
//--update--begin------author:scott-----date:20210512-----for:出差借款子流程,加载表单数据为空问题---------
log.info("----------传入子流程的数据ID--------------: "+execution.getVariable(WorkFlowGlobals.DATA_ID));
if(oConvertUtils.isNotEmpty(execution.getVariable(WorkFlowGlobals.DATA_ID))){
execution.setVariable(WorkFlowGlobals.BPM_DATA_ID, execution.getVariable(WorkFlowGlobals.DATA_ID));
//如果是设计器表单,则还需要设置设计表单数据ID
if(oConvertUtils.isNotEmpty(BPM_DES_DATA_ID)){
execution.setVariable(WorkFlowGlobals.BPM_DES_DATA_ID, execution.getVariable(WorkFlowGlobals.DATA_ID));
}
}else{
//未传入id值,则获取主表数据id给子流程【online表单需要】
execution.setVariable(WorkFlowGlobals.BPM_DATA_ID, businessKey);
//如果是设计器表单,则还需要设置设计表单数据ID
if(oConvertUtils.isNotEmpty(BPM_DES_DATA_ID)){
execution.setVariable(WorkFlowGlobals.BPM_DES_DATA_ID, BPM_DES_DATA_ID);
}
}
//--update--end------author:scott-----date:20210512-----for:出差借款子流程,加载表单数据为空问题---------
//子流程实例set业务号和主流程保持一致
runtimeService.updateBusinessKey(execution.getProcessInstanceId(), businessKey);
}
}
@@ -0,0 +1,76 @@
/**
* @Project: jeecg
* @Title: ProcessEndListener.java
* @Package com.oa.manager.workFlow.listener.execution
* @date 2013-8-16 下午2:04:12
* @Copyright: 2013
*/
package org.jeecg.modules.extbpm.listener.execution;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
/**
* 类名:SubProcessStartListener
* 功能:子流程启动监听
* @author zhoujf
*
*/
@Slf4j
public class SubProcessStartListener implements ExecutionListener{
private static final long serialVersionUID = 1L;
/**
* 这里可以加逻辑,定义给子流程传递变量
* @param execution
* @throws Exception
*/
@Override
public void notify(DelegateExecution execution) {
RuntimeService runtimeService = SpringContextUtils.getBean(RuntimeService.class);
String mainProcessId = oConvertUtils.getString(execution.getVariable(WorkFlowGlobals.JG_SUB_MAIN_PROCESS_ID));
log.info("------------主流程的ID--------------["+mainProcessId+"]");
if(oConvertUtils.isNotEmpty(mainProcessId)){
String bizTitle = oConvertUtils.getString(runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_BIZ_TITLE));
String businessKey = oConvertUtils.getString(runtimeService.getVariable(mainProcessId, WorkFlowGlobals.BPM_DATA_ID));
//设计表单数据ID
String BPM_DES_DATA_ID = oConvertUtils.getString(runtimeService.getVariable(mainProcessId, WorkFlowGlobals.BPM_DES_DATA_ID));
execution.setVariable(WorkFlowGlobals.BPM_BIZ_TITLE, bizTitle);
//获取主表表名【online表单需要】
String tableName = oConvertUtils.getString(runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_FORM_KEY));
execution.setVariable(WorkFlowGlobals.BPM_FORM_KEY, tableName);
//--update--begin------author:scott-----date:20210512-----for:出差借款子流程,加载表单数据为空问题---------
if(execution.hasVariable(WorkFlowGlobals.DATA_ID)){
log.info("----------传入子流程的,数据ID--------------[ " + execution.getVariable(WorkFlowGlobals.DATA_ID) +"]");
execution.setVariable(WorkFlowGlobals.BPM_DATA_ID, execution.getVariable(WorkFlowGlobals.DATA_ID));
//如果是设计器表单,则还需要设置设计表单数据ID
if(oConvertUtils.isNotEmpty(BPM_DES_DATA_ID)){
execution.setVariable(WorkFlowGlobals.BPM_DES_DATA_ID, execution.getVariable(WorkFlowGlobals.DATA_ID));
}
}else{
//未传入id值,则获取主表数据id给子流程【online表单需要】
execution.setVariable(WorkFlowGlobals.BPM_DATA_ID, businessKey);
//如果是设计器表单,则还需要设置设计表单数据ID
if(oConvertUtils.isNotEmpty(BPM_DES_DATA_ID)){
execution.setVariable(WorkFlowGlobals.BPM_DES_DATA_ID, BPM_DES_DATA_ID);
}
}
//--update--end------author:scott-----date:20210512-----for:出差借款子流程,加载表单数据为空问题---------
//子流程实例set业务号和主流程保持一致
runtimeService.updateBusinessKey(execution.getProcessInstanceId(), businessKey);
}
}
}
@@ -0,0 +1,16 @@
package org.jeecg.modules.extbpm.listener.global;
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
/**
* Activiti的事件处理器
* @author zhoujf
*
*/
public interface FlowEventHandler {
/**
* 事件处理器
* @param event
*/
public void handle(FlowableEvent event);
}
@@ -0,0 +1,56 @@
package org.jeecg.modules.extbpm.listener.global;
import java.util.HashMap;
import java.util.Map;
import org.flowable.common.engine.api.delegate.event.AbstractFlowableEventListener;
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
import org.jeecg.common.util.SpringContextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Activiti的全局事件监听器,即所有事件均需要在这里统一分发处理
* @author zhoujf
*
*/
public class GlobalEventListener extends AbstractFlowableEventListener {
/**
* 日志处理器
*/
public final static Logger logger = LoggerFactory.getLogger(GlobalEventListener.class);
//事件及事件的处理器
//private Map<String,EventHandler> handlers=new HashMap<String, EventHandler>();
//更换为以下模式,可以防止Spring容器启动时,ProcessEngine尚未创建,而业务类中又使用了这个引用
private Map<String,String> handlers=new HashMap<String, String>();
@Override
public void onEvent(FlowableEvent event) {
String eventType=event.getType().name();
logger.debug("envent type is ========>" + eventType);
//根据事件的类型ID,找到对应的事件处理器
String eventHandlerBeanId=handlers.get(eventType);
if(eventHandlerBeanId!=null){
FlowEventHandler handler=(FlowEventHandler)SpringContextUtils.getBean(eventHandlerBeanId);
handler.handle(event);
}
}
@Override
public boolean isFailOnException() {
return false;
}
public Map<String, String> getHandlers() {
return handlers;
}
public void setHandlers(Map<String, String> handlers) {
this.handlers = handlers;
}
}
@@ -0,0 +1,129 @@
package org.jeecg.modules.extbpm.listener.global;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.event.impl.FlowableEntityEventImpl;
import org.flowable.engine.impl.context.Context;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.modules.bpm.service.ActivitiService;
import org.jeecg.modules.extbpm.process.entity.ExtActBpmLog;
import org.jeecg.modules.extbpm.process.service.IExtActBpmLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* @TODO 待测试1 【这个监听类好像没人用】
*
* 全局自动完成首个任务监听器
* Created by Vim 2018/11/21 9:46
*
* @author Vim
*/
@Component("taskCreatedEventListener")
public class TaskCreatedEventListener implements FlowableEventListener {
@Autowired
private IExtActBpmLogService extActBpmLogService;
@Autowired
protected RuntimeService runtimeService;
@Lazy
@Autowired
private ISysBaseAPI sysBaseAPI;
@Override
public void onEvent(FlowableEvent event) {
if (!(event instanceof FlowableEntityEventImpl)) {
return;
}
FlowableEntityEventImpl entityEvent = (FlowableEntityEventImpl) event;
Object entity = entityEvent.getEntity();
//是否是任务实体类
if (!(entity instanceof TaskEntity)) {
return;
}
TaskEntity taskEntity = (TaskEntity) entity;
//是否是在任务节点创建时
if (FlowableEngineEventType.TASK_CREATED.equals(event.getType())) {
//找到流程第一个userTask节点
FlowElement firstElement = this.findFirstFlowElement(taskEntity);
//对比节点是否相同,因为有可能有子流程的节点进来
if (firstElement != null && taskEntity.getTaskDefinitionKey().equals(firstElement.getId())) {
Context.getProcessEngineConfiguration().getTaskService().complete(taskEntity.getId());
//---------------------------------------------------------------------------------
//记录审批日志
String username = JwtUtil.getUserNameByToken(SpringContextUtils.getHttpServletRequest());
LoginUser user = sysBaseAPI.getUserByName(username);
String processInstanceId = taskEntity.getProcessInstanceId();
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
ExtActBpmLog bpmlog = new ExtActBpmLog();
if(processInstance!=null) {
bpmlog.setBusinessKey(processInstance.getBusinessKey());
bpmlog.setProcName(processInstance.getName());
}
bpmlog.setOpTime(new Date());
bpmlog.setOpUserId(username);
if(user!=null){
bpmlog.setOpUserName(user.getRealname());
}
bpmlog.setProcInstId(processInstanceId);
bpmlog.setRemarks("发起流程");
bpmlog.setTaskDefKey(taskEntity.getTaskDefinitionKey());
bpmlog.setTaskId(taskEntity.getId());
bpmlog.setTaskName(taskEntity.getName());
extActBpmLogService.save(bpmlog);
//---------------------------------------------------------------------------------
}
}
}
/**
* 查找流程第一个userTask
*/
private FlowElement findFirstFlowElement(TaskEntity taskEntity) {
RepositoryService repositoryService = SpringContextUtils.getBean(RepositoryService.class);
BpmnModel bpmnModel = repositoryService.getBpmnModel(taskEntity.getProcessDefinitionId());
for (FlowElement flowElement : bpmnModel.getProcesses().get(0).getFlowElements()) {
if (flowElement instanceof StartEvent) {
return bpmnModel.getFlowElement(((StartEvent) flowElement).getOutgoingFlows().get(0).getTargetRef());
}
}
return null;
}
@Override
public boolean isFailOnException() {
return false;
}
@Override
public boolean isFireOnTransactionLifecycleEvent() {
return false;
}
@Override
public String getOnTransaction() {
return null;
}
}
@@ -0,0 +1,32 @@
package org.jeecg.modules.extbpm.listener.service;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
/**
* @Description: 流程服务节点
* 参考: https://blog.csdn.net/shirdrn/article/details/NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
* https://www.iteye.com/blog/topmanopensource-1315238
* @author: scott
* @date: 2020/3/30 19:58
*/
@Slf4j
public class ServiceTask implements JavaDelegate {
//重写委托的提交方法
@Override
public void execute(DelegateExecution execution) {
log.info("serviceTask已经执行已经执行!");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("variavles=" + execution.getVariables());
execution.setVariable("task1", "I am task 1");
log.info("I am task 1.");
}
}
@@ -0,0 +1,32 @@
package org.jeecg.modules.extbpm.listener.task;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;
import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 子流程开始监听
*/
@Service("subProcessListener")
public class SubProcessListener implements TaskListener{
private static final long serialVersionUID = 1L;
@Autowired
private RuntimeService runtimeService;
@Override
public void notify(DelegateTask delegateTask) {
//获取主流程实例
String mainProcessId = (String)delegateTask.getVariable(WorkFlowGlobals.JG_SUB_MAIN_PROCESS_ID);
String bizTitle = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_BIZ_TITLE);
String businessKey = (String)runtimeService.getVariable(mainProcessId, WorkFlowGlobals.BPM_DATA_ID);
delegateTask.setVariable(WorkFlowGlobals.BPM_BIZ_TITLE, bizTitle);
//子流程实例set业务号和主流程保持一致
runtimeService.updateBusinessKey(delegateTask.getProcessInstanceId(), businessKey);
}
}
@@ -0,0 +1,126 @@
package org.jeecg.modules.extbpm.listener.task;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.service.delegate.DelegateTask;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.modules.bpm.service.ActivitiService;
import org.jeecg.modules.extbpm.process.entity.ExtActBpmLog;
import org.jeecg.modules.extbpm.process.service.IExtActBpmLogService;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* 首节点任务被创建时自动执行通过
*
* @author zhoujf
* A task has been created. This is thrown when task is fully initialized (before TaskListener.EVENTNAME_CREATE)
*/
public class TaskCreatedAutoSubmitListener implements TaskListener {
private static final long serialVersionUID = 1L;
private static ISysBaseAPI sysBaseAPI;
private static IExtActBpmLogService extActBpmLogService;
protected static RuntimeService runtimeService;
private static ActivitiService activitiService;
private static TaskService taskService;
static {
sysBaseAPI = SpringContextUtils.getBean(ISysBaseAPI.class);
extActBpmLogService = SpringContextUtils.getBean(IExtActBpmLogService.class);
runtimeService = SpringContextUtils.getBean(RuntimeService.class);
activitiService = SpringContextUtils.getBean(ActivitiService.class);
taskService = SpringContextUtils.getBean(TaskService.class);
}
@Override
public void notify(DelegateTask delegateTask) {
try {
//如果是流程的第一步,则自动提交
this.onCreate(delegateTask);
} catch (Exception ex) {
ex.printStackTrace();
}
}
private void onCreate(DelegateTask delegateTask) throws Exception {
//找到流程第一个userTask节点
FlowElement firstElement = this.findFirstFlowElement(delegateTask);
//判断如果当前节点不是首节点,则不做任何处理
if (!firstElement.getId().equals(delegateTask.getTaskDefinitionKey())) {
return;
}
//判断是否存在历史节点,存在说明第一个节点已经自动审批过
List<Map<String,Object>> histListNode = activitiService.getHistTaskNodeList(delegateTask.getProcessInstanceId());
if(histListNode!=null&&histListNode.size()>0) {
return;
}
String applyUserId = (String)delegateTask.getVariable("applyUserId");
//设置任务处理人为发起人
taskService.setAssignee(delegateTask.getId(), applyUserId);
//自动完成
taskService.complete(delegateTask.getId(),delegateTask.getVariables());
//----------------------------------------------------------------------------
//记录审批日志
String username = JwtUtil.getUserNameByToken(SpringContextUtils.getHttpServletRequest());
LoginUser user = sysBaseAPI.getUserByName(username);
String processInstanceId = delegateTask.getProcessInstanceId();
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
ExtActBpmLog bpmlog = new ExtActBpmLog();
if(processInstance!=null) {
bpmlog.setBusinessKey(processInstance.getBusinessKey());
bpmlog.setProcName(processInstance.getName());
}
bpmlog.setOpTime(new Date());
bpmlog.setOpUserId(username);
if(user!=null){
bpmlog.setOpUserName(user.getRealname());
}
bpmlog.setProcInstId(processInstanceId);
bpmlog.setRemarks("系统自动完成");
bpmlog.setTaskDefKey(delegateTask.getTaskDefinitionKey());
bpmlog.setTaskId(delegateTask.getId());
bpmlog.setTaskName(delegateTask.getName());
extActBpmLogService.save(bpmlog);
//----------------------------------------------------------------------------
}
public RepositoryService getRepositoryService(){
return SpringContextUtils.getBean(RepositoryService.class);
}
/**
*
* @TODO 待测试1
*
* 查找流程第一个userTask
*/
private FlowElement findFirstFlowElement(DelegateTask taskEntity) {
RepositoryService repositoryService = SpringContextUtils.getBean(RepositoryService.class);
BpmnModel bpmnModel = repositoryService.getBpmnModel(taskEntity.getProcessDefinitionId());
for (FlowElement flowElement : bpmnModel.getProcesses().get(0).getFlowElements()) {
if (flowElement instanceof StartEvent) {
return bpmnModel.getFlowElement(((StartEvent) flowElement).getOutgoingFlows().get(0).getTargetRef());
}
}
return null;
}
}
@@ -0,0 +1,112 @@
package org.jeecg.modules.extbpm.listener.task;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;
import org.jeecg.common.desform.api.IDesformBaseApi;
import org.jeecg.common.desform.vo.DesignFormDataVo;
import org.jeecg.common.miniflow.ISignalProcessStartApi;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
import org.jeecg.modules.extbpm.process.entity.ExtActDesignFlowData;
import org.jeecg.modules.extbpm.process.entity.ExtActProcess;
import org.jeecg.modules.extbpm.process.mapper.ExtActDesignFlowDataMapper;
import org.jeecg.modules.extbpm.process.service.IExtActProcessService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import java.util.HashMap;
import java.util.Map;
/**
* @author scott
* 通过节点监听更新业务数据在流程里面的变量数据
*/
@Slf4j
public class TaskUpdateFormDataListener implements TaskListener {
private static final long serialVersionUID = 1L;
private static IExtActProcessService extActProcessService;
private static ExtActDesignFlowDataMapper extActDesignFlowDataMapper;
private static IDesformBaseApi desformBaseApi;
private static ISignalProcessStartApi signalProcessStartApi;
static {
extActProcessService = SpringContextUtils.getBean(IExtActProcessService.class);
extActDesignFlowDataMapper = SpringContextUtils.getBean(ExtActDesignFlowDataMapper.class);
desformBaseApi = SpringContextUtils.getBean(IDesformBaseApi.class);
try {
signalProcessStartApi = SpringContextUtils.getBean(ISignalProcessStartApi.class);
} catch (NoSuchBeanDefinitionException e) {
}
}
@Override
public void notify(DelegateTask delegateTask) {
//获取主流程实例
String processInstanceId = delegateTask.getProcessInstanceId();
String bpmDataId = (String) delegateTask.getVariable(WorkFlowGlobals.BPM_DATA_ID);
String businessKey = (String) delegateTask.getVariable(WorkFlowGlobals.BPM_FORM_KEY);
String bpmDesFormCode = (String) delegateTask.getVariable(WorkFlowGlobals.BPM_DES_FORM_CODE);
Map<String, Object> dataForm = new HashMap<String, Object>();
//表单老数据(流程变量中的字段值)
DesignFormDataVo oldFormDataVo = new DesignFormDataVo();
if(WorkFlowGlobals.DESIGN_FORM_DRAFT_TABLE.equals(businessKey)){
//如果是表单设计器的数据(需要关联通过中间表取数据)
ExtActDesignFlowData extActDesignFlowData = extActDesignFlowDataMapper.selectById(bpmDataId);
//DesignFormDataDTO designFormDataDTO = extActDesignFlowDataMapper.getDesignFormDataById(extActDesignFlowData.getDesformDataId());
//dataForm = JSONHelper.json2MapNoObj(designFormDataDTO.getDesformDataJson());
DesignFormDataVo designFormDataVo = desformBaseApi.getDataById(extActDesignFlowData.getDesformCode(),extActDesignFlowData.getDesformDataId());
dataForm = designFormDataVo.getDesformData();
// 复制数据
BeanUtils.copyProperties(designFormDataVo, oldFormDataVo);
}else if(!WorkFlowGlobals.DESIGN_FORM_DRAFT_TABLE.equals(businessKey) && oConvertUtils.isNotEmpty(bpmDesFormCode)){
DesignFormDataVo designFormDataVo = desformBaseApi.getTranslateDataById(bpmDesFormCode, bpmDataId);
dataForm = designFormDataVo.getDesformData();
// 复制数据
BeanUtils.copyProperties(designFormDataVo, oldFormDataVo);
}else{
//update-begin---author:scott ---date:2023-11-19 for:【QQYUN-6845】简流填写节点配置了 加签 使用加签时 报错----------
//获取对应的物理表的业务数据
if(oConvertUtils.isNotEmpty(businessKey) && oConvertUtils.isNotEmpty(bpmDataId)){
dataForm = extActProcessService.getDataById(businessKey, bpmDataId);
}
//update-end---author:scott ---date::2023-11-19 for:【QQYUN-6845】简流填写节点配置了 加签 使用加签时 报错----------
}
if (dataForm != null && !dataForm.isEmpty()) {
for (Map.Entry<String, Object> entry : dataForm.entrySet()) {
String mapKey = entry.getKey();
Object mapValue = entry.getValue();
//TODO 存在大小写问题
// 1.先备份流程变量中的表单值,用于触发简流事件
if(oldFormDataVo!=null && oldFormDataVo.getDesformData()!=null){
oldFormDataVo.getDesformData().put(mapKey, delegateTask.getVariable(mapKey));
}
// 2.更新流程变量中的表单数据为新值
log.info(" 同步更变流程变量 execution.setVariable => "+ mapKey + ":" + mapValue);
delegateTask.setVariable(mapKey, mapValue);
}
//update-begin---author:scott ---date:2023-12-06 for:填报节点提交流程后,抛出的设计器表单的简流触发事件---
// 流程填报节点提交流程后,抛出简流的表单事件
try {
if(oConvertUtils.isNotEmpty(bpmDesFormCode)){
String definitionId = delegateTask.getProcessDefinitionId();
String processKey = definitionId.contains(":") ? definitionId.substring(0, definitionId.indexOf(":")) : definitionId;
ExtActProcess extActProcess = extActProcessService.getExtActProcessByKey(processKey);
if(extActProcess!=null && "1".equals(extActProcess.getTriggerOtherProcess())) {
String action = "update";
signalProcessStartApi.signalStartProcess(action, bpmDataId, oldFormDataVo, bpmDesFormCode, null);
}
}
} catch (NoSuchBeanDefinitionException e) {
log.warn("低代码模式无简流模块,不支持触发流程信号!");
}
//update-end---author:scott ---date::2023-12-06 for:填报节点提交流程后,抛出的设计器表单的简流触发事件---
}
}
}
@@ -0,0 +1,109 @@
package org.jeecg.modules.extbpm.process.common;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
public class CodeUtil {
public static String[] chars = new String[] { "a", "b", "c", "d", "e", "f",
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
"t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
"W", "X", "Y", "Z" };
/** 随机数总长度 */
private static final int maxLength = 10;
/** 随机编码 */
private static final int[] r = new int[]{7, 9, 6, 2, 8 , 1, 3, 0, 5, 4};
/**
* 生成8位无重复随机码
* @return
*/
public static String generateShortUuid() {
StringBuffer shortBuffer = new StringBuffer();
String uuid = UUID.randomUUID().toString().replace("-", "");
for (int i = 0; i < 8; i++) {
String str = uuid.substring(i * 4, i * 4 + 4);
int x = Integer.parseInt(str, 16);
shortBuffer.append(chars[x % 0x3E]);
}
return shortBuffer.toString();
}
/**
* 生成数字类型的UUID
* @return
*/
public static String getUUIDNum() {
int machineId = 1;//最大支持1-9个集群机器部署
int hashCodeV = UUID.randomUUID().toString().hashCode();
if(hashCodeV < 0) {//有可能是负数
hashCodeV = - hashCodeV;
}
// 0 代表前面补充0
// 4 代表长度为4
// d 代表参数为正数型
return machineId + String.format("%015d", hashCodeV);
}
/**
     * 生成固定长度随机码
     * @param n    长度
     */
private static long getRandom(long n) {
long min = 1, max = 9;
for (int i = 1; i < n; i++) {
min *= 10;
max *= 10;
}
long rangeLong = (((long) (new Random().nextDouble() * (max - min)))) + min;
return rangeLong;
}
/**
  * 更具id进行加密+加随机数组成固定长度编码
  */
public static String generateCode(Long id) {
String idStr = id.toString();
StringBuilder idsbs = new StringBuilder();
for (int i = idStr.length() - 1 ; i >= 0; i--) {
idsbs.append(r[idStr.charAt(i)-'0']);
}
return idsbs.append(getRandom(maxLength - idStr.length())).toString();
}
/**
  * 更具id进行加密+加随机数组成固定长度编码
  */
public static String generateCode(String pre) {
StringBuilder idsbs = new StringBuilder();
idsbs.append(pre);
return idsbs.append(getRandom(maxLength - pre.length())).toString();
}
public static void main(String[] args) {
Set<String> codes = new HashSet<String>();
String code = "";
for(int i=0;i<1000;i++) {
// code= generateShortUuid();
// code = generateCode("PPP");
code = getUUIDNum();
codes.add(code);
System.out.println(code);
}
System.out.println("---------size-------"+codes.size());
}
}
@@ -0,0 +1,44 @@
package org.jeecg.modules.extbpm.process.common;
/**
* 流程办理风格枚举
* @author junfeng.zhou
* @version $ v1.0 $
*/
public enum ProcDealStyleEnum {
DEFAULT("default", "默认",""),
office("office", "流程与表单自定义","office/"),
SINGLE("single", "单页面","single/"),
SINGLE_SUBMIT("singlesubmit", "单页面并提交表单","singlesubmit/");
private String code;//风格编码
private String name;//风格名称
private String viewName;//风格页面路径
private ProcDealStyleEnum(String code, String name,String viewName) {
this.code = code;
this.name = name;
this.viewName = viewName;
}
public static ProcDealStyleEnum toEnum(String code) {
for (ProcDealStyleEnum type : ProcDealStyleEnum.values()) {
if (type.code.equals(code)) {
return type;
}
}
return ProcDealStyleEnum.DEFAULT;
// throw new IllegalArgumentException("the style argument of " + code
// + " has no correspond ProcDealStyleEnum enums.");
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
public String getViewName() {
return viewName;
}
}
@@ -0,0 +1,204 @@
package org.jeecg.modules.extbpm.process.common;
/**
* 项目名称:jeecg
* 类名称:Globals
* 类描述: 全局变量定义
* 创建人:zhoujf
* @version
*
*/
public final class WorkFlowGlobals {
/**
* 流程发布状态
*/
public static Integer Process_Deploy_NO=0;//未发布
public static Integer Process_Deploy_YES=1;//已发布
/**
* BPM_BUS_STATUS[流程业务单据状态位]
* 待提交:1/处理中:2/处理完毕:3
*/
public static String BPM_BUS_STATUS_1 = "1";//待提交
public static String BPM_BUS_STATUS_2 = "2";//处理中
public static String BPM_BUS_STATUS_3 = "3";//处理完毕
public static String BPM_BUS_STATUS_4 = "4";//流程作废
public static String BPM_BUS_STATUS_5 = "5";//流程挂起
/**
* BPM 流程对应的表单KEY
*/
public static String BPM_FORM_KEY = "BPM_FORM_KEY";
/**
* BPM 流程流程开始节点名字
*/
public static String BPM_NODE_START = "start";
/**
* BPM 流程流程开始节点名字
*/
public static String BPM_NODE_END = "end";
/**
* BPM 流程对应的流程实例ID KEY[当前流程]
*/
public static String JG_LOCAL_PROCESS_ID = "JG_LOCAL_PROCESS_ID";
/**
* BPM 流程对应的流程实例ID KEY[主流程]
*/
public static String JG_SUB_MAIN_PROCESS_ID = "JG_SUB_MAIN_PROCESS_ID";
/**
* 业务数据对应ID
*/
public static String BPM_DATA_ID = "BPM_DATA_ID";
public static String DATA_ID = "id";
/**
* 日期字段触发流程的字段名
*/
public static String TIME_TRIGGER_FIELD = "TIME_TRIGGER_FIELD";
/**
* 删除数据对象(删除数据走流程)
*/
public static final String BPM_DELETE_DATA = "BPM_DELETE_DATA";
/**
* 流程发起人
*/
public static String APPLY_USER_ID = "applyUserId";
/**
* 设计器表单编码
*/
public static String BPM_DES_FORM_CODE = "BPM_DES_FORM_CODE";
/**
* 设计器表单数据ID
*/
public static String BPM_DES_DATA_ID = "BPM_DES_DATA_ID";
/**
* 设计器表单关联编码——固定前缀
*/
public static String BPM_DES_FORM_RELATION_CODE_PREFIX = "desform_";
/**
* BPM 节点对应的表单URL(全局)
*/
public static String BPM_FORM_CONTENT_URL = "BPM_FORM_CONTENT_URL";
/**
* BPM 节点对应的表单URL(全局) - 移动端
*/
public static String BPM_FORM_CONTENT_URL_MOBILE = "BPM_FORM_CONTENT_URL_MOBILE";
/**
* BPM 流程状态
*/
public static String BPM_STATUS = "bpm_status";
/**
* BPM 业务标题表达式(全局)
*/
public static String BPM_BIZ_TITLE = "bpm_biz_title";
/**
* BPM 流程办理风格(全局)
*/
public static String BPM_PROC_DEAL_STYLE = "bpm_proc_deal_style";
/**
* BPM 业务表单类型流程变量
*/
public static String BPM_FORM_TYPE = "BPM_FORM_TYPE";
/**
* BPM 业务表单类型 表单类型:1:Online表单,2:设计器表单,3:自定义开发
*/
public static String BPM_FORM_TYPE_1 = "1";
public static String BPM_FORM_TYPE_2 = "2";
public static String BPM_FORM_TYPE_3 = "3";
/**
* BPM 流程对应的流程业务KEY
*/
public static String BPM_FORM_BUSINESSKEY = "BPM_FORM_BUSINESSKEY";
/**
* 会签【用户组常量】
*/
public static String ASSIGNEE_USER_ID_LIST = "assigneeUserIdList";
/**
* 流程追回状态
*/
public static String PROCESS_CALLBACKPROCESS_STATUS = "callBackProcess";
/**
* 流程作废状态
*/
public static String PROCESS_INVALIDPROCESS_STATUS = "invalidProcess";
/**
* 流程驳回状态
*/
public static String PROCESS_REJECTPROCESS_STATUS = "rejectProcess";
/**
* BPM 节点对应的表单URL类型--pc电脑端 mobile移动端
*/
public static String SUFFIX_BPM_FORM_URL = "pc";
public static String SUFFIX_BPM_FORM_URL_MOBILE = "mobile";
/**
* BPM_OP_TYPE[流程日志操作类型]
* 普通任务:1/会签任务:2/驳回:3/作废:4/取回:5
*/
public static String BPM_OP_TYPE_1 = "1";//普通任务
public static String BPM_OP_TYPE_2 = "2";//会签任务
public static String BPM_OP_TYPE_3 = "3";//驳回
public static String BPM_OP_TYPE_4 = "4";//作废
public static String BPM_OP_TYPE_5 = "5";//取回
/**
* 定时任务,自动委派用户ID
*/
public static final String SYS_AGENT_JOB_OPUSER_ID = "System Job";
/**
* 表单设计器,流程中间草稿表
*/
public static final String DESIGN_FORM_DRAFT_TABLE = "EXT_ACT_DESIGN_FLOW_DATA";
/**
* 流程用户组类型
* role 角色
* depart 部门
* position 职务
*/
public static final String ACTIVITI_GROUP_TYPE_DEP = "depart";
public static final String ACTIVITI_GROUP_TYPE_ROLE = "role";
public static final String ACTIVITI_GROUP_TYPE_POSITION = "position";
/**
* 动态key
*/
public static final String DESIGN_FORM_DRAFT_TABLE_KEY = "NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=";
/**
* 当前驳回节点key
*/
public static final String CURRENT_REJECT_NODE_KEY = "CURRENT_REJECT_NODE_KEY";
/**
* 并发运行方式(默认并行)
* 并行0、串行 1
*/
public static String RUN_CONCURRENT_MODE_PARALLEL = "0";
public static String RUN_CONCURRENT_MODE_SERIAL = "1";
/**
* 子流程中的节点 需要添加一个标识
*/
public static String SUB_EVENT = "subEvent_";
/**
* 流程信号启动类型:按钮触发
*/
public static String START_BUTTON_EVENT = "buttonEvent";
}

Some files were not shown because too many files have changed in this diff Show More