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;
|
}
|
}
|