feat(wcs): wire core module into RuoYi

This commit is contained in:
zgw
2026-08-10 15:49:15 +08:00
parent 4696cdb950
commit 81d96a671c
5 changed files with 196 additions and 0 deletions

View File

@@ -107,6 +107,19 @@
<artifactId>HslCommunication</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

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

View File

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

View File

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

View File

@@ -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<WorkflowDefinitionProvider> workflowDefinitions,
ObjectProvider<DomainEventPublisher> 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");
}
}
}