Merge remote-tracking branch 'origin/feature/REQ-5965' into dev

This commit is contained in:
wangli 2025-11-05 10:31:03 +08:00
commit 0602f69140
4 changed files with 41 additions and 38 deletions

View File

@ -162,7 +162,7 @@ public class CustomApproveTaskCmd extends AbstractCommand<Void> implements Seria
nextApprover);
}
Boolean logNodeHidden = (Boolean) task.getTransientVariable(TASK_LOG_NODE_HAS_BEEN_HIDDEN);
if (logNodeHidden) {
if (Objects.nonNull(logNodeHidden) && logNodeHidden) {
task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + taskId, HIDDEN.getStatus());
} else {
task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + taskId, APPROVED.getStatus());

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.core.service;
import cn.axzo.workflow.common.model.dto.CustomDocDTO;
import cn.axzo.workflow.common.model.dto.SimpleDocDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.doc.ApproverReadStatusDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.doc.ChangeApproverReadStatusDTO;
@ -18,5 +19,5 @@ public interface ExtAxReadRecordService extends IService<ExtAxReadRecord> {
List<SimpleDocDTO> queryReadStatus(ApproverReadStatusDTO dto);
Boolean changeReadStatus(ChangeApproverReadStatusDTO dto);
Boolean changeReadStatus(ChangeApproverReadStatusDTO dto, List<CustomDocDTO> customDocs);
}

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.core.service.impl;
import cn.axzo.workflow.common.model.dto.CustomDocDTO;
import cn.axzo.workflow.common.model.dto.SimpleDocDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.doc.ApproverReadStatusDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.doc.ChangeApproverReadStatusDTO;
@ -11,7 +12,6 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.TaskService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@ -34,8 +34,6 @@ public class ExtAxReadRecordServiceImpl extends ServiceImpl<ExtAxReadRecordMappe
@Resource
private ExtAxReadRecordMapper extAxReadRecordMapper;
@Resource
private TaskService taskService;
@Resource
private ExtAxModelDocService modelDocService;
@Override
@ -48,7 +46,7 @@ public class ExtAxReadRecordServiceImpl extends ServiceImpl<ExtAxReadRecordMappe
}
@Override
public Boolean changeReadStatus(ChangeApproverReadStatusDTO dto) {
public Boolean changeReadStatus(ChangeApproverReadStatusDTO dto, List<CustomDocDTO> customDocs) {
ExtAxReadRecord entity = new ExtAxReadRecord();
entity.setProcessInstanceId(dto.getProcessInstanceId());
entity.setPersonId(Long.valueOf(dto.getAssigner().getPersonId()));
@ -63,7 +61,11 @@ public class ExtAxReadRecordServiceImpl extends ServiceImpl<ExtAxReadRecordMappe
record.setPersonId(Long.valueOf(dto.getAssigner().getPersonId()));
record.setReadStatus(Lists.newArrayList(SimpleDocDTO.builder()
.id(dto.getDocId())
.tag(modelDocService.get(dto.getDocId()).getTag())
.tag(dto.getDocId() > 0 ? modelDocService.get(dto.getDocId()).getTag()
: customDocs.stream()
.filter(i -> Objects.equals(i.getId(), dto.getDocId()))
.findFirst()
.orElse(new CustomDocDTO()).getFileTag())
.readStatus(dto.getReadStatus())
.build()));
extAxReadRecordMapper.insert(record);
@ -76,7 +78,11 @@ public class ExtAxReadRecordServiceImpl extends ServiceImpl<ExtAxReadRecordMappe
} else {
record.getReadStatus().add(SimpleDocDTO.builder()
.id(dto.getDocId())
.tag(modelDocService.get(dto.getDocId()).getTag())
.tag(dto.getDocId() > 0 ? modelDocService.get(dto.getDocId()).getTag()
: customDocs.stream()
.filter(i -> Objects.equals(i.getId(), dto.getDocId()))
.findFirst()
.orElse(new CustomDocDTO()).getFileTag())
.readStatus(dto.getReadStatus())
.build());
}

View File

@ -7,6 +7,7 @@ import cn.axzo.oss.http.model.ApiSignUrlDownloadResponse;
import cn.axzo.workflow.client.feign.bpmn.ProcessInstanceApi;
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.dto.CustomDocDTO;
import cn.axzo.workflow.common.model.dto.SignFileDTO;
import cn.axzo.workflow.common.model.dto.SimpleDocDTO;
import cn.axzo.workflow.common.model.request.bpmn.log.LogApproveSearchDTO;
@ -68,6 +69,7 @@ import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.springframework.beans.BeanUtils;
@ -97,6 +99,7 @@ import java.util.stream.Stream;
import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_DOC_ID_NOT_IN_MODEL;
import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_DOC_READ_PARAM_ERROR;
import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_EXT_LOG_PARAM_ERROR;
import static cn.axzo.workflow.common.constant.BpmnConstants.SIGN_BIZ_CUSTOM_DOCS;
import static cn.azxo.framework.common.model.CommonResponse.success;
/**
@ -308,8 +311,7 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController
@PutMapping("/status/update")
@RepeatSubmit
@Override
public CommonResponse<Boolean> updateProcessStatus(@NotBlank(message = "流程定义 ID 不能为空") @RequestParam String processDefinitionId,
@NotNull(message = "状态不能为空") @RequestParam Integer status) {
public CommonResponse<Boolean> updateProcessStatus(@NotBlank(message = "流程定义 ID 不能为空") @RequestParam String processDefinitionId, @NotNull(message = "状态不能为空") @RequestParam Integer status) {
log.info("获得流程实例 updateProcessStatus===>>>参数:{},{}", processDefinitionId, status);
Boolean result = bpmnProcessInstanceService.updateProcessStatus(processDefinitionId, status);
return success(result);
@ -325,8 +327,7 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController
@Operation(summary = "获取审批流程实例的运行图")
@GetMapping("/graphical")
@Override
public CommonResponse<ObjectNode> processInstanceGraphical(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId,
@Nullable @RequestParam(required = false) String tenantId) {
public CommonResponse<ObjectNode> processInstanceGraphical(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId, @Nullable @RequestParam(required = false) String tenantId) {
return success(bpmnProcessInstanceService.getProcessInstanceGraphical(processInstanceId, tenantId));
}
@ -340,8 +341,7 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController
@Operation(summary = "获取指定实例的全节点执行顺序推算")
@GetMapping("/node/forecasting")
@Override
public CommonResponse<List<ProcessNodeDetailVO>> processInstanceNodeForecast(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId,
@Nullable String tenantId) {
public CommonResponse<List<ProcessNodeDetailVO>> processInstanceNodeForecast(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId, @Nullable String tenantId) {
return success(bpmnProcessInstanceService.getProcessInstanceNodeForecast(processInstanceId, tenantId));
}
@ -358,10 +358,7 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController
@Operation(summary = "推断指定流程实例的过滤掉部分节点执行顺序")
@GetMapping("/node/filter/forecasting")
@Override
public CommonResponse<List<ProcessNodeDetailVO>> processInstanceFilterNodeForecast(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId,
@Nullable @RequestParam(required = false) String tenantId,
@RequestParam(required = false, defaultValue = "false") Boolean allNode,
@Nullable @RequestParam(required = false) List<String> nodeDefinitionKeys) {
public CommonResponse<List<ProcessNodeDetailVO>> processInstanceFilterNodeForecast(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId, @Nullable @RequestParam(required = false) String tenantId, @RequestParam(required = false, defaultValue = "false") Boolean allNode, @Nullable @RequestParam(required = false) List<String> nodeDefinitionKeys) {
if (allNode) {
return success(bpmnProcessInstanceService.getProcessInstanceNodeForecast(processInstanceId, tenantId));
} else {
@ -379,10 +376,8 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController
@Operation(summary = "获取指定流程实例的流程变量")
@GetMapping("/cooperation-org")
@Override
public CommonResponse<Map<String, Object>> getProcessVariables(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId,
@Nullable String tenantId) {
HistoricProcessInstance processInstance = bpmnProcessInstanceService.getProcessInstance(processInstanceId,
tenantId, true);
public CommonResponse<Map<String, Object>> getProcessVariables(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId, @Nullable String tenantId) {
HistoricProcessInstance processInstance = bpmnProcessInstanceService.getProcessInstance(processInstanceId, tenantId, true);
return success(processInstance.getProcessVariables());
}
@ -477,18 +472,10 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController
}
private void parseSignatureUrl(BpmnProcessInstanceLogVO log) {
List<String> signUrls = log.getTaskDetails().stream()
.filter(i -> StringUtils.hasText(i.getTaskId()))
.map(BpmnTaskInstanceLogVO::getSignatureUrl)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
List<String> signUrls = log.getTaskDetails().stream().filter(i -> StringUtils.hasText(i.getTaskId())).map(BpmnTaskInstanceLogVO::getSignatureUrl).filter(Objects::nonNull).distinct().collect(Collectors.toList());
if (!CollectionUtils.isEmpty(signUrls)) {
Map<String, String> ossUrlMap = ListUtils.emptyIfNull(getSignPrivateUrl(signUrls)).stream()
.collect(Collectors.toMap(ApiSignUrlDownloadResponse::getFileKey, ApiSignUrlDownloadResponse::getSignUrl, (s, t) -> s));
log.getTaskDetails().stream()
.filter(i -> StringUtils.hasText(i.getTaskId()))
.forEach(i -> i.setSignatureUrl(ossUrlMap.getOrDefault(i.getSignatureUrl(), null)));
Map<String, String> ossUrlMap = ListUtils.emptyIfNull(getSignPrivateUrl(signUrls)).stream().collect(Collectors.toMap(ApiSignUrlDownloadResponse::getFileKey, ApiSignUrlDownloadResponse::getSignUrl, (s, t) -> s));
log.getTaskDetails().stream().filter(i -> StringUtils.hasText(i.getTaskId())).forEach(i -> i.setSignatureUrl(ossUrlMap.getOrDefault(i.getSignatureUrl(), null)));
}
}
@ -520,8 +507,7 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController
}
ApiSignUrlDownloadRequest ossRequest = new ApiSignUrlDownloadRequest();
ossRequest.setFileKeys(fileKeys);
Map</*fileKey*/String, /*ossUrl*/ApiSignUrlDownloadResponse> ossUrlMap = RpcExternalUtil.rpcProcessor(() -> serverFileServiceApi.signUrlFetchDownload(ossRequest), "批量获取文件 OSS 地址", ossRequest)
.stream().collect(Collectors.toMap(ApiSignUrlDownloadResponse::getFileKey, Function.identity(), (s, t) -> s));
Map</*fileKey*/String, /*ossUrl*/ApiSignUrlDownloadResponse> ossUrlMap = RpcExternalUtil.rpcProcessor(() -> serverFileServiceApi.signUrlFetchDownload(ossRequest), "批量获取文件 OSS 地址", ossRequest).stream().collect(Collectors.toMap(ApiSignUrlDownloadResponse::getFileKey, Function.identity(), (s, t) -> s));
docs.forEach(i -> {
if (StringUtils.hasText(i.getFileKey())) {
ApiSignUrlDownloadResponse ossFileInfo = ossUrlMap.getOrDefault(i.getFileKey(), null);
@ -553,10 +539,20 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController
}
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
List<DocBaseVO> relationDocs = commandExecutor.execute(new CustomGetModelDocsCmd(dto.getProcessInstanceId(), true, extAxModelDocMapper, extAxReModelService));
relationDocs.stream().filter(i -> Objects.equals(i.getId(), dto.getDocId()))
.findAny().orElseThrow(() -> new WorkflowEngineException(PROCESS_DOC_ID_NOT_IN_MODEL));
return success(readRecordService.changeReadStatus(dto));
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
List<CustomDocDTO> customDocs = ListUtils.emptyIfNull(runtimeService.getVariable(dto.getProcessInstanceId(), SIGN_BIZ_CUSTOM_DOCS, List.class));
// 检查 relationDocs 中是否存在具有指定 id 的对象
boolean existsInRelationDocs = relationDocs.stream().anyMatch(doc -> Objects.equals(doc.getId(), dto.getDocId()));
// 检查 customDocs 中是否存在具有指定 id 的对象
boolean existsInCustomDocs = customDocs.stream().anyMatch(doc -> Objects.equals(doc.getId(), dto.getDocId()));
if (!existsInRelationDocs && !existsInCustomDocs) {
throw new WorkflowEngineException(PROCESS_DOC_ID_NOT_IN_MODEL);
}
return success(readRecordService.changeReadStatus(dto, customDocs));
}
@Override