同步最新代码到简化分支

This commit is contained in:
YunaiV 2025-02-09 11:48:36 +08:00
parent 2f6fb86795
commit 31dc3bd841
12 changed files with 0 additions and 585 deletions

View File

@ -1,32 +0,0 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* BPM 自动去重的类型的枚举
*
* @author Lesan
*/
@Getter
@AllArgsConstructor
public enum BpmAutoApproveTypeEnum implements ArrayValuable<Integer> {
NONE(0, "不自动通过"),
APPROVE_ALL(1, "仅审批一次,后续重复的审批节点均自动通过"),
APPROVE_SEQUENT(2, "仅针对连续审批的节点自动通过");
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmAutoApproveTypeEnum::getType).toArray(Integer[]::new);
private final Integer type;
private final String name;
@Override
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -1,31 +0,0 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* BPM 延迟器类型枚举
*
* @author Lesan
*/
@Getter
@AllArgsConstructor
public enum BpmDelayTimerTypeEnum implements ArrayValuable<Integer> {
FIXED_TIME_DURATION(1, "固定时长"),
FIXED_DATE_TIME(2, "固定日期");
private final Integer type;
private final String name;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmDelayTimerTypeEnum::getType).toArray(Integer[]::new);
@Override
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -1,31 +0,0 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* BPM HTTP 请求参数设置类型用于 Simple 设计器任务监听器和触发器配置
*
* @author Lesan
*/
@Getter
@AllArgsConstructor
public enum BpmHttpRequestParamTypeEnum implements ArrayValuable<Integer> {
FIXED_VALUE(1, "固定值"),
FROM_FORM(2, "表单");
private final Integer type;
private final String name;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmHttpRequestParamTypeEnum::getType).toArray(Integer[]::new);
@Override
public Integer[] array() {
return ARRAYS;
}
}

View File

@ -1,43 +0,0 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* BPM Simple 触发器类型枚举
*
* @author jason
*/
@Getter
@AllArgsConstructor
public enum BpmTriggerTypeEnum implements ArrayValuable<Integer> {
HTTP_REQUEST(1, "发起 HTTP 请求"),
UPDATE_NORMAL_FORM(2, "更新流程表单"); // TODO @jasonFORM_UPDATE
/**
* 触发器执行动作类型
*/
private final Integer type;
/**
* 触发器执行动作描述
*/
private final String desc;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmTriggerTypeEnum::getType).toArray(Integer[]::new);
@Override
public Integer[] array() {
return ARRAYS;
}
public static BpmTriggerTypeEnum typeOf(Integer type) {
return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
}
}

View File

@ -1,24 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form;
import lombok.Data;
/**
* 流程表单字段 VO
*/
@Data
public class BpmFormFieldVO {
/**
* 字段类型
*/
private String type;
/**
* 字段标识
*/
private String field;
/**
* 字段标题
*/
private String title;
}

View File

@ -1,59 +0,0 @@
package cn.iocoder.yudao.module.bpm.dal.redis;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
/**
* BPM 流程 Id 编码的 Redis DAO
*
* @author Lesan
*/
@Repository
public class BpmProcessIdRedisDAO {
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* 生成序号使用定义的 processIdRule 规则生成
*
* @param processIdRule 规则
* @return 序号
*/
public String generate(BpmModelMetaInfoVO.ProcessIdRule processIdRule) {
// 生成日期前缀
String infix = "";
switch (processIdRule.getInfix()) {
case "DAY":
infix = DateUtil.format(LocalDateTime.now(), "yyyyMMDD");
break;
case "HOUR":
infix = DateUtil.format(LocalDateTime.now(), "yyyyMMDDHH");
break;
case "MINUTE":
infix = DateUtil.format(LocalDateTime.now(), "yyyyMMDDHHmm");
break;
case "SECOND":
infix = DateUtil.format(LocalDateTime.now(), "yyyyMMDDHHmmss");
break;
}
// 生成序号
String noPrefix = processIdRule.getPrefix() + infix + processIdRule.getPostfix();
String key = RedisKeyConstants.BPM_PROCESS_ID + noPrefix;
Long no = stringRedisTemplate.opsForValue().increment(key);
if (StrUtil.isEmpty(infix)) {
// 特殊没有前缀则不能过期不能每次都是从 0 开始
stringRedisTemplate.expire(key, Duration.ofDays(1L));
}
return noPrefix + String.format("%0" + processIdRule.getLength() + "d", no);
}
}

View File

@ -1,15 +0,0 @@
package cn.iocoder.yudao.module.bpm.dal.redis;
/**
* BPM Redis Key 枚举类
*
* @author 芋道源码
*/
public interface RedisKeyConstants {
/**
* 流程 ID 的缓存
*/
String BPM_PROCESS_ID = "bpm:process_id:";
}

