Merge branch 'feature/countersign_ext' into dev

This commit is contained in:
wangli 2025-10-21 11:51:34 +08:00
commit 8b1fc0e108
5 changed files with 149 additions and 0 deletions

View File

@ -252,6 +252,14 @@ public interface BpmnConstants {
* 签署业务发起流程实例时重新选择的文档tag 集合
*/
String SIGN_PROCESS_ENABLE_DOC_IDS = "[_SIGN_PROCESS_ENABLE_DOC_IDS_]";
/**
* 签署业务自定义业务传入的文档集合
*/
String SIGN_BIZ_CUSTOM_DOCS = "[_SIGN_BIZ_CUSTOM_DOCS_]";
/**
* 签署业务自定义文档的顺序位置类型
*/
String SIGN_BIZ_CUSTOM_DOC_ADD_TYPE = "[_SIGN_BIZ_CUSTOM_DOC_ADD_TYPE_]";
/**
* 签署业务基于业务自定义变量的传入
*/

View File

@ -0,0 +1,49 @@
package cn.axzo.workflow.common.model.dto;
import cn.axzo.workflow.common.enums.FileTypeEnum;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 签署文件记录信息
*
* @author wangli
* @since 2025-04-03 11:21
*/
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CustomDocDTO implements Serializable {
private static final long serialVersionUID = -8709597975507074853L;
/**
* 文件名称,可能会包含变量
*/
private String fileName;
/**
* 文件的标签
*/
private String fileTag;
/**
* 文件 wps code
* <p>
* wps 文件的标识通过{@link cn.axzo.nanopart.doc.api.anonymous.DocAnonymousDatabaseApi#createFile(cn.axzo.nanopart.doc.api.anonymous.request.AnonymousCreateFileRequest)} 接口创建文件后返回的 fileCode
*/
private String fileCode;
/**
* 文件的类型
*/
private FileTypeEnum fileType;
}

View File

@ -2,6 +2,7 @@ package cn.axzo.workflow.common.model.request.bpmn.process;
import cn.axzo.workflow.common.constant.BpmnConstants;
import cn.axzo.workflow.common.model.dto.CooperationOrgDTO;
import cn.axzo.workflow.common.model.dto.CustomDocDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ -120,6 +121,22 @@ public class BpmnProcessInstanceCreateDTO extends BpmnProcessInstanceCreateWithF
@ApiModelProperty(value = "签署业务发起时,选择的文档")
private List<Long> docIds;
/**
* "签字业务"专用
* <p>
* 业务自定义的文档
*/
@ApiModelProperty(value = "业务自定义文档")
private List<CustomDocDTO> customDocs;
/**
* "签字业务"专用自定义文档的顺序位置信息在流程模板配置文档之前还是之后
* <p>
* 可选值first(之前)last(之后), 如果为空默认为 last
*/
@ApiModelProperty(value = "自定义文档顺序位置", notes = "可选值first(之前)、last(之后), 如果为空,默认为 last")
private String docAddType;
/**
* 仅针对签署业务设置审批完成后的最终签署人列表该属性仅为透传业务消费时请从 MQ 广播事件中的 variables 中通过 key= {@link BpmnConstants#SIGNATORIES } 获取
*/

View File

@ -14,6 +14,7 @@ import cn.axzo.workflow.common.enums.BusinessTypeEnum;
import cn.axzo.workflow.common.enums.ButtonVisibleScopeEnum;
import cn.axzo.workflow.common.enums.WorkspaceType;
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.BpmnApproveConf;
@ -152,8 +153,10 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static cn.axzo.workflow.client.config.WorkflowRequestInterceptor.HEADER_SERVER_NAME;
import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CREATE_PARAM_ERROR;
@ -187,6 +190,8 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.OLD_INTERNAL_INITIA
import static cn.axzo.workflow.common.constant.BpmnConstants.PENDING_TEMPLATE_VARIABLE;
import static cn.axzo.workflow.common.constant.BpmnConstants.PROCESS_OWNERSHIP_APPLICATION;
import static cn.axzo.workflow.common.constant.BpmnConstants.SIGNATORIES;
import static cn.axzo.workflow.common.constant.BpmnConstants.SIGN_BIZ_CUSTOM_DOCS;
import static cn.axzo.workflow.common.constant.BpmnConstants.SIGN_BIZ_CUSTOM_DOC_ADD_TYPE;
import static cn.axzo.workflow.common.constant.BpmnConstants.SIGN_PROCESS_ENABLE_DOC_IDS;
import static cn.axzo.workflow.common.constant.BpmnConstants.SIGN_VARIABLE;
import static cn.axzo.workflow.common.constant.BpmnConstants.WORKFLOW_ENGINE_VERSION;
@ -495,6 +500,8 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
dto.getVariables().put(SIGN_PROCESS_ENABLE_DOC_IDS, dto.getDocIds());
dto.getVariables().put(SIGN_VARIABLE, dto.getBizCustomVariables());
dto.getVariables().put(SIGNATORIES, dto.getSignatories());
dto.getVariables().put(SIGN_BIZ_CUSTOM_DOCS, dto.getCustomDocs());
dto.getVariables().put(SIGN_BIZ_CUSTOM_DOC_ADD_TYPE, StringUtils.hasText(dto.getDocAddType()) ? dto.getDocAddType() : "last");
}
});
dto.getVariables().put(INTERNAL_INITIATOR, dto.getInitiator().toJson());
@ -1873,6 +1880,9 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor();
List<DocBaseVO> docs = commandExecutor.execute(new CustomGetModelDocsCmd(dto.getProcessInstanceId(), true, extAxModelDocMapper, extAxReModelService));
// 获取业务自定义传入的文档
getAndAddBizCustomDocs(dto.getProcessInstanceId(), docs);
Map<Long, Boolean> readStatusMap = new HashMap<>();
if (Objects.nonNull(dto.getAssigner())) {
readStatusMap.putAll(extAxReadRecordService.queryReadStatus(ApproverReadStatusDTO.builder()
@ -1895,4 +1905,44 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic
t.setReadStatus(readStatusMap.getOrDefault(t.getId(), false));
});
}
private List<DocBaseVO> getAndAddBizCustomDocs(String processInstanceId, List<DocBaseVO> docs) {
List<CustomDocDTO> bizCustomDocs = Optional.ofNullable(
runtimeService.getVariable(processInstanceId, SIGN_BIZ_CUSTOM_DOCS, List.class))
.orElse(Collections.emptyList());
String customAddType = runtimeService.getVariable(processInstanceId, SIGN_BIZ_CUSTOM_DOC_ADD_TYPE, String.class);
boolean appendLast = "last".equals(customAddType);
Comparator<DocBaseVO> orderComparator = Comparator.comparing(
DocBaseVO::getOrder, Comparator.nullsFirst(Comparator.naturalOrder()));
Optional<DocBaseVO> baseOpt;
Stream<DocBaseVO> filtered = docs.stream().filter(Objects::nonNull);
if (appendLast) {
baseOpt = filtered.max(orderComparator);
} else {
baseOpt = filtered.min(orderComparator);
}
int baseOrder = baseOpt.map(DocBaseVO::getOrder).orElse(0);
String tenantId = baseOpt.map(DocBaseVO::getTenantId).orElse(null);
AtomicInteger orderCounter = new AtomicInteger(baseOrder);
int delta = appendLast ? 1 : -1;
List<DocBaseVO> docBaseVOS = BeanMapper.copyList(bizCustomDocs, DocBaseVO.class, (s, t) -> {
t.setStatus(true);
t.setTempFile(false);
t.setOrder(orderCounter.addAndGet(delta));
t.setTenantId(tenantId);
});
if (appendLast) {
docs.addAll(docBaseVOS);
} else {
docs.addAll(0, docBaseVOS);
}
return docs;
}
}

