Browse Source

add:新增加common-flow-online 模块

master
sunquan 1 week ago
parent
commit
c463302211
  1. 34
      common/common-flow-online/pom.xml
  2. 88
      common/common-flow-online/src/main/java/apelet/common/flow/online/aop/FlowMultiDatabaseWriteAspect.java
  3. 38
      common/common-flow-online/src/main/java/apelet/common/flow/online/aop/FlowMultiDatabaseWriteExAspect.java
  4. 13
      common/common-flow-online/src/main/java/apelet/common/flow/online/config/FlowOnlineAutoConfig.java
  5. 20
      common/common-flow-online/src/main/java/apelet/common/flow/online/config/FlowOnlineProperties.java
  6. 1437
      common/common-flow-online/src/main/java/apelet/common/flow/online/controller/FlowOnlineOperationController.java
  7. 94
      common/common-flow-online/src/main/java/apelet/common/flow/online/listener/ApprovalSettingsByCreateListener.java
  8. 101
      common/common-flow-online/src/main/java/apelet/common/flow/online/object/TransactionalFlowBusinessData.java
  9. 13
      common/common-flow-online/src/main/java/apelet/common/flow/online/service/FlowEntryOnlineService.java
  10. 163
      common/common-flow-online/src/main/java/apelet/common/flow/online/service/FlowOnlineOperationService.java
  11. 319
      common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowEntryOnlineServiceImpl.java
  12. 102
      common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowOnlineBusinessServiceImpl.java
  13. 434
      common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowOnlineOperationServiceImpl.java
  14. 2
      common/common-flow-online/src/main/resources/META-INF/spring.factories
  15. 1
      common/pom.xml

34
common/common-flow-online/pom.xml

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>common</artifactId>
<groupId>apelet</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-flow-online</artifactId>
<version>1.0.0</version>
<name>common-flow-online</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>apelet</groupId>
<artifactId>common-flow</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>apelet</groupId>
<artifactId>common-online</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>apelet</groupId>
<artifactId>common-orm</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>

88
common/common-flow-online/src/main/java/apelet/common/flow/online/aop/FlowMultiDatabaseWriteAspect.java

@ -0,0 +1,88 @@ @@ -0,0 +1,88 @@
package apelet.common.flow.online.aop;
import apelet.common.core.object.TokenData;
import apelet.common.core.util.AopTargetUtil;
import apelet.common.core.util.MyDateUtil;
import apelet.common.flow.dao.FlowTransProducerMapper;
import apelet.common.flow.model.FlowTaskComment;
import apelet.common.flow.model.FlowTransProducer;
import apelet.common.flow.online.object.TransactionalFlowBusinessData;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
/**
* 拦截流程业务多数据库数据写入的AOP对象
*
* @author guifc
* @date 2023-08-04
*/
@Aspect
@Component
@Slf4j
public class FlowMultiDatabaseWriteAspect {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private FlowTransProducerMapper flowTransProducerMapper;
@Pointcut("execution(public * apelet.common.flow..service.impl..*(..)) " +
"&& @annotation(apelet.common.core.annotation.MultiDatabaseWriteMethod)")
public void multiDatabaseWriteMethodPointCut() {
// 空注释,避免sonar警告
}
@Around("multiDatabaseWriteMethodPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
String initMethod = AopTargetUtil.getFullMethodName(joinPoint);
TransactionalFlowBusinessData data = TransactionalFlowBusinessData.getOrCreateFromRequestAttribute();
if (data.getInitMethod() == null) {
data.setInitMethod(initMethod);
}
try {
// 调用原来的方法
Object result = joinPoint.proceed();
if (StrUtil.equals(initMethod, data.getInitMethod()) && CollUtil.isNotEmpty(data.getSqlDataList())) {
this.fillupTransactionalBusinessData();
FlowTransProducer producerData = BeanUtil.copyProperties(data, FlowTransProducer.class);
producerData.setAppCode(TokenData.takeFromRequest().getAppCode());
producerData.setTryTimes(1);
String sqlData = JSON.toJSONStringWithDateFormat(
data.getSqlDataList(), MyDateUtil.COMMON_SHORT_DATETIME_FORMAT);
producerData.setSqlData(sqlData);
flowTransProducerMapper.insert(producerData);
eventPublisher.publishEvent(data);
}
return result;
} catch (Exception e) {
TransactionalFlowBusinessData.removeFromRequestAttribute();
throw e;
} finally {
if (StrUtil.equals(initMethod, data.getInitMethod())) {
TransactionalFlowBusinessData.removeFromRequestAttribute();
}
}
}
private void fillupTransactionalBusinessData() {
FlowTaskComment comment = FlowTaskComment.getFromRequest();
TransactionalFlowBusinessData data = TransactionalFlowBusinessData.getFromRequestAttribute();
if (comment != null) {
data.setProcessInstanceId(comment.getProcessInstanceId());
data.setTaskId(comment.getTaskId());
data.setTaskKey(comment.getTaskKey());
data.setTaskName(comment.getTaskName());
data.setTaskComment(comment.getTaskComment());
}
}
}

38
common/common-flow-online/src/main/java/apelet/common/flow/online/aop/FlowMultiDatabaseWriteExAspect.java

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
package apelet.common.flow.online.aop;
import lombok.extern.slf4j.Slf4j;
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.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 主要用于拦截FlowTransactionBusinessDataListener监听器类抛出的异常
*
* @author guifc
* @date 2023-08-04
*/
@Aspect
@Component
@Order(1)
@Slf4j
public class FlowMultiDatabaseWriteExAspect {
@Pointcut("execution(public * apelet.common.flow.online.service.impl..*(..)) " +
"&& @annotation(apelet.common.core.annotation.MultiDatabaseWriteMethod)")
public void multiDatabaseWriteMethodPointCut() {
// 空注释,避免sonar警告
}
@Around("multiDatabaseWriteMethodPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
try {
return joinPoint.proceed();
} catch (Exception e) {
log.error("FlowMultiDatabaseWriteExAspect throw", e);
throw e;
}
}
}

13
common/common-flow-online/src/main/java/apelet/common/flow/online/config/FlowOnlineAutoConfig.java

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
package apelet.common.flow.online.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
/**
* common-flow-online模块的自动配置引导类
*
* @author guifc
* @date 2023-08-04
*/
@EnableConfigurationProperties({FlowOnlineProperties.class})
public class FlowOnlineAutoConfig {
}

20
common/common-flow-online/src/main/java/apelet/common/flow/online/config/FlowOnlineProperties.java

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
package apelet.common.flow.online.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 在线表单工作流模块的配置对象
*
* @author guifc
* @date 2023-08-04
*/
@Data
@ConfigurationProperties(prefix = "common-flow-online")
public class FlowOnlineProperties {
/**
* 在线表单的URL前缀
*/
private String urlPrefix;
}

1437
common/common-flow-online/src/main/java/apelet/common/flow/online/controller/FlowOnlineOperationController.java

File diff suppressed because it is too large Load Diff

94
common/common-flow-online/src/main/java/apelet/common/flow/online/listener/ApprovalSettingsByCreateListener.java

