184 lines
7.2 KiB
Java
184 lines
7.2 KiB
Java
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):在后台查看与发布流程定义。
|
|
*
|
|
* <p>不改 {@link ApprovalFlowService#getOrCreate}:后台发布的新版本号恒大于代码基线,
|
|
* 因此 getOrCreate 的「库中版本 >= 基线即用库中版本」判定自然选用后台配置。</p>
|
|
*
|
|
* <p>版本不可变:发布只是插入新行,已提交实例继续引用其 definition_id;新版本
|
|
* 只对之后提交的单据生效。保存即发布,没有草稿态。</p>
|
|
*/
|
|
@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<ApprovalFlowVO> list() {
|
|
List<ApprovalFlowVO> list = new ArrayList<>();
|
|
for (ApprovalDocumentType type : documentRegistry.all()) {
|
|
list.add(toVO(type, findLatest(type.flowCode())));
|
|
}
|
|
return list;
|
|
}
|
|
|
|
/** 历史版本(倒序,最多 HISTORY_LIMIT 条)。 */
|
|
public List<ApprovalFlowVersionVO> history(String flowCode) {
|
|
documentRegistry.require(flowCode);
|
|
List<ApprovalFlowDefinition> definitions = flowDefinitionMapper.selectList(
|
|
new LambdaQueryWrapper<ApprovalFlowDefinition>()
|
|
.eq(ApprovalFlowDefinition::getFlowCode, flowCode)
|
|
.orderByDesc(ApprovalFlowDefinition::getVersion)
|
|
.last("LIMIT " + HISTORY_LIMIT));
|
|
List<ApprovalFlowVersionVO> 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<ApprovalNode> 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<ApprovalFieldSpec> 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<ApprovalNode> parseNodes(String nodesJson) {
|
|
if (nodesJson == null || nodesJson.trim().isEmpty()) {
|
|
return Collections.emptyList();
|
|
}
|
|
try {
|
|
List<ApprovalNode> 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<ApprovalNode> 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<ApprovalFlowDefinition>()
|
|
.eq(ApprovalFlowDefinition::getFlowCode, flowCode)
|
|
.orderByDesc(ApprovalFlowDefinition::getVersion)
|
|
.last("LIMIT 1"));
|
|
}
|
|
}
|