feat(wcs): persist transport orders and workflows

This commit is contained in:
zgw
2026-08-10 15:24:11 +08:00
parent f4ae3df382
commit 4696cdb950
14 changed files with 886 additions and 0 deletions

View File

@@ -0,0 +1,116 @@
package com.ruoyi.wcs.infrastructure.persistence.task;
import com.ruoyi.wcs.domain.task.TaskStepType;
import com.ruoyi.wcs.domain.task.TransportOrder;
import com.ruoyi.wcs.domain.task.TransportOrderType;
import com.ruoyi.wcs.domain.task.WorkflowDefinition;
import com.ruoyi.wcs.domain.task.WorkflowInstance;
import com.ruoyi.wcs.domain.task.WorkflowStepDefinition;
import org.junit.jupiter.api.Test;
import org.springframework.dao.OptimisticLockingFailureException;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class MyBatisTransportOrderRepositoryTest {
@Test
void rejectsAStateUpdateWhenThePersistedVersionChanged() {
RecordingMapper mapper = new RecordingMapper();
TransportOrder order = order();
order.plan("WF-1");
mapper.selected = TransportOrderRow.fromDomain(order);
order.start();
mapper.updateCount = 0;
MyBatisTransportOrderRepository repository =
new MyBatisTransportOrderRepository(mapper);
assertThrows(OptimisticLockingFailureException.class,
() -> repository.save(order));
assertEquals(1L, mapper.expectedVersion);
}
@Test
void insertsANewOrderWorkflowAndItsInitialSteps() {
RecordingMapper mapper = new RecordingMapper();
TransportOrder order = order();
WorkflowDefinition definition = new WorkflowDefinition(
TransportOrderType.INBOUND,
Arrays.asList(
new WorkflowStepDefinition("MOVE", TaskStepType.MOVE,
true, true, Collections.<String>emptyList()),
new WorkflowStepDefinition("DROP", TaskStepType.UNLOAD,
true, true, Collections.singletonList("MOVE"))));
WorkflowInstance workflow = WorkflowInstance.create("WF-1", "ORDER-1", definition);
order.plan(workflow.getWorkflowId());
new MyBatisTransportOrderRepository(mapper).save(order, workflow);
assertEquals(1, mapper.orderInserts);
assertEquals(1, mapper.workflowInserts);
assertEquals(2, mapper.stepInserts);
assertEquals(1, mapper.dependencyInserts);
}
private TransportOrder order() {
return TransportOrder.accept(
"ORDER-1", "REQ-1", TransportOrderType.INBOUND,
"LOAD-1", "PORT-1", "SPACE-1", 50,
Instant.parse("2026-08-04T00:00:00Z"));
}
private static final class RecordingMapper implements TransportOrderMapper {
private TransportOrderRow selected;
private int updateCount = 1;
private long expectedVersion = -1L;
private int orderInserts;
private int workflowInserts;
private int stepInserts;
private int dependencyInserts;
@Override
public TransportOrderRow selectByExternalRequestId(String externalRequestId) {
return selected;
}
@Override
public TransportOrderRow selectByOrderId(String orderId) {
return selected;
}
@Override
public int insertOrder(TransportOrderRow row) {
orderInserts++;
return 1;
}
@Override
public int updateState(TransportOrderRow row, long expectedVersion) {
this.expectedVersion = expectedVersion;
return updateCount;
}
@Override
public int insertWorkflow(WorkflowRow row) {
workflowInserts++;
return 1;
}
@Override
public int insertTaskStep(TaskStepRow row) {
stepInserts++;
return 1;
}
@Override
public int insertTaskDependency(TaskDependencyRow row) {
dependencyInserts++;
return 1;
}
}
}

View File

@@ -0,0 +1,33 @@
package com.ruoyi.wcs.infrastructure.persistence.task;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.session.Configuration;
import org.junit.jupiter.api.Test;
import java.io.InputStream;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
class TransportOrderMapperXmlTest {
@Test
void registersAllFoundationPersistenceStatements() {
String resource = "mapper/wcs/task/TransportOrderMapper.xml";
InputStream input = getClass().getClassLoader().getResourceAsStream(resource);
assertNotNull(input, "missing " + resource);
Configuration configuration = new Configuration();
XMLMapperBuilder builder = new XMLMapperBuilder(
input, configuration, resource, configuration.getSqlFragments());
builder.parse();
String namespace = TransportOrderMapper.class.getName() + ".";
assertTrue(configuration.hasStatement(namespace + "selectByExternalRequestId"));
assertTrue(configuration.hasStatement(namespace + "insertOrder"));
assertTrue(configuration.hasStatement(namespace + "updateState"));
assertTrue(configuration.hasStatement(namespace + "insertWorkflow"));
assertTrue(configuration.hasStatement(namespace + "insertTaskStep"));
assertTrue(configuration.hasStatement(namespace + "insertTaskDependency"));
}
}

View File

@@ -0,0 +1,45 @@
package com.ruoyi.wcs.infrastructure.persistence.task;
import com.ruoyi.wcs.domain.task.TransportOrder;
import com.ruoyi.wcs.domain.task.TransportOrderType;
import org.junit.jupiter.api.Test;
import java.time.Instant;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class TransportOrderRowMapperTest {
@Test
void preservesOrderStateAndVersionAcrossPersistenceMapping() {
TransportOrder source = TransportOrder.accept(
"ORDER-1", "REQ-1", TransportOrderType.OUTBOUND,
"LOAD-1", "SPACE-1", "PORT-1", 80,
Instant.parse("2026-08-04T00:00:00Z"));
source.plan("WF-1");
source.start();
source.clearUncommittedEvents();
TransportOrderRow row = TransportOrderRow.fromDomain(source);
TransportOrder restored = row.toDomain();
assertEquals(source.getState(), restored.getState());
assertEquals(2L, restored.getStateVersion());
assertEquals("WF-1", restored.getWorkflowId());
assertTrue(restored.getUncommittedEvents().isEmpty());
}
@Test
void mapsNullableLoadAndResultFieldsWithoutInventingValues() {
TransportOrder source = TransportOrder.accept(
"ORDER-2", "REQ-2", TransportOrderType.INVENTORY,
null, "SPACE-2", "SPACE-2", 10,
Instant.parse("2026-08-04T00:00:00Z"));
TransportOrder restored = TransportOrderRow.fromDomain(source).toDomain();
assertEquals(null, restored.getLoadId());
assertEquals(null, restored.getResultCode());
}
}