diff --git a/antflow-base/pom.xml b/antflow-base/pom.xml
index 7a03c58..10e38cb 100644
--- a/antflow-base/pom.xml
+++ b/antflow-base/pom.xml
@@ -200,6 +200,12 @@
3.1.4
provided
+
+ org.aspectj
+ aspectjweaver
+ 1.9.22
+ provided
+
diff --git a/antflow-base/src/main/java/org/openoa/base/aspect/ExceptionLoggingAspect.java b/antflow-base/src/main/java/org/openoa/base/aspect/ExceptionLoggingAspect.java
new file mode 100644
index 0000000..d711796
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/aspect/ExceptionLoggingAspect.java
@@ -0,0 +1,86 @@
+package org.openoa.base.aspect;
+
+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.reflect.MethodSignature;
+import org.openoa.base.constant.StringConstants;
+import org.openoa.base.entity.MethodReplayEntity;
+import org.openoa.base.interf.MethodReplay;
+import org.openoa.base.mapper.MethodReplayMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ClassUtils;
+
+import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
+import java.util.zip.CRC32;
+
+@Aspect
+@Component
+@Slf4j
+public class ExceptionLoggingAspect {
+
+ @Autowired
+ private MethodReplayMapper methodReplayMapper;
+ @Value("${methodreplay.on:true}")
+ private boolean methodReplayOn;
+
+ @Around("@annotation(methodReplay)")
+ public Object aroundMethodExecution(ProceedingJoinPoint joinPoint, MethodReplay methodReplay) throws Throwable {
+ Object result;
+ try {
+ result = joinPoint.proceed();
+ } catch (Exception e) {
+
+ if(methodReplayOn){
+ // 如果抓取到异常就记录 类全路径 方法名 参数 异常信息 当前时间 全都存表
+
+ // 获取类全路径
+ String className = joinPoint.getTarget().getClass().getName();
+ if(ClassUtils.isCglibProxy(joinPoint.getTarget())){
+ className=ClassUtils.getUserClass(joinPoint.getTarget()).getName();
+ }
+ // 获取方法名
+ String methodName = joinPoint.getSignature().getName();
+ // 获取参数类型
+ MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+ Method method = signature.getMethod();
+ Class>[] parameterTypes = method.getParameterTypes();
+ // 获取参数
+ Object[] args = joinPoint.getArgs();
+ // 打印类全路径、方法名和参数
+ log.info("MethodReplay接受到异常,Class name:{},Method name:{},parameterTypes:{}, Arguments:{}", className, methodName, JSON.toJSONString(parameterTypes), JSON.toJSONString(args));
+ // 入库
+ MethodReplayEntity methodReplayEntity = new MethodReplayEntity();
+ methodReplayEntity.setProjectName(StringConstants.PROJECT_NAME);
+ methodReplayEntity.setClassName(className);
+ methodReplayEntity.setMethodName(methodName);
+ methodReplayEntity.setParamType(JSON.toJSONString(parameterTypes));
+ methodReplayEntity.setArgs(JSON.toJSONString(args));
+ String message=e.getMessage();
+ if(message.length()>100){
+ message=message.substring(0,100);
+ }
+ methodReplayEntity.setErrorMsg(message);
+ methodReplayEntity.setAlreadyReplayTimes(0);
+ methodReplayEntity.setMaxReplayTimes(methodReplay.maxReplayTimes());
+ String sum=StringConstants.PROJECT_NAME + className + methodName + methodReplayEntity.getParamType() + methodReplayEntity.getArgs();
+ CRC32 crc32=new CRC32();
+ crc32.update(sum.getBytes(StandardCharsets.UTF_8));
+ methodReplayEntity.setId(crc32.getValue());
+ MethodReplayEntity exist = methodReplayMapper.selectById(methodReplayEntity);
+ if (exist == null) {
+ methodReplayMapper.insert(methodReplayEntity);
+ }
+ }
+ throw e;
+ }
+ return result;
+ }
+
+
+}
diff --git a/antflow-base/src/main/java/org/openoa/base/constant/MsgTopics.java b/antflow-base/src/main/java/org/openoa/base/constant/MsgTopics.java
new file mode 100644
index 0000000..6dfaa99
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/constant/MsgTopics.java
@@ -0,0 +1,10 @@
+package org.openoa.base.constant;
+
+public interface MsgTopics {
+
+ /**
+ * 工作流状态变化发送消息topic
+ */
+ String WORKFLOW_EVENT_PUSH="oa_workflow_event_push";
+
+}
diff --git a/antflow-base/src/main/java/org/openoa/base/constant/StringConstants.java b/antflow-base/src/main/java/org/openoa/base/constant/StringConstants.java
index 90064f7..9914ba4 100644
--- a/antflow-base/src/main/java/org/openoa/base/constant/StringConstants.java
+++ b/antflow-base/src/main/java/org/openoa/base/constant/StringConstants.java
@@ -1,5 +1,7 @@
package org.openoa.base.constant;
+import org.activiti.engine.impl.pvm.runtime.StartingExecution;
+
/**
* @Classname StringConstant
* @Description TODO
@@ -24,4 +26,5 @@ public class StringConstants {
public static final String ADAPTOR_FACTORY_BEANNAME="jimuAdaptorFactory";
public static final String TASK_ASSIGNEE_NAME="assigneeName";
+ public static final String PROJECT_NAME="antFlow";
}
diff --git a/antflow-base/src/main/java/org/openoa/base/constant/enums/BusinessCallbackEnum.java b/antflow-base/src/main/java/org/openoa/base/constant/enums/BusinessCallbackEnum.java
new file mode 100644
index 0000000..2525de5
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/constant/enums/BusinessCallbackEnum.java
@@ -0,0 +1,53 @@
+package org.openoa.base.constant.enums;
+
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.EnumUtils;
+import org.openoa.base.interf.BusinessCallBackAdaptor;
+import org.openoa.base.service.BusinessCallBackFace;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+/**
+ * @Author tylerzhou
+ */
+@Slf4j
+public enum BusinessCallbackEnum {
+ PROCESS_EVENT_CALLBACK(1, ProcessBusinessCallBackTypeEnum.class, "流程类回调枚举"),
+ ;
+ @Getter
+ private final Integer code;
+ @Getter
+ private final Class extends BusinessCallBackFace> clsz;
+ @Getter
+ private final String desc;
+
+ BusinessCallbackEnum(Integer code, Class extends BusinessCallBackFace> clsz, String desc) {
+ this.code = code;
+ this.clsz = clsz;
+ this.desc = desc;
+ }
+ public static Map>getAllAdaptorsByType(){
+
+ Map> allAdaptorsOfType=new HashMap<>();
+ for (BusinessCallbackEnum businessCallbackEnum : BusinessCallbackEnum.values()) {
+ Class extends BusinessCallBackFace> clsz = businessCallbackEnum.getClsz();
+
+ BusinessCallBackFace businessCallBackFace = null;
+ try {
+ businessCallBackFace= clsz.getEnumConstants()[0];
+ Set> allAdaptors = businessCallBackFace.getAllAdaptors();
+ ListbusinessCallBackAdaptors=new ArrayList<>();
+ for (Class adaptor : allAdaptors) {
+ BusinessCallBackAdaptor businessCallBackAdaptor = adaptor.newInstance();
+ businessCallBackAdaptors.add(businessCallBackAdaptor);
+ }
+ allAdaptorsOfType.put(businessCallbackEnum,businessCallBackAdaptors);
+ }catch (Exception ex){
+ log.error("error occur while creating instance by reflection");
+ }
+ }
+ return allAdaptorsOfType;
+ }
+}
diff --git a/antflow-base/src/main/java/org/openoa/base/constant/enums/ProcessBusinessCallBackTypeEnum.java b/antflow-base/src/main/java/org/openoa/base/constant/enums/ProcessBusinessCallBackTypeEnum.java
new file mode 100644
index 0000000..c6db680
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/constant/enums/ProcessBusinessCallBackTypeEnum.java
@@ -0,0 +1,49 @@
+package org.openoa.base.constant.enums;
+
+
+import lombok.Getter;
+import org.openoa.base.interf.BusinessCallBackAdaptor;
+import org.openoa.base.service.BusinessCallBackFace;
+import org.openoa.base.service.ProcessEventSendMessageAdaptor;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * @Author tylerzhou
+ */
+public enum ProcessBusinessCallBackTypeEnum implements BusinessCallBackFace {
+ Send_MQ_Message(1, ProcessEventSendMessageAdaptor.class,"发送事件消息到mq队列")
+ ,;
+ @Getter
+ private Integer code;
+
+ private Class extends BusinessCallBackAdaptor> clsz;
+ @Getter
+ private String desc;
+
+ ProcessBusinessCallBackTypeEnum(Integer code, Class extends BusinessCallBackAdaptor>clsz, String desc){
+ this.code = code;
+ this.clsz = clsz;
+ this.desc = desc;
+ }
+
+ @Override
+ public Class extends BusinessCallBackAdaptor> getClsz() {
+ return this.clsz;
+ }
+
+ public static ProcessBusinessCallBackTypeEnum getEnumByCode(Integer code){
+ for (ProcessBusinessCallBackTypeEnum callBackTypeEnum : ProcessBusinessCallBackTypeEnum.values()) {
+ if(Objects.equals(callBackTypeEnum.getCode(), code)){
+ return callBackTypeEnum;
+ }
+ }
+ return null;
+ }
+ @Override
+ public BusinessCallBackAdaptor getAdaptorByCode(Integer code) {
+ return null;
+ }
+}
diff --git a/antflow-base/src/main/java/org/openoa/base/entity/MethodReplayEntity.java b/antflow-base/src/main/java/org/openoa/base/entity/MethodReplayEntity.java
new file mode 100644
index 0000000..fedd233
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/entity/MethodReplayEntity.java
@@ -0,0 +1,19 @@
+package org.openoa.base.entity;
+
+import lombok.Data;
+
+@Data
+public class MethodReplayEntity {
+
+ private Long id;
+ private String projectName;
+ private String className;
+ private String methodName;
+ private String paramType;
+ private String args;
+ private String nowTime;
+ private String errorMsg;
+ private Integer alreadyReplayTimes;
+ private Integer maxReplayTimes;
+
+}
diff --git a/antflow-base/src/main/java/org/openoa/base/interf/BpmBusinessProcessService.java b/antflow-base/src/main/java/org/openoa/base/interf/BpmBusinessProcessService.java
new file mode 100644
index 0000000..c1c3d27
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/interf/BpmBusinessProcessService.java
@@ -0,0 +1,7 @@
+package org.openoa.base.interf;
+
+import org.openoa.base.entity.BpmBusinessProcess;
+
+public interface BpmBusinessProcessService {
+ BpmBusinessProcess getBpmBusinessProcess(String processCode);
+}
diff --git a/antflow-base/src/main/java/org/openoa/base/interf/BusinessCallBackAdaptor.java b/antflow-base/src/main/java/org/openoa/base/interf/BusinessCallBackAdaptor.java
new file mode 100644
index 0000000..ec63689
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/interf/BusinessCallBackAdaptor.java
@@ -0,0 +1,6 @@
+package org.openoa.base.interf;
+
+public interface BusinessCallBackAdaptor {
+ void doCallBack(P param);
+ R formattedValue(P value);
+}
diff --git a/antflow-base/src/main/java/org/openoa/base/interf/MethodReplay.java b/antflow-base/src/main/java/org/openoa/base/interf/MethodReplay.java
new file mode 100644
index 0000000..bdb3e9b
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/interf/MethodReplay.java
@@ -0,0 +1,15 @@
+package org.openoa.base.interf;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface MethodReplay {
+
+ // 最大重放次数
+ int maxReplayTimes() default 3;
+
+}
diff --git a/antflow-base/src/main/java/org/openoa/base/mapper/MethodReplayMapper.java b/antflow-base/src/main/java/org/openoa/base/mapper/MethodReplayMapper.java
new file mode 100644
index 0000000..88615f8
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/mapper/MethodReplayMapper.java
@@ -0,0 +1,26 @@
+package org.openoa.base.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.openoa.base.entity.MethodReplayEntity;
+import org.openoa.base.vo.MethodReplayDTO;
+
+import java.util.List;
+
+/**
+ * @author duggle.du
+ */
+@Mapper
+public interface MethodReplayMapper {
+
+ int insert(MethodReplayEntity methodReplayEntity);
+
+ List select(MethodReplayDTO methodReplayDTO);
+
+ int addAlreadyReplayTimes(MethodReplayEntity methodReplayEntity);
+
+ int delete(MethodReplayEntity methodReplayEntity);
+
+ MethodReplayEntity selectById(MethodReplayEntity methodReplayEntity);
+
+ void deleteByIds(MethodReplayDTO methodReplayDTO);
+}
diff --git a/antflow-base/src/main/java/org/openoa/base/service/BaseSendMqMsgAdaptor.java b/antflow-base/src/main/java/org/openoa/base/service/BaseSendMqMsgAdaptor.java
new file mode 100644
index 0000000..c960999
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/service/BaseSendMqMsgAdaptor.java
@@ -0,0 +1,38 @@
+package org.openoa.base.service;
+
+import com.alibaba.fastjson.JSON;
+import org.apache.commons.lang3.NotImplementedException;
+import org.openoa.base.interf.BusinessCallBackAdaptor;
+import org.openoa.base.util.SpringBeanUtils;
+
+/**
+ * @Author tylerzhou
+ * Date on 2021/8/20
+ */
+public abstract class BaseSendMqMsgAdaptor implements BusinessCallBackAdaptor {
+
+ @Override
+ public void doCallBack(P param) {
+ R r = formattedValue(param);
+ if (r==null) {
+ return;
+ }
+ sendMqMessage(r);
+ }
+
+ private void sendMqMessage(R value) {
+ if (value==null) {
+ return;
+ }
+ String message = JSON.toJSONString(value);
+ //todo
+ throw new NotImplementedException("send mq message method not implemented yet at the moment");
+ }
+
+ protected abstract String getTopicName();
+
+ protected String getUserName() {
+ //todo
+ throw new NotImplementedException("not implemented yet");
+ }
+}
diff --git a/antflow-base/src/main/java/org/openoa/base/service/BusinessCallBackFace.java b/antflow-base/src/main/java/org/openoa/base/service/BusinessCallBackFace.java
new file mode 100644
index 0000000..27cfe7a
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/service/BusinessCallBackFace.java
@@ -0,0 +1,73 @@
+package org.openoa.base.service;
+
+import org.apache.commons.lang3.EnumUtils;
+import org.openoa.base.interf.BusinessCallBackAdaptor;
+import org.openoa.base.util.SpringBeanUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.context.annotation.AnnotationConfigUtils;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * @Author tylerzhou
+ */
+public interface BusinessCallBackFace {
+ default Set> getAllAdaptors(){
+ Enum anEnum = (Enum) this;
+ List enumList = EnumUtils.getEnumList(anEnum.getClass());
+ Set>resultSet=new HashSet<>();
+ for (Object o : enumList) {
+ BusinessCallBackFace callBackFace = (BusinessCallBackFace)o;
+ Class clsz = (Class)callBackFace.getClsz();
+ resultSet.add(clsz);
+ }
+ return resultSet;
+ }
+ Integer getCode();
+ Class extends BusinessCallBackAdaptor> getClsz();
+ default BusinessCallBackAdaptor getAdaptorByCode(Integer code){
+ Enum anEnum = (Enum) this;
+ List enumList = EnumUtils.getEnumList(anEnum.getClass());
+ for (Object en :enumList){
+ BusinessCallBackFace callBackFace = (BusinessCallBackFace) en;
+ Integer adaptorCode = callBackFace.getCode();
+ if(Objects.equals(adaptorCode,code)){
+ Class extends BusinessCallBackAdaptor> clsz = callBackFace.getClsz();
+ BusinessCallBackAdaptor callBackAdaptor=null;
+ try {
+ callBackAdaptor=clsz.newInstance();
+ return callBackAdaptor;
+ }catch (Exception e){
+
+ }
+ }
+ }
+ return null;
+ }
+ default BusinessCallBackAdaptor getClszAdaptorInstance(){
+ BusinessCallBackAdaptor adaptor=null;
+ try {
+ try {
+
+ Component annotation = AnnotationUtils.findAnnotation(this.getClsz(),Component.class);
+ if(annotation!=null){
+ adaptor= SpringBeanUtils.getBean(this.getClsz());
+ }
+ }catch (Exception e){
+
+
+ }
+ if(adaptor==null){
+ adaptor= this.getClsz().newInstance();
+ }
+ }catch (Exception e){
+
+ }
+ return adaptor;
+ }
+}
diff --git a/antflow-base/src/main/java/org/openoa/base/service/ConfigUtil.java b/antflow-base/src/main/java/org/openoa/base/service/ConfigUtil.java
new file mode 100644
index 0000000..7d3b73d
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/service/ConfigUtil.java
@@ -0,0 +1,19 @@
+package org.openoa.base.service;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ConfigUtil {
+
+ private static Boolean outsideCallbackOn;
+
+ @Value("${outside.callback.switch:false}")
+ public void setOutsideCallbackOn(boolean outsideCallbackOn) {
+ ConfigUtil.outsideCallbackOn = outsideCallbackOn;
+ }
+
+ public static boolean getOutsideCallbackOn() {
+ return Boolean.TRUE.equals(outsideCallbackOn);
+ }
+}
\ No newline at end of file
diff --git a/antflow-base/src/main/java/org/openoa/base/service/ProcessEventSendMessageAdaptor.java b/antflow-base/src/main/java/org/openoa/base/service/ProcessEventSendMessageAdaptor.java
new file mode 100644
index 0000000..59708fe
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/service/ProcessEventSendMessageAdaptor.java
@@ -0,0 +1,62 @@
+package org.openoa.base.service;
+
+
+
+import org.apache.commons.lang3.StringUtils;
+import org.openoa.base.constant.MsgTopics;
+import org.openoa.base.entity.BpmBusinessProcess;
+import org.openoa.base.exception.JiMuBizException;
+import org.openoa.base.interf.BpmBusinessProcessService;
+import org.openoa.base.util.SecurityUtils;
+import org.openoa.base.util.SpringBeanUtils;
+import org.openoa.base.vo.BusinessDataVo;
+import org.openoa.base.vo.MqProcessEventVo;
+
+import java.util.Date;
+import java.util.Optional;
+
+/**
+ * 流程提交,同意,撤销,打回,完成等触发发送消息事件
+ *
+ * @Author tylerzhou
+ * Date on 2021/8/19
+ */
+public class ProcessEventSendMessageAdaptor extends BaseSendMqMsgAdaptor {
+ @Override
+ public void doCallBack(BusinessDataVo param) {
+ if (param==null) {
+ return;
+ }
+ super.doCallBack(param);
+ }
+
+ @Override
+ protected String getTopicName() {
+ return MsgTopics.WORKFLOW_EVENT_PUSH;
+ }
+
+ @Override
+ public MqProcessEventVo formattedValue(BusinessDataVo businessDataVo) {
+ if (businessDataVo==null) {
+ return null;
+ }
+ String processNumber = businessDataVo.getProcessNumber();
+ if (StringUtils.isEmpty(processNumber)) {
+ throw new JiMuBizException("未获取到流程编号!");
+ }
+ BpmBusinessProcessService bpmBusinessProcessService = SpringBeanUtils.getBean(BpmBusinessProcessService.class);
+ BpmBusinessProcess bpmBusinessProcess = bpmBusinessProcessService.getBpmBusinessProcess(processNumber);
+ if (bpmBusinessProcess==null) {
+ return null;
+ }
+ MqProcessEventVo mqProcessEventVo = new MqProcessEventVo();
+ mqProcessEventVo.setProcessCode(processNumber);
+ mqProcessEventVo.setBusinessId(bpmBusinessProcess.getBusinessId());
+ mqProcessEventVo.setProcInstId(bpmBusinessProcess.getProcInstId());
+ mqProcessEventVo.setButtonOperationType(businessDataVo.getMsgProcessEventEnum().getCode());
+ mqProcessEventVo.setTaskId(businessDataVo.getTaskId());
+ mqProcessEventVo.setOpTime(new Date());
+ mqProcessEventVo.setOperationUserId(SecurityUtils.getLogInEmpIdSafe());
+ return mqProcessEventVo;
+ }
+}
diff --git a/antflow-base/src/main/java/org/openoa/base/util/Retryer.java b/antflow-base/src/main/java/org/openoa/base/util/Retryer.java
new file mode 100644
index 0000000..166978f
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/util/Retryer.java
@@ -0,0 +1,51 @@
+package org.openoa.base.util;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+public interface Retryer extends Cloneable {
+
+ void continueOrPropagate(RuntimeException e);
+
+ class Default implements Retryer {
+ private long period;
+ private long maxPeriod;
+ private int attempt;
+ private int maxAttempts;
+
+ public Default() {
+ this(SECONDS.toMillis(1), 3);
+ }
+
+ public Default(long period, int maxAttempts){
+ this.period = period;
+ this.maxPeriod = SECONDS.toMillis(10);
+ this.maxAttempts = maxAttempts;
+ this.attempt = 1;
+ }
+
+ public Default(long period, long maxPeriod, int maxAttempts){
+ this.period = period;
+ this.maxPeriod = maxPeriod;
+ this.maxAttempts = maxAttempts;
+ this.attempt = 1;
+ }
+
+ @Override
+ public void continueOrPropagate(RuntimeException e) {
+ if (attempt++ >= maxAttempts) {
+ throw e;
+ }
+ try {
+ Thread.sleep(nextMaxInterval());
+ } catch (InterruptedException ignored) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ long nextMaxInterval() {
+ long interval = (long) (period * Math.pow(1.5, attempt - 1.0));
+ return interval > maxPeriod ? maxPeriod : interval;
+ }
+
+ }
+}
diff --git a/antflow-base/src/main/java/org/openoa/base/vo/BusinessDataVo.java b/antflow-base/src/main/java/org/openoa/base/vo/BusinessDataVo.java
index f12956e..edb9582 100644
--- a/antflow-base/src/main/java/org/openoa/base/vo/BusinessDataVo.java
+++ b/antflow-base/src/main/java/org/openoa/base/vo/BusinessDataVo.java
@@ -5,6 +5,7 @@
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
+import org.openoa.base.constant.enums.MsgProcessEventEnum;
import org.openoa.base.dto.PageDto;
import java.util.List;
@@ -229,4 +230,6 @@ public class BusinessDataVo extends PageDto {
* level nodes
*/
private ListoutSideLevelNodes;
+
+ private MsgProcessEventEnum msgProcessEventEnum;
}
diff --git a/antflow-base/src/main/java/org/openoa/base/vo/MethodReplayDTO.java b/antflow-base/src/main/java/org/openoa/base/vo/MethodReplayDTO.java
new file mode 100644
index 0000000..f40afe0
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/vo/MethodReplayDTO.java
@@ -0,0 +1,19 @@
+package org.openoa.base.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class MethodReplayDTO {
+
+ private List delIds;
+ private Long id;
+ private String projectName;
+ private String className;
+ private String methodName;
+ private String args;
+ private String nowTime;
+ private String errorMsg;
+
+}
diff --git a/antflow-base/src/main/java/org/openoa/base/vo/MqProcessEventVo.java b/antflow-base/src/main/java/org/openoa/base/vo/MqProcessEventVo.java
new file mode 100644
index 0000000..7f63e94
--- /dev/null
+++ b/antflow-base/src/main/java/org/openoa/base/vo/MqProcessEventVo.java
@@ -0,0 +1,50 @@
+package org.openoa.base.vo;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.openoa.base.constant.enums.MsgProcessEventEnum;
+
+import java.util.Date;
+
+/**
+ * 工作流
+ * @Author tylerzhou
+ */
+@Getter
+@Setter
+public class MqProcessEventVo {
+ /**
+ * 流程编号(必填)
+ *
+ */
+ private String processCode;
+ /**
+ * 流程formCode
+ */
+ private String formCode;
+ /**
+ * 流程实例id
+ */
+ private String procInstId;
+ /**
+ * 业务表businessId
+ */
+ private String businessId;
+ /**
+ * 流程任务节点的id
+ */
+ private String taskId;
+ /**
+ * 操作时间
+ */
+ private Date opTime;
+ /**
+ * 按钮事件类型 (必填)
+ * @see MsgProcessEventEnum
+ */
+ private Integer buttonOperationType;
+ /**
+ * 触发当前按钮事件人员id
+ */
+ private String operationUserId;
+}
diff --git a/antflow-engine/pom.xml b/antflow-engine/pom.xml
index b74190c..fbf56c7 100644
--- a/antflow-engine/pom.xml
+++ b/antflow-engine/pom.xml
@@ -172,6 +172,12 @@
8.0.27
provided
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.14
+ provided
+
org.aspectj
aspectjweaver
diff --git a/antflow-engine/src/main/java/org/openoa/engine/bpmnconf/service/biz/BpmBusinessProcessServiceImpl.java b/antflow-engine/src/main/java/org/openoa/engine/bpmnconf/service/biz/BpmBusinessProcessServiceImpl.java
index d66e158..521ac6f 100644
--- a/antflow-engine/src/main/java/org/openoa/engine/bpmnconf/service/biz/BpmBusinessProcessServiceImpl.java
+++ b/antflow-engine/src/main/java/org/openoa/engine/bpmnconf/service/biz/BpmBusinessProcessServiceImpl.java
@@ -6,6 +6,7 @@
import org.openoa.base.constant.enums.ProcessEnum;
import org.openoa.base.constant.enums.ProcessStateEnum;
import org.openoa.base.entity.BpmBusinessProcess;
+import org.openoa.base.interf.BpmBusinessProcessService;
import org.openoa.engine.bpmnconf.mapper.BpmBusinessProcessMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -22,7 +23,7 @@
*/
@Slf4j
@Service
-public class BpmBusinessProcessServiceImpl extends ServiceImpl {
+public class BpmBusinessProcessServiceImpl extends ServiceImpl implements BpmBusinessProcessService {
@Autowired
diff --git a/antflow-engine/src/main/java/org/openoa/engine/bpmnconf/service/biz/callback/BusinessCallBackFactory.java b/antflow-engine/src/main/java/org/openoa/engine/bpmnconf/service/biz/callback/BusinessCallBackFactory.java
new file mode 100644
index 0000000..99da677
--- /dev/null
+++ b/antflow-engine/src/main/java/org/openoa/engine/bpmnconf/service/biz/callback/BusinessCallBackFactory.java
@@ -0,0 +1,87 @@
+package org.openoa.engine.bpmnconf.service.biz.callback;
+
+import lombok.extern.slf4j.Slf4j;
+import org.openoa.base.constant.enums.BusinessCallbackEnum;
+import org.openoa.base.interf.BusinessCallBackAdaptor;
+import org.openoa.base.service.BusinessCallBackFace;
+import org.openoa.base.util.Retryer;
+import org.springframework.util.CollectionUtils;
+
+import java.util.*;
+
+/**
+ * @Author tylerzhou
+ */
+@Slf4j
+public class BusinessCallBackFactory {
+ private static Map> allAdaptorsOfType = null;
+
+ static {
+ allAdaptorsOfType = BusinessCallbackEnum.getAllAdaptorsByType();
+ }
+
+ private BusinessCallBackFactory() {
+
+ }
+
+ private static final BusinessCallBackFactory instance = new BusinessCallBackFactory();
+
+ public static BusinessCallBackFactory build() {
+ return instance;
+ }
+
+ /**
+ * 对单个adaptor的回调
+ *
+ * @param params
+ * @param callbackEnum
+ * @param code {@link BusinessCallBackFace#getCode()}
+ */
+ public void doCallBack(T params, BusinessCallbackEnum callbackEnum, Integer code) {
+ List callBackAdaptorsOfType = getCallBackAdaptorsOfType(callbackEnum);
+ if (CollectionUtils.isEmpty(callBackAdaptorsOfType)) {
+ return;
+ }
+ Class extends BusinessCallBackFace> clsz = callbackEnum.getClsz();
+
+ BusinessCallBackFace[] enumFaces = clsz.getEnumConstants();
+ for (BusinessCallBackFace callBackFace : enumFaces) {
+ if(Objects.equals(callBackFace.getCode(),code)){
+ BusinessCallBackAdaptor businessCallBackAdaptor = callBackFace.getClszAdaptorInstance();
+ Retryer retryer=new Retryer.Default();
+ while (true){
+ try {
+ businessCallBackAdaptor.doCallBack(params);
+ break;
+ }catch (RuntimeException e){
+ retryer.continueOrPropagate(e);
+ }
+ }
+
+ }
+ }
+
+ }
+
+ /**
+ * 对某一业务类型下的所有adaptor回调
+ *
+ * @param params
+ * @param callbackEnum
+ */
+ public void doCallBacks(T params, BusinessCallbackEnum callbackEnum) {
+ List callBackAdaptors = this.getCallBackAdaptorsOfType(callbackEnum);
+ for (BusinessCallBackAdaptor callBackAdaptor : callBackAdaptors) {
+ try {
+ callBackAdaptor.doCallBack(params);
+ } catch (Exception e) {
+ log.error("error while executing callback method,className:{}", callBackAdaptor.getClass().getName(), e);
+ }
+ }
+ }
+
+ private List getCallBackAdaptorsOfType(BusinessCallbackEnum businessCallbackEnum) {
+ List businessCallBackAdaptors = allAdaptorsOfType.get(businessCallbackEnum);
+ return businessCallBackAdaptors;
+ }
+}
diff --git a/antflow-engine/src/main/java/org/openoa/engine/bpmnconf/util/JsonUtils.java b/antflow-engine/src/main/java/org/openoa/engine/bpmnconf/util/JsonUtils.java
new file mode 100644
index 0000000..492a3a7
--- /dev/null
+++ b/antflow-engine/src/main/java/org/openoa/engine/bpmnconf/util/JsonUtils.java
@@ -0,0 +1,41 @@
+package org.openoa.engine.bpmnconf.util;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+/**
+ * @Author tylerzhou
+ */
+public class JsonUtils {
+ private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);
+ private static final ObjectMapper mapper;
+
+ static {
+ mapper=new ObjectMapper();
+ mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ //mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
+ }
+ public static String transfer2JsonString(T value) {
+
+ StringWriter sw = new StringWriter();
+
+ try {
+ JsonGenerator gen = (new JsonFactory()).createGenerator(sw);
+ mapper.writeValue(gen, value);
+ gen.close();
+ } catch (IOException var5) {
+ logger.error(var5.getMessage(), "value=[" + value + "]");
+ }
+
+ return sw.toString();
+ }
+}
diff --git a/antflow-engine/src/main/java/org/openoa/engine/bpmnconf/util/MsgSendMqUtil.java b/antflow-engine/src/main/java/org/openoa/engine/bpmnconf/util/MsgSendMqUtil.java
new file mode 100644
index 0000000..14b3225
--- /dev/null
+++ b/antflow-engine/src/main/java/org/openoa/engine/bpmnconf/util/MsgSendMqUtil.java
@@ -0,0 +1,30 @@
+package org.openoa.engine.bpmnconf.util;
+
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.openoa.base.constant.enums.BusinessCallbackEnum;
+import org.openoa.base.constant.enums.ProcessBusinessCallBackTypeEnum;
+import org.openoa.base.vo.BusinessDataVo;
+import org.openoa.engine.bpmnconf.service.biz.callback.BusinessCallBackFactory;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @Author tylerzhou
+ */
+@Slf4j
+public class MsgSendMqUtil {
+
+
+ public static void sendProcessEventChangeMessageCallBack(BusinessDataVo vo){
+ if(vo==null){
+ return;
+ }
+ try {
+ BusinessCallBackFactory.build().doCallBack(vo, BusinessCallbackEnum.PROCESS_EVENT_CALLBACK, ProcessBusinessCallBackTypeEnum.Send_MQ_Message.getCode());
+ }catch (Exception e){
+ log.error("流程消息发送失败,{}", JsonUtils.transfer2JsonString(vo),e);
+ }
+ }
+}
diff --git a/antflow-engine/src/main/java/org/openoa/engine/conf/config/RestInterceptor.java b/antflow-engine/src/main/java/org/openoa/engine/conf/config/RestInterceptor.java
new file mode 100644
index 0000000..8283ce5
--- /dev/null
+++ b/antflow-engine/src/main/java/org/openoa/engine/conf/config/RestInterceptor.java
@@ -0,0 +1,90 @@
+package org.openoa.engine.conf.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.openoa.base.exception.JiMuBizException;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StopWatch;
+import org.springframework.util.StringUtils;
+
+import javax.annotation.Resource;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ *
+ * @className: RestInterceptor
+ * @author: Tyler Zhou
+ **/
+@Slf4j
+@Component
+public class RestInterceptor implements ClientHttpRequestInterceptor {
+
+
+
+ @Override
+ public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
+
+ StopWatch watch = new StopWatch();
+ watch.start();
+ ClientHttpResponse execute = clientHttpRequestExecution.execute(httpRequest, bytes);
+ watch.stop();
+ long totalTimeMillis = watch.getTotalTimeMillis();
+ intermediateProcessRequest(httpRequest, bytes);
+ intermediateProcessResponse(execute, totalTimeMillis);
+ return execute;
+ }
+
+ private String getRequestBody(byte[] body) throws UnsupportedEncodingException {
+ if (body != null && body.length > 0) {
+ return (new String(body, StandardCharsets.UTF_8));
+ } else {
+ return null;
+ }
+ }
+
+ private void intermediateProcessRequest(HttpRequest request, byte[] body) throws IOException {
+ log.info("request method : " + request.getMethod() + " request body : " + getRequestBody(body));
+ }
+
+ private void intermediateProcessResponse(ClientHttpResponse response, long totalMils) throws IOException {
+ String body = getBodyString(response);
+ HttpStatus statusCode = response.getStatusCode();
+ double totalSeconds = totalMils / 1000d;
+ log.info("response status code:{} response status text:{},请求耗时:{}, response body :{}",
+ response.getStatusCode(), response.getStatusText(),totalSeconds, body);
+
+ if(HttpStatus.TOO_MANY_REQUESTS==statusCode){
+ throw new JiMuBizException("请求限流");
+ }
+ }
+
+ private String getBodyString(ClientHttpResponse response) throws IOException {
+ try {
+ if (response != null && response.getBody() != null) // &&
+ {
+ StringBuilder inputStringBuilder = new StringBuilder();
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), StandardCharsets.UTF_8));
+ String line = bufferedReader.readLine();
+ while (line != null) {
+ inputStringBuilder.append(line);
+ inputStringBuilder.append('\n');
+ line = bufferedReader.readLine();
+ }
+ return inputStringBuilder.toString();
+ } else {
+ return null;
+ }
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ return null;
+ }
+ }
+}
diff --git a/antflow-engine/src/main/java/org/openoa/engine/conf/config/RestTemplateConfig.java b/antflow-engine/src/main/java/org/openoa/engine/conf/config/RestTemplateConfig.java
new file mode 100644
index 0000000..ce2f491
--- /dev/null
+++ b/antflow-engine/src/main/java/org/openoa/engine/conf/config/RestTemplateConfig.java
@@ -0,0 +1,130 @@
+package org.openoa.engine.conf.config;
+
+import com.alibaba.fastjson.parser.Feature;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.alibaba.fastjson.support.config.FastJsonConfig;
+import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
+import org.apache.http.HeaderElement;
+import org.apache.http.HeaderElementIterator;
+import org.apache.http.client.HttpClient;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.message.BasicHeaderElementIterator;
+import org.apache.http.protocol.HTTP;
+import org.openoa.engine.vo.HttpClientProperties;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.BufferingClientHttpRequestFactory;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.web.client.RestTemplate;
+
+import javax.net.ssl.SSLContext;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@Configuration
+public class RestTemplateConfig {
+
+ @Bean
+ RestTemplate restTemplate(@Qualifier("clientHttpRequestFactory") ClientHttpRequestFactory requestFactory,RestInterceptor restInterceptor) {
+ requestFactory = requestFactory instanceof BufferingClientHttpRequestFactory ? requestFactory : new BufferingClientHttpRequestFactory(requestFactory);
+ RestTemplate restTemplate = new RestTemplate(requestFactory);
+ List> messageConverters = restTemplate.getMessageConverters();
+ for (HttpMessageConverter> c : messageConverters) {
+ if (c instanceof StringHttpMessageConverter) {
+ ((StringHttpMessageConverter) c).setDefaultCharset(StandardCharsets.UTF_8);
+ }
+ }
+ restTemplate.getInterceptors().add(restInterceptor);
+ restTemplate.getMessageConverters().add(0,this.getFastJsonConverter());
+ return restTemplate;
+
+ }
+
+ @Bean
+ @ConfigurationProperties(prefix = "spring.resttemplate")
+ HttpClientProperties httpClientProperties() {
+ return new HttpClientProperties();
+ }
+
+ @Bean(name = "clientHttpRequestFactory")
+ ClientHttpRequestFactory clientHttpRequestFactory(HttpClientProperties httpClientProperties) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
+ //如果不使用HttpClient的连接池,则使用restTemplate默认的SimpleClientHttpRequestFactory,底层基于HttpURLConnection
+
+ if (!httpClientProperties.isUseHttpClientPool()) {
+ SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+ factory.setConnectTimeout(httpClientProperties.getConnectTimeout());
+ factory.setReadTimeout(httpClientProperties.getReadTimeout());
+ return factory;
+ }
+
+ TrustStrategy acceptingTrustStrategy = (x509Certificates, s) -> true;
+ SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
+ SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
+
+ ConnectionKeepAliveStrategy myStrategy = (response, context) -> {
+ HeaderElementIterator it = new BasicHeaderElementIterator
+ (response.headerIterator(HTTP.CONN_KEEP_ALIVE));
+ while (it.hasNext()) {
+ HeaderElement he = it.nextElement();
+ String param = he.getName();
+ String value = he.getValue();
+ if (value != null && param.equalsIgnoreCase
+ ("timeout")) {
+ return Long.parseLong(value) * 1000;
+ }
+ }
+ return 5 * 1000;
+ };
+ //HttpClient4.3及以上版本不手动设置HttpClientConnectionManager,默认就会使用连接池PoolingHttpClientConnectionManager
+ HttpClient httpClient = HttpClientBuilder
+ .create()
+ .setMaxConnTotal(httpClientProperties.getMaxTotalConnect())
+ .setMaxConnPerRoute(httpClientProperties.getMaxConnectPerRoute())
+ .evictExpiredConnections()
+ .evictIdleConnections(5000, TimeUnit.MILLISECONDS)
+ .setSSLSocketFactory(csf)
+ .setKeepAliveStrategy(myStrategy)
+ .build();
+
+ HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
+ factory.setConnectTimeout(httpClientProperties.getConnectTimeout());
+ factory.setReadTimeout(httpClientProperties.getReadTimeout());
+ factory.setConnectionRequestTimeout(httpClientProperties.getConnectionRequestTimeout());
+ return factory;
+ }
+ //自定义报文转换器
+ FastJsonHttpMessageConverter getFastJsonConverter(){
+
+ FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
+ FastJsonConfig fastJsonConfig = new FastJsonConfig();
+ // 设置feature
+ fastJsonConfig.setFeatures(Feature.AllowISO8601DateFormat);
+ // 设置日期格式、关闭循环引用检测
+ fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteDateUseDateFormat,
+ SerializerFeature.DisableCircularReferenceDetect);
+ //指定全局日期格式
+ fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
+ fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
+
+ //设定MediaType
+ fastJsonHttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON,MediaType.APPLICATION_JSON_UTF8));
+ return fastJsonHttpMessageConverter;
+ }
+
+}
\ No newline at end of file
diff --git a/antflow-engine/src/main/java/org/openoa/engine/factory/CallbackAdaptor.java b/antflow-engine/src/main/java/org/openoa/engine/factory/CallbackAdaptor.java
new file mode 100644
index 0000000..08844b1
--- /dev/null
+++ b/antflow-engine/src/main/java/org/openoa/engine/factory/CallbackAdaptor.java
@@ -0,0 +1,36 @@
+package org.openoa.engine.factory;
+
+
+import org.openoa.base.vo.BpmnConfVo;
+import org.openoa.engine.vo.CallbackReqVo;
+import org.openoa.engine.vo.CallbackRespVo;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+public interface CallbackAdaptor {
+
+ req formatRequest(BpmnConfVo bpmnConfVo);
+
+ resp formatResponce(String resultJson);
+
+ /**
+ * 获得创建的空的回调返回对象
+ *
+ * @return
+ * @throws IllegalAccessException
+ * @throws InstantiationException
+ */
+ default resp getNewRespObj() throws IllegalAccessException, InstantiationException {
+ Type[] genericInterfaces = this.getClass().getGenericInterfaces();
+
+ Type type = genericInterfaces[0];
+
+ ParameterizedType p = (ParameterizedType) type;
+
+ Class> cls = (Class) p.getActualTypeArguments()[1];
+
+ return (resp) cls.newInstance();
+ }
+
+}
diff --git a/antflow-engine/src/main/java/org/openoa/engine/factory/ThirdPartyCallbackFactory.java b/antflow-engine/src/main/java/org/openoa/engine/factory/ThirdPartyCallbackFactory.java
new file mode 100644
index 0000000..92d6c46
--- /dev/null
+++ b/antflow-engine/src/main/java/org/openoa/engine/factory/ThirdPartyCallbackFactory.java
@@ -0,0 +1,342 @@
+package org.openoa.engine.factory;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.collect.Maps;
+
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.EntityUtils;
+import org.openoa.base.constant.enums.CallbackTypeEnum;
+import org.openoa.base.exception.JiMuBizException;
+import org.openoa.base.service.ConfigUtil;
+import org.openoa.base.util.DateUtil;
+import org.openoa.base.util.SecurityUtils;
+import org.openoa.base.util.SpringBeanUtils;
+import org.openoa.base.vo.BaseIdTranStruVo;
+import org.openoa.base.vo.BpmVerifyInfoVo;
+import org.openoa.base.vo.BpmnConfVo;
+import org.openoa.engine.bpmnconf.confentity.OutSideBpmCallbackUrlConf;
+import org.openoa.engine.bpmnconf.service.biz.BpmVerifyInfoBizServiceImpl;
+import org.openoa.engine.bpmnconf.service.impl.OutSideBpmBusinessPartyServiceImpl;
+import org.openoa.engine.bpmnconf.service.impl.OutSideBpmCallbackUrlConfServiceImpl;
+import org.openoa.engine.bpmnconf.util.JsonUtils;
+import org.openoa.engine.vo.CallbackReqVo;
+import org.openoa.engine.vo.CallbackRespVo;
+import org.openoa.engine.vo.OutSideBpmAccessProcessRecordVo;
+import org.springframework.http.MediaType;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.URLEncoder;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.stream.Collectors;
+
+import static org.openoa.base.constant.enums.OpLogFlagEnum.BusinessException;
+
+@Slf4j
+@Data
+public class ThirdPartyCallbackFactory {
+
+ private static volatile ThirdPartyCallbackFactory thirdPartyCallbackFactory;
+
+ private static final ThreadLocalRandom random = ThreadLocalRandom.current();
+
+ private ThirdPartyCallbackFactory() {
+
+ }
+
+ /**
+ * CallbackFactory构建方法
+ *
+ * @return
+ */
+ public static ThirdPartyCallbackFactory build() {
+
+ if (thirdPartyCallbackFactory==null) {
+ synchronized (ThirdPartyCallbackFactory.class){
+ if(thirdPartyCallbackFactory==null){
+ thirdPartyCallbackFactory = new ThirdPartyCallbackFactory();
+ }
+ }
+ }
+ return thirdPartyCallbackFactory;
+ }
+
+ /**
+ * 执行回调
+ *
+ * @param url
+ * @param callbackTypeEnum
+ * @param bpmnConfVo
+ * @param
+ * @return
+ */
+ public T doCallback(String url, CallbackTypeEnum callbackTypeEnum, BpmnConfVo bpmnConfVo,
+ String processNum, String businessId) {
+
+ boolean callBackSwitch = ConfigUtil.getOutsideCallbackOn();
+
+ CallbackReqVo callbackReqVo = null;
+
+ String resultJson = StringUtils.EMPTY;
+
+ //设置请求头
+ Map heads = Maps.newHashMap();
+
+ try {
+
+ if (ObjectUtils.isEmpty(bpmnConfVo.getBusinessPartyId())) {
+ throw new JiMuBizException("业务方缺失,操作失败!");
+ }
+
+ CallbackAdaptor callbackAdaptor = getCallbackAdaptor(callbackTypeEnum.getBeanId());
+
+ if (callbackAdaptor==null) {
+ return null;
+ }
+
+ //关闭回调逻辑
+ if (!callBackSwitch) {
+ CallbackRespVo newRespObj = callbackAdaptor.getNewRespObj();
+ if (!StringUtils.isEmpty(businessId)) {
+ Integer rendomNum = Math.abs(random.nextInt(Integer.MAX_VALUE));
+ newRespObj.setBusinessId(String.valueOf(rendomNum));
+ }
+ return (T) newRespObj;
+ }
+
+ callbackReqVo = callbackAdaptor.formatRequest(bpmnConfVo);
+
+ //事件类型
+ callbackReqVo.setEventType(callbackTypeEnum.getMark());
+
+ //查询业务方标识
+ OutSideBpmBusinessPartyServiceImpl bean = SpringBeanUtils.getBean(OutSideBpmBusinessPartyServiceImpl.class);
+ String businessPartyMarkById = bean.getBusinessPartyMarkById(bpmnConfVo.getBusinessPartyId());
+
+ //设置入参业务方标识
+ callbackReqVo.setBusinessPartyMark(businessPartyMarkById);
+
+ //表单编号
+ callbackReqVo.setFormCode(formatFormCode(businessPartyMarkById, bpmnConfVo.getFormCode()));
+
+ //流程编号
+ callbackReqVo.setProcessNum(processNum);
+
+ //如果流程编号不为空则根据流程编号查询流程路径
+ if (!StringUtils.isEmpty(processNum)) {
+ BpmVerifyInfoBizServiceImpl bpmVerifyInfoNewService = SpringBeanUtils.getBean(BpmVerifyInfoBizServiceImpl.class);
+ boolean finishFlag = callbackTypeEnum.equals(CallbackTypeEnum.PROC_FINISH_CALL_BACK);
+ List bpmVerifyInfoVos = bpmVerifyInfoNewService.getBpmVerifyInfoVos(processNum,finishFlag);
+ callbackReqVo.setProcessRecord(bpmVerifyInfoVos
+ .stream()
+ .map(o -> OutSideBpmAccessProcessRecordVo
+ .builder()
+ .nodeName(o.getTaskName())
+ .approvalTime(Optional.ofNullable(o.getVerifyDate())
+ .map(od -> DateUtil.SDF_DATETIME_PATTERN.format(od))
+ .orElse(StringUtils.EMPTY))
+ .approvalStatusName(o.getVerifyStatusName())
+ .approvalUserName(o.getVerifyUserName())
+ .build())
+ .collect(Collectors.toList()));
+ }
+
+ //对接方返回业务编号
+ callbackReqVo.setBusinessId(businessId);
+
+ //获取当前登录人信息
+ BaseIdTranStruVo loginedEmployee = SecurityUtils.getLogInEmpInfo();
+ if (ObjectUtils.isEmpty(loginedEmployee)){
+ loginedEmployee = new BaseIdTranStruVo();
+ }
+ //查询外部流程url配置获取"用户标识"及"api-key"
+ OutSideBpmCallbackUrlConfServiceImpl outSideBpmCallbackUrlConfService = SpringBeanUtils.getBean(OutSideBpmCallbackUrlConfServiceImpl.class);
+ OutSideBpmCallbackUrlConf outSideBpmCallbackUrlConf = outSideBpmCallbackUrlConfService.getOutSideBpmCallbackUrlConf(bpmnConfVo.getId(), bpmnConfVo.getBusinessPartyId());
+
+ heads.put("sso-service", getCurrentSysDomain());//域名
+ if (outSideBpmCallbackUrlConf!=null) {
+ heads.put("api-client-id", outSideBpmCallbackUrlConf.getApiClientId());//用户标识
+ heads.put("api-client-secret", outSideBpmCallbackUrlConf.getApiClientSecrent());//api-key
+ }
+ heads.put("sso-uid", loginedEmployee.getId());//当前登录人username
+ heads.put("sso-name", URLEncoder.encode(loginedEmployee.getName(), "UTF-8"));//当前登录人真实姓名
+ log.info("执行外部工作流回调,request:{} , processNumber:{} , callBackUrl:{} , 操作人:{} ,请求参数:{}",callbackTypeEnum.getDesc() , processNum, url, loginedEmployee.getName(), JsonUtils.transfer2JsonString(callbackReqVo));
+
+ resultJson = doPost(url, heads, callbackReqVo);
+ log.info("执行外部工作流回调,response:{} , processNumber:{} , callBackUrl:{} , 操作人:{} ,请求参数:{} , response:{}",callbackTypeEnum.getDesc() , processNum, url, loginedEmployee.getName(), JsonUtils.transfer2JsonString(callbackReqVo), resultJson);
+
+ if (StringUtils.isEmpty(resultJson)) {
+ return null;
+ }
+
+ JSONObject resultObject = JSON.parseObject(resultJson);
+
+ Object status = resultObject.get("status");
+
+ String successMark = "000000";
+
+ if (!ObjectUtils.isEmpty(status) && successMark.equals(status.toString())) {
+ CallbackRespVo callbackRespVo = callbackAdaptor.formatResponce(resultJson);
+ if (callbackRespVo!=null) {
+
+ //设置出参业务方标识
+ callbackRespVo.setBusinessPartyMark(businessPartyMarkById);
+
+ //返回结果
+ return (T) callbackRespVo;
+ }
+ } else {
+
+ Object message = resultObject.get("message");
+
+ String messageStr = StringUtils.EMPTY;
+ if (!ObjectUtils.isEmpty(message)) {
+ messageStr = message.toString();
+ }
+
+ if (!StringUtils.isEmpty(messageStr)) {
+ throw new JiMuBizException(messageStr);
+ } else {
+ throw new JiMuBizException("工作流对外服务回调失败");
+ }
+
+ }
+
+ } catch (JiMuBizException e) {
+ log.error("工作流对外服务回调失败,回调类型:{},请求头信息{},入参:{},出参:{}",
+ callbackTypeEnum.getMark(),
+ JSON.toJSONString(heads),
+ JSON.toJSONString(Optional.ofNullable(callbackReqVo).orElse(new CallbackReqVo())),
+ resultJson, e);
+ throw new JiMuBizException(e.getMessage());
+ } catch (Exception e) {
+ log.error("工作流对外服务回调失败,回调类型:{},请求头信息{},入参:{},出参:{}",
+ callbackTypeEnum.getMark(),
+ JSON.toJSONString(heads),
+ JSON.toJSONString(Optional.ofNullable(callbackReqVo).orElse(new CallbackReqVo())),
+ resultJson, e);
+ return null;
+ }
+
+ return null;
+ }
+
+ /**
+ * 格式化表单编号
+ *
+ * @param businessPartyMarkById
+ * @param formCode
+ * @return
+ */
+ public String formatFormCode(String businessPartyMarkById, String formCode) {
+ if (formCode.startsWith(businessPartyMarkById)) {
+ return formCode;
+ }
+ return StringUtils.join(businessPartyMarkById, "_", formCode);
+ }
+
+ /**
+ * 获得回调适配
+ *
+ * @param eventType
+ * @return
+ */
+ private CallbackAdaptor getCallbackAdaptor(String eventType) {
+ return SpringBeanUtils.getBean(eventType, CallbackAdaptor.class);
+ }
+
+ /**
+ * 获得OA域名
+ *
+ * @return
+ */
+ private String getCurrentSysDomain() {
+
+ HttpServletRequest request = getHttpServletRequest();
+
+ String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
+
+ return basePath;
+ }
+
+ /**
+ * 获得HttpServletRequest对象
+ *
+ * @return
+ */
+ private HttpServletRequest getHttpServletRequest() {
+ ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+ return requestAttributes.getRequest();
+ }
+
+ /**
+ * 调用POST接口调用
+ *
+ * @param url
+ * @param heads
+ * @param object
+ * @return
+ * @throws Exception
+ */
+ private String doPost(String url, Map heads, Object object) throws Exception {
+ String resultStr;
+
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+
+ // 创建http POST请求
+ HttpPost httpPost = new HttpPost(url);
+
+ CloseableHttpResponse response = null;
+
+ try {
+
+ //头部配置Map不为空则向头部追加参数
+ if (!CollectionUtils.isEmpty(heads)) {
+ heads.forEach((key, val) -> {
+ httpPost.addHeader(key, val);
+ });
+ }
+
+ httpPost.addHeader(HTTP.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
+
+ String input = JSON.toJSONString(object);
+
+ StringEntity stringEntity = new StringEntity(input, "UTF-8");
+ stringEntity.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
+
+ //设置参数到请求对象中
+ httpPost.setEntity(stringEntity);
+
+ response = httpclient.execute(httpPost);
+
+ resultStr = EntityUtils.toString(response.getEntity(), "UTF-8");
+
+ } catch (Exception e) {
+ log.error("工作流对外服务回调POST接口失败:", e);
+ throw new Exception(e);
+ } finally {
+ if (response!=null) {
+ response.close();
+ }
+ httpclient.close();
+ }
+
+ return resultStr;
+ }
+
+}
diff --git a/antflow-engine/src/main/java/org/openoa/engine/utils/HttpClientUtil.java b/antflow-engine/src/main/java/org/openoa/engine/utils/HttpClientUtil.java
new file mode 100644
index 0000000..cd95192
--- /dev/null
+++ b/antflow-engine/src/main/java/org/openoa/engine/utils/HttpClientUtil.java
@@ -0,0 +1,100 @@
+package org.openoa.engine.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.client.utils.URIBuilder;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.http.*;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Map;
+
+/**
+ * 请不要在应用未启动之前使用此此中静态方法
+ * @className: HttpClientUtil
+ * @author: Tyler Zhou
+ **/
+@Component
+@Slf4j
+public class HttpClientUtil implements ApplicationContextAware {
+ private static RestTemplate restTemplate;
+
+ public static String doGet(String url) {
+ return doGet(url,null,null,String.class);
+ }
+ public static String doGet(String url, HttpHeaders headers) {
+ return doGet(url,headers, null,String.class);
+ }
+ public static T doGet(String url,HttpHeaders headers, Map param,Class cls){
+
+ try {
+ // 创建uri
+ URIBuilder builder = new URIBuilder(url);
+ if (param != null) {
+ for (String key : param.keySet()) {
+ builder.addParameter(key, param.get(key));
+ }
+ }
+ URI uri = builder.build();
+ HttpEntity requestEntity = new HttpEntity<>(null,headers);
+
+ ResponseEntity exchange = restTemplate.exchange(uri,HttpMethod.GET,requestEntity,cls);
+ T body = exchange.getBody();
+ return body;
+ } catch (URISyntaxException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static String doPostForm(String url, MultiValueMapparams){
+ return doPostForm(url,params,null,String.class);
+ }
+ public static String doPostForm(String url, MultiValueMap params, HttpHeaders headers){
+ return doPostForm(url,params,headers,String.class);
+ }
+ public static T doPostForm(String url, MultiValueMap params, HttpHeaders headers, Class cls){
+ if(headers==null){
+ headers=new HttpHeaders();
+ }
+ headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+ HttpEntity> requestEntity = new HttpEntity<>(params, headers);
+ ResponseEntity exchange = restTemplate.exchange(url, HttpMethod.POST, requestEntity, cls);
+ T body = exchange.getBody();
+ return body;
+ }
+ public static String doPostJson(String url, Object body){
+ return doPostJson(url,body,null,String.class);
+ }
+ public static String doPostJson(String url, Object body, HttpHeaders headers){
+ return doPostJson(url,body,headers,String.class);
+ }
+ public static String doPostXml(String url,Object body){
+ return doPostContent(url,body,null,MediaType.APPLICATION_XML,String.class);
+ }
+ public static T doPostJson(String url, Object body, HttpHeaders headers, Class tClass){
+ return doPostContent(url,body,headers,MediaType.APPLICATION_JSON,tClass);
+ }
+
+ public static T doPostContent(String url,Object body,HttpHeaders headers,MediaType mediaType,Class tClass){
+ if(headers==null){
+ headers=new HttpHeaders();
+ }
+ headers.setContentType(mediaType);
+ HttpEntity
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ org.apache.httpcomponents
+ httpmime
+
diff --git a/web/src/main/resources/script/bpm_init_db.sql b/web/src/main/resources/script/bpm_init_db.sql
index 9586c4c..d19b052 100644
--- a/web/src/main/resources/script/bpm_init_db.sql
+++ b/web/src/main/resources/script/bpm_init_db.sql
@@ -686,6 +686,26 @@ CREATE TABLE if not exists `t_user_email_send`
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='user email send';
+create table if not exists t_method_replay
+(
+ id int auto_increment
+ primary key,
+ PROJECT_NAME varchar(100) null comment 'project name',
+ CLASS_NAME varchar(255) null,
+ METHOD_NAME varchar(255) null,
+ PARAM_TYPE varchar(255) null,
+ ARGS text null,
+ NOW_TIME timestamp null,
+ ERROR_MSG text null,
+ ALREADY_REPLAY_TIMES int null,
+ MAX_REPLAY_TIMES int null
+)ENGINE = InnoDB
+ DEFAULT CHARSET = utf8mb4 comment 'method replay records';
+
+create index t_method_replay_NOW_TIME_index
+ on t_method_replay (NOW_TIME);
+
+
CREATE TABLE if not exists `t_user_entrust`
(
`id` int(11) NOT NULL AUTO_INCREMENT,