package jnpf.elnService.impl;
|
|
import jnpf.elnEntity.field.ElnFieldVersionEntity;
|
import jnpf.elnEntity.runtime.ElnRecordEntity;
|
import jnpf.elnEntity.runtime.ElnRecordFieldValueEntity;
|
import jnpf.elnEntity.runtime.ElnRecordForms;
|
import jnpf.elnEntity.runtime.ElnRecordRepeatRowEntity;
|
import jnpf.elnEntity.runtime.ElnRecordRepeatValueEntity;
|
import jnpf.elnEntity.runtime.ElnRecordRevisionEntity;
|
import jnpf.elnEntity.runtime.ElnRecordVos;
|
import jnpf.elnMapper.ElnFieldVersionMapper;
|
import jnpf.elnMapper.ElnRecordFieldValueMapper;
|
import jnpf.elnMapper.ElnRecordMapper;
|
import jnpf.elnMapper.ElnRecordRepeatRowMapper;
|
import jnpf.elnMapper.ElnRecordRepeatValueMapper;
|
import jnpf.elnMapper.ElnRecordRevisionMapper;
|
import jnpf.elnService.ElnTestWorkItemSnapshotProvider;
|
import jnpf.elnService.ElnRecordFormulaCalculationService;
|
import jnpf.elnService.template.ElnTemplateFileService;
|
import jnpf.exception.DataException;
|
import jnpf.lims.api.model.TestWorkItemSnapshotBatch;
|
import jnpf.lims.api.model.TestWorkItemSnapshot;
|
import jnpf.lims.api.model.TestWorkItemSnapshotProperty;
|
import org.junit.Test;
|
import org.springframework.transaction.support.TransactionSynchronization;
|
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
|
import java.lang.reflect.Proxy;
|
import java.math.BigDecimal;
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.Collections;
|
import java.util.Date;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Set;
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertNotEquals;
|
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.fail;
|
|
public class ElnRecordRevisionServiceImplTest {
|
|
@Test
|
public void auditRejectedCreatesExclusiveRevisionAndRefreshesAuthoritativeValues() {
|
Fixture fixture = new Fixture("audit_rejected");
|
|
ElnRecordVos.Created created = fixture.open("revision-operation-1");
|
|
assertFalse(created.isExisting());
|
assertEquals("record-1", created.getRecordId());
|
assertNotEquals("revision-1", created.getRevisionId());
|
assertEquals(created.getRevisionId() + ".docx", created.getDocument().getFileId());
|
assertEquals("HISTORICAL", fixture.sourceRevision.getBizStatus());
|
assertEquals(created.getRevisionId(), fixture.record.getDangqianXiudingId());
|
assertEquals(5, fixture.record.getFVersion().intValue());
|
assertEquals(1, fixture.markHistoricalCalls.get());
|
assertEquals(1, fixture.switchCalls.get());
|
assertEquals(1, fixture.copyCalls.get());
|
assertTrue(fixture.copySource.startsWith("["));
|
assertTrue(fixture.copySource.contains("revision-1.docx"));
|
assertEquals(created.getRevisionId(), fixture.copyTarget);
|
assertEquals(3, fixture.insertedValues.size());
|
|
ElnRecordFieldValueEntity manual = fixture.insertedValues.get(0);
|
assertEquals("manual value", manual.getZhiWenben());
|
assertEquals("MANUAL", manual.getZhiLaiyuan());
|
|
ElnRecordFieldValueEntity lims = fixture.insertedValues.get(1);
|
assertEquals("new basis", lims.getZhiWenben());
|
assertEquals("LIMS_TASK", lims.getZhiLaiyuan());
|
assertEquals("test.basis", lims.getYuanShuxing());
|
assertEquals("item-1", lims.getYuanYinyongId());
|
|
ElnRecordFieldValueEntity system = fixture.insertedValues.get(2);
|
assertEquals("新操作人", system.getZhiWenben());
|
assertEquals("SYSTEM", system.getZhiLaiyuan());
|
assertEquals("user-2", system.getYuanYinyongId());
|
assertEquals(1, fixture.recalculateAllCalls.get());
|
assertEquals(1, fixture.formulaUpdateCalls.get());
|
assertEquals(1, fixture.fenceAcquireCalls.get());
|
assertEquals(1, fixture.fenceCompleteCalls.get());
|
assertEquals(0, fixture.fenceReleaseCalls.get());
|
|
assertEquals(4, fixture.insertedRepeatRows.size());
|
assertEquals(4, fixture.insertedRepeatValues.size());
|
for (int index = 0; index < fixture.sourceRepeatRows.size(); index++) {
|
ElnRecordRepeatRowEntity source = fixture.sourceRepeatRows.get(index);
|
ElnRecordRepeatRowEntity copied = fixture.insertedRepeatRows.get(index);
|
assertNotEquals(source.getId(), copied.getId());
|
assertEquals(created.getRevisionId(), copied.getXiudingId());
|
assertEquals(source.getRepeatGroupCode(), copied.getRepeatGroupCode());
|
assertEquals(source.getRowId(), copied.getRowId());
|
assertEquals(source.getRowOrder(), copied.getRowOrder());
|
assertEquals(source.getOperationCode(), copied.getOperationCode());
|
assertEquals(source.getOperationStatus(), copied.getOperationStatus());
|
assertEquals(source.getDeleteReason(), copied.getDeleteReason());
|
assertEquals(source.getFDeleteMark(), copied.getFDeleteMark());
|
assertEquals(0, copied.getFVersion().intValue());
|
assertEquals("user-2", copied.getFCreatorUserId());
|
assertEquals("revision-1", source.getXiudingId());
|
assertEquals(source.getRowOrder(), source.getFVersion());
|
|
ElnRecordRepeatValueEntity sourceValue = fixture.sourceRepeatValues.get(index);
|
ElnRecordRepeatValueEntity copiedValue = fixture.insertedRepeatValues.get(index);
|
assertNotEquals(sourceValue.getId(), copiedValue.getId());
|
assertEquals(created.getRevisionId(), copiedValue.getXiudingId());
|
assertEquals(sourceValue.getRepeatRowId(), copiedValue.getRepeatRowId());
|
assertEquals(sourceValue.getRepeatGroupCode(), copiedValue.getRepeatGroupCode());
|
assertEquals(sourceValue.getMubanBangdingId(), copiedValue.getMubanBangdingId());
|
assertEquals(sourceValue.getZhiWenben(), copiedValue.getZhiWenben());
|
assertEquals(sourceValue.getFDeleteMark(), copiedValue.getFDeleteMark());
|
assertEquals(0, copiedValue.getFVersion().intValue());
|
assertEquals("user-2", copiedValue.getFCreatorUserId());
|
assertEquals("revision-1", sourceValue.getXiudingId());
|
}
|
}
|
|
@Test
|
public void auditRejectedCopiesConfiguredSourceSnapshotWithoutRefreshingIt() {
|
Fixture fixture = new Fixture("audit_rejected");
|
ElnRecordFieldValueEntity source = fixture.sourceValues.get(1);
|
Date acquiredAt = new Date(123456L);
|
Date dateValue = new Date(654321L);
|
source.setZhiWenben("configured snapshot");
|
source.setZhiShuzhi(new BigDecimal("123.456789012345678901"));
|
source.setZhiRiqi(dateValue);
|
source.setYuanShuxing("verification.test_basis");
|
source.setYuanShuxingBanben(1);
|
source.setYuanYinyongLeixing("LIMS_TASK");
|
source.setYuanYinyongId("item-1");
|
source.setYuanHuoquShijian(acquiredAt);
|
source.setYuanQuzhiZhuangtai("CLEARED");
|
source.setYuanQuzhiYuanyin("按审核要求保留空值");
|
source.setYuanXueyuan("verification.task_snapshot@1/test_basis");
|
fixture.versionResults.remove(1);
|
|
fixture.open("revision-configured-source-copy");
|
|
ElnRecordFieldValueEntity copied = fixture.insertedValues.get(1);
|
assertEquals("configured snapshot", copied.getZhiWenben());
|
assertEquals(0, new BigDecimal("123.456789012345678901")
|
.compareTo(copied.getZhiShuzhi()));
|
assertEquals(dateValue, copied.getZhiRiqi());
|
assertEquals("LIMS_TASK", copied.getZhiLaiyuan());
|
assertEquals("verification.test_basis", copied.getYuanShuxing());
|
assertEquals(Integer.valueOf(1), copied.getYuanShuxingBanben());
|
assertEquals("LIMS_TASK", copied.getYuanYinyongLeixing());
|
assertEquals("item-1", copied.getYuanYinyongId());
|
assertEquals(acquiredAt, copied.getYuanHuoquShijian());
|
assertEquals("CLEARED", copied.getYuanQuzhiZhuangtai());
|
assertEquals("按审核要求保留空值", copied.getYuanQuzhiYuanyin());
|
assertEquals("verification.task_snapshot@1/test_basis", copied.getYuanXueyuan());
|
}
|
|
@Test
|
public void cancelledAlsoCreatesANewRevisionOnTheSameRecord() {
|
Fixture fixture = new Fixture("cancelled");
|
|
ElnRecordVos.Created created = fixture.open("revision-operation-cancelled");
|
|
assertFalse(created.isExisting());
|
assertEquals("record-1", created.getRecordId());
|
assertEquals(2, fixture.currentRevision.getXiudingHao().intValue());
|
assertEquals("revision-1", fixture.currentRevision.getLaiyuanXiudingId());
|
}
|
|
@Test
|
public void pendingTestReusesCurrentDraftWithoutRefreshingSnapshots() {
|
Fixture fixture = new Fixture("pending_test");
|
|
ElnRecordVos.Created created = fixture.open("ordinary-open");
|
|
assertTrue(created.isExisting());
|
assertEquals("record-1", created.getRecordId());
|
assertEquals("revision-1", created.getRevisionId());
|
assertEquals(0, fixture.insertedValues.size());
|
assertEquals(0, fixture.versionReads.get());
|
assertEquals(0, fixture.copyCalls.get());
|
assertEquals(0, fixture.markHistoricalCalls.get());
|
assertEquals(0, fixture.switchCalls.get());
|
assertEquals(0, fixture.recalculateAllCalls.get());
|
}
|
|
@Test
|
public void copiesCrossTemplateLineageWithoutReadingTheSourceAgain() {
|
Fixture fixture = new Fixture("audit_rejected");
|
ElnRecordFieldValueEntity source = fixture.sourceValues.get(1);
|
fixture.versionResults.remove(1);
|
source.setZhiLaiyuan("CROSS_TEMPLATE");
|
source.setZhiWenben("cross-template snapshot");
|
source.setKuamoLaiyuanJiluId("source-record");
|
source.setKuamoLaiyuanXiudingId("source-revision");
|
source.setKuamoLaiyuanZhiId("source-value");
|
source.setKuamoLaiyuanZiduanBanbenId("source-field-version");
|
source.setKuamoHuoquShijian(new Date(123456L));
|
|
fixture.open("revision-cross-copy");
|
|
ElnRecordFieldValueEntity copied = fixture.insertedValues.get(1);
|
assertEquals("CROSS_TEMPLATE", copied.getZhiLaiyuan());
|
assertEquals("cross-template snapshot", copied.getZhiWenben());
|
assertEquals("source-record", copied.getKuamoLaiyuanJiluId());
|
assertEquals("source-revision", copied.getKuamoLaiyuanXiudingId());
|
assertEquals("source-value", copied.getKuamoLaiyuanZhiId());
|
assertEquals("source-field-version", copied.getKuamoLaiyuanZiduanBanbenId());
|
assertEquals(new Date(123456L), copied.getKuamoHuoquShijian());
|
assertEquals(2, fixture.versionReads.get());
|
}
|
|
@Test
|
public void repeatedOpenInTheSameReturnCycleReusesTheNewDraft() {
|
Fixture fixture = new Fixture("audit_rejected");
|
ElnRecordVos.Created first = fixture.open("revision-operation-1");
|
|
ElnRecordVos.Created second = fixture.open("revision-operation-2");
|
|
assertTrue(second.isExisting());
|
assertEquals(first.getRevisionId(), second.getRevisionId());
|
assertEquals(1, fixture.copyCalls.get());
|
assertEquals(1, fixture.markHistoricalCalls.get());
|
assertEquals(1, fixture.switchCalls.get());
|
}
|
|
@Test
|
public void repeatedOperationCodeIsIdempotent() {
|
Fixture fixture = new Fixture("audit_rejected");
|
ElnRecordVos.Created first = fixture.open("revision-operation-1");
|
|
ElnRecordVos.Created second = fixture.open("revision-operation-1");
|
|
assertTrue(second.isExisting());
|
assertEquals(first.getRevisionId(), second.getRevisionId());
|
assertEquals(1, fixture.copyCalls.get());
|
assertEquals(1, fixture.switchCalls.get());
|
assertEquals(4, fixture.insertedRepeatRows.size());
|
assertEquals(4, fixture.insertedRepeatValues.size());
|
}
|
|
@Test
|
public void repeatValueCopyFailureStopsBeforeCurrentSwitchAndFormulaCalculation() {
|
Fixture fixture = new Fixture("audit_rejected");
|
fixture.repeatValueInsertResult = 0;
|
|
expectDataException(() -> fixture.open("revision-operation-1"), "重复字段值复制失败");
|
|
assertEquals(4, fixture.insertedRepeatRows.size());
|
assertEquals(0, fixture.insertedRepeatValues.size());
|
assertEquals(0, fixture.switchCalls.get());
|
assertEquals(0, fixture.recalculateAllCalls.get());
|
assertEquals("revision-1", fixture.record.getDangqianXiudingId());
|
assertEquals(1, fixture.fenceReleaseCalls.get());
|
}
|
|
@Test
|
public void repeatRowCopyFailureStopsBeforeValuesAndCurrentSwitch() {
|
Fixture fixture = new Fixture("audit_rejected");
|
fixture.repeatRowInsertResult = 0;
|
|
expectDataException(() -> fixture.open("revision-operation-1"), "重复行复制失败");
|
|
assertEquals(0, fixture.insertedRepeatRows.size());
|
assertEquals(0, fixture.insertedRepeatValues.size());
|
assertEquals(0, fixture.switchCalls.get());
|
assertEquals(0, fixture.recalculateAllCalls.get());
|
assertEquals("revision-1", fixture.record.getDangqianXiudingId());
|
assertEquals(1, fixture.fenceReleaseCalls.get());
|
}
|
|
@Test
|
public void historicalTransitionConflictStopsBeforeCopyAndCurrentSwitch() {
|
Fixture fixture = new Fixture("audit_rejected");
|
fixture.markHistoricalResult = 0;
|
|
expectDataException(() -> fixture.open("revision-operation-1"), "状态冲突");
|
|
assertEquals(0, fixture.copyCalls.get());
|
assertEquals(0, fixture.switchCalls.get());
|
assertEquals("revision-1", fixture.record.getDangqianXiudingId());
|
assertEquals(1, fixture.fenceReleaseCalls.get());
|
}
|
|
@Test
|
public void currentRevisionCasConflictIsRejectedBeforeFormulaCalculation() {
|
Fixture fixture = new Fixture("audit_rejected");
|
fixture.switchResult = 0;
|
|
expectDataException(() -> fixture.open("revision-operation-1"), "当前修订切换冲突");
|
|
assertEquals(1, fixture.switchCalls.get());
|
assertEquals(0, fixture.recalculateAllCalls.get());
|
assertEquals(1, fixture.fenceReleaseCalls.get());
|
}
|
|
@Test
|
public void activeOnlyOfficeSessionStopsRevisionBeforeHistoricalMutation() {
|
Fixture fixture = new Fixture("audit_rejected");
|
fixture.fenceBusy = true;
|
|
expectDataException(() -> fixture.open("revision-operation-1"), "正在编辑");
|
|
assertEquals(1, fixture.fenceAcquireCalls.get());
|
assertEquals(0, fixture.markHistoricalCalls.get());
|
assertEquals(0, fixture.copyCalls.get());
|
assertEquals(0, fixture.switchCalls.get());
|
}
|
|
@Test
|
public void transactionCommitCompletesFenceOnlyAfterCommit() {
|
Fixture fixture = new Fixture("audit_rejected");
|
TransactionSynchronizationManager.initSynchronization();
|
try {
|
fixture.open("revision-operation-1");
|
assertEquals(0, fixture.fenceCompleteCalls.get());
|
assertEquals(0, fixture.fenceReleaseCalls.get());
|
|
for (TransactionSynchronization synchronization
|
: TransactionSynchronizationManager.getSynchronizations()) {
|
synchronization.afterCompletion(TransactionSynchronization.STATUS_COMMITTED);
|
}
|
|
assertEquals(1, fixture.fenceCompleteCalls.get());
|
assertEquals(0, fixture.fenceReleaseCalls.get());
|
} finally {
|
TransactionSynchronizationManager.clearSynchronization();
|
}
|
}
|
|
@Test
|
public void transactionRollbackReleasesFenceAfterFailure() {
|
Fixture fixture = new Fixture("audit_rejected");
|
fixture.markHistoricalResult = 0;
|
TransactionSynchronizationManager.initSynchronization();
|
try {
|
expectDataException(() -> fixture.open("revision-operation-1"), "状态冲突");
|
assertEquals(0, fixture.fenceCompleteCalls.get());
|
assertEquals(0, fixture.fenceReleaseCalls.get());
|
|
for (TransactionSynchronization synchronization
|
: TransactionSynchronizationManager.getSynchronizations()) {
|
synchronization.afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
|
}
|
|
assertEquals(0, fixture.fenceCompleteCalls.get());
|
assertEquals(1, fixture.fenceReleaseCalls.get());
|
} finally {
|
TransactionSynchronizationManager.clearSynchronization();
|
}
|
}
|
|
private static void expectDataException(Runnable action, String messagePart) {
|
try {
|
action.run();
|
fail("预期抛出 DataException");
|
} catch (DataException expected) {
|
assertTrue(expected.getMessage().contains(messagePart));
|
}
|
}
|
|
private static final class Fixture {
|
private final String taskStatus;
|
private final ElnRecordEntity record = record();
|
private final ElnRecordRevisionEntity sourceRevision = revision();
|
private final List<ElnRecordFieldValueEntity> sourceValues = sourceValues();
|
private final List<ElnRecordRepeatRowEntity> sourceRepeatRows = sourceRepeatRows();
|
private final List<ElnRecordRepeatValueEntity> sourceRepeatValues =
|
sourceRepeatValues(sourceRepeatRows);
|
private final Map<String, ElnRecordRevisionEntity> revisionsByOperation = new LinkedHashMap<>();
|
private final List<ElnRecordFieldValueEntity> insertedValues = new ArrayList<>();
|
private final List<ElnRecordRepeatRowEntity> insertedRepeatRows = new ArrayList<>();
|
private final List<ElnRecordRepeatValueEntity> insertedRepeatValues = new ArrayList<>();
|
private final AtomicInteger markHistoricalCalls = new AtomicInteger();
|
private final AtomicInteger switchCalls = new AtomicInteger();
|
private final AtomicInteger copyCalls = new AtomicInteger();
|
private final AtomicInteger recalculateAllCalls = new AtomicInteger();
|
private final AtomicInteger formulaUpdateCalls = new AtomicInteger();
|
private final AtomicInteger fenceAcquireCalls = new AtomicInteger();
|
private final AtomicInteger fenceCompleteCalls = new AtomicInteger();
|
private final AtomicInteger fenceReleaseCalls = new AtomicInteger();
|
private final AtomicInteger revisionLookupCalls = new AtomicInteger();
|
private final AtomicInteger versionReads = new AtomicInteger();
|
private final List<ElnFieldVersionEntity> versionResults = new ArrayList<>();
|
private final ElnRecordRevisionServiceImpl service;
|
private ElnRecordRevisionEntity currentRevision = sourceRevision;
|
private String lookupOperationCode;
|
private String copySource;
|
private String copyTarget;
|
private int markHistoricalResult = 1;
|
private int switchResult = 1;
|
private int repeatRowInsertResult = 1;
|
private int repeatValueInsertResult = 1;
|
private boolean fenceBusy;
|
|
private Fixture(String taskStatus) {
|
this.taskStatus = taskStatus;
|
ElnRecordMapper recordMapper = proxy(ElnRecordMapper.class, (method, args) -> {
|
if ("selectLiveByTestWorkItem".equals(method)
|
|| "selectLiveForUpdate".equals(method)) return record;
|
if ("switchCurrentRevision".equals(method)) {
|
switchCalls.incrementAndGet();
|
return switchResult;
|
}
|
return null;
|
});
|
ElnRecordRevisionMapper revisionMapper = proxy(
|
ElnRecordRevisionMapper.class, (method, args) -> {
|
if ("selectOne".equals(method)) {
|
return revisionLookupCalls.getAndIncrement() % 2 == 0
|
? currentRevision : revisionsByOperation.get(lookupOperationCode);
|
}
|
if ("markHistorical".equals(method)) {
|
markHistoricalCalls.incrementAndGet();
|
if (markHistoricalResult == 1) {
|
sourceRevision.setBizStatus("HISTORICAL");
|
sourceRevision.setFVersion(sourceRevision.getFVersion() + 1);
|
}
|
return markHistoricalResult;
|
}
|
if ("insert".equals(method)) {
|
currentRevision = (ElnRecordRevisionEntity) args[0];
|
revisionsByOperation.put(
|
currentRevision.getXiudingCaozuoBianma(), currentRevision);
|
return 1;
|
}
|
return null;
|
});
|
ElnRecordFieldValueMapper valueMapper = proxy(
|
ElnRecordFieldValueMapper.class, (method, args) -> {
|
if ("selectList".equals(method)) return new ArrayList<>(sourceValues);
|
if ("insert".equals(method)) {
|
insertedValues.add((ElnRecordFieldValueEntity) args[0]);
|
return 1;
|
}
|
if ("updateById".equals(method)) {
|
formulaUpdateCalls.incrementAndGet();
|
return 1;
|
}
|
return null;
|
});
|
ElnRecordRepeatRowMapper repeatRowMapper = proxy(
|
ElnRecordRepeatRowMapper.class, (method, args) -> {
|
if ("selectList".equals(method)) {
|
return new ArrayList<>(sourceRepeatRows);
|
}
|
if ("insert".equals(method)) {
|
if (repeatRowInsertResult == 1) {
|
insertedRepeatRows.add((ElnRecordRepeatRowEntity) args[0]);
|
}
|
return repeatRowInsertResult;
|
}
|
return null;
|
});
|
ElnRecordRepeatValueMapper repeatValueMapper = proxy(
|
ElnRecordRepeatValueMapper.class, (method, args) -> {
|
if ("selectList".equals(method)) {
|
return new ArrayList<>(sourceRepeatValues);
|
}
|
if ("insert".equals(method)) {
|
if (repeatValueInsertResult == 1) {
|
insertedRepeatValues.add((ElnRecordRepeatValueEntity) args[0]);
|
}
|
return repeatValueInsertResult;
|
}
|
return null;
|
});
|
versionResults.addAll(Arrays.asList(
|
version("version-manual", "definition-manual", null, null),
|
version("version-lims", "definition-lims", "LIMS_TASK", "test.basis"),
|
version("version-system", "definition-system", "SYSTEM", "current_user.real_name")));
|
ElnFieldVersionMapper versionMapper = proxy(
|
ElnFieldVersionMapper.class, (method, args) ->
|
"selectOne".equals(method)
|
? versionResults.get(versionReads.getAndIncrement()) : null);
|
ElnTemplateFileService fileService = new ElnTemplateFileService(null, null) {
|
@Override
|
public String copyDocx(String sourceAttachments, String targetTemplateId) {
|
copyCalls.incrementAndGet();
|
copySource = sourceAttachments;
|
copyTarget = targetTemplateId;
|
return "[{\"fileId\":\"" + targetTemplateId + ".docx\","
|
+ "\"name\":\"" + targetTemplateId + ".docx\","
|
+ "\"fileExtension\":\"docx\"}]";
|
}
|
};
|
ElnRecordFormulaCalculationService calculation =
|
new ElnRecordFormulaCalculationService() {
|
@Override
|
public List<ElnRecordFieldValueEntity> recalculateAffected(
|
String tenantId, ElnRecordEntity record,
|
ElnRecordRevisionEntity revision,
|
List<ElnRecordFieldValueEntity> values,
|
Set<String> changedBindingIds,
|
String operationCode, String operatorUserId) {
|
return Collections.emptyList();
|
}
|
|
@Override
|
public List<ElnRecordFieldValueEntity> recalculateAll(
|
String tenantId, ElnRecordEntity actualRecord,
|
ElnRecordRevisionEntity actualRevision,
|
List<ElnRecordFieldValueEntity> values,
|
String operationCode, String operatorUserId) {
|
recalculateAllCalls.incrementAndGet();
|
assertEquals("tenant-1", tenantId);
|
assertEquals(currentRevision.getId(), actualRevision.getId());
|
assertEquals(3, values.size());
|
assertEquals("user-2", operatorUserId);
|
ElnRecordFieldValueEntity calculated = values.get(0);
|
calculated.setJisuanZhuangtai("SUCCESS");
|
return Collections.singletonList(calculated);
|
}
|
};
|
ElnTestWorkItemSnapshotProvider taskProvider = new ElnTestWorkItemSnapshotProvider() {
|
@Override
|
public TestWorkItemSnapshotBatch resolve(
|
List<String> testWorkItemIds, List<String> properties) {
|
throw new AssertionError("必须使用显式负责人快照");
|
}
|
|
@Override
|
public TestWorkItemSnapshotBatch resolveAsActor(
|
String tenantId, String userId,
|
List<String> testWorkItemIds, List<String> properties) {
|
assertEquals("tenant-1", tenantId);
|
assertEquals("user-2", userId);
|
assertEquals(Collections.singletonList("item-1"), testWorkItemIds);
|
assertEquals(Collections.singletonList("test.basis"), properties);
|
return snapshot(Fixture.this.taskStatus);
|
}
|
};
|
ElnRecordRevisionServiceImpl.RecordRevisionFence fence =
|
new ElnRecordRevisionServiceImpl.RecordRevisionFence() {
|
@Override
|
public String acquire(String tenantId, String recordId,
|
String fileId, String fileType) {
|
fenceAcquireCalls.incrementAndGet();
|
assertEquals("tenant-1", tenantId);
|
assertEquals("record-1", recordId);
|
assertEquals("revision-1.docx", fileId);
|
assertEquals("annex", fileType);
|
if (fenceBusy) {
|
throw new DataException("记录文档正在编辑");
|
}
|
return "fence-token";
|
}
|
|
@Override
|
public void complete(ElnRecordRevisionServiceImpl.FenceLease lease) {
|
fenceCompleteCalls.incrementAndGet();
|
}
|
|
@Override
|
public void release(ElnRecordRevisionServiceImpl.FenceLease lease) {
|
fenceReleaseCalls.incrementAndGet();
|
}
|
};
|
service = new ElnRecordRevisionServiceImpl(
|
taskProvider, recordMapper, revisionMapper, valueMapper, versionMapper,
|
repeatRowMapper, repeatValueMapper, fileService, calculation, fence,
|
() -> new ElnRecordRevisionServiceImpl.UserContext(
|
"tenant-1", "user-2", "新操作人"));
|
}
|
|
private ElnRecordVos.Created open(String operationCode) {
|
lookupOperationCode = operationCode;
|
ElnRecordForms.Create command = new ElnRecordForms.Create();
|
command.setTestWorkItemId("item-1");
|
command.setOperationCode(operationCode);
|
return service.open(command);
|
}
|
}
|
|
private static ElnRecordEntity record() {
|
ElnRecordEntity record = new ElnRecordEntity();
|
record.setId("record-1");
|
record.setJianyanXiangId("item-1");
|
record.setMubanId("template-1");
|
record.setDangqianXiudingId("revision-1");
|
record.setFTenantId("tenant-1");
|
record.setFVersion(4);
|
record.setFDeleteMark(0);
|
return record;
|
}
|
|
private static ElnRecordRevisionEntity revision() {
|
ElnRecordRevisionEntity revision = new ElnRecordRevisionEntity();
|
revision.setId("revision-1");
|
revision.setDianziJiluId("record-1");
|
revision.setXiudingHao(1);
|
revision.setXiudingCaozuoBianma("create-operation");
|
revision.setJiluWendang("revision-1.docx");
|
revision.setBizStatus("DRAFT");
|
revision.setFTenantId("tenant-1");
|
revision.setFVersion(2);
|
revision.setFDeleteMark(0);
|
return revision;
|
}
|
|
private static List<ElnRecordFieldValueEntity> sourceValues() {
|
return Arrays.asList(
|
value("value-manual", "binding-manual", "definition-manual",
|
"version-manual", "manual_field", "MANUAL", "manual value"),
|
value("value-lims", "binding-lims", "definition-lims",
|
"version-lims", "test_basis", "LIMS_TASK", "old basis"),
|
value("value-system", "binding-system", "definition-system",
|
"version-system", "operator_name", "SYSTEM", "旧操作人"));
|
}
|
|
private static List<ElnRecordRepeatRowEntity> sourceRepeatRows() {
|
ElnRecordRepeatRowEntity active = repeatRow(
|
"repeat-physical-active", "group-a", "row-a1", 1, "ACTIVE", 0);
|
ElnRecordRepeatRowEntity prepared = repeatRow(
|
"repeat-physical-prepared", "group-a", "row-a2", 2, "PREPARED", 0);
|
ElnRecordRepeatRowEntity deleting = repeatRow(
|
"repeat-physical-deleting", "group-b", "row-b1", 1,
|
"DELETE_PREPARED", 0);
|
deleting.setDeleteOperationCode("delete-operation-b1");
|
deleting.setDeleteReason("录入错误");
|
deleting.setDeleteRequestedUserId("user-1");
|
deleting.setDeleteRequestedTime(new Date(1000L));
|
ElnRecordRepeatRowEntity deleted = repeatRow(
|
"repeat-physical-deleted", "group-b", "row-b2", 2, "DELETED", 1);
|
deleted.setDeleteOperationCode("delete-operation-b2");
|
deleted.setDeleteReason("样品作废");
|
deleted.setDeleteRequestedUserId("user-1");
|
deleted.setDeleteRequestedTime(new Date(2000L));
|
deleted.setFDeleteUserId("user-1");
|
deleted.setFDeleteTime(new Date(3000L));
|
return Arrays.asList(active, prepared, deleting, deleted);
|
}
|
|
private static ElnRecordRepeatRowEntity repeatRow(
|
String id, String groupCode, String rowId, int rowOrder,
|
String status, int deleteMark) {
|
ElnRecordRepeatRowEntity row = new ElnRecordRepeatRowEntity();
|
row.setId(id);
|
row.setDianziJiluId("record-1");
|
row.setXiudingId("revision-1");
|
row.setMubanId("template-1");
|
row.setRepeatGroupCode(groupCode);
|
row.setRowId(rowId);
|
row.setRowOrder(rowOrder);
|
row.setOperationCode("append-" + rowId);
|
row.setOperationStatus(status);
|
row.setFTenantId("tenant-1");
|
row.setFVersion(rowOrder);
|
row.setFDeleteMark(deleteMark);
|
return row;
|
}
|
|
private static List<ElnRecordRepeatValueEntity> sourceRepeatValues(
|
List<ElnRecordRepeatRowEntity> rows) {
|
List<ElnRecordRepeatValueEntity> values = new ArrayList<>();
|
for (ElnRecordRepeatRowEntity row : rows) {
|
ElnRecordRepeatValueEntity value = new ElnRecordRepeatValueEntity();
|
value.setId("repeat-value-" + row.getRowId());
|
value.setDianziJiluId("record-1");
|
value.setXiudingId("revision-1");
|
value.setMubanId("template-1");
|
value.setRepeatRowId(row.getRowId());
|
value.setRepeatGroupCode(row.getRepeatGroupCode());
|
value.setMubanBangdingId("repeat-binding-" + row.getRepeatGroupCode());
|
value.setZiduanDingyiId("repeat-definition-" + row.getRepeatGroupCode());
|
value.setZiduanBianma("result");
|
value.setZiduanBanbenId("repeat-version-" + row.getRepeatGroupCode());
|
value.setBangdingJuese("REPEAT");
|
value.setBangdingZhuangtai("ACTIVE");
|
value.setZhiWenben("value-" + row.getRowId());
|
value.setZhiLaiyuan("MANUAL");
|
value.setFTenantId("tenant-1");
|
value.setFVersion(row.getFVersion());
|
value.setFDeleteMark(row.getFDeleteMark());
|
value.setFDeleteUserId(row.getFDeleteUserId());
|
value.setFDeleteTime(row.getFDeleteTime());
|
values.add(value);
|
}
|
return values;
|
}
|
|
private static ElnRecordFieldValueEntity value(
|
String id, String bindingId, String definitionId, String versionId,
|
String code, String source, String text) {
|
ElnRecordFieldValueEntity value = new ElnRecordFieldValueEntity();
|
value.setId(id);
|
value.setDianziJiluId("record-1");
|
value.setXiudingId("revision-1");
|
value.setMubanId("template-1");
|
value.setMubanBangdingId(bindingId);
|
value.setZiduanDingyiId(definitionId);
|
value.setZiduanBanbenId(versionId);
|
value.setZiduanBianma(code);
|
value.setBangdingJuese("PRIMARY");
|
value.setBangdingZhuangtai("ACTIVE");
|
value.setZhiLaiyuan(source);
|
value.setZhiWenben(text);
|
value.setFTenantId("tenant-1");
|
value.setFVersion(3);
|
value.setFDeleteMark(0);
|
return value;
|
}
|
|
private static ElnFieldVersionEntity version(
|
String id, String definitionId, String source, String property) {
|
ElnFieldVersionEntity version = new ElnFieldVersionEntity();
|
version.setId(id);
|
version.setZiduanDingyiId(definitionId);
|
version.setShujuLeixing("TEXT");
|
if (source != null) {
|
version.setBangdingPeizhi("{\"read\":{\"source\":\"" + source
|
+ "\",\"property\":\"" + property + "\"}}");
|
}
|
return version;
|
}
|
|
private static TestWorkItemSnapshotBatch snapshot(String status) {
|
TestWorkItemSnapshotProperty property = new TestWorkItemSnapshotProperty();
|
property.setProperty("test.basis");
|
property.setDataType("TEXT");
|
property.setValue("new basis");
|
property.setSource("LIMS_TASK");
|
property.setSourceRef("item-1");
|
property.setOutcomeCode("SUCCESS");
|
TestWorkItemSnapshot item = new TestWorkItemSnapshot();
|
item.setTestWorkItemId("item-1");
|
item.setTaskStatus(status);
|
item.setOutcomeCode("SUCCESS");
|
item.setProperties(Collections.singletonList(property));
|
TestWorkItemSnapshotBatch batch = new TestWorkItemSnapshotBatch();
|
batch.setItems(Collections.singletonList(item));
|
return batch;
|
}
|
|
@SuppressWarnings("unchecked")
|
private static <T> T proxy(Class<T> type, MapperInvocation invocation) {
|
return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[]{type},
|
(proxy, method, args) -> {
|
if (method.getDeclaringClass() == Object.class) {
|
if ("toString".equals(method.getName())) return type.getSimpleName() + "Fake";
|
if ("hashCode".equals(method.getName())) return System.identityHashCode(proxy);
|
if ("equals".equals(method.getName())) return proxy == args[0];
|
}
|
Object result = invocation.invoke(method.getName(), args);
|
if (result != null || !method.getReturnType().isPrimitive()) return result;
|
if (method.getReturnType() == boolean.class) return false;
|
return 0;
|
});
|
}
|
|
@FunctionalInterface
|
private interface MapperInvocation {
|
Object invoke(String method, Object[] args);
|
}
|
}
|