From 81d96a671ca47951928cfe4585e5f20a257ed004 Mon Sep 17 00:00:00 2001 From: zgw <13623675548@163.com> Date: Mon, 10 Aug 2026 15:49:15 +0800 Subject: [PATCH] feat(wcs): wire core module into RuoYi --- ruoyi-admin/pom.xml | 13 +++ .../wcs/AdminWcsBoundaryArchitectureTest.java | 20 +++++ .../wcs/TestPersistenceConfiguration.java | 21 +++++ .../java/com/ruoyi/wcs/WcsCoreWiringTest.java | 56 ++++++++++++ .../com/ruoyi/wcs/WcsCoreConfiguration.java | 86 +++++++++++++++++++ 5 files changed, 196 insertions(+) create mode 100644 ruoyi-admin/src/test/java/com/ruoyi/wcs/AdminWcsBoundaryArchitectureTest.java create mode 100644 ruoyi-admin/src/test/java/com/ruoyi/wcs/TestPersistenceConfiguration.java create mode 100644 ruoyi-admin/src/test/java/com/ruoyi/wcs/WcsCoreWiringTest.java create mode 100644 ruoyi-wcs/src/main/java/com/ruoyi/wcs/WcsCoreConfiguration.java diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index 126d948..e1da1a6 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -107,6 +107,19 @@ HslCommunication + + org.springframework.boot + spring-boot-starter-test + test + + + + com.tngtech.archunit + archunit-junit5 + 1.2.1 + test + + diff --git a/ruoyi-admin/src/test/java/com/ruoyi/wcs/AdminWcsBoundaryArchitectureTest.java b/ruoyi-admin/src/test/java/com/ruoyi/wcs/AdminWcsBoundaryArchitectureTest.java new file mode 100644 index 0000000..75bf196 --- /dev/null +++ b/ruoyi-admin/src/test/java/com/ruoyi/wcs/AdminWcsBoundaryArchitectureTest.java @@ -0,0 +1,20 @@ +package com.ruoyi.wcs; + +import com.tngtech.archunit.core.domain.JavaClasses; +import com.tngtech.archunit.core.importer.ClassFileImporter; +import org.junit.jupiter.api.Test; + +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; + +class AdminWcsBoundaryArchitectureTest { + + @Test + void adminAdaptersCannotReachCorePersistenceImplementations() { + JavaClasses classes = new ClassFileImporter() + .importPackages("com.ruoyi.api", "com.ruoyi.project"); + + noClasses().should().dependOnClassesThat() + .resideInAPackage("com.ruoyi.wcs.infrastructure.persistence..") + .check(classes); + } +} diff --git a/ruoyi-admin/src/test/java/com/ruoyi/wcs/TestPersistenceConfiguration.java b/ruoyi-admin/src/test/java/com/ruoyi/wcs/TestPersistenceConfiguration.java new file mode 100644 index 0000000..75725f7 --- /dev/null +++ b/ruoyi-admin/src/test/java/com/ruoyi/wcs/TestPersistenceConfiguration.java @@ -0,0 +1,21 @@ +package com.ruoyi.wcs; + +import com.ruoyi.wcs.port.out.DomainEventPublisher; +import com.ruoyi.wcs.port.out.WorkflowDefinitionProvider; +import org.mockito.Mockito; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TestPersistenceConfiguration { + + @Bean + public WorkflowDefinitionProvider workflowDefinitionProvider() { + return Mockito.mock(WorkflowDefinitionProvider.class); + } + + @Bean + public DomainEventPublisher domainEventPublisher() { + return Mockito.mock(DomainEventPublisher.class); + } +} diff --git a/ruoyi-admin/src/test/java/com/ruoyi/wcs/WcsCoreWiringTest.java b/ruoyi-admin/src/test/java/com/ruoyi/wcs/WcsCoreWiringTest.java new file mode 100644 index 0000000..f3e17b5 --- /dev/null +++ b/ruoyi-admin/src/test/java/com/ruoyi/wcs/WcsCoreWiringTest.java @@ -0,0 +1,56 @@ +package com.ruoyi.wcs; + +import com.ruoyi.wcs.application.task.TransportOrderApplicationService; +import com.ruoyi.wcs.domain.shared.IdGenerator; +import com.ruoyi.wcs.domain.shared.WcsClock; +import com.ruoyi.wcs.infrastructure.persistence.task.MyBatisTransportOrderRepository; +import com.ruoyi.wcs.port.in.TransportOrderUseCase; +import com.ruoyi.wcs.port.out.TransportOrderRepository; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class WcsCoreWiringTest { + + @Test + void exposesTheTransportOrderUseCaseFromTheWcsModule() { + try (AnnotationConfigApplicationContext context = + new AnnotationConfigApplicationContext()) { + context.register(WcsCoreConfiguration.class, + TestPersistenceConfiguration.class); + context.refresh(); + + assertTrue(context.getBean(TransportOrderUseCase.class) + instanceof TransportOrderApplicationService); + assertTrue(context.getBean(TransportOrderRepository.class) + instanceof MyBatisTransportOrderRepository); + assertTrue(context.containsBeanDefinition("transportOrderMapper")); + + WcsClock clock = context.getBean(WcsClock.class); + IdGenerator ids = context.getBean(IdGenerator.class); + assertNotNull(clock.now()); + String firstId = ids.nextId(); + String secondId = ids.nextId(); + assertFalse(firstId.trim().isEmpty()); + assertFalse(firstId.equals(secondId)); + } + } + + @Test + void startsWithoutCoreAdaptersAndRejectsNewOrdersWithoutAffectingLegacyStartup() { + try (AnnotationConfigApplicationContext context = + new AnnotationConfigApplicationContext()) { + context.register(WcsCoreConfiguration.class); + context.refresh(); + + TransportOrderUseCase useCase = context.getBean(TransportOrderUseCase.class); + IllegalStateException error = assertThrows(IllegalStateException.class, + () -> useCase.cancel("REQ-1")); + assertTrue(error.getMessage().contains("not configured")); + } + } +} diff --git a/ruoyi-wcs/src/main/java/com/ruoyi/wcs/WcsCoreConfiguration.java b/ruoyi-wcs/src/main/java/com/ruoyi/wcs/WcsCoreConfiguration.java new file mode 100644 index 0000000..c8367ff --- /dev/null +++ b/ruoyi-wcs/src/main/java/com/ruoyi/wcs/WcsCoreConfiguration.java @@ -0,0 +1,86 @@ +package com.ruoyi.wcs; + +import com.ruoyi.wcs.application.task.CreateTransportOrderCommand; +import com.ruoyi.wcs.application.task.TransportOrderApplicationService; +import com.ruoyi.wcs.application.task.TransportOrderResult; +import com.ruoyi.wcs.domain.shared.IdGenerator; +import com.ruoyi.wcs.domain.shared.WcsClock; +import com.ruoyi.wcs.infrastructure.persistence.task.MyBatisTransportOrderRepository; +import com.ruoyi.wcs.infrastructure.persistence.task.TransportOrderMapper; +import com.ruoyi.wcs.port.in.TransportOrderUseCase; +import com.ruoyi.wcs.port.out.DomainEventPublisher; +import com.ruoyi.wcs.port.out.TransportOrderRepository; +import com.ruoyi.wcs.port.out.WorkflowDefinitionProvider; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; + +import java.time.Instant; +import java.util.UUID; + +@Configuration +@MapperScan(basePackageClasses = TransportOrderMapper.class, lazyInitialization = "true") +public class WcsCoreConfiguration { + + @Bean + public WcsClock wcsClock() { + return Instant::now; + } + + @Bean + public IdGenerator wcsIdGenerator() { + return () -> UUID.randomUUID().toString(); + } + + @Bean + public TransportOrderRepository transportOrderRepository( + @Lazy TransportOrderMapper mapper) { + return new MyBatisTransportOrderRepository(mapper); + } + + @Bean + public TransportOrderUseCase transportOrderUseCase( + TransportOrderRepository repository, + ObjectProvider workflowDefinitions, + ObjectProvider eventPublishers, + IdGenerator idGenerator, + WcsClock clock) { + WorkflowDefinitionProvider workflowDefinitionProvider = + workflowDefinitions.getIfAvailable(); + DomainEventPublisher eventPublisher = eventPublishers.getIfAvailable(); + if (workflowDefinitionProvider == null || eventPublisher == null) { + return new UnavailableTransportOrderUseCase(); + } + return new TransportOrderApplicationService(repository, + workflowDefinitionProvider, eventPublisher, idGenerator, clock); + } + + private static final class UnavailableTransportOrderUseCase + implements TransportOrderUseCase { + @Override + public TransportOrderResult create(CreateTransportOrderCommand command) { + throw unavailable(); + } + + @Override + public TransportOrderResult cancel(String externalRequestId) { + throw unavailable(); + } + + @Override + public TransportOrderResult suspend(String orderId, String reason) { + throw unavailable(); + } + + @Override + public TransportOrderResult resume(String orderId) { + throw unavailable(); + } + + private static IllegalStateException unavailable() { + return new IllegalStateException("WCS core adapters are not configured"); + } + } +}