package jnpf.elnService.cross.impl;
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import jnpf.elnEntity.field.ElnFieldDefEntity;
|
import jnpf.elnEntity.field.ElnFieldVersionEntity;
|
import jnpf.elnEntity.template.ElnTemplateBindingEntity;
|
import jnpf.elnEntity.template.ElnTemplateCrossReferenceEntity;
|
import jnpf.elnEntity.template.ElnTemplateVersionEntity;
|
import jnpf.elnEntity.template.ElnTemplateVos;
|
import jnpf.elnMapper.ElnFieldDefMapper;
|
import jnpf.elnMapper.ElnFieldVersionMapper;
|
import jnpf.elnMapper.ElnTemplateBindingMapper;
|
import jnpf.elnMapper.ElnTemplateCrossReferenceMapper;
|
import jnpf.elnMapper.ElnTemplateMapper;
|
import jnpf.elnService.cross.ElnCrossTemplateScalarReferencePolicy;
|
import jnpf.elnService.template.ElnTemplatePublishValidator;
|
import org.springframework.stereotype.Service;
|
import org.springframework.util.StringUtils;
|
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.LinkedHashMap;
|
import java.util.LinkedHashSet;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Set;
|
|
/** Publication is the final gate for rule lineage and unsupported template dependency chains. */
|
@Service
|
public class ElnCrossTemplatePublishValidator implements ElnTemplatePublishValidator {
|
private final ElnTemplateMapper templates;
|
private final ElnTemplateBindingMapper bindings;
|
private final ElnTemplateCrossReferenceMapper references;
|
private final ElnFieldDefMapper fieldDefinitions;
|
private final ElnFieldVersionMapper fieldVersions;
|
|
public ElnCrossTemplatePublishValidator(ElnTemplateMapper templates, ElnTemplateBindingMapper bindings,
|
ElnTemplateCrossReferenceMapper references, ElnFieldDefMapper fieldDefinitions,
|
ElnFieldVersionMapper fieldVersions) {
|
this.templates = templates;
|
this.bindings = bindings;
|
this.references = references;
|
this.fieldDefinitions = fieldDefinitions;
|
this.fieldVersions = fieldVersions;
|
}
|
|
@Override
|
public List<ElnTemplateVos.Diagnostic> validate(String tenantId, String templateId) {
|
List<ElnTemplateVos.Diagnostic> result = new ArrayList<>();
|
ElnTemplateVersionEntity target = template(tenantId, templateId);
|
if (target == null || !StringUtils.hasText(target.getMubanBianhao())) return result;
|
List<ElnTemplateCrossReferenceEntity> all = liveRules(tenantId);
|
Map<String, ElnTemplateBindingEntity> bindingById = new LinkedHashMap<>();
|
for (ElnTemplateBindingEntity binding : liveBindings(tenantId)) bindingById.put(binding.getId(), binding);
|
for (ElnTemplateCrossReferenceEntity rule : all) {
|
ElnTemplateBindingEntity binding = bindingById.get(rule.getMubanBangdingId());
|
if (binding == null || !templateId.equals(binding.getMubanId())) continue;
|
if (!"ACTIVE".equals(binding.getBangdingZhuangtai())) continue;
|
if (!"PRIMARY".equals(binding.getKongjianJuese())
|
|| StringUtils.hasText(binding.getRepeatGroupCode())) {
|
result.add(error("CROSS_TEMPLATE_TARGET_INVALID", "跨模板规则必须绑定当前模板的活动普通主字段", binding.getId()));
|
}
|
ElnTemplateVersionEntity sourceTemplate = template(tenantId, rule.getLaiyuanMubanId());
|
ElnFieldDefEntity sourceField = fieldDefinition(
|
tenantId, rule.getLaiyuanZiduanDingyiId());
|
ElnFieldVersionEntity sourceVersion = fieldVersion(tenantId, rule.getLaiyuanZiduanBanbenId());
|
ElnTemplateBindingEntity sourceBinding = sourceBinding(
|
rule, bindingById.values());
|
if (sourceTemplate == null || !"PUBLISHED".equals(sourceTemplate.getBizStatus())
|
|| !"enabled".equals(sourceTemplate.getBizEnabled())
|
|| !rule.getLaiyuanMubanBianhao().equals(sourceTemplate.getMubanBianhao())
|
|| sourceField == null || !"enabled".equals(sourceField.getBizEnabled())
|
|| sourceVersion == null
|
|| !rule.getLaiyuanZiduanDingyiId().equals(sourceVersion.getZiduanDingyiId())
|
|| !ElnCrossTemplateScalarReferencePolicy.isEligible(
|
templateId, sourceTemplate.getId(), sourceTemplate.getBizStatus(),
|
sourceField.getZuoyongFanwei(), sourceVersion, sourceBinding)) {
|
result.add(error("CROSS_TEMPLATE_SOURCE_INVALID", "跨模板来源模板或精确字段版本已失效", binding == null ? null : binding.getId()));
|
}
|
}
|
Map<String, List<String>> graph = graph(all, bindingById);
|
List<String> path = new ArrayList<>(); path.add(target.getMubanBianhao());
|
inspectPath(target.getMubanBianhao(), graph, path, new LinkedHashSet<>(), result);
|
return result;
|
}
|
|
private static void inspectPath(String node, Map<String, List<String>> graph, List<String> path,
|
Set<String> visiting, List<ElnTemplateVos.Diagnostic> result) {
|
if (!visiting.add(node)) return;
|
for (String next : graph.getOrDefault(node, Collections.emptyList())) {
|
List<String> candidate = new ArrayList<>(path); candidate.add(next);
|
if (path.contains(next)) {
|
result.add(error("CROSS_TEMPLATE_DEPENDENCY_CYCLE", "跨模板字段引用存在循环:" + join(candidate), null));
|
} else if (candidate.size() > 2) {
|
result.add(error("CROSS_TEMPLATE_INDIRECT_CHAIN", "跨模板字段引用不支持间接链:" + join(candidate), null));
|
} else {
|
inspectPath(next, graph, candidate, visiting, result);
|
}
|
}
|
visiting.remove(node);
|
}
|
|
private static Map<String, List<String>> graph(List<ElnTemplateCrossReferenceEntity> rules,
|
Map<String, ElnTemplateBindingEntity> bindingById) {
|
Map<String, List<String>> result = new LinkedHashMap<>();
|
for (ElnTemplateCrossReferenceEntity rule : rules) {
|
ElnTemplateBindingEntity binding = bindingById.get(rule.getMubanBangdingId());
|
if (binding != null && "ACTIVE".equals(binding.getBangdingZhuangtai())
|
&& StringUtils.hasText(binding.getMubanBianhao())
|
&& StringUtils.hasText(rule.getLaiyuanMubanBianhao())) {
|
result.computeIfAbsent(binding.getMubanBianhao(), ignored -> new ArrayList<>()).add(rule.getLaiyuanMubanBianhao());
|
}
|
}
|
return result;
|
}
|
|
private List<ElnTemplateCrossReferenceEntity> liveRules(String tenantId) {
|
List<ElnTemplateCrossReferenceEntity> result = references.selectList(Wrappers.<ElnTemplateCrossReferenceEntity>lambdaQuery()
|
.eq(ElnTemplateCrossReferenceEntity::getFTenantId, tenantId).eq(ElnTemplateCrossReferenceEntity::getFDeleteMark, 0));
|
return result == null ? Collections.emptyList() : result;
|
}
|
private List<ElnTemplateBindingEntity> liveBindings(String tenantId) {
|
List<ElnTemplateBindingEntity> result = bindings.selectList(Wrappers.<ElnTemplateBindingEntity>lambdaQuery()
|
.eq(ElnTemplateBindingEntity::getFTenantId, tenantId).eq(ElnTemplateBindingEntity::getFDeleteMark, 0));
|
return result == null ? Collections.emptyList() : result;
|
}
|
private ElnTemplateVersionEntity template(String tenantId, String id) { return templates.selectOne(Wrappers.<ElnTemplateVersionEntity>lambdaQuery().eq(ElnTemplateVersionEntity::getFTenantId, tenantId).eq(ElnTemplateVersionEntity::getId, id).eq(ElnTemplateVersionEntity::getFDeleteMark, 0)); }
|
private ElnFieldDefEntity fieldDefinition(String tenantId, String id) { return fieldDefinitions.selectOne(Wrappers.<ElnFieldDefEntity>lambdaQuery().eq(ElnFieldDefEntity::getFTenantId, tenantId).eq(ElnFieldDefEntity::getId, id).eq(ElnFieldDefEntity::getFDeleteMark, 0)); }
|
private ElnFieldVersionEntity fieldVersion(String tenantId, String id) { return fieldVersions.selectOne(Wrappers.<ElnFieldVersionEntity>lambdaQuery().eq(ElnFieldVersionEntity::getFTenantId, tenantId).eq(ElnFieldVersionEntity::getId, id).eq(ElnFieldVersionEntity::getFDeleteMark, 0)); }
|
private static ElnTemplateBindingEntity sourceBinding(ElnTemplateCrossReferenceEntity rule,
|
Iterable<ElnTemplateBindingEntity> bindings) {
|
for (ElnTemplateBindingEntity binding : bindings) {
|
if (rule.getLaiyuanMubanId().equals(binding.getMubanId())
|
&& rule.getLaiyuanZiduanDingyiId().equals(binding.getZiduanDingyiId())
|
&& rule.getLaiyuanZiduanBanbenId().equals(binding.getZiduanBanbenId())
|
&& "ACTIVE".equals(binding.getBangdingZhuangtai())
|
&& "PRIMARY".equals(binding.getKongjianJuese())
|
&& !StringUtils.hasText(binding.getRepeatGroupCode())) return binding;
|
}
|
return null;
|
}
|
private static String join(List<String> path) { return String.join(" -> ", path); }
|
private static ElnTemplateVos.Diagnostic error(String code, String message, String bindingId) { ElnTemplateVos.Diagnostic diagnostic = new ElnTemplateVos.Diagnostic(); diagnostic.setSeverity("ERROR"); diagnostic.setCode(code); diagnostic.setMessage(message); diagnostic.setBindingId(bindingId); return diagnostic; }
|
}
|