@ -0,0 +1,94 @@ @@ -0,0 +1,94 @@
package apelet.common.flow.online.listener;
import apelet.common.core.util.ApplicationContextHolder;
import apelet.common.flow.model.FlowEntry;
import apelet.common.flow.model.FlowNodeTasks;
import apelet.common.flow.model.FlowWorkOrder;
import apelet.common.flow.service.FlowEntryService;
import apelet.common.flow.service.FlowNodeTasksService;
import apelet.common.flow.service.FlowWorkOrderService;
import apelet.common.online.dto.OnlinePluginExecuteDto;
import apelet.common.online.model.OnlineForm;
import apelet.common.online.service.OnlineFormService;
import apelet.common.online.service.OnlineOperationService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.service.delegate.DelegateTask;
import java.util.ArrayList;
import java.util.List;
/**
* 审批设置插件by创建事件
*/
public class ApprovalSettingsByCreateListener implements TaskListener {
private final transient FlowNodeTasksService flowNodeTasksService =
ApplicationContextHolder.getBean(FlowNodeTasksService.class);
private final transient FlowEntryService flowEntryService =
ApplicationContextHolder.getBean(FlowEntryService.class);
private final transient OnlineFormService onlineFormService =
ApplicationContextHolder.getBean(OnlineFormService.class);
private final transient FlowWorkOrderService flowWorkOrderService =
ApplicationContextHolder.getBean(FlowWorkOrderService.class);
private final transient OnlineOperationService onlineOperationService =
ApplicationContextHolder.getBean(OnlineOperationService.class);
private final transient RuntimeService runtimeService =
ApplicationContextHolder.getBean(RuntimeService.class);
@Override
public void notify(DelegateTask delegateTask) {
String eventName = delegateTask.getEventName();
List<Integer> setList = new ArrayList<>();
if (eventName.equals("create")) {
setList.add(1);
} else if (eventName.equals("assignment")) {
setList.add(2);
} else if (eventName.equals("complete")) {
String operationType = (String)delegateTask.getVariable("operationType");
if(operationType != null){
if(operationType.equals("agree")){
setList.add(5);
}else if(operationType.equals("reject")) {
setList.add(6);
}else if(operationType.equals("refuse")){
}
}
setList.add(3);
} else if (eventName.equals("delete")) {
setList.add(4);
}
String taskKey = delegateTask.getTaskDefinitionKey();
String processDefinitionId = delegateTask.getProcessDefinitionId();
LambdaQueryWrapper<FlowNodeTasks> wrapper = new LambdaQueryWrapper<>();
// wrapper.eq(FlowNodeTasks::getFlowId, processDefinitionId);
wrapper.eq(FlowNodeTasks::getTaskKey, taskKey);
wrapper.in(FlowNodeTasks::getExecutionTiming, setList);
List<FlowNodeTasks> list = flowNodeTasksService.list(wrapper);
if (list != null && list.size() > 0) {
for (FlowNodeTasks flowNodeTasks : list) {
LambdaQueryWrapper<FlowEntry> wrapperFlowEntry = new LambdaQueryWrapper<>();
wrapperFlowEntry.eq(FlowEntry::getEntryId, flowNodeTasks.getFlowId());
FlowEntry flowEntry = flowEntryService.getById(flowNodeTasks.getFlowId());
OnlineForm form = onlineFormService.getOnlineFormFromCache(flowEntry.getDefaultFormId());
Long formId = form.getFormId();
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(delegateTask.getProcessInstanceId())
.singleResult();
String billId = processInstance.getBusinessKey();
if (billId != null && formId > 0) {
OnlinePluginExecuteDto onlinePluginExecuteDto = new OnlinePluginExecuteDto();
onlinePluginExecuteDto.setBillId(String.valueOf(billId));
onlinePluginExecuteDto.setFormId(formId);
onlinePluginExecuteDto.setButtonName(flowNodeTasks.getOperation());
onlineOperationService.executePlugin(onlinePluginExecuteDto, null, null);
}
}
}
}
}

101
common/common-flow-online/src/main/java/apelet/common/flow/online/object/TransactionalFlowBusinessData.java

@ -0,0 +1,101 @@ @@ -0,0 +1,101 @@
package apelet.common.flow.online.object;
import apelet.common.core.util.ContextUtil;
import apelet.common.online.object.TransactionalBusinessData;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.servlet.http.HttpServletRequest;
/**
* 跨库存储工作流的业务数据的事物性事件数据
*
* @author guifc
* @date 2023-08-04
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class TransactionalFlowBusinessData extends TransactionalBusinessData {
/**
* 流程实例Id
*/
private String processInstanceId;
/**
* 流程任务Id
*/
private String taskId;
/**
* 流程任务定义标识
*/
private String taskKey;
/**
* 流程任务定义名称
*/
private String taskName;
/**
* 审批注释
*/
private String taskComment;
private static final ThreadLocal<TransactionalFlowBusinessData> FLOW_BUSINESS_DATA_THREAD_LOCAL = new ThreadLocal<>();
public TransactionalFlowBusinessData() {
super();
}
public TransactionalFlowBusinessData(HttpServletRequest request) {
super(request);
}
/**
* 获取HttpServletRequest属性中的事物性时间对象如果没有情趣对象则从线程本地存储中获取
*
* @return 返回设置在当前请求属性中的事务性时间对象如果不存在则返回NULL
*/
public static TransactionalFlowBusinessData getFromRequestAttribute() {
HttpServletRequest request = ContextUtil.getHttpRequest();
if (request != null) {
return (TransactionalFlowBusinessData) request.getAttribute(BUSINESS_DATA_ATTR_KEY);
}
return FLOW_BUSINESS_DATA_THREAD_LOCAL.get();
}
/**
* 获取HttpServletRequest属性中的事物性时间对象如果存在直接返回否则创建新对象并设置到当前请求的指定属性中
* 如果没有情趣对象则从线程本地存储中创建
*
* @return 返回设置在当前请求属性中的事务性时间对象
*/
public static TransactionalFlowBusinessData getOrCreateFromRequestAttribute() {
HttpServletRequest request = ContextUtil.getHttpRequest();
TransactionalFlowBusinessData data;
if (request != null) {
data = (TransactionalFlowBusinessData) request.getAttribute(BUSINESS_DATA_ATTR_KEY);
if (data != null) {
return data;
}
data = new TransactionalFlowBusinessData(request);
request.setAttribute(BUSINESS_DATA_ATTR_KEY, data);
} else {
data = FLOW_BUSINESS_DATA_THREAD_LOCAL.get();
if (data != null) {
return data;
}
data = new TransactionalFlowBusinessData();
FLOW_BUSINESS_DATA_THREAD_LOCAL.set(data);
}
return data;
}
/**
* 移除该对象
*/
public static void removeFromRequestAttribute() {
if (ContextUtil.hasRequestContext()) {
ContextUtil.getHttpRequest().removeAttribute(BUSINESS_DATA_ATTR_KEY);
} else {
FLOW_BUSINESS_DATA_THREAD_LOCAL.remove();
}
}
}

13
common/common-flow-online/src/main/java/apelet/common/flow/online/service/FlowEntryOnlineService.java

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
package apelet.common.flow.online.service;
import apelet.common.flow.model.FlowEntry;
import javax.xml.stream.XMLStreamException;
public interface FlowEntryOnlineService {
void publish(FlowEntry flowEntry, String initTaskInfo) throws XMLStreamException;
void processFlowTaskExtList(FlowEntry flowEntry, String taskInfo) throws XMLStreamException;
}

163
common/common-flow-online/src/main/java/apelet/common/flow/online/service/FlowOnlineOperationService.java

