package com.gxwebsoft.approval.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.fasterxml.jackson.databind.ObjectMapper; import com.gxwebsoft.approval.document.ApprovalDocumentRegistry; import com.gxwebsoft.approval.document.ApprovalDocumentType; import com.gxwebsoft.approval.entity.ApprovalFlowDefinition; import com.gxwebsoft.approval.mapper.ApprovalFlowDefinitionMapper; import com.gxwebsoft.approval.param.ApprovalFlowPublishParam; import com.gxwebsoft.approval.support.ApprovalFieldSpec; import com.gxwebsoft.approval.support.ApprovalNode; import com.gxwebsoft.approval.vo.ApprovalFlowVO; import com.gxwebsoft.approval.vo.ApprovalFlowVersionVO; import com.gxwebsoft.common.core.exception.BusinessException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * 流程配置服务(ADR 0015):在后台查看与发布流程定义。 * *

不改 {@link ApprovalFlowService#getOrCreate}:后台发布的新版本号恒大于代码基线, * 因此 getOrCreate 的「库中版本 >= 基线即用库中版本」判定自然选用后台配置。

* *

版本不可变:发布只是插入新行,已提交实例继续引用其 definition_id;新版本 * 只对之后提交的单据生效。保存即发布,没有草稿态。

*/ @Service public class ApprovalFlowConfigService { private static final Logger log = LoggerFactory.getLogger(ApprovalFlowConfigService.class); private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private static final int MAX_PUBLISH_ATTEMPTS = 3; private static final int HISTORY_LIMIT = 50; @Resource private ApprovalFlowDefinitionMapper flowDefinitionMapper; @Resource private ApprovalDocumentRegistry documentRegistry; @Resource private ApprovalNodeValidator nodeValidator; @Resource private ObjectMapper objectMapper; /** 全部已注册流程的当前版本;未发布过的流程返回空节点,前端据此显示「尚未发布」。 */ public List list() { List list = new ArrayList<>(); for (ApprovalDocumentType type : documentRegistry.all()) { list.add(toVO(type, findLatest(type.flowCode()))); } return list; } /** 历史版本(倒序,最多 HISTORY_LIMIT 条)。 */ public List history(String flowCode) { documentRegistry.require(flowCode); List definitions = flowDefinitionMapper.selectList( new LambdaQueryWrapper() .eq(ApprovalFlowDefinition::getFlowCode, flowCode) .orderByDesc(ApprovalFlowDefinition::getVersion) .last("LIMIT " + HISTORY_LIMIT)); List list = new ArrayList<>(); for (ApprovalFlowDefinition definition : definitions) { ApprovalFlowVersionVO vo = new ApprovalFlowVersionVO(); vo.setDefinitionId(definition.getId()); vo.setVersion(definition.getVersion()); vo.setName(definition.getName()); vo.setPublishTime(formatTime(definition)); vo.setNodes(parseNodes(definition.getNodesJson())); list.add(vo); } return list; } /** 发布新版本:校验节点后插入新行,版本号取库中最新版与代码基线的较大者加一。 */ public ApprovalFlowVO publish(ApprovalFlowPublishParam param) { if (param == null || param.getFlowCode() == null) { throw new BusinessException("请选择要发布的流程"); } String flowCode = param.getFlowCode(); ApprovalDocumentType type = documentRegistry.require(flowCode); List nodes = nodeValidator.validate(flowCode, param.getNodes()); String nodesJson = writeNodes(nodes); for (int attempt = 0; attempt < MAX_PUBLISH_ATTEMPTS; attempt++) { ApprovalFlowDefinition latest = findLatest(flowCode); ApprovalFlowDefinition created = new ApprovalFlowDefinition(); created.setFlowCode(flowCode); created.setVersion(nextVersion(latest, type)); created.setName(flowName(latest, type)); created.setNodesJson(nodesJson); try { flowDefinitionMapper.insert(created); log.info("审批流程定义已发布: flowCode={}, version={}", flowCode, created.getVersion()); return toVO(type, created); } catch (DuplicateKeyException e) { log.info("审批流程定义发布冲突,重试: flowCode={}", flowCode); } } throw new BusinessException("流程定义发布冲突,请重试"); } private int nextVersion(ApprovalFlowDefinition latest, ApprovalDocumentType type) { int baseline = Math.max(1, type.latestVersion()); if (latest == null) { return baseline; } return Math.max(latest.getVersion(), baseline) + 1; } private String flowName(ApprovalFlowDefinition latest, ApprovalDocumentType type) { if (latest != null && latest.getName() != null && !latest.getName().trim().isEmpty()) { return latest.getName(); } return type.flowName(); } private ApprovalFlowVO toVO(ApprovalDocumentType type, ApprovalFlowDefinition latest) { ApprovalFlowVO vo = new ApprovalFlowVO(); vo.setFlowCode(type.flowCode()); vo.setFlowName(type.flowName()); vo.setFactoryVersion(type.latestVersion()); vo.setFactoryNodes(parseNodes(type.nodesJson())); vo.setResolvers(nodeValidator.resolverOptions()); List specs = type.fieldSpecs(); vo.setFieldSpecs(specs == null ? Collections.emptyList() : specs); if (latest != null) { vo.setDefinitionId(latest.getId()); vo.setVersion(latest.getVersion()); vo.setPublishTime(formatTime(latest)); vo.setNodes(parseNodes(latest.getNodesJson())); } else { vo.setNodes(Collections.emptyList()); } return vo; } private List parseNodes(String nodesJson) { if (nodesJson == null || nodesJson.trim().isEmpty()) { return Collections.emptyList(); } try { List nodes = objectMapper.readValue(nodesJson, objectMapper.getTypeFactory().constructCollectionType(List.class, ApprovalNode.class)); return nodes == null ? Collections.emptyList() : nodes; } catch (Exception e) { log.warn("审批流程定义节点解析失败", e); throw new BusinessException("审批流程定义格式错误"); } } private String writeNodes(List nodes) { try { return objectMapper.writeValueAsString(nodes); } catch (Exception e) { throw new BusinessException("流程节点格式错误"); } } private String formatTime(ApprovalFlowDefinition definition) { return definition.getCreateTime() == null ? null : definition.getCreateTime().format(TIME_FORMAT); } private ApprovalFlowDefinition findLatest(String flowCode) { return flowDefinitionMapper.selectOne(new LambdaQueryWrapper() .eq(ApprovalFlowDefinition::getFlowCode, flowCode) .orderByDesc(ApprovalFlowDefinition::getVersion) .last("LIMIT 1")); } }