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");
+ }
+ }
+}