@ -0,0 +1,163 @@ @@ -0,0 +1,163 @@
package apelet.common.flow.online.service;
import apelet.common.flow.model.FlowTaskComment;
import apelet.common.flow.model.FlowTransProducer;
import apelet.common.flow.model.FlowWorkOrder;
import apelet.common.online.model.OnlineDatasource;
import apelet.common.online.model.OnlineDatasourceRelation;
import apelet.common.online.model.OnlineTable;
import com.alibaba.fastjson.JSONObject;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 流程操作服务接口
*
* @author guifc
* @date 2023-08-04
*/
public interface FlowOnlineOperationService {
/**
* 启动流程实例并将业务主键Id传给新启动的流程实例
*
* @param processDefinitionId 流程实例Id
* @param dataId 业务主键Id
*/
ProcessInstance startWithBusinessKey(String processDefinitionId, String dataId);
/**
* 启动流程实例并将业务主键Id传给新启动的流程实例
*
* @param processDefinitionId 流程实例Id
* @param dataId 业务主键Id
*/
void startWithBusinessKeyExt(String processDefinitionId, String dataId,Long formId);
/**
* 保存在线表单的数据同时启动流程如果当前用户是第一个用户任务的Assignee
* 或者第一个用户任务的Assignee是流程发起人变量该方法还会自动Take第一个任务
*
* @param processDefinitionId 流程定义Id
* @param flowTaskComment 流程审批批注对象
* @param taskVariableData 流程任务的变量数据
* @param table 表对象
* @param data 表数据
*/
void saveNewAndStartProcess(
String processDefinitionId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable table,
JSONObject data);
/**
* 保存在线表单的数据同时启动流程如果当前用户是第一个用户任务的Assignee
* 或者第一个用户任务的Assignee是流程发起人变量该方法还会自动Take第一个任务
*
* @param processDefinitionId 流程定义Id
* @param flowTaskComment 流程审批批注对象
* @param taskVariableData 流程任务的变量数据
* @param masterTable 主表对象
* @param masterData 主表数据
* @param slaveDataListMap 关联从表数据Map
*/
void saveNewAndStartProcess(
String processDefinitionId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable masterTable,
JSONObject masterData,
Map<OnlineDatasourceRelation, List<JSONObject>> slaveDataListMap);
/**
* 保存在线表单的草稿数据同时启动一个流程实例
*
* @param processDefinitionId 流程定义Id
* @param tableId 在线表单主表Id
* @param masterData 主表数据
* @param slaveData 所有关联从表数据
* @return 流程工单对象
*/
FlowWorkOrder saveNewDraftAndStartProcess(
String processDefinitionId, Long tableId, JSONObject masterData, JSONObject slaveData);
/**
* 保存在线表单的数据同时Take用户任务
*
* @param processInstanceId 流程实例Id
* @param taskId 流程任务Id
* @param flowTaskComment 流程审批批注对象
* @param taskVariableData 流程任务的变量数据
* @param table 表对象
* @param data 表数据
*/
void saveNewAndTakeTask(
String processInstanceId,
String taskId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable table,
JSONObject data);
/**
* 保存在线表单的数据同时Take用户任务
*
* @param processInstanceId 流程实例Id
* @param taskId 流程任务Id
* @param flowTaskComment 流程审批批注对象
* @param taskVariableData 流程任务的变量数据
* @param masterTable 主表对象
* @param masterData 主表数据
* @param slaveDataListMap 关联从表数据Map
*/
void saveNewAndTakeTask(
String processInstanceId,
String taskId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable masterTable,
JSONObject masterData,
Map<OnlineDatasourceRelation, List<JSONObject>> slaveDataListMap);
/**
* 保存业务表数据同时接收流程任务
*
* @param task 流程任务
* @param flowTaskComment 流程审批批注对象
* @param taskVariableData 流程任务的变量数据
* @param datasource 主表所在数据源
* @param masterData 主表数据
* @param masterDataId 主表数据主键
* @param slaveDataListMap 从表数据
*/
void updateAndTakeTask(
Task task,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineDatasource datasource,
JSONObject masterData,
String masterDataId,
Map<OnlineDatasourceRelation, List<JSONObject>> slaveDataListMap);
/**
* 修复业务数据
*
* @param flowTransProducer 流程操作流水生产者对象
*/
void fixBusinessData(FlowTransProducer flowTransProducer);
/**
* 获取在线表单工作流Id所关联的权限数据包括权限字列表和权限资源列表
*
* @param onlineFormEntryIds 在线表单工作流Id集合
* @return 参数中在线表单工作流Id集合所关联的权限数据
*/
List<Map<String, Object>> calculatePermData(Set<Long> onlineFormEntryIds);
}

319
common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowEntryOnlineServiceImpl.java

@ -0,0 +1,319 @@ @@ -0,0 +1,319 @@
package apelet.common.flow.online.service.impl;
import apelet.common.core.object.TokenData;
import apelet.common.core.util.MyModelUtil;
import apelet.common.flow.constant.FlowConstant;
import apelet.common.flow.dao.FlowEntryMapper;
import apelet.common.flow.dao.FlowEntryPublishMapper;
import apelet.common.flow.dao.FlowEntryPublishVariableMapper;
import apelet.common.flow.listener.*;
import apelet.common.flow.model.*;
import apelet.common.flow.model.constant.FlowEntryStatus;
import apelet.common.flow.object.*;
import apelet.common.flow.online.listener.ApprovalSettingsByCreateListener;
import apelet.common.flow.online.service.FlowEntryOnlineService;
import apelet.common.flow.service.*;
import apelet.common.flow.util.BaseFlowIdentityExtHelper;
import apelet.common.flow.util.FlowCustomExtFactory;
import apelet.common.flow.util.FlowRedisKeyUtil;
import apelet.common.redis.util.CommonRedisUtil;
import apelet.common.sequence.wrapper.IdGeneratorWrapper;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.Cleanup;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
@Service("flowEntryOnlineService")
public class FlowEntryOnlineServiceImpl implements FlowEntryOnlineService {
@Autowired
private FlowEntryMapper flowEntryMapper;
@Autowired
private FlowEntryPublishMapper flowEntryPublishMapper;
@Autowired
private FlowEntryPublishVariableMapper flowEntryPublishVariableMapper;
@Autowired
private FlowEntryVariableService flowEntryVariableService;
@Autowired
private FlowCategoryService flowCategoryService;
@Autowired
private FlowTaskExtService flowTaskExtService;
@Autowired
private FlowApiService flowApiService;
@Autowired
private FlowCustomExtFactory flowCustomExtFactory;
@Autowired
private RepositoryService repositoryService;
@Autowired
private IdGeneratorWrapper idGenerator;
@Autowired
private CommonRedisUtil commonRedisUtil;
@Transactional(rollbackFor = Exception.class)
@Override
public void publish(FlowEntry flowEntry, String initTaskInfo) throws XMLStreamException {
commonRedisUtil.evictFormCache(
FlowRedisKeyUtil.makeFlowEntryKey(flowEntry.getProcessDefinitionKey()));
FlowCategory flowCategory = flowCategoryService.getById(flowEntry.getCategoryId());
InputStream xmlStream = new ByteArrayInputStream(
flowEntry.getBpmnXml().getBytes(StandardCharsets.UTF_8));
@Cleanup XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(xmlStream);
BpmnXMLConverter converter = new BpmnXMLConverter();
BpmnModel bpmnModel = converter.convertToBpmnModel(reader);
bpmnModel.getMainProcess().setName(flowEntry.getProcessDefinitionName());
bpmnModel.getMainProcess().setId(flowEntry.getProcessDefinitionKey());
flowApiService.addProcessInstanceEndListener(bpmnModel, FlowFinishedListener.class);
List<FlowTaskExt> flowTaskExtList = flowTaskExtService.buildTaskExtList(bpmnModel);
if (StrUtil.isNotBlank(flowEntry.getExtensionData())) {
FlowEntryExtensionData flowEntryExtensionData =
JSON.parseObject(flowEntry.getExtensionData(), FlowEntryExtensionData.class);
this.mergeTaskNotifyData(flowEntryExtensionData, flowTaskExtList);
this.processFlowTaskRevive(flowEntryExtensionData, bpmnModel);
}
this.processFlowTaskExtList(flowTaskExtList, bpmnModel);
TokenData tokenData = TokenData.takeFromRequest();
Deployment deploy = repositoryService.createDeployment()
.addBpmnModel(flowEntry.getProcessDefinitionKey() + ".bpmn", bpmnModel)
.tenantId(tokenData.getTenantId() != null ? tokenData.getTenantId().toString() : tokenData.getAppCode())
.name(flowEntry.getProcessDefinitionName())
.key(flowEntry.getProcessDefinitionKey())
.category(flowCategory.getCode())
.deploy();
ProcessDefinition processDefinition = flowApiService.getProcessDefinitionByDeployId(deploy.getId());
FlowEntryPublish flowEntryPublish = new FlowEntryPublish();
flowEntryPublish.setEntryPublishId(idGenerator.nextLongId());
flowEntryPublish.setEntryId(flowEntry.getEntryId());
flowEntryPublish.setProcessDefinitionId(processDefinition.getId());
flowEntryPublish.setDeployId(processDefinition.getDeploymentId());
flowEntryPublish.setPublishVersion(processDefinition.getVersion());
flowEntryPublish.setActiveStatus(true);
flowEntryPublish.setMainVersion(flowEntry.getStatus().equals(FlowEntryStatus.UNPUBLISHED));
flowEntryPublish.setCreateUserId(TokenData.takeFromRequest().getUserId());
flowEntryPublish.setPublishTime(new Date());
flowEntryPublish.setInitTaskInfo(initTaskInfo);
flowEntryPublish.setExtensionData(flowEntry.getExtensionData());
flowEntryPublish.setStartConditions(flowEntry.getStartConditions());
AnalyzedNode rootNode = flowApiService.analyzeBpmnRoads(processDefinition.getId());
flowEntryPublish.setAnalyzedNodeJson(JSON.toJSONString(rootNode));
flowEntryPublishMapper.insert(flowEntryPublish);
FlowEntry updatedFlowEntry = new FlowEntry();
updatedFlowEntry.setEntryId(flowEntry.getEntryId());
updatedFlowEntry.setStatus(FlowEntryStatus.PUBLISHED);
updatedFlowEntry.setLatestPublishTime(new Date());
// 对于从未发布过的工作,第一次发布的时候会将本地发布置位主版本。
if (flowEntry.getStatus().equals(FlowEntryStatus.UNPUBLISHED)) {
updatedFlowEntry.setMainEntryPublishId(flowEntryPublish.getEntryPublishId());
}
flowEntryMapper.updateById(updatedFlowEntry);
FlowEntryVariable flowEntryVariableFilter = new FlowEntryVariable();
flowEntryVariableFilter.setEntryId(flowEntry.getEntryId());
List<FlowEntryVariable> flowEntryVariableList =
flowEntryVariableService.getFlowEntryVariableList(flowEntryVariableFilter, null);
if (CollUtil.isNotEmpty(flowTaskExtList)) {
flowTaskExtList.forEach(t -> t.setProcessDefinitionId(processDefinition.getId()));
flowTaskExtService.saveBatch(flowTaskExtList);
}
this.insertEntryPublishVariables(flowEntryVariableList, flowEntryPublish.getEntryPublishId());
}
@Override
public void processFlowTaskExtList(FlowEntry flowEntry,String taskInfo) throws XMLStreamException {
InputStream xmlStream = new ByteArrayInputStream(
flowEntry.getBpmnXml().getBytes(StandardCharsets.UTF_8));
@Cleanup XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(xmlStream);
BpmnXMLConverter converter = new BpmnXMLConverter();
BpmnModel bpmnModel = converter.convertToBpmnModel(reader);
bpmnModel.getMainProcess().setName(flowEntry.getProcessDefinitionName());
bpmnModel.getMainProcess().setId(flowEntry.getProcessDefinitionKey());
List<FlowTaskExt> flowTaskExtList = flowTaskExtService.buildTaskExtList(bpmnModel);
List<FlowElement> elementList = new LinkedList<>();
this.calculateAllElementList(bpmnModel.getMainProcess().getFlowElements(), elementList);
Map<String, FlowElement> elementMap = elementList.stream()
.filter(UserTask.class::isInstance).collect(Collectors.toMap(FlowElement::getId, c -> c));
for (FlowTaskExt t : flowTaskExtList) {
UserTask userTask = (UserTask) elementMap.get(t.getTaskId());
// create 创建
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "create");
// assignment 分配/指派
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "assignment");
// complete 完成
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "complete");
// delete 删除
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "delete");
}
}
private void calculateAllElementList(Collection<FlowElement> elements, List<FlowElement> resultList) {
resultList.addAll(elements);
for (FlowElement element : elements) {
if (element instanceof SubProcess) {
this.calculateAllElementList(((SubProcess) element).getFlowElements(), resultList);
}
}
}
private void mergeTaskNotifyData(FlowEntryExtensionData flowEntryExtensionData, List<FlowTaskExt> flowTaskExtList) {
if (CollUtil.isEmpty(flowEntryExtensionData.getNotifyTypes())) {
return;
}
List<String> flowTaskNotifyTypes =
flowEntryExtensionData.getNotifyTypes().stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
if (CollUtil.isEmpty(flowTaskNotifyTypes)) {
return;
}
for (FlowTaskExt flowTaskExt : flowTaskExtList) {
if (flowTaskExt.getExtraDataJson() == null) {
JSONObject o = new JSONObject();
o.put(FlowConstant.USER_TASK_NOTIFY_TYPES_KEY, flowTaskNotifyTypes);
flowTaskExt.setExtraDataJson(o.toJSONString());
} else {
FlowUserTaskExtData taskExtData =
JSON.parseObject(flowTaskExt.getExtraDataJson(), FlowUserTaskExtData.class);
if (CollUtil.isEmpty(taskExtData.getFlowNotifyTypeList())) {
taskExtData.setFlowNotifyTypeList(flowTaskNotifyTypes);
} else {
Set<String> notifyTypesSet = taskExtData.getFlowNotifyTypeList()
.stream().filter(StrUtil::isNotBlank).collect(Collectors.toSet());
notifyTypesSet.addAll(flowTaskNotifyTypes);
taskExtData.setFlowNotifyTypeList(new LinkedList<>(notifyTypesSet));
}
flowTaskExt.setExtraDataJson(JSON.toJSONString(taskExtData));
}
}
}
private void processFlowTaskRevive(FlowEntryExtensionData flowEntryExtensionData, BpmnModel bpmnModel) {
if (BooleanUtil.isTrue(flowEntryExtensionData.getSupportRevive())) {
List<UserTask> mainProcessUserTasks = bpmnModel.getMainProcess().getFlowElements()
.stream().filter(UserTask.class::isInstance).map(t -> (UserTask) t).collect(Collectors.toList());
mainProcessUserTasks.forEach(t -> flowApiService.addTaskCreateListener(t, FlowTaskReviveListener.class));
}
}
private void processFlowTaskExtList(List<FlowTaskExt> flowTaskExtList, BpmnModel bpmnModel) {
List<FlowElement> elementList = new LinkedList<>();
this.calculateAllElementList(bpmnModel.getMainProcess().getFlowElements(), elementList);
this.doAddLatestApprovalStatusListener(elementList);
Map<String, FlowElement> elementMap = elementList.stream()
.filter(UserTask.class::isInstance).collect(Collectors.toMap(FlowElement::getId, c -> c));
BaseFlowIdentityExtHelper flowIdentityExtHelper = flowCustomExtFactory.getFlowIdentityExtHelper();
for (FlowTaskExt t : flowTaskExtList) {
UserTask userTask = (UserTask) elementMap.get(t.getTaskId());
flowApiService.addTaskCreateListener(userTask, FlowUserTaskListener.class);
// create 创建
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "create");
// assignment 分配/指派
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "assignment");
// complete 完成
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "complete");
// delete 删除
flowApiService.addTaskListenerByEvent(userTask, ApprovalSettingsByCreateListener.class, "delete");
Map<String, List<ExtensionAttribute>> attributes = userTask.getAttributes();
if (CollUtil.isNotEmpty(attributes.get(FlowConstant.USER_TASK_AUTO_SKIP_KEY))) {
flowApiService.addTaskCreateListener(userTask, AutoSkipTaskListener.class);
}
// 如果流程图中包含部门领导审批和上级部门领导审批的选项,就需要注册 FlowCustomExtFactory 工厂中的
// BaseFlowIdentityExtHelper 对象,该注册操作需要业务模块中实现。
if (StrUtil.equals(t.getGroupType(), FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER)) {
userTask.setCandidateGroups(
CollUtil.newArrayList("${" + FlowConstant.GROUP_TYPE_UP_DEPT_POST_LEADER_VAR + "}"));
Assert.notNull(flowIdentityExtHelper);
flowApiService.addTaskCreateListener(userTask, flowIdentityExtHelper.getUpDeptPostLeaderListener());
} else if (StrUtil.equals(t.getGroupType(), FlowConstant.GROUP_TYPE_DEPT_POST_LEADER)) {
userTask.setCandidateGroups(
CollUtil.newArrayList("${" + FlowConstant.GROUP_TYPE_DEPT_POST_LEADER_VAR + "}"));
Assert.notNull(flowIdentityExtHelper);
flowApiService.addTaskCreateListener(userTask, flowIdentityExtHelper.getDeptPostLeaderListener());
} else if (StrUtil.equals(t.getGroupType(), FlowConstant.GROUP_TYPE_POST)) {
Assert.notNull(t.getDeptPostListJson());
List<FlowTaskPostCandidateGroup> groupDataList =
JSON.parseArray(t.getDeptPostListJson(), FlowTaskPostCandidateGroup.class);
List<String> candidateGroupList =
FlowTaskPostCandidateGroup.buildCandidateGroupList(groupDataList);
userTask.setCandidateGroups(candidateGroupList);
}
this.processFlowTaskExtListener(userTask, t);
}
}
private void insertEntryPublishVariables(List<FlowEntryVariable> entryVariableList, Long entryPublishId) {
if (CollUtil.isEmpty(entryVariableList)) {
return;
}
List<FlowEntryPublishVariable> entryPublishVariableList =
MyModelUtil.copyCollectionTo(entryVariableList, FlowEntryPublishVariable.class);
for (FlowEntryPublishVariable variable : entryPublishVariableList) {
variable.setVariableId(idGenerator.nextLongId());
variable.setEntryPublishId(entryPublishId);
}
flowEntryPublishVariableMapper.insertList(entryPublishVariableList);
}
private void doAddLatestApprovalStatusListener(Collection<FlowElement> elementList) {
List<FlowElement> sequenceFlowList =
elementList.stream().filter(SequenceFlow.class::isInstance).collect(Collectors.toList());
for (FlowElement sequenceFlow : sequenceFlowList) {
FlowElementExtProperty extProperty = flowTaskExtService.buildFlowElementExt(sequenceFlow);
if (extProperty != null && extProperty.getLatestApprovalStatus() != null) {
List<FieldExtension> fieldExtensions = new LinkedList<>();
FieldExtension fieldExtension = new FieldExtension();
fieldExtension.setFieldName(FlowConstant.LATEST_APPROVAL_STATUS_KEY);
fieldExtension.setStringValue(extProperty.getLatestApprovalStatus().toString());
fieldExtensions.add(fieldExtension);
flowApiService.addExecutionListener(
sequenceFlow, UpdateLatestApprovalStatusListener.class, "start", fieldExtensions);
}
}
List<SubProcess> subProcesseList = elementList.stream()
.filter(SubProcess.class::isInstance).map(SubProcess.class::cast).collect(Collectors.toList());
for (SubProcess subProcess : subProcesseList) {
this.doAddLatestApprovalStatusListener(subProcess.getFlowElements());
}
}
private void processFlowTaskExtListener(UserTask userTask, FlowTaskExt taskExt) {
if (StrUtil.isBlank(taskExt.getExtraDataJson())) {
return;
}
FlowUserTaskExtData userTaskExtData =
JSON.parseObject(taskExt.getExtraDataJson(), FlowUserTaskExtData.class);
if (CollUtil.isNotEmpty(userTaskExtData.getFlowNotifyTypeList())) {
flowApiService.addTaskCreateListener(userTask, FlowTaskNotifyListener.class);
}
if (userTaskExtData.getRejectType().equals(FlowUserTaskExtData.REJECT_TYPE_BACK_TO_SOURCE)) {
flowApiService.addTaskCreateListener(userTask, FlowTaskRejectBackListener.class);
}
if (StrUtil.isNotBlank(userTaskExtData.getTimeoutHandleWay())) {
flowApiService.addTaskCreateListener(userTask, FlowTaskTimeoutListener.class);
}
if (StrUtil.isNotBlank(userTaskExtData.getEmptyUserHandleWay())) {
flowApiService.addTaskCreateListener(userTask, FlowEmptyUserHandleListener.class);
}
}
}