View File

@ -1,55 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.EnumMap;
import java.util.List;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate.BEAN_NAME;
/**
* 处理触发器任务 {@link JavaDelegate} 的实现类
* <p>
* 目前只有 Simple 设计器触发器节点使用
*
* @author jason
*/
@Component(BEAN_NAME)
@Slf4j
public class BpmTriggerTaskDelegate implements JavaDelegate {
public static final String BEAN_NAME = "bpmTriggerTaskDelegate";
@Resource
private List<BpmTrigger> triggers;
private final EnumMap<BpmTriggerTypeEnum, BpmTrigger> triggerMap = new EnumMap<>(BpmTriggerTypeEnum.class);
@PostConstruct
private void init() {
triggers.forEach(trigger -> triggerMap.put(trigger.getType(), trigger));
}
@Override
public void execute(DelegateExecution execution) {
FlowElement flowElement = execution.getCurrentFlowElement();
BpmTriggerTypeEnum bpmTriggerType = BpmnModelUtils.parserTriggerType(flowElement);
BpmTrigger bpmTrigger = triggerMap.get(bpmTriggerType);
if (bpmTrigger == null) {
log.error("[execute][FlowElement({}), {} 找不到匹配的触发器]", execution.getCurrentActivityId(), flowElement);
return;
}
bpmTrigger.execute(execution.getProcessInstanceId(), BpmnModelUtils.parserTriggerParam(flowElement));
}
}

View File

@ -1,96 +0,0 @@
package cn.iocoder.yudao.module.bpm.service.task.listener;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.el.FixedValue;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.Map;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseListenerConfig;
// TODO @芋艿可能会想换个包地址
/**
* BPM 用户任务通用监听器
*
* @author Lesan
*/
@Component
@Slf4j
@Scope("prototype")
public class BpmUserTaskListener implements TaskListener {
public static final String DELEGATE_EXPRESSION = "${bpmUserTaskListener}";
@Resource
private BpmProcessInstanceService processInstanceService;
@Resource
private RestTemplate restTemplate;
@Setter
private FixedValue listenerConfig;
@Override
public void notify(DelegateTask delegateTask) {
// 1. 获取所需基础信息
HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(delegateTask.getProcessInstanceId());
BpmSimpleModelNodeVO.ListenerHandler listenerHandler = parseListenerConfig(listenerConfig);
// 2. 获取请求头和请求体
Map<String, Object> processVariables = processInstance.getProcessVariables();
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
SimpleModelUtils.addHttpRequestParam(headers, listenerHandler.getHeader(), processVariables);
SimpleModelUtils.addHttpRequestParam(body, listenerHandler.getBody(), processVariables);
// 2.1 请求头默认参数
if (StrUtil.isNotEmpty(delegateTask.getTenantId())) {
headers.add(HEADER_TENANT_ID, delegateTask.getTenantId());
}
// 2.2 请求体默认参数
// TODO @芋艿哪些默认参数后续再调研下感觉可以搞个 task 字段把整个 delegateTask 放进去
body.add("processInstanceId", delegateTask.getProcessInstanceId());
body.add("assignee", delegateTask.getAssignee());
body.add("taskDefinitionKey", delegateTask.getTaskDefinitionKey());
body.add("taskId", delegateTask.getId());
// 3. 异步发起请求
// TODO @芋艿确认要同步还是异步
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
try {
ResponseEntity<String> responseEntity = restTemplate.exchange(listenerHandler.getPath(), HttpMethod.POST,
requestEntity, String.class);
log.info("[notify][监听器:{},事件类型:{},请求头:{},请求体:{},响应结果:{}]",
DELEGATE_EXPRESSION,
delegateTask.getEventName(),
headers,
body,
responseEntity);
} catch (RestClientException e) {
log.error("[error][监听器:{},事件类型:{},请求头:{},请求体:{},请求出错:{}]",
DELEGATE_EXPRESSION,
delegateTask.getEventName(),
headers,
body,
e.getMessage());
}
// 4. 是否需要后续操作TODO 芋艿待定
}
}

View File

