刘光辉
4 小时以前 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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
package jnpf.elnService.impl;
 
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jnpf.elnEntity.runtime.ElnRecordEntity;
import jnpf.elnEntity.runtime.ElnRecordFieldValueEntity;
import jnpf.elnEntity.template.ElnTemplateBindingEntity;
import jnpf.elnEntity.template.ElnTemplateCrossReferenceEntity;
import jnpf.elnMapper.ElnRecordFieldValueMapper;
import jnpf.elnMapper.ElnTemplateCrossReferenceMapper;
import jnpf.elnService.ElnCrossTemplateSnapshotResolver;
import jnpf.elnService.ElnTestRequestIdentityLookup;
import jnpf.elnService.ElnRecordSourceResolver;
import jnpf.exception.DataException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
 
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
 
@Component
public class ElnCrossTemplateSnapshotResolverImpl implements ElnCrossTemplateSnapshotResolver {
    private final ElnTemplateCrossReferenceMapper referenceMapper;
    private final ElnRecordFieldValueMapper valueMapper;
    private final ElnTestRequestIdentityLookup identityLookup;
    private final ElnRecordSourceResolver sourceResolver;
 
    public ElnCrossTemplateSnapshotResolverImpl(ElnTemplateCrossReferenceMapper referenceMapper,
            ElnRecordFieldValueMapper valueMapper, ElnTestRequestIdentityLookup identityLookup,
            ElnRecordSourceResolver sourceResolver) {
        this.referenceMapper = referenceMapper;
        this.valueMapper = valueMapper;
        this.identityLookup = identityLookup;
        this.sourceResolver = sourceResolver;
    }
 
    @Override
    public Map<String, ElnRecordFieldValueEntity> resolve(String tenantId, String targetTestWorkItemId,
            List<ElnTemplateBindingEntity> targetBindings) {
        Map<String, ElnTemplateCrossReferenceEntity> rules = new LinkedHashMap<>();
        if (targetBindings == null || targetBindings.isEmpty()) return Collections.emptyMap();
        for (ElnTemplateBindingEntity binding : targetBindings) {
            if (binding == null || !StringUtils.hasText(binding.getId())) continue;
            ElnTemplateCrossReferenceEntity rule = rule(tenantId, binding.getId());
            if (rule != null) rules.put(binding.getId(), rule);
        }
        return resolveRules(tenantId, targetTestWorkItemId, rules);
    }
 
    @Override
    public Map<String, ElnRecordFieldValueEntity> resolveByBindingIds(String tenantId,
            String targetTestWorkItemId, Set<String> targetBindingIds) {
        if (targetBindingIds == null || targetBindingIds.isEmpty()) return Collections.emptyMap();
        Map<String, ElnTemplateCrossReferenceEntity> rules = new LinkedHashMap<>();
        for (String bindingId : targetBindingIds) {
            if (!StringUtils.hasText(bindingId)) continue;
            ElnTemplateCrossReferenceEntity rule = rule(tenantId, bindingId);
            if (rule != null) rules.put(bindingId, rule);
        }
        return resolveRules(tenantId, targetTestWorkItemId, rules);
    }
 
    @Override
    public List<SnapshotIssue> validatePersistedSnapshots(String tenantId,
            String targetTestWorkItemId, List<ElnRecordFieldValueEntity> targetValues) {
        List<ElnRecordFieldValueEntity> snapshots = crossTemplateValues(targetValues);
        if (snapshots.isEmpty()) return Collections.emptyList();
 
        Map<String, ElnTestRequestIdentityLookup.Identity> identities;
        try {
            identities = identityLookup.resolve(tenantId,
                    Collections.singletonList(targetTestWorkItemId));
        } catch (RuntimeException exception) {
            return issuesForAll(tenantId, snapshots, "SOURCE_RECORD_IDENTITY_INVALID",
                    message(exception, "目标检验项请验单身份解析失败"));
        }
        ElnTestRequestIdentityLookup.Identity target = identities == null
                ? null : identities.get(targetTestWorkItemId);
        if (target == null || !StringUtils.hasText(target.getTestRequestId())) {
            return issuesForAll(tenantId, snapshots, "SOURCE_RECORD_IDENTITY_INVALID",
                    "目标检验项缺少请验单身份");
        }
 
        List<SnapshotIssue> issues = new ArrayList<>();
        for (ElnRecordFieldValueEntity snapshot : snapshots) {
            validateSnapshot(tenantId, target.getTestRequestId(), snapshot, issues);
        }
        return issues;
    }
 
    private ElnTemplateCrossReferenceEntity rule(String tenantId, String bindingId) {
        return referenceMapper.selectOne(Wrappers.<ElnTemplateCrossReferenceEntity>lambdaQuery()
                .eq(ElnTemplateCrossReferenceEntity::getFTenantId, tenantId)
                .eq(ElnTemplateCrossReferenceEntity::getMubanBangdingId, bindingId)
                .eq(ElnTemplateCrossReferenceEntity::getFDeleteMark, 0));
    }
 