View File

@ -3,6 +3,7 @@ package cn.axzo.workflow.server.controller.listener.task;
import cn.axzo.nanopart.doc.api.anonymous.DocAnonymousDatabaseApi;
import cn.axzo.nanopart.doc.api.index.request.CopyNodeRequest;
import cn.axzo.workflow.common.enums.FileTypeEnum;
import cn.axzo.workflow.common.model.dto.CustomDocDTO;
import cn.axzo.workflow.common.model.dto.SignFileDTO;
import cn.axzo.workflow.common.model.dto.VariableObjectDTO;
import cn.axzo.workflow.common.model.request.bpmn.BpmnSignConf;
@ -23,6 +24,7 @@ import cn.axzo.workflow.server.common.util.WpsUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
@ -39,6 +41,8 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import static cn.axzo.workflow.common.constant.BpmnConstants.SIGN_BIZ_CUSTOM_DOCS;
import static cn.axzo.workflow.common.constant.BpmnConstants.SIGN_BIZ_CUSTOM_DOC_ADD_TYPE;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER;
/**
@ -96,6 +100,27 @@ public class FirstCopyTemplateFileTaskEvent_105_Listener extends AbstractBpmnEve
} else {
// 复制基础模板
List<SignFileDTO> docTemplates = copyTempTemplate(docs);
// 处理业务自定义文档
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
List<CustomDocDTO> customDocs = runtimeService.getVariable(processInstanceId, SIGN_BIZ_CUSTOM_DOCS, List.class);
List<SignFileDTO> customDocTemplates = new ArrayList<>();
for (int i = 0; i < customDocs.size(); i++) {
customDocTemplates.add(SignFileDTO.builder()
.id(i - (i + 1L)) // 负数 ID避免冲突
.fileName(customDocs.get(i).getFileName())
.templateName(customDocs.get(i).getFileName())
.fileTag(customDocs.get(i).getFileTag())
.fileCode(customDocs.get(i).getFileCode())
.fileType(customDocs.get(i).getFileType())
.build());
}
String customAddType = runtimeService.getVariable(processInstanceId, SIGN_BIZ_CUSTOM_DOC_ADD_TYPE, String.class);
if (Objects.equals("last", customAddType)) {
docTemplates.addAll(customDocTemplates);
} else {
docTemplates.addAll(0, customDocTemplates);
}
processSign.setDocTemplate(docTemplates);
List<SignFileDTO> archives = replaceTemplateVariable(docTemplates, processEngineConfiguration, processInstanceId);