@ -1,124 +0,0 @@
package cn.iocoder.yudao.module.bpm.service.task.trigger;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
/**
* BPM 发送 HTTP 请求触发器
*
* @author jason
*/
@Component
@Slf4j
public class BpmHttpRequestTrigger implements BpmTrigger {
@Resource
private BpmProcessInstanceService processInstanceService;
@Resource
private RestTemplate restTemplate;
@Override
public BpmTriggerTypeEnum getType() {
return BpmTriggerTypeEnum.HTTP_REQUEST;
}
@Override
public void execute(String processInstanceId, String param) {
// 1. 解析 http 请求配置
HttpRequestTriggerSetting setting = JsonUtils.parseObject(param, HttpRequestTriggerSetting.class);
if (setting == null) {
log.error("[execute][流程({}) HTTP 触发器请求配置为空]", processInstanceId);
return;
}
// 2.1 设置请求头
ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
Map<String, Object> processVariables = processInstance.getProcessVariables();
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add(HEADER_TENANT_ID, processInstance.getTenantId());
SimpleModelUtils.addHttpRequestParam(headers, setting.getHeader(), processVariables);
// 2.2 设置请求体
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
SimpleModelUtils.addHttpRequestParam(body, setting.getBody(), processVariables);
body.add("processInstanceId", processInstanceId);
// TODO @芋艿要不要抽象一个 Http 请求的工具类方便复用呢
// 3. 发起请求
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
ResponseEntity<String> responseEntity;
try {
responseEntity = restTemplate.exchange(setting.getUrl(), HttpMethod.POST,
requestEntity, String.class);
log.info("[execute][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity);
} catch (RestClientException e) {
log.error("[execute][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage());
return;
}
// 4.1 判断是否需要解析返回值
if (StrUtil.isEmpty(responseEntity.getBody())
|| !responseEntity.getStatusCode().is2xxSuccessful()
|| CollUtil.isEmpty(setting.getResponse())) {
return;
}
// 4.2 解析返回值, 返回值必须符合 CommonResult 规范
CommonResult<Map<String, Object>> respResult = JsonUtils.parseObjectQuietly(
responseEntity.getBody(), new TypeReference<CommonResult<Map<String, Object>>>() {});
if (respResult == null || !respResult.isSuccess()){
return;
}
// 4.3 获取需要更新的流程变量
Map<String, Object> updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), setting.getResponse());
// 4.4 更新流程变量
if (CollUtil.isNotEmpty(updateVariables)) {
processInstanceService.updateProcessInstanceVariables(processInstanceId, updateVariables);
}
}
/**
* 从请求返回值获取需要更新的流程变量
*
* @param result 请求返回结果
* @param responseSettings 返回设置
* @return 需要更新的流程变量
*/
private Map<String, Object> getNeedUpdatedVariablesFromResponse(Map<String,Object> result,
List<KeyValue<String, String>> responseSettings) {
Map<String, Object> updateVariables = new HashMap<>();
if (CollUtil.isEmpty(result)) {
return updateVariables;
}
responseSettings.forEach(responseSetting -> {
if (StrUtil.isNotEmpty(responseSetting.getKey()) && result.containsKey(responseSetting.getValue())) {
updateVariables.put(responseSetting.getKey(), result.get(responseSetting.getValue()));
}
});
return updateVariables;
}
}

View File

@ -1,30 +0,0 @@
package cn.iocoder.yudao.module.bpm.service.task.trigger;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
// TODO @芋艿可能会想换个包地址
/**
* BPM 触发器接口
* <p>
* 处理不同的动作
*
* @author jason
*/
public interface BpmTrigger {
/**
* 对应触发器类型
*
* @return 触发器类型
*/
BpmTriggerTypeEnum getType();
/**
* 触发器执行
*
* @param processInstanceId 流程实例编号
* @param param 触发器参数
*/
void execute(String processInstanceId, String param);
}

View File

@ -1,45 +0,0 @@
package cn.iocoder.yudao.module.bpm.service.task.trigger;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.NormalFormTriggerSetting;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
// TODO @jason改成 BpmFormUpdateTrigger
/**
* BPM 更新流程表单触发器
*
* @author jason
*/
@Component
@Slf4j
public class BpmUpdateNormalFormTrigger implements BpmTrigger {
@Resource
private BpmProcessInstanceService processInstanceService;
@Override
public BpmTriggerTypeEnum getType() {
return BpmTriggerTypeEnum.UPDATE_NORMAL_FORM;
}
@Override
public void execute(String processInstanceId, String param) {
// 1. 解析更新流程表单配置
NormalFormTriggerSetting setting = JsonUtils.parseObject(param, NormalFormTriggerSetting.class);
if (setting == null) {
log.error("[execute][流程({}) 更新流程表单触发器配置为空]", processInstanceId);
return;
}
// 2.更新流程变量
if (CollUtil.isNotEmpty(setting.getUpdateFormFields())) {
processInstanceService.updateProcessInstanceVariables(processInstanceId, setting.getUpdateFormFields());
}
}
}