    private void validateSnapshot(String tenantId, String testRequestId,
            ElnRecordFieldValueEntity snapshot, List<SnapshotIssue> issues) {
        ElnTemplateCrossReferenceEntity rule = rule(tenantId, snapshot.getMubanBangdingId());
        if (rule == null) {
            issues.add(issue("CROSS_TEMPLATE_RULE_MISSING", "跨模板来源规则不存在",
                    snapshot, null));
            return;
        }
        try {
            ElnRecordEntity source = sourceResolver.resolve(
                    tenantId, testRequestId, rule.getLaiyuanMubanBianhao());
            if (!Objects.equals(snapshot.getKuamoLaiyuanJiluId(), source.getId())) {
                issues.add(issue("SOURCE_RECORD_DRIFT", "跨模板源记录已变化", snapshot, rule));
                return;
            }
            if (!Objects.equals(snapshot.getKuamoLaiyuanXiudingId(), source.getDangqianXiudingId())) {
                issues.add(issue("SOURCE_REVISION_DRIFT", "跨模板源记录当前修订已变化", snapshot, rule));
                return;
            }
 
            ElnRecordFieldValueEntity current = currentSourceValue(tenantId, source, rule);
            if (current == null || !hasValue(current)) {
                issues.add(issue("SOURCE_FIELD_VALUE_INVALID", "跨模板源字段值为空或不存在",
                        snapshot, rule));
                return;
            }
            if ("CROSS_TEMPLATE".equals(current.getZhiLaiyuan())) {
                issues.add(issue("SOURCE_FIELD_CHAIN_NOT_SUPPORTED", "跨模板源字段形成了间接引用链",
                        snapshot, rule));
                return;
            }
            if (!Objects.equals(snapshot.getKuamoLaiyuanZhiId(), current.getId())) {
                issues.add(issue("SOURCE_VALUE_ID_DRIFT", "跨模板源字段值身份已变化", snapshot, rule));
                return;
            }
            if (!Objects.equals(rule.getLaiyuanZiduanBanbenId(), current.getZiduanBanbenId())
                    || !Objects.equals(snapshot.getKuamoLaiyuanZiduanBanbenId(), current.getZiduanBanbenId())) {
                issues.add(issue("SOURCE_FIELD_VERSION_DRIFT", "跨模板源字段固定版本已变化",
                        snapshot, rule));
                return;
            }
            if (!sameValue(snapshot, current)) {
                issues.add(issue("SOURCE_VALUE_DRIFT", "跨模板源字段值已变化", snapshot, rule));
            }
        } catch (RuntimeException exception) {
            issues.add(issue(code(exception, "SOURCE_RECORD_INVALID"),
                    message(exception, "跨模板源记录当前不可用"), snapshot, rule));
        }
    }
 
    private ElnRecordFieldValueEntity currentSourceValue(String tenantId, ElnRecordEntity source,
            ElnTemplateCrossReferenceEntity rule) {
        return valueMapper.selectOne(Wrappers.<ElnRecordFieldValueEntity>lambdaQuery()
                .eq(ElnRecordFieldValueEntity::getFTenantId, tenantId)
                .eq(ElnRecordFieldValueEntity::getDianziJiluId, source.getId())
                .eq(ElnRecordFieldValueEntity::getXiudingId, source.getDangqianXiudingId())
                .eq(ElnRecordFieldValueEntity::getZiduanDingyiId, rule.getLaiyuanZiduanDingyiId())
                .eq(ElnRecordFieldValueEntity::getBangdingJuese, "PRIMARY")
                .eq(ElnRecordFieldValueEntity::getBangdingZhuangtai, "ACTIVE")
                .and(q -> q.isNull(ElnRecordFieldValueEntity::getFDeleteMark)
                        .or().eq(ElnRecordFieldValueEntity::getFDeleteMark, 0)));
    }
 
    private List<SnapshotIssue> issuesForAll(String tenantId,
            List<ElnRecordFieldValueEntity> snapshots,
            String code, String message) {
        List<SnapshotIssue> issues = new ArrayList<>();
        for (ElnRecordFieldValueEntity snapshot : snapshots) {
            ElnTemplateCrossReferenceEntity rule = rule(tenantId, snapshot.getMubanBangdingId());
            issues.add(issue(code, message, snapshot, rule));
        }
        return issues;
    }
 
    private static List<ElnRecordFieldValueEntity> crossTemplateValues(
            List<ElnRecordFieldValueEntity> values) {
        if (values == null || values.isEmpty()) return Collections.emptyList();
        List<ElnRecordFieldValueEntity> snapshots = new ArrayList<>();
        for (ElnRecordFieldValueEntity value : values) {
            if (value != null && "CROSS_TEMPLATE".equals(value.getZhiLaiyuan())) snapshots.add(value);
        }
        return snapshots;
    }
 
