刘光辉
6 小时以前 06f314545ac3514d7ce28939920866ae8188d31c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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; }
}