package jnpf.limsService.impl; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import jnpf.lims.api.model.TestWorkItemSnapshotBatch; import jnpf.lims.api.model.TestWorkItemSnapshot; import jnpf.lims.api.model.TestWorkItemSnapshotProperty; import jnpf.lims.api.model.TestWorkItemSnapshotRequest; import jnpf.limsEntity.LimsJianyanXiangEntity; import jnpf.limsMapper.LimsJianyanXiangMapper; import org.apache.ibatis.annotations.Select; import org.junit.Test; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.List; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; public class LimsTestWorkItemSnapshotServiceImplTest { @Test public void taskLookupFiltersDeletedTasksButResolvesSoftDeletedProjectHistory() throws Exception { Method method = LimsJianyanXiangMapper.class.getMethod( "selectSnapshotTasks", String.class, List.class); assertArrayEquals(new String[]{"Ilg@true"}, method.getAnnotation(InterceptorIgnore.class).others()); String sql = String.join(" ", method.getAnnotation(Select.class).value()); assertTrue(sql.contains("f_tenant_id = #{tenantId}")); assertTrue(sql.contains("task.f_delete_mark IS NULL OR task.f_delete_mark = 0")); assertTrue(sql.contains("LEFT JOIN lims_qingyandan")); assertTrue(sql.contains("LEFT JOIN zhiliang_biaozhun_guanli_zixiang")); assertTrue(sql.contains("quality_standard.gongyang_danwei AS qualityStandardSource")); assertTrue(sql.contains("FROM jianyan_xiangmu_guanli")); assertTrue(sql.contains("FROM jianyan_xiangmu_guanli GROUP BY f_tenant_id, bianma")); assertTrue(sql.contains("GROUP BY f_tenant_id, bianma")); assertTrue(sql.contains("project.f_tenant_id = task.f_tenant_id")); assertTrue(sql.contains("project.bianma = task.jianyan_xiangmu_bianma")); assertTrue(sql.contains("project.match_count AS testDefinitionMatchCount")); assertTrue(sql.contains("task.qingyandan_id AS qingyandanId")); } @Test public void returnsFrozenBasisForNormalExternalAndWaterTestWorkItems() { LimsTestWorkItemSnapshotServiceImpl service = new LimsTestWorkItemSnapshotServiceImpl( (tenantId, ids) -> Arrays.asList( task("normal", tenantId, "pending_test", "user-a", null, "普通依据"), task("external", tenantId, "pending_test", null, "user-a", "外检依据"), task("water", tenantId, "pending_test", "user-a", null, "水系统冻结依据"))); TestWorkItemSnapshotBatch result = service.resolve(request("normal", "external", "water"), "0", "user-a"); assertEquals(3, result.getItems().size()); assertSnapshot(result.getItems().get(0), "normal", "普通依据"); assertSnapshot(result.getItems().get(1), "external", "外检依据"); assertSnapshot(result.getItems().get(2), "water", "水系统冻结依据"); } @Test public void returnsResolvedTestDefinitionReferenceInsteadOfQualityStandardItemId() { LimsJianyanXiangEntity task = task( "normal", "0", "pending_test", "user-a", null, "普通依据"); LimsTestWorkItemSnapshotServiceImpl service = new LimsTestWorkItemSnapshotServiceImpl( (tenantId, ids) -> Collections.singletonList(task)); TestWorkItemSnapshot item = service.resolve( request("normal"), "0", "user-a").getItems().get(0); assertEquals("test-definition-normal", item.getTestDefinitionId()); assertEquals("CODE-normal", item.getTestDefinitionCode()); assertEquals("项目-normal", item.getTestDefinitionName()); } @Test public void exposesTheFrozenTestRequestIdentityOnEachReadableSnapshot() { LimsJianyanXiangEntity task = task( "normal", "0", "pending_test", "user-a", null, "依据"); task.setQingyandanId("request-20260901"); LimsTestWorkItemSnapshotServiceImpl service = new LimsTestWorkItemSnapshotServiceImpl( (tenantId, ids) -> Collections.singletonList(task)); TestWorkItemSnapshot item = service.resolve( request("normal"), "0", "user-a").getItems().get(0); assertEquals("SUCCESS", item.getOutcomeCode()); assertEquals("request-20260901", item.getTestRequestId()); } @Test public void rejectsSnapshotsWithoutTheFrozenTestRequestIdentity() { LimsJianyanXiangEntity task = task( "normal", "0", "pending_test", "user-a", null, "依据"); task.setQingyandanId(null); LimsTestWorkItemSnapshotServiceImpl service = new LimsTestWorkItemSnapshotServiceImpl( (tenantId, ids) -> Collections.singletonList(task)); TestWorkItemSnapshot item = service.resolve( request("normal"), "0", "user-a").getItems().get(0); assertEquals("TEST_REQUEST_INVALID", item.getOutcomeCode()); assertEquals("检验项缺少请验单身份", item.getReason()); assertNull(item.getTestRequestId()); } @Test public void rejectsMissingOrAmbiguousTestDefinitionReference() { LimsJianyanXiangEntity missingCode = task( "missing-code", "0", "pending_test", "user-a", null, "依据"); missingCode.setJianyanXiangmuBianma(null); LimsJianyanXiangEntity unresolved = task( "unresolved", "0", "pending_test", "user-a", null, "依据"); unresolved.setTestDefinitionId(null); unresolved.setTestDefinitionMatchCount(0L); LimsJianyanXiangEntity ambiguous = task( "ambiguous", "0", "pending_test", "user-a", null, "依据"); ambiguous.setTestDefinitionMatchCount(2L); LimsTestWorkItemSnapshotServiceImpl service = new LimsTestWorkItemSnapshotServiceImpl( (tenantId, ids) -> Arrays.asList(missingCode, unresolved, ambiguous)); TestWorkItemSnapshotBatch result = service.resolve( request("missing-code", "unresolved", "ambiguous"), "0", "user-a"); for (TestWorkItemSnapshot item : result.getItems()) { assertEquals("TEST_DEFINITION_INVALID", item.getOutcomeCode()); assertNull(item.getTestDefinitionId()); assertNull(item.getTestDefinitionCode()); assertNull(item.getTestDefinitionName()); assertEquals("NOT_AVAILABLE", item.getProperties().get(0).getOutcomeCode()); } } @Test public void reportsEachRejectedOrMissingValueWithoutDroppingTheBatch() { LimsJianyanXiangEntity forbidden = task("forbidden", "0", "pending_test", "other-user", null, "依据"); LimsJianyanXiangEntity created = task("created", "0", "created", "user-a", null, "依据"); LimsJianyanXiangEntity pendingAssign = task("pending-assign", "0", "pending_assign", "user-a", null, "依据"); LimsJianyanXiangEntity deleted = task("deleted", "0", "pending_test", "user-a", null, "依据"); deleted.setDeleteMark(1); LimsJianyanXiangEntity missing = task("missing", "0", "pending_test", "user-a", null, null); LimsJianyanXiangEntity otherTenant = task("other-tenant", "other", "pending_test", "user-a", null, "依据"); LimsTestWorkItemSnapshotServiceImpl service = new LimsTestWorkItemSnapshotServiceImpl( (tenantId, ids) -> Arrays.asList(forbidden, created, pendingAssign, deleted, missing, otherTenant)); TestWorkItemSnapshotBatch result = service.resolve(request( "forbidden", "created", "pending-assign", "deleted", "missing", "other-tenant"), "0", "user-a"); assertEquals(6, result.getItems().size()); assertEquals("FORBIDDEN", result.getItems().get(0).getOutcomeCode()); assertEquals("STATE_NOT_ALLOWED", result.getItems().get(1).getOutcomeCode()); assertEquals("STATE_NOT_ALLOWED", result.getItems().get(2).getOutcomeCode()); assertEquals("DELETED", result.getItems().get(3).getOutcomeCode()); assertEquals("SUCCESS", result.getItems().get(4).getOutcomeCode()); assertEquals("MISSING", result.getItems().get(4).getProperties().get(0).getOutcomeCode()); assertNull(result.getItems().get(4).getProperties().get(0).getValue()); assertEquals("NOT_FOUND", result.getItems().get(5).getOutcomeCode()); assertEquals("NOT_AVAILABLE", result.getItems().get(0).getProperties().get(0).getOutcomeCode()); assertNull(result.getItems().get(0).getProperties().get(0).getValue()); } @Test public void rejectsUnknownPropertyForEveryTestWorkItem() { LimsTestWorkItemSnapshotServiceImpl service = new LimsTestWorkItemSnapshotServiceImpl( (tenantId, ids) -> Collections.singletonList(task( "normal", tenantId, "pending_test", "user-a", null, "依据"))); TestWorkItemSnapshotRequest request = new TestWorkItemSnapshotRequest(); request.setTestWorkItemIds(Arrays.asList("normal", "not-found")); request.setProperties(Collections.singletonList("sample.code")); TestWorkItemSnapshotBatch result = service.resolve(request, "0", "user-a"); assertEquals(2, result.getItems().size()); for (TestWorkItemSnapshot item : result.getItems()) { TestWorkItemSnapshotProperty property = item.getProperties().get(0); assertEquals("sample.code", property.getProperty()); assertEquals("UNSUPPORTED_PROPERTY", property.getOutcomeCode()); } } @Test public void returnsSampleAndTestPropertiesFromOneWorkItemSnapshot() { LimsJianyanXiangEntity task = task("normal", "0", "pending_test", "user-a", null, "依据"); task.setChanpinMingcheng("阿司匹林片"); task.setPihao("B20260827"); task.setGuige("100mg"); task.setShengchanRiqi(date(2026, Calendar.AUGUST, 1)); task.setQualityStandardSource("企业标准"); task.setJianyanLeixing("初试"); LimsTestWorkItemSnapshotServiceImpl service = new LimsTestWorkItemSnapshotServiceImpl( (tenantId, ids) -> Collections.singletonList(task)); TestWorkItemSnapshotRequest request = new TestWorkItemSnapshotRequest(); request.setTestWorkItemIds(Collections.singletonList("normal")); request.setProperties(Arrays.asList("sample.name", "sample.batch_no", "sample.production_date", "quality_standard.source", "sample.specification", "test.type")); TestWorkItemSnapshot item = service.resolve(request, "0", "user-a").getItems().get(0); assertEquals("SUCCESS", item.getOutcomeCode()); assertProperty(item, "sample.name", "TEXT", "阿司匹林片"); assertProperty(item, "sample.batch_no", "TEXT", "B20260827"); assertProperty(item, "sample.production_date", "DATE", "2026-08-01"); assertProperty(item, "quality_standard.source", "TEXT", "企业标准"); assertProperty(item, "sample.specification", "TEXT", "100mg"); assertProperty(item, "test.type", "TEXT", "初试"); } @Test public void reportsMissingForBlankOptionalRelatedProperties() { LimsTestWorkItemSnapshotServiceImpl service = new LimsTestWorkItemSnapshotServiceImpl( (tenantId, ids) -> Collections.singletonList(task( "normal", tenantId, "pending_test", "user-a", null, "依据"))); TestWorkItemSnapshotRequest request = new TestWorkItemSnapshotRequest(); request.setTestWorkItemIds(Collections.singletonList("normal")); request.setProperties(Arrays.asList("sample.production_date", "quality_standard.source", "test.type")); TestWorkItemSnapshot item = service.resolve(request, "0", "user-a").getItems().get(0); for (TestWorkItemSnapshotProperty property : item.getProperties()) { assertEquals("MISSING", property.getOutcomeCode()); assertNull(property.getValue()); } } @Test public void readsLegacyInspectionAliasesForExistingElnBindings() { LimsJianyanXiangEntity legacyTask = task( "normal", "0", "pending_test", "user-a", null, "历史依据"); legacyTask.setJianyanLeixing("初试"); LimsTestWorkItemSnapshotServiceImpl service = new LimsTestWorkItemSnapshotServiceImpl( (tenantId, ids) -> Collections.singletonList(legacyTask)); TestWorkItemSnapshotRequest request = new TestWorkItemSnapshotRequest(); request.setTestWorkItemIds(Collections.singletonList("normal")); request.setProperties(Arrays.asList("inspection.basis", "inspection.type")); List properties = service.resolve(request, "0", "user-a") .getItems().get(0).getProperties(); assertEquals("inspection.basis", properties.get(0).getProperty()); assertEquals("SUCCESS", properties.get(0).getOutcomeCode()); assertEquals("历史依据", properties.get(0).getValue()); assertEquals("inspection.type", properties.get(1).getProperty()); assertEquals("SUCCESS", properties.get(1).getOutcomeCode()); assertEquals("初试", properties.get(1).getValue()); } private static TestWorkItemSnapshotRequest request(String... ids) { TestWorkItemSnapshotRequest request = new TestWorkItemSnapshotRequest(); request.setTestWorkItemIds(Arrays.asList(ids)); request.setProperties(Collections.singletonList("test.basis")); return request; } private static LimsJianyanXiangEntity task(String id, String tenantId, String status, String jianchaRen, String waijianLururen, String basis) { LimsJianyanXiangEntity task = new LimsJianyanXiangEntity(); task.setId(id); task.setQingyandanId("request-" + id); task.setForeignId("quality-standard-item-" + id); task.setTestDefinitionId("test-definition-" + id); task.setTestDefinitionMatchCount(1L); task.setJianyanXiangmuBianma("CODE-" + id); task.setJianyanXiangmuMingcheng("项目-" + id); task.setTenantId(tenantId); task.setJianyanZhuangtai(status); task.setJianchaRen(jianchaRen); task.setWaijianLururen(waijianLururen); task.setShifouWaijian(waijianLururen == null ? "no" : "yes"); task.setBiaozhunGuiding(basis); return task; } private static void assertSnapshot(TestWorkItemSnapshot item, String id, String basis) { assertEquals(id, item.getTestWorkItemId()); assertEquals("test-definition-" + id, item.getTestDefinitionId()); assertEquals("CODE-" + id, item.getTestDefinitionCode()); assertEquals("项目-" + id, item.getTestDefinitionName()); assertEquals("SUCCESS", item.getOutcomeCode()); TestWorkItemSnapshotProperty property = item.getProperties().get(0); assertEquals("test.basis", property.getProperty()); assertEquals("TEXT", property.getDataType()); assertEquals("LIMS_TASK", property.getSource()); assertEquals(id, property.getSourceRef()); assertEquals("SUCCESS", property.getOutcomeCode()); assertEquals(basis, property.getValue()); } private static void assertProperty(TestWorkItemSnapshot item, String code, String type, String value) { for (TestWorkItemSnapshotProperty property : item.getProperties()) { if (code.equals(property.getProperty())) { assertEquals(type, property.getDataType()); assertEquals("SUCCESS", property.getOutcomeCode()); assertEquals(value, property.getValue()); return; } } throw new AssertionError("缺少属性:" + code); } private static Date date(int year, int month, int day) { Calendar calendar = Calendar.getInstance(); calendar.clear(); calendar.set(year, month, day); return calendar.getTime(); } }