    private static SnapshotIssue issue(String code, String message,
            ElnRecordFieldValueEntity snapshot, ElnTemplateCrossReferenceEntity rule) {
        return new SnapshotIssue(code, message, snapshot.getMubanBangdingId(),
                snapshot.getZiduanBianma(), rule == null ? null : rule.getLaiyuanMubanBianhao());
    }
 
    private static boolean sameValue(ElnRecordFieldValueEntity snapshot,
            ElnRecordFieldValueEntity source) {
        return Objects.equals(snapshot.getZhiWenben(), source.getZhiWenben())
                && decimalEquals(snapshot.getZhiShuzhi(), source.getZhiShuzhi())
                && dateEquals(snapshot.getZhiRiqi(), source.getZhiRiqi());
    }
 
    private static boolean decimalEquals(BigDecimal left, BigDecimal right) {
        return left == null ? right == null : right != null && left.compareTo(right) == 0;
    }
 
    private static boolean dateEquals(Date left, Date right) {
        return left == null ? right == null : right != null && left.getTime() == right.getTime();
    }
 
    private static String code(RuntimeException exception, String fallback) {
        String value = exception == null ? null : exception.getMessage();
        if (!StringUtils.hasText(value)) return fallback;
        int separator = value.indexOf(':');
        String candidate = separator < 0 ? value : value.substring(0, separator);
        return candidate.matches("[A-Z][A-Z0-9_]*") ? candidate : fallback;
    }
 
    private static String message(RuntimeException exception, String fallback) {
        return exception != null && StringUtils.hasText(exception.getMessage())
                ? exception.getMessage() : fallback;
    }
 
    private Map<String, ElnRecordFieldValueEntity> resolveRules(String tenantId,
            String targetTestWorkItemId, Map<String, ElnTemplateCrossReferenceEntity> rules) {
        Map<String, ElnRecordFieldValueEntity> snapshots = new LinkedHashMap<>();
        if (rules.isEmpty()) return snapshots;
        Map<String, ElnTestRequestIdentityLookup.Identity> identities = identityLookup.resolve(
                tenantId, Collections.singletonList(targetTestWorkItemId));
        if (identities == null) identities = Collections.emptyMap();
        ElnTestRequestIdentityLookup.Identity target = identities.get(targetTestWorkItemId);
        if (target == null || !StringUtils.hasText(target.getTestRequestId())) {
            throw new DataException("SOURCE_RECORD_IDENTITY_INVALID: 目标检验项缺少请验单身份");
        }
        for (Map.Entry<String, ElnTemplateCrossReferenceEntity> entry : rules.entrySet()) {
            String bindingId = entry.getKey();
            ElnTemplateCrossReferenceEntity rule = entry.getValue();
            ElnRecordEntity source = sourceResolver.resolve(tenantId, target.getTestRequestId(), rule.getLaiyuanMubanBianhao());
            ElnRecordFieldValueEntity sourceValue = valueMapper.selectOne(
                    Wrappers.<ElnRecordFieldValueEntity>lambdaQuery().eq(ElnRecordFieldValueEntity::getFTenantId, tenantId)
                            .eq(ElnRecordFieldValueEntity::getDianziJiluId, source.getId())
                            .eq(ElnRecordFieldValueEntity::getXiudingId, source.getDangqianXiudingId())
                            .eq(ElnRecordFieldValueEntity::getZiduanDingyiId, rule.getLaiyuanZiduanDingyiId())
                            .eq(ElnRecordFieldValueEntity::getZiduanBanbenId, rule.getLaiyuanZiduanBanbenId())
                            .eq(ElnRecordFieldValueEntity::getBangdingJuese, "PRIMARY")
                            .eq(ElnRecordFieldValueEntity::getBangdingZhuangtai, "ACTIVE")
                            .and(q -> q.isNull(ElnRecordFieldValueEntity::getFDeleteMark)
                                    .or().eq(ElnRecordFieldValueEntity::getFDeleteMark, 0)));
            if (sourceValue == null || !hasValue(sourceValue)) {
                throw new DataException("SOURCE_FIELD_VALUE_INVALID: 源字段值为空或版本不符");
            }
            if ("CROSS_TEMPLATE".equals(sourceValue.getZhiLaiyuan())) {
                throw new DataException("SOURCE_FIELD_CHAIN_NOT_SUPPORTED: 源字段不能来自跨模板引用");
            }
            snapshots.put(bindingId, sourceValue);
        }
        return snapshots;
    }
 
    private static boolean hasValue(ElnRecordFieldValueEntity value) {
        return StringUtils.hasText(value.getZhiWenben())
                || value.getZhiShuzhi() != null || value.getZhiRiqi() != null;
    }
}