102
common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowOnlineBusinessServiceImpl.java

@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
package apelet.common.flow.online.service.impl;
import apelet.common.core.annotation.MyDataSource;
import apelet.common.core.constant.ApplicationConstant;
import apelet.common.flow.base.service.BaseFlowOnlineService;
import apelet.common.flow.model.FlowWorkOrder;
import apelet.common.flow.online.object.TransactionalFlowBusinessData;
import apelet.common.flow.util.FlowCustomExtFactory;
import apelet.common.online.exception.OnlineRuntimeException;
import apelet.common.online.model.*;
import apelet.common.online.model.constant.FieldKind;
import apelet.common.online.service.*;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.List;
/**
* 在线表单和流程监听器进行数据对接时的服务实现类
*
* @author guifc
* @date 2023-08-04
*/
@Slf4j
@MyDataSource(ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE)
@Service("flowOnlineBusinessService")
public class FlowOnlineBusinessServiceImpl implements BaseFlowOnlineService {
@Autowired
private FlowCustomExtFactory flowCustomExtFactory;
@Autowired
private OnlineTableService onlineTableService;
@Autowired
private OnlineDatasourceService onlineDatasourceService;
@Autowired
private OnlineDatasourceRelationService onlineDatasourceRelationService;
@Autowired
private OnlineOperationService onlineOperationService;
@PostConstruct
public void doRegister() {
flowCustomExtFactory.getOnlineBusinessDataExtHelper().setOnlineBusinessService(this);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateFlowStatus(FlowWorkOrder workOrder) {
OnlineTable onlineTable = onlineTableService.getOnlineTableFromCache(workOrder.getOnlineTableId());
if (onlineTable == null) {
log.error("OnlineTableId [{}] doesn't exist while calling FlowOnlineBusinessServiceImpl.updateFlowStatus",
workOrder.getOnlineTableId());
return;
}
String dataId = workOrder.getBusinessKey();
this.handleTransactionalFlowBusinessData(workOrder, "FlowOnlineBusinessServiceImpl.updateFlowStatus");
for (OnlineColumn column : onlineTable.getColumnMap().values()) {
if (ObjectUtil.equals(column.getFieldKind(), FieldKind.FLOW_FINISHED_STATUS)) {
onlineOperationService.updateColumn(onlineTable, dataId, column, workOrder.getFlowStatus());
}
if (ObjectUtil.equals(column.getFieldKind(), FieldKind.FLOW_APPROVAL_STATUS)) {
onlineOperationService.updateColumn(onlineTable, dataId, column, workOrder.getLatestApprovalStatus());
}
}
}
@Override
public void deleteBusinessData(FlowWorkOrder workOrder) {
OnlineTable onlineTable = onlineTableService.getOnlineTableFromCache(workOrder.getOnlineTableId());
if (onlineTable == null) {
log.error("OnlineTableId [{}] doesn't exist while calling FlowOnlineBusinessServiceImpl.deleteBusinessData",
workOrder.getOnlineTableId());
return;
}
OnlineDatasource datasource =
onlineDatasourceService.getOnlineDatasourceByMasterTableId(onlineTable.getTableId());
List<OnlineDatasourceRelation> relationList =
onlineDatasourceRelationService.getOnlineDatasourceRelationListFromCache(CollUtil.newHashSet(datasource.getDatasourceId()));
String dataId = workOrder.getBusinessKey();
for (OnlineDatasourceRelation relation : relationList) {
OnlineTable slaveTable = onlineTableService.getOnlineTableFromCache(relation.getSlaveTableId());
if (slaveTable == null) {
throw new OnlineRuntimeException("数据验证失败,数据源关联 [" + relation.getRelationName() + "] 的从表Id不存在!");
}
relation.setSlaveTable(slaveTable);
}
this.handleTransactionalFlowBusinessData(workOrder, "FlowOnlineBusinessServiceImpl.deleteBusinessData");
onlineOperationService.delete(onlineTable, relationList, dataId);
}
private void handleTransactionalFlowBusinessData(FlowWorkOrder workOrder, String desc) {
TransactionalFlowBusinessData eventData = TransactionalFlowBusinessData.getFromRequestAttribute();
if (eventData != null) {
eventData.setProcessInstanceId(workOrder.getProcessInstanceId());
eventData.setOperationDesc(desc);
}
}
}

434
common/common-flow-online/src/main/java/apelet/common/flow/online/service/impl/FlowOnlineOperationServiceImpl.java

@ -0,0 +1,434 @@ @@ -0,0 +1,434 @@
package apelet.common.flow.online.service.impl;
import apelet.common.core.annotation.MultiDatabaseWriteMethod;
import apelet.common.core.annotation.MyDataSource;
import apelet.common.core.config.CoreProperties;
import apelet.common.core.constant.ApplicationConstant;
import apelet.common.core.object.CallResult;
import apelet.common.core.object.ObjectValue;
import apelet.common.dbutil.provider.DataSourceProvider;
import apelet.common.dbutil.provider.PostgreSqlProvider;
import apelet.common.flow.config.FlowProperties;
import apelet.common.flow.constant.FlowApprovalType;
import apelet.common.flow.constant.FlowConstant;
import apelet.common.flow.constant.FlowTaskStatus;
import apelet.common.flow.dao.FlowTransProducerMapper;
import apelet.common.flow.exception.FlowOperationException;
import apelet.common.flow.model.*;
import apelet.common.flow.online.object.TransactionalFlowBusinessData;
import apelet.common.flow.online.service.FlowOnlineOperationService;
import apelet.common.flow.service.FlowApiService;
import apelet.common.flow.service.FlowEntryService;
import apelet.common.flow.service.FlowWorkOrderService;
import apelet.common.generator.utils.OrmGenDataSourceUtil;
import apelet.common.online.config.OnlineProperties;
import apelet.common.online.exception.OnlineRuntimeException;
import apelet.common.online.model.*;
import apelet.common.online.object.TransactionalBusinessData;
import apelet.common.online.service.*;
import apelet.common.online.util.OnlineDataSourceUtil;
import apelet.common.orm.impl.Selector;
import apelet.common.orm.impl.SelectorItem;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
import java.sql.Connection;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@MyDataSource(ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE)
@Service("flowOnlineOperationService")
public class FlowOnlineOperationServiceImpl implements FlowOnlineOperationService {
@Autowired
private FlowApiService flowApiService;
@Autowired
private FlowWorkOrderService flowWorkOrderService;
@Autowired
private FlowEntryService flowEntryService;
@Autowired
private OnlineOperationService onlineOperationService;
@Autowired
private OnlineDatasourceService onlineDatasourceService;
@Autowired
private OnlineProperties onlineProperties;
@Autowired
private FlowProperties flowProperties;
@Autowired
private CoreProperties coreProperties;
@Autowired
private OnlineDataSourceUtil dataSourceUtil;
@Autowired
private FlowTransProducerMapper flowTransProducerMapper;
@Autowired
private OnlineFormService onlineFormService;
@Autowired
private OnlineTableService onlineTableService;
@Autowired
private OrmGenDataSourceUtil ormGenDataSourceUtil;
@MultiDatabaseWriteMethod
@Transactional(rollbackFor = Exception.class)
@Override
public ProcessInstance startWithBusinessKey(String processDefinitionId, String dataId) {
ProcessInstance instance = flowApiService.start(processDefinitionId, dataId);
flowWorkOrderService.saveNew(instance, dataId, null, null);
return instance;
}
@Override
public void startWithBusinessKeyExt(String processDefinitionId, String dataId, Long formId) {
OnlineForm OnlineForm = onlineFormService.getOnlineFormFromCache(formId);
OnlineTable onlineTable = onlineTableService.getOnlineTableFromCache(OnlineForm.getMasterTableId());
Selector selector = new Selector();
selector.getList().add(new SelectorItem("billstatus"));
ObjectValue objectValue = ormGenDataSourceUtil.queryOne(onlineTable.getTableName(),Long.valueOf(dataId),selector);
if(objectValue == null){
String errorMessage = "无效数据!";
throw new RuntimeException(errorMessage);
}
if(!objectValue.get("billstatus").equals("A")){
throw new RuntimeException("只有保存状态才能提交");
}
objectValue.put("billstatus","B");
try {
ormGenDataSourceUtil.update(onlineTable.getTableName(),objectValue,selector);
}catch (Exception e){
e.printStackTrace();
String errorMessage = "更新失败!错误信息:"+e.getMessage();
throw new RuntimeException(errorMessage);
}
if(processDefinitionId != null){
JSONObject taskVariableData = new JSONObject();
ProcessInstance instance = this.startWithBusinessKey(processDefinitionId,dataId);
FlowTaskComment flowTaskComment = new FlowTaskComment();
flowTaskComment.setApprovalType("agree");
flowTaskComment.setTaskComment("发起人");
flowApiService.takeFirstTask(instance.getProcessInstanceId(), flowTaskComment, taskVariableData);
}
}
@MultiDatabaseWriteMethod
@Transactional(rollbackFor = Exception.class)
@Override
public void saveNewAndStartProcess(
String processDefinitionId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable table,
JSONObject data) {
this.saveNewAndStartProcess(processDefinitionId, flowTaskComment, taskVariableData, table, data, null);
}
@MultiDatabaseWriteMethod
@Transactional(rollbackFor = Exception.class)
@Override
public void saveNewAndStartProcess(
String processDefinitionId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable masterTable,
JSONObject masterData,
Map<OnlineDatasourceRelation, List<JSONObject>> slaveDataListMap) {
Object dataId = onlineOperationService.saveNewWithRelation(masterTable, masterData, slaveDataListMap);
Assert.notNull(dataId);
if (taskVariableData == null) {
taskVariableData = new JSONObject();
}
taskVariableData.put(FlowConstant.MASTER_DATA_KEY, masterData);
taskVariableData.put(FlowConstant.SLAVE_DATA_KEY, this.normailizeSlaveDataListMap(slaveDataListMap));
taskVariableData.put(FlowConstant.MASTER_TABLE_KEY, masterTable);
ProcessInstance instance = flowApiService.start(processDefinitionId, dataId);
flowWorkOrderService.saveNew(instance, dataId, masterTable.getTableId(), null);
flowApiService.takeFirstTask(instance.getProcessInstanceId(), flowTaskComment, taskVariableData);
// 这里需要在创建工单后再次更新一下工单状态,在flowApiService.completeTask中的更新,
// 因为当时没有创建工单对象,更新会不起任何作用,所以这里要补偿一下。
Integer approvalStatus = MapUtil.getInt(taskVariableData, FlowConstant.LATEST_APPROVAL_STATUS_KEY);
flowWorkOrderService.updateLatestApprovalStatusByProcessInstanceId(instance.getId(), approvalStatus);
}
@Transactional(rollbackFor = Exception.class)
@Override
public FlowWorkOrder saveNewDraftAndStartProcess(
String processDefinitionId, Long tableId, JSONObject masterData, JSONObject slaveData) {
ProcessInstance instance = flowApiService.start(processDefinitionId, null);
return flowWorkOrderService.saveNewWithDraft(
instance, tableId, null, JSON.toJSONString(masterData), JSON.toJSONString(slaveData));
}
@MultiDatabaseWriteMethod
@Transactional(rollbackFor = Exception.class)
@Override
public void saveNewAndTakeTask(
String processInstanceId,
String taskId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable table,
JSONObject data) {
this.saveNewAndTakeTask(
processInstanceId, taskId, flowTaskComment, taskVariableData, table, data, null);
}
@MultiDatabaseWriteMethod
@Transactional(rollbackFor = Exception.class)
@Override
public void saveNewAndTakeTask(
String processInstanceId,
String taskId,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineTable masterTable,
JSONObject masterData,
Map<OnlineDatasourceRelation, List<JSONObject>> slaveDataListMap) {
Object dataId = onlineOperationService.saveNewWithRelation(masterTable, masterData, slaveDataListMap);
Assert.notNull(dataId);
Task task = flowApiService.getProcessInstanceActiveTask(processInstanceId, taskId);
flowApiService.setBusinessKeyForProcessInstance(processInstanceId, dataId);
Map<String, Object> variables =
flowApiService.initAndGetProcessInstanceVariables(task.getProcessDefinitionId());
if (taskVariableData == null) {
taskVariableData = new JSONObject();
}
taskVariableData.putAll(variables);
taskVariableData.put(FlowConstant.MASTER_DATA_KEY, masterData);
taskVariableData.put(FlowConstant.SLAVE_DATA_KEY, this.normailizeSlaveDataListMap(slaveDataListMap));
taskVariableData.put(FlowConstant.MASTER_TABLE_KEY, masterTable);
flowApiService.completeTask(task, flowTaskComment, taskVariableData, null);
ProcessInstance instance = flowApiService.getProcessInstance(processInstanceId);
FlowWorkOrder flowWorkOrder =
flowWorkOrderService.getFlowWorkOrderByProcessInstanceId(instance.getProcessInstanceId());
if (flowWorkOrder == null) {
flowWorkOrderService.saveNew(instance, dataId, masterTable.getTableId(), null);
} else {
flowWorkOrder.setBusinessKey(dataId.toString());
flowWorkOrder.setUpdateTime(new Date());
flowWorkOrder.setFlowStatus(FlowTaskStatus.SUBMITTED);
flowWorkOrderService.updateById(flowWorkOrder);
}
}
@MultiDatabaseWriteMethod
@Transactional(rollbackFor = Exception.class)
@Override
public void updateAndTakeTask(
Task task,
FlowTaskComment flowTaskComment,
JSONObject taskVariableData,
OnlineDatasource datasource,
JSONObject masterData,
String masterDataId,
Map<OnlineDatasourceRelation, List<JSONObject>> slaveDataListMap) {
int flowStatus = FlowTaskStatus.APPROVING;
if (flowTaskComment.getApprovalType().equals(FlowApprovalType.REFUSE)) {
flowStatus = FlowTaskStatus.REFUSED;
} else if (flowTaskComment.getApprovalType().equals(FlowApprovalType.STOP)) {
flowStatus = FlowTaskStatus.FINISHED;
}
OnlineTable masterTable = datasource.getMasterTable();
Long datasourceId = datasource.getDatasourceId();
flowWorkOrderService.updateFlowStatusByProcessInstanceId(task.getProcessInstanceId(), flowStatus);
this.updateMasterData(masterTable, masterData, masterDataId);
if (slaveDataListMap != null) {
for (Map.Entry<OnlineDatasourceRelation, List<JSONObject>> relationEntry : slaveDataListMap.entrySet()) {
Long relationId = relationEntry.getKey().getRelationId();
onlineOperationService.updateRelationData(
masterTable, masterData, masterDataId, datasourceId, relationId, relationEntry.getValue());
}
}
if (flowTaskComment.getApprovalType().equals(FlowApprovalType.STOP)) {
Integer s = MapUtil.getInt(taskVariableData, FlowConstant.LATEST_APPROVAL_STATUS_KEY);
flowWorkOrderService.updateLatestApprovalStatusByProcessInstanceId(task.getProcessInstanceId(), s);
CallResult stopResult = flowApiService.stopProcessInstance(
task.getProcessInstanceId(), flowTaskComment.getTaskComment(), flowStatus);
if (!stopResult.isSuccess()) {
throw new FlowOperationException(stopResult.getErrorMessage());
}
} else {
if (taskVariableData == null) {
taskVariableData = new JSONObject();
}
taskVariableData.put(FlowConstant.MASTER_DATA_KEY, masterData);
taskVariableData.put(FlowConstant.SLAVE_DATA_KEY, this.normailizeSlaveDataListMap(slaveDataListMap));
taskVariableData.put(FlowConstant.MASTER_TABLE_KEY, masterTable);
flowApiService.completeTask(task, flowTaskComment, taskVariableData, null);
}
}
@Override
public void fixBusinessData(FlowTransProducer flowTransProducer) {
try {
String sql = "SELECT * FROM zz_flow_trans_consumer WHERE trans_id = " + flowTransProducer.getTransId();
List<Map<String, Object>> dataList = dataSourceUtil.query(flowTransProducer.getDblinkId(), sql);
if (CollUtil.isNotEmpty(dataList)) {
return;
}
} catch (Exception e) {
log.error("Failed to call dataSourceUtil.query", e);
throw new OnlineRuntimeException(e.getMessage());
}
TransactionalFlowBusinessData businessData = new TransactionalFlowBusinessData();
businessData.setTransId(flowTransProducer.getTransId());
businessData.setDblinkId(flowTransProducer.getDblinkId());
List<TransactionalBusinessData.BusinessSqlData> sqlDataList =
JSON.parseArray(flowTransProducer.getSqlData(), TransactionalBusinessData.BusinessSqlData.class);
businessData.setSqlDataList(sqlDataList);
try {
this.doHandle(businessData);
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new OnlineRuntimeException(e.getMessage());
}
}
@Override
public List<Map<String, Object>> calculatePermData(Set<Long> onlineFormEntryIds) {
if (CollUtil.isEmpty(onlineFormEntryIds)) {
return new LinkedList<>();
}
List<Map<String, Object>> permDataList = new LinkedList<>();
List<FlowEntry> flowEntries = flowEntryService.getInList(onlineFormEntryIds);
Set<Long> pageIds = flowEntries.stream().map(FlowEntry::getPageId).collect(Collectors.toSet());
Map<Long, String> pageAndVariableNameMap =
onlineDatasourceService.getPageIdAndVariableNameMapByPageIds(pageIds);
for (FlowEntry flowEntry : flowEntries) {
JSONObject permData = new JSONObject();
permData.put("entryId", flowEntry.getEntryId());
String key = StrUtil.upperFirst(flowEntry.getProcessDefinitionKey());
List<String> permCodeList = new LinkedList<>();
String formPermCode = "form" + key;
permCodeList.add(formPermCode);
permCodeList.add(formPermCode + ":fragment" + key);
permData.put("permCodeList", permCodeList);
String flowUrlPrefix = flowProperties.getUrlPrefix();
String onlineUrlPrefix = onlineProperties.getUrlPrefix();
List<String> permList = CollUtil.newLinkedList(
onlineUrlPrefix + "/onlineForm/view",
onlineUrlPrefix + "/onlineForm/render",
onlineUrlPrefix + "/onlineOperation/listByOneToManyRelationId/" + pageAndVariableNameMap.get(flowEntry.getPageId()),
flowUrlPrefix + "/flowOperation/viewInitialHistoricTaskInfo",
flowUrlPrefix + "/flowOperation/startOnly",
flowUrlPrefix + "/flowOperation/viewInitialTaskInfo",
flowUrlPrefix + "/flowOperation/viewRuntimeTaskInfo",
flowUrlPrefix + "/flowOperation/viewProcessBpmn",
flowUrlPrefix + "/flowOperation/viewHighlightFlowData",
flowUrlPrefix + "/flowOperation/listFlowTaskComment",
flowUrlPrefix + "/flowOperation/cancelWorkOrder",
flowUrlPrefix + "/flowOperation/listRuntimeTask",
flowUrlPrefix + "/flowOperation/listHistoricProcessInstance",
flowUrlPrefix + "/flowOperation/listHistoricTask",
flowUrlPrefix + "/flowOperation/freeJumpTo",
flowUrlPrefix + "/flowOnlineOperation/startPreview",
flowUrlPrefix + "/flowOnlineOperation/viewUserTask",
flowUrlPrefix + "/flowOnlineOperation/viewHistoricProcessInstance",
flowUrlPrefix + "/flowOnlineOperation/submitUserTask",
flowUrlPrefix + "/flowOnlineOperation/upload",
flowUrlPrefix + "/flowOnlineOperation/download",
flowUrlPrefix + "/flowOperation/submitConsign",
flowUrlPrefix + "/flowOnlineOperation/startAndTakeUserTask/" + flowEntry.getProcessDefinitionKey(),
flowUrlPrefix + "/flowOnlineOperation/startAndSaveDraft/" + flowEntry.getProcessDefinitionKey(),
flowUrlPrefix + "/flowOnlineOperation/listWorkOrder/" + flowEntry.getProcessDefinitionKey(),
flowUrlPrefix + "/flowOnlineOperation/printWorkOrder/" + flowEntry.getProcessDefinitionKey()
);
permData.put("permList", permList);
permDataList.add(permData);
}
return permDataList;
}
// @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
// public void bulkHandleBusinessData(TransactionalFlowBusinessData businessData) {
// try {
// this.doHandle(businessData);
// // 这里会切换回原有的在线表单表所在的数据库上线文。
// flowTransProducerMapper.deleteById(businessData.getTransId());
// } catch (Exception e) {
// log.error("Failed to commit online business data [** " + JSON.toJSONString(businessData) + " **]", e);
// FlowTransProducer transProducer = flowTransProducerMapper.selectById(businessData.getTransId());
// transProducer.setErrorReason(e.getMessage());
// flowTransProducerMapper.updateById(transProducer);
// businessData.setErrorReason(e.getMessage());
// throw new OnlineRuntimeException(e.getMessage());
// }
// }
private void doHandle(TransactionalFlowBusinessData businessData) throws Exception {
Connection conn = null;
try {
conn = dataSourceUtil.getConnection(businessData.getDblinkId());
conn.setAutoCommit(false);
for (TransactionalBusinessData.BusinessSqlData s : businessData.getSqlDataList()) {
List<Object> paramList = null;
if (CollUtil.isNotEmpty(s.getColumnValueList())) {
paramList = s.getColumnValueList().stream().map(Object.class::cast).collect(Collectors.toList());
}
dataSourceUtil.execute(conn, s.getSql(), paramList);
}
this.insertFlowConsumerTrans(conn, businessData);
conn.commit();
} catch (Exception e) {
if (conn != null) {
conn.rollback();
}
log.error(e.getMessage(), e);
throw new OnlineRuntimeException(e.getMessage());
} finally {
if (conn != null) {
conn.close();
}
}
}
private void insertFlowConsumerTrans(Connection conn, TransactionalFlowBusinessData businessData) {
DataSourceProvider provider = dataSourceUtil.getProvider(businessData.getDblinkId());
Long transId = businessData.getTransId();
String sql = "INSERT INTO zz_flow_trans_consumer VALUES(?,?)";
if (provider instanceof PostgreSqlProvider) {
dataSourceUtil.execute(conn, sql, CollUtil.newArrayList(transId, new java.sql.Date(System.currentTimeMillis())));
} else {
dataSourceUtil.execute(conn, sql, CollUtil.newArrayList(transId, new Date()));
}
}
private void updateMasterData(OnlineTable masterTable, JSONObject masterData, String dataId) {
if (masterData == null) {
return;
}
// 如果存在主表数据,就执行主表数据的更新。
Map<String, Object> originalMasterData =
onlineOperationService.getMasterData(masterTable, null, null, dataId);
for (Map.Entry<String, Object> entry : originalMasterData.entrySet()) {
masterData.putIfAbsent(entry.getKey(), entry.getValue());
}
if (!onlineOperationService.update(masterTable, masterData)) {
throw new FlowOperationException("主表数据不存在!");
}
}
private Map<String, List<JSONObject>> normailizeSlaveDataListMap(
Map<OnlineDatasourceRelation, List<JSONObject>> slaveDataListMap) {
if (slaveDataListMap == null || slaveDataListMap.size() == 0) {
return null;
}
Map<String, List<JSONObject>> resultMap = new HashMap<>(slaveDataListMap.size());
for (Map.Entry<OnlineDatasourceRelation, List<JSONObject>> entry : slaveDataListMap.entrySet()) {
resultMap.put(entry.getKey().getSlaveTable().getTableName(), entry.getValue());
}
return resultMap;
}
}

2
common/common-flow-online/src/main/resources/META-INF/spring.factories

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
apelet.common.flow.online.config.FlowOnlineAutoConfig

1
common/pom.xml

@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
<modules>
<module>common-flow</module>
<module>common-flow-online</module>
<module>common-online</module>
<module>common-tenant</module>

Loading…
Cancel
Save