Merge remote-tracking branch 'origin/master' into feature/REQ-4418_Enum

# Conflicts:
#	workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/SupportRefreshProperties.java
#	workflow-engine-server/src/main/java/cn/axzo/workflow/server/alter/DingTalkAlter.java
#	workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/WorkflowEngineStarterAutoConfiguration.java
#	workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/mq/check/ImplementationReadyChecker.java
This commit is contained in:
wangli 2025-10-10 14:24:07 +08:00
commit d3ca61723e
35 changed files with 661 additions and 159 deletions

View File

@ -6,10 +6,13 @@ import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.common.model.request.form.definition.StartFormSearchDTO;
import cn.axzo.workflow.common.model.request.form.instance.FormDetailDTO;
import cn.axzo.workflow.common.model.request.form.instance.FormSearchDTO;
import cn.axzo.workflow.common.model.request.form.instance.FromDataSearchDTO;
import cn.axzo.workflow.common.model.response.form.FormVO;
import cn.axzo.workflow.common.model.response.form.definition.FormDefinitionVO;
import cn.axzo.workflow.common.model.response.form.instance.FormDataVO;
import cn.axzo.workflow.common.model.response.form.instance.FormInstanceVO;
import cn.azxo.framework.common.model.CommonResponse;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@ -36,6 +39,7 @@ public interface FormAdminApi {
/**
* 获取指定审批业务的流程表单设置
*
* @param dto
* @return
*/
@ -55,4 +59,14 @@ public interface FormAdminApi {
@PostMapping("/api/form/admin/instance/render")
@InvokeMode(SYNC)
CommonResponse<FormInstanceVO> getFormInstance(@Validated @RequestBody FormDetailDTO dto);
/**
* 获取指定表单审批的实例数据
*
* @param dto
* @return
*/
@PostMapping("/api/form/admin/instance/form/data")
@InvokeMode(SYNC)
CommonResponse<List<FormDataVO>> getFormData(@Validated @RequestBody FromDataSearchDTO dto);
}

View File

@ -0,0 +1,143 @@
package cn.axzo.workflow.common.enums;
import cn.axzo.workflow.common.model.dto.AmountFieldDTO;
import cn.axzo.workflow.common.model.dto.ContactsPersonDTO;
import cn.axzo.workflow.common.model.dto.UploadFieldDTO;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 流程引擎表单字段类型枚举
* <p>
* 参考文档{@see https://alidocs.dingtalk.com/i/nodes/ZgpG2NdyVXKy17o6fQ5nKGvMWMwvDqPk}
*
* @author wangli
* @since 2025-08-04 11:44
*/
public enum FormFieldTypeEnum {
input("input", "文本", new TypeReference<String>() {
}),
textarea("textarea", "多行文本", new TypeReference<String>() {
}),
upload("upload", "上传文件", new TypeReference<List<UploadFieldDTO>>() {
}),
image("image", "图片", new TypeReference<List<UploadFieldDTO>>() {
}),
date("date", "日期", new TypeReference<String>() {
}),
customComponent("customComponent", "自定义组件", new TypeReference<List<Map<String, Object>>>() {
}),
taskOrder("taskOrder", "任务顺序", new TypeReference<Map<String, Object>>() {
}),
rectifyOrder("rectifyOrder", "整改顺序", new TypeReference<Map<String, Object>>() {
}),
changeSignatureOrder("changeSignatureOrder", "变更签署顺序", new TypeReference<Map<String, Object>>() {
}),
contacts("contacts", "联系人", new TypeReference<List<ContactsPersonDTO>>() {
}),
amount("amount", "金额", new TypeReference<AmountFieldDTO>() {
}),
decimal("decimal", "小数", new TypeReference<Map<String, Object>>() {
}),
;
private final String type;
private final String desc;
private final TypeReference<?> typeReference;
FormFieldTypeEnum(String type, String desc, TypeReference<?> typeReference) {
this.type = type;
this.desc = desc;
this.typeReference = typeReference;
}
public String getType() {
return type;
}
public String getDesc() {
return desc;
}
public TypeReference<?> getTypeReference() {
return typeReference;
}
public static FormFieldTypeEnum valueOfType(String type) {
return Arrays.stream(FormFieldTypeEnum.values())
.filter(i -> Objects.equals(i.getType(), type))
.findAny()
.orElse(null);
}
public static Object parseValue(String type, Object fieldValue, Map<String, Object> fieldParams) {
FormFieldTypeEnum fieldType = valueOfType(type);
if (fieldType == null) {
return null;
}
TypeReference<?> typeReference = fieldType.getTypeReference();
if (typeReference == null || typeReference.getType() == null) {
return fieldValue;
}
if (fieldValue == null) {
return null;
}
ObjectMapper objectMapper = new ObjectMapper();
try {
// 如果已经是目标类型直接返回
if (objectMapper.constructType(typeReference.getType()).getRawClass().isInstance(fieldValue)) {
return fieldValue;
}
// 先转为字符串再反序列化
String json = fieldValue.toString();
if (!(fieldValue instanceof String)) {
json = objectMapper.writeValueAsString(fieldValue);
}
Object defaultValue = handleDefault(fieldParams, json, fieldType);
if (Objects.nonNull(defaultValue)) {
return fieldValue;
}
if (Objects.equals(type, "decimal")) {
// 特殊处理 decimal 类型确保返回的 Map 包含 unit 字段
Map<String, Object> decimalMap = new HashMap<>();
decimalMap.put("value", fieldValue);
decimalMap.put("unit", fieldParams.getOrDefault("unit", ""));
return decimalMap;
}
return objectMapper.readValue(json, typeReference);
} catch (Exception e) {
throw new RuntimeException("字段值解析失败: " + fieldValue, e);
}
}
private static Object handleDefault(Map<String, Object> fieldParams, String json, FormFieldTypeEnum fieldType) {
if (Objects.equals("[]", json)) {
switch (fieldType) {
case upload:
case image:
case customComponent:
case taskOrder:
case rectifyOrder:
case changeSignatureOrder:
case contacts:
// 对于这些类型返回空列表而不是空字符串
return Collections.emptyList();
case decimal:
HashMap<String, Object> map = new HashMap<>();
map.put("value", 0);
map.put("unit", fieldParams.getOrDefault("unit", ""));
return map;
default:
return "";
}
}
return null;
}
}

View File

@ -11,6 +11,10 @@ import lombok.Data;
@Data
public class NextNodePreCheckAlterDTO {
private String processDefinitionKey;
private String processDefinitionName;
private String processInstanceId;
private String activityId;

View File

@ -12,6 +12,11 @@ import java.util.Date;
*/
@Data
public class AlterDTO {
private String processDefinitionKey;
private String processDefinitionName;
private String processInstanceId;
private String activityId;

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.common.model.dto;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -15,6 +16,7 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Builder
@JsonIgnoreProperties(ignoreUnknown = true)
public class ContactsPersonDTO {
/**
* xx:xx:xx

View File

@ -22,5 +22,6 @@ import java.io.Serializable;
public class TermNodeAddTimerJobDTO implements Serializable {
private String processInstanceId;
private String activityId;
private String timeCycle;
private Integer delayTime;
private String timeUnit;
}

View File

@ -7,6 +7,8 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 流程关联文档搜索入参模型
*
@ -22,6 +24,7 @@ public class DocQueryDTO {
/**
* 流程实例 ID
* 该参数与 processDefinitionKey 二选一, 如果有值则优先使用实例 ID仅查询实例下使用的文档
*/
@ApiModelProperty(value = "流程实例 ID")
private String processInstanceId;
@ -32,6 +35,13 @@ public class DocQueryDTO {
@ApiModelProperty(value = "流程定义 KEY业务 ID")
private String processDefinitionKey;
/**
* 流程定义 KEY 列表业务 ID 列表
* 当选择使用 processDefinitionKey/s 则查询模板对应默认的文档列表
*/
@ApiModelProperty(value = "流程定义 KEY 列表(业务 ID 列表)")
private List<String> processDefinitionKeys;
/**
* 租户 ID对应工作台 ID
*/

View File

@ -7,6 +7,8 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 自定义分类的查询入参模型
*/
@ -31,6 +33,11 @@ public class CategorySearchDTO extends BpmPageParam {
*/
@ApiModelProperty(value = "字典值", example = "new_business")
private String value;
/**
* 字典类型列表, bpm_model_category
*/
@ApiModelProperty(value = "字典值列表", example = "[\"new_business\", \"another_business\"]")
private List<String> values;
/**
* 字典状态, 0 停用, 1 启用
@ -44,6 +51,12 @@ public class CategorySearchDTO extends BpmPageParam {
@ApiModelProperty(value = "工作台类型值, 1企业工作台 2项目部工作台 3政务监管工作台 6OMS工作台")
private String workspaceTypeCode;
/**
* 工作台类型值列表, 1企业工作台 2项目部工作台 3政务监管工作台 6OMS工作台
*/
@ApiModelProperty(value = "工作台类型值列表, 1企业工作台 2项目部工作台 3政务监管工作台 6OMS工作台")
private List<String> workspaceTypeCodes;
/**
* 租户 ID
*/
@ -55,6 +68,11 @@ public class CategorySearchDTO extends BpmPageParam {
*/
@ApiModelProperty(value = "业务类型")
private BusinessTypeEnum businessType;
/**
* 业务类型列表
*/
@ApiModelProperty(value = "业务类型列表")
private List<BusinessTypeEnum> businessTypes;
@ApiModelProperty(value = "根据创建时间排序")
private String orderCreateAt;

View File

@ -0,0 +1,32 @@
package cn.axzo.workflow.common.model.request.form.instance;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* 表单数据搜索入参模型
*
* @author wangli
* @since 2025-07-29 14:33
*/
@ApiModel("表单数据搜索入参模型")
@Data
public class FromDataSearchDTO implements Serializable {
/**
* 流程实例 ID
*/
@ApiModelProperty(value = "流程实例ID")
@NotBlank(message = "流程实例 ID 不能为空")
private String processInstanceId;
/**
* 任务 ID, 获取指定任务绑定的表单数据时使用一般不传值获取最新的表单数据即可
*/
@ApiModelProperty(value = "任务 ID")
private String taskId;
}

View File

@ -0,0 +1,44 @@
package cn.axzo.workflow.common.model.response.form.instance;
import cn.axzo.workflow.common.enums.FormFieldTypeEnum;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import java.util.Map;
import java.util.Objects;
/**
* 表单实例数据的响应模型
*
* @author wangli
* @since 2025-08-04 10:54
*/
@ApiModel("表单实例数据的响应模型")
@Data
public class FormDataVO {
private String fieldId;
private Object fieldValue;
private FormFieldTypeEnum fieldType;
/**
* 数字表单组件的格式化值数字+单位
*
* @return
*/
public Object getFormatDecimalValue() {
switch (fieldType) {
case decimal:
Map<String, Object> decimalMap = (Map<String, Object>) fieldValue;
Object value = decimalMap.getOrDefault("value", "");
if (Objects.isNull(value)) {
return "";
} else {
return value + "" + decimalMap.getOrDefault("unit", "");
}
default:
return fieldValue;
}
}
}

View File

@ -19,7 +19,6 @@ import java.util.concurrent.TimeUnit;
@Data
@RefreshScope
public class SupportRefreshProperties {
@Value("${workflow.apiLog.enable: false}")
private Boolean apiLogEnable;

View File

@ -1,24 +1,19 @@
package cn.axzo.workflow.core.engine.cmd;
import cn.axzo.workflow.common.model.dto.TermNodeAddTimerJobDTO;
import cn.axzo.workflow.core.converter.json.NotSupportConverter;
import cn.axzo.workflow.core.engine.job.AsyncTermNodeAlterJobHandler;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.TimerEventDefinition;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.ManagementService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.jobexecutor.TimerEventHandler;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.TimerUtil;
import org.flowable.engine.runtime.Execution;
import org.flowable.job.service.TimerJobService;
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
import org.springframework.util.CollectionUtils;
import java.io.Serializable;
import java.util.List;
import java.util.Date;
/**
* 自定义添加定时任务的逻辑
@ -41,37 +36,39 @@ public class CustomAddTimerJobCmd extends AbstractCommand<Void> implements Seria
@Override
public Void executeInternal(CommandContext commandContext) {
log.info("CustomAddTimerJobCmd start. instanceId: {}, activityId: {}, timeCycle: {}", dto.getProcessInstanceId(), dto.getActivityId(), dto.getTimeCycle());
log.info("CustomAddTimerJobCmd start. instanceId: {}, activityId: {}, delayTime: {}, timeUnit: {}", dto.getProcessInstanceId(), dto.getActivityId(), dto.getDelayTime(), dto.getTimeUnit());
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
ManagementService managementService = processEngineConfiguration.getManagementService();
String tableName = managementService.getTableName(Execution.class);
List<Execution> list = processEngineConfiguration.getRuntimeService()
.createNativeExecutionQuery()
.sql("SELECT * FROM " + tableName + " WHERE PROC_INST_ID_ = #{instanceId} AND ACT_ID_ = #{activityId} AND IS_ACTIVE_ = 1 AND TASK_COUNT_ = 1")
.parameter("instanceId", dto.getProcessInstanceId())
.parameter("activityId", dto.getActivityId())
.list();
if (CollectionUtils.isEmpty(list)) {
return null;
Date alterTime;
switch (dto.getTimeUnit()) {
case "M":
alterTime = DateUtil.offsetDay(new Date(), dto.getDelayTime());
break;
case "H":
alterTime = DateUtil.offsetHour(new Date(), dto.getDelayTime());
break;
default:
alterTime = DateUtil.offsetSecond(new Date(), dto.getDelayTime());
break;
}
if (list.get(list.size() - 1) instanceof ExecutionEntity) {
ExecutionEntity executionEntity = (ExecutionEntity) list.get(list.size() - 1);
TimerEventDefinition timerEventDefinition = new TimerEventDefinition();
timerEventDefinition.setTimeCycle(dto.getTimeCycle());
TimerJobEntity timerJob = TimerUtil.createTimerEntityForTimerEventDefinition(timerEventDefinition,
new NotSupportConverter.NotSupportFlowElement(),
false, executionEntity, AsyncTermNodeAlterJobHandler.TYPE,
TimerEventHandler.createConfiguration(executionEntity.getCurrentActivityId(), null,
timerEventDefinition.getCalendarName()));
if (timerJob != null) {
CommandContextUtil.getTimerJobService().scheduleTimerJob(timerJob);
}
} else {
log.warn("未找到 execution entity");
}
managementService.executeCommand(context -> {
TimerJobService timerJobService = CommandContextUtil.getTimerJobService();
TimerJobEntity timerJobEntity = timerJobService.createTimerJob();
timerJobEntity.setJobType("timer");
timerJobEntity.setJobHandlerType(AsyncTermNodeAlterJobHandler.TYPE); // 这里填写你自定义的 JobHandler 类型
timerJobEntity.setProcessInstanceId(dto.getProcessInstanceId());
timerJobEntity.setExecutionId(null);
timerJobEntity.setDuedate(alterTime); // 立即执行
timerJobEntity.setRepeat(null); // 不重复
timerJobEntity.setRetries(1);
timerJobEntity.setJobHandlerConfiguration(dto.getActivityId()); // 可选传递参数
timerJobService.scheduleTimerJob(timerJobEntity);
return null;
});
return null;
}
}

View File

@ -0,0 +1,44 @@
package cn.axzo.workflow.core.engine.cmd;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.form.api.FormEngineConfigurationApi;
import org.flowable.form.api.FormInstance;
import org.flowable.form.api.FormService;
import org.springframework.util.StringUtils;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
/**
* cn.axzo.workflow.core.engine.cmd.CustomGetFormInstanceLatestValuesCmd 的另一种实现吧
*
* @author wangli
* @since 2025-07-29 15:19
*/
public class CustomGetFormDataValuesCmd implements Command<byte[]> {
private final String processInstanceId;
private final String taskId;
public CustomGetFormDataValuesCmd(String processInstanceId, String taskId) {
this.processInstanceId = processInstanceId;
this.taskId = taskId;
}
@Override
public byte[] execute(CommandContext commandContext) {
FormEngineConfigurationApi formEngineConfiguration = CommandContextUtil.getFormEngineConfiguration(commandContext);
FormService formService = formEngineConfiguration.getFormService();
List<FormInstance> list = formService.createFormInstanceQuery().processInstanceId(processInstanceId).list();
FormInstance formInstance = list.stream().max(Comparator.comparing(FormInstance::getSubmittedDate)).orElse(null);
if (StringUtils.hasText(taskId)) {
formInstance = list.stream().filter(i -> Objects.equals(i.getTaskId(), taskId)).findFirst()
.orElse(formInstance);
}
return formInstance.getFormValueBytes();
}
}

View File

@ -35,6 +35,7 @@ import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCES
* @since 2025-01-21 10:24
*/
public class CustomGetFormInstanceLatestValuesCmd extends GetFormInstanceValuesCmd {
private static final long serialVersionUID = -1017881786777839488L;
private String processInstanceId;
private Boolean throwException;

View File

@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@ -52,6 +53,7 @@ public class CustomGetModelDocsCmd implements Command<List<DocBaseVO>> {
* eg. we1:1:202504021034000000001 or we1
*/
private String processDefinitionId;
private List<String> getProcessDefinitionIds;
private String tenantId;
private Boolean filterEnable = true;
/**
@ -84,9 +86,10 @@ public class CustomGetModelDocsCmd implements Command<List<DocBaseVO>> {
this.extAxReModelService = extAxReModelService;
}
public CustomGetModelDocsCmd(String processInstanceId, String processDefinitionId, String tenantId, ExtAxModelDocMapper extAxModelDocMapper, ExtAxReModelService extAxReModelService) {
public CustomGetModelDocsCmd(String processInstanceId, String processDefinitionId, List<String> processDefinitionIds, String tenantId, ExtAxModelDocMapper extAxModelDocMapper, ExtAxReModelService extAxReModelService) {
this.processInstanceId = processInstanceId;
this.processDefinitionId = processDefinitionId;
this.getProcessDefinitionIds = processDefinitionIds;
this.tenantId = tenantId;
this.extAxModelDocMapper = extAxModelDocMapper;
this.extAxReModelService = extAxReModelService;
@ -107,7 +110,8 @@ public class CustomGetModelDocsCmd implements Command<List<DocBaseVO>> {
check();
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
RepositoryService repositoryService = processEngineConfiguration.getRepositoryService();
ProcessDefinition processDefinition;
List<ProcessDefinition> processDefinitions = new ArrayList<>();
List<Long> enableDocIds = new ArrayList<>();
if (StringUtils.hasText(processInstanceId)) {
HistoryService historyService = processEngineConfiguration.getHistoryService();
@ -129,29 +133,61 @@ public class CustomGetModelDocsCmd implements Command<List<DocBaseVO>> {
}
}
}
processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(instance.getProcessDefinitionId()).singleResult();
processDefinitions.add(repositoryService.createProcessDefinitionQuery().processDefinitionId(instance.getProcessDefinitionId()).singleResult());
} else {
List<ProcessDefinition> definitions = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey(parseProcessDefinitionKey())
.list();
List<ProcessDefinition> definitions = new ArrayList<>();
if (StringUtils.hasText(processDefinitionId)) {
definitions.addAll(repositoryService.createProcessDefinitionQuery()
.processDefinitionKey(parseProcessDefinitionKey(processDefinitionId))
.list());
} else {
List<String> definitionIds = getProcessDefinitionIds.stream().filter(StringUtils::hasText).map(this::parseProcessDefinitionKey).collect(Collectors.toList());
definitions.addAll(repositoryService.createNativeProcessDefinitionQuery()
.sql("SELECT * FROM ACT_RE_PROCDEF WHERE KEY_ IN " + definitionIds.stream().map(i -> "'" + i + "'").collect(Collectors.joining(",", "(", ")")))
.list());
}
if (CollectionUtils.isEmpty(definitions)) {
return Collections.emptyList();
}
Optional<ProcessDefinition> first = definitions.stream().filter(i -> i.getTenantId().equals(tenantId))
// key 分组每组取 version 最大的 ProcessDefinition
Map<String, List<ProcessDefinition>> maxVersionMap = definitions.stream()
// .filter(i -> i.getTenantId().equals(tenantId))
.collect(Collectors.groupingBy(
ProcessDefinition::getKey,
Collectors.toList()));
maxVersionMap.forEach((k, definitionList) -> {
Optional<ProcessDefinition> first = definitionList.stream()
.filter(i -> Objects.equals(i.getKey(), k))
.filter(i -> i.getTenantId().equals(tenantId))
.max(Comparator.comparing(ProcessDefinition::getVersion));
if (first.isPresent()) {
processDefinition = first.get();
ProcessDefinition processDefinition = first.get();
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
Model model = commandExecutor.execute(new CustomGetModelByDefinitionIdCmd(processDefinition.getId()));
BpmnModelExtVO modelStatus = extAxReModelService.getStatusByModelId(model.getId());
if (Objects.equals(0, modelStatus.getStatus())) {
processDefinition = definitions.stream().filter(i -> i.getTenantId().equals(NO_TENANT_ID)).max(Comparator.comparing(ProcessDefinition::getVersion)).get();
processDefinitions.add(definitions.stream()
.filter(i -> Objects.equals(i.getKey(), k))
.filter(i -> i.getTenantId().equals(NO_TENANT_ID))
.max(Comparator.comparing(ProcessDefinition::getVersion)).get());
} else {
processDefinitions.add(processDefinition);
}
} else {
processDefinition = first.orElseGet(() -> definitions.stream().filter(i -> i.getTenantId().equals(NO_TENANT_ID)).max(Comparator.comparing(ProcessDefinition::getVersion)).get());
processDefinitions.add(first.orElseGet(() -> definitions.stream()
.filter(i -> Objects.equals(i.getKey(), k))
.filter(i -> i.getTenantId().equals(NO_TENANT_ID))
.max(Comparator.comparing(ProcessDefinition::getVersion)).get()));
}
});
}
List<ExtAxModelDoc> docs = new ArrayList<>();
if (!CollectionUtils.isEmpty(processDefinitions)) {
for (ProcessDefinition processDefinition : processDefinitions) {
ExtAxModelDoc query = new ExtAxModelDoc();
query.setModelKey(processDefinition.getKey());
if (Objects.equals(Boolean.TRUE, filterEnable)) {
@ -159,7 +195,9 @@ public class CustomGetModelDocsCmd implements Command<List<DocBaseVO>> {
}
query.setTenantId(processDefinition.getTenantId());
query.setTempFile(false);
List<ExtAxModelDoc> docs = extAxModelDocMapper.selectList(buildQueryWrapper(query));
docs.addAll(extAxModelDocMapper.selectList(buildQueryWrapper(query)));
}
}
if (filterSelect) {
docs = docs.stream().filter(i -> enableDocIds.contains(i.getId())).collect(Collectors.toList());
@ -167,7 +205,7 @@ public class CustomGetModelDocsCmd implements Command<List<DocBaseVO>> {
return BeanMapper.copyList(docs, DocBaseVO.class, (s, t) -> t.setFileType(FileTypeEnum.valueOfType(s.getFileType())));
}
private String parseProcessDefinitionKey() {
private String parseProcessDefinitionKey(String processDefinitionId) {
if (StringUtils.hasText(processDefinitionId)) {
return processDefinitionId.split(":")[0];
}
@ -175,7 +213,7 @@ public class CustomGetModelDocsCmd implements Command<List<DocBaseVO>> {
}
private void check() {
if (!StringUtils.hasText(processInstanceId) && !StringUtils.hasText(processDefinitionId)) {
if (!StringUtils.hasText(processInstanceId) && (!StringUtils.hasText(processDefinitionId)) && CollectionUtils.isEmpty(getProcessDefinitionIds)) {
throw new WorkflowEngineException(MODEL_FILE_QUERY_ERROR);
}
}

View File

@ -75,7 +75,7 @@ public class CustomGetProcessInstanceVariablesToObjectCmd extends AbstractComman
private final String processInstanceId;
private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
private static final List<String> SUPPORTED_FORM_TYPES = Lists.newArrayList("input", "date", "textarea", "image", "contacts", "amount");
private static final List<String> SUPPORTED_FORM_TYPES = Lists.newArrayList("input", "date", "textarea", "image", "contacts", "amount", "decimal");
public CustomGetProcessInstanceVariablesToObjectCmd(String processInstanceId) {
this.processInstanceId = processInstanceId;
@ -210,6 +210,15 @@ public class CustomGetProcessInstanceVariablesToObjectCmd extends AbstractComman
.type(convert(field.getType()))
.build());
}
} else if (Objects.equals(field.getType(), "decimal")) {
if (StringUtils.hasText(fieldValue.toString())) {
variables.add(VariableObjectDTO.builder()
.key(field.getId())
.desc(field.getName())
.value(fieldValue + String.valueOf(field.getParam("unit")))
.type(convert(field.getType()))
.build());
}
} else {
variables.add(VariableObjectDTO.builder()
.key(field.getId())
@ -300,6 +309,7 @@ public class CustomGetProcessInstanceVariablesToObjectCmd extends AbstractComman
case "textarea":
case "amount":
case "contacts":
case "decimal":
return VariableObjectDTO.Type.text;
case "image":
return VariableObjectDTO.Type.img;

View File

@ -18,7 +18,7 @@ import org.flowable.variable.api.delegate.VariableScope;
* @since 2024-09-09 14:36
*/
@Slf4j
public class AsyncActivityTriggerJobHandler extends AbstractJobHandler implements JobHandler {
public class AsyncActivityTriggerJobHandler extends AbstractExecuteWithLockJobHandler implements JobHandler {
public static final String TYPE = "async-activity-trigger";
@Override
@ -27,7 +27,7 @@ public class AsyncActivityTriggerJobHandler extends AbstractJobHandler implement
}
@Override
public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
public void executeInternal(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
log.info("AsyncActivityTriggerJobHandler executing...");
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);

View File

@ -14,7 +14,7 @@ import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.variable.api.delegate.VariableScope;
@Slf4j
public class AsyncCancelProcessInstanceJobHandler extends AbstractJobHandler implements JobHandler {
public class AsyncCancelProcessInstanceJobHandler extends AbstractExecuteWithLockJobHandler implements JobHandler {
public static final String TYPE = "async-cancel-process";
@ -30,11 +30,12 @@ public class AsyncCancelProcessInstanceJobHandler extends AbstractJobHandler imp
}
@Override
public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
public void executeInternal(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
log.info("AsyncCancelProcessInstanceHandler executing...,jobInfo:{}", JSONUtil.toJsonStr(job));
log(job);
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
BpmnProcessInstanceCancelDTO dto = JSONUtil.toBean(job.getCustomValues(), BpmnProcessInstanceCancelDTO.class);
processEngineConfiguration.getCommandExecutor().execute(new CustomCancelProcessInstanceCmd((SuperBpmnProcessInstanceCancelDTO) dto, extAxHiTaskInstService));
}
}

View File

@ -3,29 +3,35 @@ package cn.axzo.workflow.core.engine.job;
import cn.axzo.basics.common.util.NumberUtil;
import cn.axzo.workflow.common.model.dto.AlterDTO;
import cn.axzo.workflow.common.model.dto.TermNodePausingDTO;
import cn.axzo.workflow.common.model.response.category.CategoryItemVO;
import cn.axzo.workflow.core.common.utils.SpringContextUtils;
import cn.axzo.workflow.core.conf.SupportRefreshProperties;
import cn.axzo.workflow.core.engine.tx.listener.DeleteTimerJobTransactionListener;
import cn.axzo.workflow.core.listener.Alter;
import cn.axzo.workflow.core.service.CategoryService;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.impl.cfg.TransactionState;
import org.flowable.common.engine.impl.context.Context;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.job.service.JobHandler;
import org.flowable.job.service.TimerJobService;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
import org.flowable.task.api.Task;
import org.flowable.variable.api.delegate.VariableScope;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import static cn.axzo.workflow.common.constant.BpmnConstants.BIZ_NODE_ALTER;
@ -53,12 +59,16 @@ public class AsyncTermNodeAlterJobHandler extends AbstractJobHandler implements
public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
log.warn("AsyncTermNodeAlterJobHandler exec start...");
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
JSONObject jsonObject = JSON.parseObject(job.getJobHandlerConfiguration());
if (!jsonObject.containsKey("activityId")) {
// JSONObject jsonObject = JSON.parseObject(job.getJobHandlerConfiguration());
// if (!jsonObject.containsKey("activityId")) {
// return;
// }
String activityId = job.getJobHandlerConfiguration();
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(job.getProcessInstanceId()).singleResult();
if(Objects.isNull(processInstance)) {
return;
}
String activityId = jsonObject.getString("activityId");
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
TermNodePausingDTO dto = runtimeService.getVariable(job.getProcessInstanceId(), BIZ_NODE_ALTER + activityId, TermNodePausingDTO.class);
TaskService taskService = processEngineConfiguration.getTaskService();
List<Task> tasks = taskService.createTaskQuery()
@ -71,26 +81,46 @@ public class AsyncTermNodeAlterJobHandler extends AbstractJobHandler implements
tasks.forEach(e -> {
sb.append("id").append(e.getId()).append(", assignee: ").append(e.getAssignee());
});
log.info("tasks size:{}", JSON.toJSONString(sb));
log.info("tasks size:{}, info: {}", tasks.size(), JSON.toJSONString(sb));
if (CollectionUtils.isEmpty(tasks) || tasks.size() > 1 || hasAssignee(tasks.get(0).getAssignee())) {
deleteTimerJob(dto);
return;
}
if (DateUtil.compare(DateUtil.date(), DateUtil.offsetMinute(tasks.get(0).getCreateTime(), refreshProperties.getPauseDelay())) <= 0) {
if (DateUtil.compare(DateUtil.date(), getDateTime(tasks.get(0).getCreateTime())) <= 0) {
ManagementService managementService = processEngineConfiguration.getManagementService();
managementService.executeCommand(context -> {
TimerJobService timerJobService = CommandContextUtil.getTimerJobService();
TimerJobEntity timerJobEntity = timerJobService.createTimerJob();
timerJobEntity.setJobType("timer");
timerJobEntity.setJobHandlerType(AsyncTermNodeAlterJobHandler.TYPE); // 这里填写你自定义的 JobHandler 类型
timerJobEntity.setProcessInstanceId(dto.getProcessInstanceId());
timerJobEntity.setExecutionId(null);
timerJobEntity.setDuedate(getDateTime(new Date())); // 立即执行
timerJobEntity.setRepeat(null); // 不重复
timerJobEntity.setRetries(1);
timerJobEntity.setJobHandlerConfiguration(dto.getActivityId()); // 可选传递参数
timerJobService.scheduleTimerJob(timerJobEntity);
return null;
});
return;
}
// 不允许重复告警
if (!refreshProperties.getRepeatAlter() && dto.getRetries() > 0) {
deleteTimerJob(dto);
return;
}
// 超过告警次数
if (refreshProperties.getAlterRetries() != 0 && dto.getRetries() >= refreshProperties.getAlterRetries()) {
return;
}
if (refreshProperties.getAlterRetries() == 0 || dto.getRetries() < refreshProperties.getAlterRetries()) {
CategoryService bean = SpringContextUtils.getBean(CategoryService.class);
Optional<CategoryItemVO> bpmModelCategory = bean.get("bpm_model_category", processInstance.getProcessDefinitionKey());
// 发送告警对象
Alter alter = SpringContextUtils.getBean(Alter.class);
AlterDTO alterDTO = new AlterDTO();
alterDTO.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
alterDTO.setProcessDefinitionName(bpmModelCategory.orElse(new CategoryItemVO()).getLabel());
alterDTO.setProcessInstanceId(dto.getProcessInstanceId());
alterDTO.setActivityId(dto.getActivityId());
alterDTO.setTaskId(tasks.get(0).getId());
@ -102,11 +132,23 @@ public class AsyncTermNodeAlterJobHandler extends AbstractJobHandler implements
// 记录告警次数
incRetries(job, dto, runtimeService, activityId);
if (refreshProperties.getAlterRetries() != 0 && dto.getRetries() >= refreshProperties.getAlterRetries()) {
deleteTimerJob(dto);
}
}
private DateTime getDateTime(Date date) {
DateTime dateTime;
switch (refreshProperties.getAlterIntervalUnit()) {
case MINUTES:
dateTime = DateUtil.offsetMinute(date, refreshProperties.getPauseDelay());
break;
case HOURS:
dateTime = DateUtil.offsetHour(date, refreshProperties.getPauseDelay());
break;
default:
dateTime = DateUtil.offsetSecond(date, refreshProperties.getPauseDelay());
break;
}
return dateTime;
}
private void incRetries(JobEntity job, TermNodePausingDTO dto, RuntimeService runtimeService, String activityId) {
@ -114,10 +156,10 @@ public class AsyncTermNodeAlterJobHandler extends AbstractJobHandler implements
runtimeService.setVariable(job.getProcessInstanceId(), BIZ_NODE_ALTER + activityId, dto);
}
private void deleteTimerJob(TermNodePausingDTO dto) {
Context.getTransactionContext().addTransactionListener(TransactionState.COMMITTED,
new DeleteTimerJobTransactionListener(dto));
}
// private void deleteTimerJob(TermNodePausingDTO dto) {
// Context.getTransactionContext().addTransactionListener(TransactionState.COMMITTED,
// new DeleteTimerJobTransactionListener(dto));
// }
private Boolean hasAssignee(String assignee) {
if (!StringUtils.hasText(assignee)) {

View File

@ -3,9 +3,11 @@ package cn.axzo.workflow.core.engine.job;
import cn.axzo.workflow.common.enums.ApproverEmptyHandleTypeEnum;
import cn.axzo.workflow.common.model.NextNodePreCheckAlterDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.response.category.CategoryItemVO;
import cn.axzo.workflow.core.common.utils.SpringContextUtils;
import cn.axzo.workflow.core.deletage.BpmnTaskAssigneeSelector;
import cn.axzo.workflow.core.listener.Alter;
import cn.axzo.workflow.core.service.CategoryService;
import cn.axzo.workflow.core.service.support.FlowNodeForecastService;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
@ -16,10 +18,12 @@ import org.flowable.bpmn.model.ServiceTask;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.query.QueryProperty;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.job.service.JobHandler;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.variable.api.delegate.VariableScope;
@ -28,6 +32,7 @@ import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApprovalMethod;
@ -56,6 +61,11 @@ public class NextActivityConfigCheckJobHandler extends AbstractJobHandler implem
CommandContextUtil.getProcessEngineConfiguration(commandContext);
FlowNodeForecastService forecastService = SpringContextUtils.getBean(FlowNodeForecastService.class);
String currentActivityId = job.getJobHandlerConfiguration();
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(job.getProcessInstanceId()).singleResult();
if (Objects.isNull(processInstance)) {
return;
}
List<FlowElement> flowElements = forecastService.performProcessForecasting(job.getProcessInstanceId(), null, currentActivityId, false);
if (CollectionUtils.isEmpty(flowElements)) {
return;
@ -65,9 +75,13 @@ public class NextActivityConfigCheckJobHandler extends AbstractJobHandler implem
} catch (Exception e) {
// 有任何异常则通过钉钉告警
log.warn("NextActivityConfigCheckJobHandler msg: {}", e.getMessage(), e);
CategoryService bean = SpringContextUtils.getBean(CategoryService.class);
Optional<CategoryItemVO> bpmModelCategory = bean.get("bpm_model_category", processInstance.getProcessDefinitionKey());
Alter alter = SpringContextUtils.getBean(Alter.class);
FlowElement flowElement = ListUtils.emptyIfNull(flowElements).stream().filter(i -> i instanceof UserTask || i instanceof ReceiveTask || i instanceof ServiceTask).findFirst().orElse(null);
NextNodePreCheckAlterDTO alterDTO = new NextNodePreCheckAlterDTO();
alterDTO.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
alterDTO.setProcessDefinitionName(bpmModelCategory.orElse(new CategoryItemVO()).getLabel());
alterDTO.setProcessInstanceId(job.getProcessInstanceId());
alterDTO.setActivityId(Objects.nonNull(flowElement) ? flowElement.getId() : null);
alterDTO.setErrorMsg(e.getMessage());

View File

@ -0,0 +1,25 @@
package cn.axzo.workflow.core.engine.listener;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.ExecutionListener;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
/**
* 部分有告警设置的在节点正常执行后将清除告警
*
* @author wangli
* @since 2025-04-24 16:03
*/
@Component
@RefreshScope
@Slf4j
@Deprecated
public class EngineAlarmClearEventListener implements ExecutionListener {
private static final long serialVersionUID = -4246836140144129539L;
@Override
public void notify(DelegateExecution execution) {
}
}

View File

@ -0,0 +1,25 @@
package cn.axzo.workflow.core.engine.listener;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
/**
* 节点运行超时告警
*
* @author wangli
* @since 2025-04-24 14:21
*/
@Component
@RefreshScope
@Slf4j
@Deprecated
public class EngineNodeTimeoutAlterDelegate implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
}
}

View File

@ -22,7 +22,7 @@ public class AddTimerJobTransactionListener implements TransactionListener {
@Override
public void execute(CommandContext commandContext) {
log.info("add timer job listener. instanceId: {}, activityId: {}, timeCycle: {}", dto.getProcessInstanceId(), dto.getActivityId(), dto.getTimeCycle());
log.info("add timer job listener. instanceId: {}, activityId: {}, delayTime: {}, timeUnit: {}", dto.getProcessInstanceId(), dto.getActivityId(), dto.getDelayTime(), dto.getTimeUnit());
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
processEngineConfiguration.getCommandExecutor().execute(new CustomAddTimerJobCmd(dto));

View File

@ -7,7 +7,6 @@ import cn.axzo.workflow.core.common.context.ActivityOperationContext;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.conf.SupportRefreshProperties;
import cn.axzo.workflow.core.engine.tx.listener.AddTimerJobTransactionListener;
import cn.axzo.workflow.core.engine.tx.listener.DeleteTimerJobTransactionListener;
import cn.axzo.workflow.core.listener.AbstractBpmnEventListener;
import cn.axzo.workflow.core.listener.BpmnActivityEventListener;
import lombok.AllArgsConstructor;
@ -91,7 +90,8 @@ public class InternalBpmnActivityEventListener_lo_Listener extends AbstractBpmnE
TermNodeAddTimerJobDTO addTimerJobDTO = new TermNodeAddTimerJobDTO();
addTimerJobDTO.setProcessInstanceId(execution.getProcessInstanceId());
addTimerJobDTO.setActivityId(execution.getCurrentActivityId());
addTimerJobDTO.setTimeCycle("R100/PT" + refreshProperties.getAlterInterval() + timeUnit);
addTimerJobDTO.setDelayTime(refreshProperties.getAlterInterval());
addTimerJobDTO.setTimeUnit(timeUnit);
Context.getTransactionContext().addTransactionListener(TransactionState.COMMITTED,
new AddTimerJobTransactionListener(addTimerJobDTO));
@ -104,27 +104,4 @@ public class InternalBpmnActivityEventListener_lo_Listener extends AbstractBpmnE
});
}
/**
* 节点已取消
*
* @param execution
*/
@Override
public void onEnd(DelegateExecution execution) {
// ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
// ManagementService managementService = processEngineConfiguration.getManagementService();
// Job timerJob = managementService.createTimerJobQuery()
// .elementId(execution.getCurrentActivityId())
// .processInstanceId(execution.getProcessInstanceId())
// .singleResult();
// if (Objects.nonNull(timerJob)) {
// CommandContextUtil.getTimerJobService().deleteTimerJob((TimerJobEntity) timerJob);
// }
TermNodePausingDTO dto = new TermNodePausingDTO();
dto.setActivityId(execution.getCurrentActivityId());
dto.setProcessInstanceId(execution.getProcessInstanceId());
dto.setRetries(1);
Context.getTransactionContext().addTransactionListener(TransactionState.COMMITTED,
new DeleteTimerJobTransactionListener(dto));
}
}

View File

@ -3,9 +3,12 @@ package cn.axzo.workflow.core.service;
import cn.axzo.workflow.common.model.dto.BpmnFormRelationSearchDTO;
import cn.axzo.workflow.common.model.request.form.definition.StartFormSearchDTO;
import cn.axzo.workflow.common.model.request.form.instance.FormDetailDTO;
import cn.axzo.workflow.common.model.request.form.instance.FromDataSearchDTO;
import cn.axzo.workflow.common.model.response.form.FormVO;
import cn.axzo.workflow.common.model.response.form.definition.FormDefinitionVO;
import cn.axzo.workflow.common.model.response.form.instance.FormDataVO;
import cn.axzo.workflow.common.model.response.form.instance.FormInstanceVO;
import com.alibaba.fastjson.JSONObject;
import java.util.List;
@ -22,4 +25,6 @@ public interface FormCoreService {
FormDefinitionVO getStartForm(StartFormSearchDTO dto);
FormInstanceVO getFormInstance(FormDetailDTO dto);
List<FormDataVO> getFormData(FromDataSearchDTO dto);
}

View File

@ -5,7 +5,6 @@ import cn.axzo.workflow.common.enums.BpmnCountersignTypeEnum;
import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnNodeBackSystemOperateDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnOptionalNodeDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCompleteDTO;
@ -51,9 +50,6 @@ import cn.axzo.workflow.core.engine.cmd.CustomResetTaskApproversAsyncCmd;
import cn.axzo.workflow.core.engine.cmd.CustomResetTaskApproversCmd;
import cn.axzo.workflow.core.engine.cmd.CustomTransferUserTaskAsyncCmd;
import cn.axzo.workflow.core.engine.cmd.CustomTransferUserTaskCmd;
import cn.axzo.workflow.core.engine.event.MessagePushEventBuilder;
import cn.axzo.workflow.core.engine.event.MessagePushEventImpl;
import cn.axzo.workflow.core.engine.event.MessagePushEventType;
import cn.axzo.workflow.core.repository.entity.ExtAxHiTaskInst;
import cn.axzo.workflow.core.service.BpmnProcessDefinitionService;
import cn.axzo.workflow.core.service.BpmnProcessTaskService;
@ -65,15 +61,12 @@ import cn.axzo.workflow.core.service.converter.BpmnTaskConverter;
import cn.axzo.workflow.core.service.converter.BpmnTaskDonePageItemConverter;
import cn.axzo.workflow.core.service.converter.BpmnTaskTodoPageItemConverter;
import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.flowable.bpmn.model.BaseElement;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
import org.flowable.engine.HistoryService;
@ -83,9 +76,6 @@ import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Attachment;
import org.flowable.engine.task.Comment;
@ -131,7 +121,6 @@ import static cn.axzo.workflow.common.code.BpmnTaskRespCode.FIND_TASK_BY_PERSON_
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.REACHED_BACKED_MAXIMUM_NUM;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_NOT_EXISTS;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_HAS_BEEN_COMPLETE;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_REMIND_ERROR_NOT_EXISTS;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_COMMENT_EXT;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
@ -156,7 +145,6 @@ import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.valueO
import static cn.axzo.workflow.common.util.BpmnNativeQueryUtil.countSql;
import static cn.axzo.workflow.common.util.BpmnNativeQueryUtil.sqlConnectors;
import static cn.axzo.workflow.core.common.utils.BpmnCollectionUtils.convertSet;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getActivitySignature;
@Service
@Slf4j
@ -916,8 +904,11 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
@Override
public String findTaskIdByInstanceIdAndPersonId(String processInstanceId, String personId) {
List<Task> list = taskService.createTaskQuery().processInstanceId(processInstanceId)
.taskAssigneeLike("%|" + personId)
TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId);
if (StringUtils.hasText(personId)) {
taskQuery.taskAssigneeLike("%|" + personId);
}
List<Task> list = taskQuery
.active()
.list();
if (CollectionUtils.isEmpty(list) || list.size() > 1) {

View File

@ -222,11 +222,14 @@ public class CategoryServiceImpl extends ServiceImpl<ExtAxDictMapper, ExtAxDict>
.eq(StringUtils.isNotBlank(dto.getDictType()), ExtAxDict::getType, dto.getDictType())
.like(StringUtils.isNotBlank(dto.getLabel()), ExtAxDict::getLabel, dto.getLabel())
.eq(StringUtils.isNotBlank(dto.getValue()), ExtAxDict::getValue, dto.getValue())
.in(!CollectionUtils.isEmpty(dto.getValues()), ExtAxDict::getValue, dto.getValues())
.eq(Objects.nonNull(dto.getStatus()), ExtAxDict::getStatus, dto.getStatus())
.eq(StringUtils.isNotBlank(dto.getWorkspaceTypeCode()), ExtAxDict::getWorkspaceTypeCode,
dto.getWorkspaceTypeCode())
.in(!CollectionUtils.isEmpty(dto.getWorkspaceTypeCodes()), ExtAxDict::getWorkspaceTypeCode, dto.getWorkspaceTypeCodes())
.eq(ExtAxDict::getTenantId, dto.getTenantId())
.eq(dto.getBusinessType() != null, ExtAxDict::getBusinessType, dto.getBusinessType())
.in(!CollectionUtils.isEmpty(dto.getBusinessTypes()), ExtAxDict::getBusinessType, dto.getBusinessTypes())
.eq(ExtAxDict::getIsDelete, 0)
.orderByDesc(Objects.equals(dto.getOrderCreateAt(), "desc"), ExtAxDict::getCreateAt);
;

View File

@ -78,7 +78,7 @@ public class ExtAxModelDocServiceImpl implements ExtAxModelDocService {
public List<DocBaseVO> docList(DocQueryDTO dto) {
CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor();
return commandExecutor.execute(new CustomGetModelDocsCmd(dto.getProcessInstanceId(),
dto.getProcessDefinitionKey(), dto.getTenantId(), extAxModelDocMapper, extAxReModelService));
dto.getProcessDefinitionKey(), dto.getProcessDefinitionKeys(), dto.getTenantId(), extAxModelDocMapper, extAxReModelService));
}
@Override

View File

@ -1,13 +1,16 @@
package cn.axzo.workflow.core.service.impl;
import cn.axzo.workflow.common.enums.FormFieldTypeEnum;
import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.dto.BpmnFormRelationSearchDTO;
import cn.axzo.workflow.common.model.request.form.definition.StartFormSearchDTO;
import cn.axzo.workflow.common.model.request.form.instance.FormDetailDTO;
import cn.axzo.workflow.common.model.request.form.instance.FromDataSearchDTO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessDefinitionVO;
import cn.axzo.workflow.common.model.response.category.CategoryItemVO;
import cn.axzo.workflow.common.model.response.form.FormVO;
import cn.axzo.workflow.common.model.response.form.definition.FormDefinitionVO;
import cn.axzo.workflow.common.model.response.form.instance.FormDataVO;
import cn.axzo.workflow.common.model.response.form.instance.FormInstanceVO;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.common.utils.FormHelper;
@ -28,12 +31,17 @@ import org.flowable.engine.impl.cmd.GetBpmnModelCmd;
import org.flowable.form.api.FormInfo;
import org.flowable.form.api.FormInstanceInfo;
import org.flowable.form.api.FormRepositoryService;
import org.flowable.form.model.FormContainer;
import org.flowable.form.model.FormField;
import org.flowable.form.model.SimpleFormModel;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -130,4 +138,41 @@ public class FormCoreServiceImpl implements FormCoreService {
bpmnProcessTaskForEsService, dto.getAssigner(), dto.getProcessInstanceId(), dto.getTaskId(), dto.getShowOriginDefaultValue()));
return formInstanceConverter.toVo(formInstanceInfo);
}
@Override
public List<FormDataVO> getFormData(FromDataSearchDTO dto) {
CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor();
FormInstanceInfo formInstanceInfo = commandExecutor.execute(new GetFormInstanceAndPermissionCmd(bpmnFormRelationService,
bpmnProcessTaskForEsService, null, dto.getProcessInstanceId(), dto.getTaskId(), true));
List<FormField> fields = null;
if (Objects.isNull(formInstanceInfo)
|| Objects.isNull(formInstanceInfo.getFormModel())
|| (formInstanceInfo.getFormModel() instanceof SimpleFormModel) && CollectionUtils.isEmpty(fields = ((SimpleFormModel) formInstanceInfo.getFormModel()).getFields())) {
return Collections.emptyList();
}
if (!CollectionUtils.isEmpty(fields)) {
List<FormDataVO> dataList = new ArrayList<>();
fields.forEach(i -> recursiveConvert(i, dataList));
return dataList;
}
return Collections.emptyList();
}
private void recursiveConvert(FormField field, List<FormDataVO> formDataList) {
if (Objects.isNull(field)) {
return;
}
if (field instanceof FormContainer) {
((FormContainer) field).getFields().forEach(subField -> subField.forEach(i -> recursiveConvert(i, formDataList)));
} else {
FormDataVO formDataVO = new FormDataVO();
formDataVO.setFieldId(field.getId());
formDataVO.setFieldType(FormFieldTypeEnum.valueOfType(field.getType()));
// TODO 根据不同数据类型进行数据转码
formDataVO.setFieldValue(FormFieldTypeEnum.parseValue(field.getType(), field.getValue(), field.getParams()));
formDataList.add(formDataVO);
}
}
}

View File

@ -146,6 +146,7 @@ public class DingTalkUtils {
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
markdown.setTitle("Notice 业务节点长时间停止告警, Env: " + profile);
markdown.setText("#### [" + profile + "]业务节点长时间停止\n" +
"> 审批业务: " + alterDTO.getProcessDefinitionName() + "(" + alterDTO.getProcessDefinitionKey() + ")" + "\n\n" +
"> 节点相关信息: " + JSONUtil.toJsonStr(alterDTO) + "\n\n" +
"> ##### [点击查看审批日志](" + getWebUrl(profile) + "/#/workflow/examples?processInstanceId=" + processInstanceId + ") \n\n" +
"> ##### 提示:如果以上地址提示未登录,请在对应环境 OMS 系统登录后并点击审批管理的功能后,再使用。或者复制实例 ID 到 OMS 中手动查询 \n\n" +
@ -175,7 +176,7 @@ public class DingTalkUtils {
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
markdown.setTitle("Notice 审批模板节点预检查告警, Env: " + profile);
markdown.setText("#### [" + profile + "]审批模板节点预检查告警\n" +
// "> 相关信息: " + JSONUtil.toJsonStr(alterDTO) + "\n\n" +
"> 审批业务: " + alterDTO.getProcessDefinitionName() + "(" + alterDTO.getProcessDefinitionKey() + ")" + "\n\n" +
"> 实例 ID" + alterDTO.getProcessInstanceId() + "\n\n" +
"> 检测节点 ID" + alterDTO.getActivityId() + "\n\n" +
"> ##### 错误信息:" + alterDTO.getErrorMsg() + "\n\n" +

View File

@ -24,7 +24,7 @@ import static org.dromara.easyes.annotation.rely.Analyzer.IK_MAX_WORD;
*/
@Data
@Settings(settingsProvider = CustomIndexSettingProvider.class, replicasNum = 1, shardsNum = 1)
@IndexName(value = "process_instance_document", keepGlobalPrefix = true, refreshPolicy = RefreshPolicy.IMMEDIATE)
@IndexName(value = "process_instance_document", keepGlobalPrefix = true, refreshPolicy = RefreshPolicy.NONE)
@Join(nodes = {@Node(parentClass = ProcessInstanceDocument.class, parentAlias = "process_instance_document", childClasses = {ProcessTaskDocument.class}, childAliases = {"process_task_document"})})
public class ProcessInstanceDocument {

View File

@ -146,10 +146,13 @@ public class TransferToAdminTaskAssigneeSelector extends AbstractBpmnTaskAssigne
req.setWorkspaceAdmin(true);
}
});
boolean flag = false;
if (CollectionUtils.isEmpty(req.getCooperateTypes())) {
flag = true;
}
List<FlowTaskAssignerResp> flowTaskAssigners =
parseApiResult(() -> flowSupportApi.listTaskAssignerAdminV2(req),
"审批节点: " + flowElement.getId() + ", 通过管理员查询审批人",
"审批节点: " + flowElement.getId() + ", 通过管理员查询审批人" + (flag ? "需要重新发布选择节点配置" : ""),
"cn.axzo.karma.client.feign.FlowSupportApi#listTaskAssignerAdminV2",
req);

View File

@ -496,7 +496,7 @@ public class BpmnProcessModelController implements ProcessModelApi {
@Operation(summary = "根据业务 ID 获取模型文档列表,自动适配公共模板和代运营")
@PostMapping(value = "/doc/list")
public CommonResponse<List<DocBaseVO>> docList(@Validated @RequestBody DocQueryDTO dto) {
if (!StringUtils.hasText(dto.getProcessInstanceId()) && !StringUtils.hasText(dto.getProcessDefinitionKey())) {
if (!StringUtils.hasText(dto.getProcessInstanceId()) && (!StringUtils.hasText(dto.getProcessDefinitionKey()) && CollectionUtils.isEmpty(dto.getProcessDefinitionKeys()))) {
throw new WorkflowEngineException(MODEL_FILE_QUERY_ERROR);
}
return success(modelDocService.docList(dto));

View File

@ -5,8 +5,10 @@ import cn.axzo.workflow.common.model.dto.BpmnFormRelationSearchDTO;
import cn.axzo.workflow.common.model.request.form.definition.StartFormSearchDTO;
import cn.axzo.workflow.common.model.request.form.instance.FormDetailDTO;
import cn.axzo.workflow.common.model.request.form.instance.FormSearchDTO;
import cn.axzo.workflow.common.model.request.form.instance.FromDataSearchDTO;
import cn.axzo.workflow.common.model.response.form.FormVO;
import cn.axzo.workflow.common.model.response.form.definition.FormDefinitionVO;
import cn.axzo.workflow.common.model.response.form.instance.FormDataVO;
import cn.axzo.workflow.common.model.response.form.instance.FormInstanceVO;
import cn.axzo.workflow.core.service.FormCoreService;
import cn.axzo.workflow.server.common.annotation.ErrorReporter;
@ -60,4 +62,10 @@ public class FormAdminController implements FormAdminApi {
public CommonResponse<FormInstanceVO> getFormInstance(@Validated @RequestBody FormDetailDTO dto) {
return success(formCoreService.getFormInstance(dto));
}
@Operation(summary = "获取指定表单审批的实例数据")
@PostMapping("/instance/form/data")
public CommonResponse<List<FormDataVO>> getFormData(@Validated @RequestBody FromDataSearchDTO dto) {
return success(formCoreService.getFormData(dto));
}
}