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

@@ -53,6 +53,29 @@ public final class TransportOrder {
sourceCode, destinationCode, priority, createdAt);
}
public static TransportOrder restore(String orderId, String externalRequestId,
TransportOrderType type, String loadId,
String sourceCode, String destinationCode,
int priority, TransportOrderState state,
String workflowId, String resultCode,
String stateReason, long stateVersion,
Instant createdAt, Instant updatedAt) {
if (stateVersion < 0L) {
throw new IllegalArgumentException("stateVersion must not be negative");
}
TransportOrder order = new TransportOrder(orderId, externalRequestId, type,
loadId, sourceCode, destinationCode, priority, createdAt);
order.state = Objects.requireNonNull(state, "state");
order.workflowId = workflowId;
order.resultCode = resultCode;
order.stateReason = stateReason;
order.stateVersion = stateVersion;
order.eventSequence = stateVersion + 1L;
order.updatedAt = Objects.requireNonNull(updatedAt, "updatedAt");
order.uncommittedEvents.clear();
return order;
}
public void plan(String workflowId) {
requireState(TransportOrderState.ACCEPTED);
this.workflowId = requireText(workflowId, "workflowId");

View File

@@ -85,6 +85,15 @@ public final class WorkflowInstance {
return Collections.unmodifiableList(new ArrayList<TaskStep>(steps.values()));
}
public Map<String, List<String>> getMandatoryDependencies() {
Map<String, List<String>> copy = new LinkedHashMap<String, List<String>>();
for (Map.Entry<String, List<String>> entry : mandatoryDependencies.entrySet()) {
copy.put(entry.getKey(), Collections.unmodifiableList(
new ArrayList<String>(entry.getValue())));
}
return Collections.unmodifiableMap(copy);
}
private DependencyState dependencyState(String stepKey) {
boolean allSucceeded = true;
for (String dependencyKey : mandatoryDependencies.get(stepKey)) {

View File

@@ -0,0 +1,76 @@
package com.ruoyi.wcs.infrastructure.persistence.task;
import com.ruoyi.wcs.domain.task.TaskStep;
import com.ruoyi.wcs.domain.task.TransportOrder;
import com.ruoyi.wcs.domain.task.WorkflowInstance;
import com.ruoyi.wcs.port.out.TransportOrderRepository;
import org.springframework.dao.OptimisticLockingFailureException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public final class MyBatisTransportOrderRepository implements TransportOrderRepository {
private final TransportOrderMapper mapper;
public MyBatisTransportOrderRepository(TransportOrderMapper mapper) {
this.mapper = Objects.requireNonNull(mapper, "mapper");
}
@Override
public TransportOrder findByExternalRequestId(String externalRequestId) {
TransportOrderRow row = mapper.selectByExternalRequestId(externalRequestId);
return row == null ? null : row.toDomain();
}
@Override
public TransportOrder findByOrderId(String orderId) {
TransportOrderRow row = mapper.selectByOrderId(orderId);
return row == null ? null : row.toDomain();
}
@Override
public void save(TransportOrder order, WorkflowInstance workflow) {
Objects.requireNonNull(order, "order");
Objects.requireNonNull(workflow, "workflow");
if (mapper.insertOrder(TransportOrderRow.fromDomain(order)) != 1) {
throw new IllegalStateException("transport order insert did not affect one row");
}
if (mapper.insertWorkflow(WorkflowRow.fromDomain(workflow)) != 1) {
throw new IllegalStateException("workflow insert did not affect one row");
}
for (TaskStep step : workflow.getSteps()) {
if (mapper.insertTaskStep(TaskStepRow.fromDomain(
workflow.getWorkflowId(), step)) != 1) {
throw new IllegalStateException("task step insert did not affect one row");
}
}
for (Map.Entry<String, List<String>> entry
: workflow.getMandatoryDependencies().entrySet()) {
for (String dependency : entry.getValue()) {
if (mapper.insertTaskDependency(new TaskDependencyRow(
workflow.getWorkflowId(), entry.getKey(), dependency)) != 1) {
throw new IllegalStateException(
"task dependency insert did not affect one row");
}
}
}
}
@Override
public void save(TransportOrder order) {
Objects.requireNonNull(order, "order");
TransportOrderRow existing = mapper.selectByOrderId(order.getOrderId());
if (existing == null) {
if (mapper.insertOrder(TransportOrderRow.fromDomain(order)) != 1) {
throw new IllegalStateException("transport order insert did not affect one row");
}
return;
}
long expectedVersion = order.getStateVersion() - 1L;
if (mapper.updateState(TransportOrderRow.fromDomain(order), expectedVersion) != 1) {
throw new OptimisticLockingFailureException(
"transport order state changed concurrently: " + order.getOrderId());
}
}
}

View File

@@ -0,0 +1,41 @@
package com.ruoyi.wcs.infrastructure.persistence.task;
public final class TaskDependencyRow {
private String workflowId;
private String stepKey;
private String dependsOnStepKey;
public TaskDependencyRow() {
}
public TaskDependencyRow(String workflowId, String stepKey,
String dependsOnStepKey) {
this.workflowId = workflowId;
this.stepKey = stepKey;
this.dependsOnStepKey = dependsOnStepKey;
}
public String getWorkflowId() {
return workflowId;
}
public void setWorkflowId(String workflowId) {
this.workflowId = workflowId;
}
public String getStepKey() {
return stepKey;
}
public void setStepKey(String stepKey) {
this.stepKey = stepKey;
}
public String getDependsOnStepKey() {
return dependsOnStepKey;
}
public void setDependsOnStepKey(String dependsOnStepKey) {
this.dependsOnStepKey = dependsOnStepKey;
}
}

View File

@@ -0,0 +1,101 @@
package com.ruoyi.wcs.infrastructure.persistence.task;
import com.ruoyi.wcs.domain.task.TaskStep;
public final class TaskStepRow {
private String stepId;
private String workflowId;
private String stepKey;
private String stepType;
private boolean required;
private String stepState;
private String reasonCode;
private String vehicleId;
private long stateVersion;
public static TaskStepRow fromDomain(String workflowId, TaskStep step) {
TaskStepRow row = new TaskStepRow();
row.stepId = step.getStepId();
row.workflowId = workflowId;
row.stepKey = step.getStepKey();
row.stepType = step.getType().name();
row.required = step.isRequired();
row.stepState = step.getState().name();
row.reasonCode = step.getReasonCode();
row.vehicleId = step.getVehicleId();
row.stateVersion = step.getStateVersion();
return row;
}
public String getStepId() {
return stepId;
}
public void setStepId(String stepId) {
this.stepId = stepId;
}
public String getWorkflowId() {
return workflowId;
}
public void setWorkflowId(String workflowId) {
this.workflowId = workflowId;
}
public String getStepKey() {
return stepKey;
}
public void setStepKey(String stepKey) {
this.stepKey = stepKey;
}
public String getStepType() {
return stepType;
}
public void setStepType(String stepType) {
this.stepType = stepType;
}
public boolean isRequired() {
return required;
}
public void setRequired(boolean required) {
this.required = required;
}
public String getStepState() {
return stepState;
}
public void setStepState(String stepState) {
this.stepState = stepState;
}
public String getReasonCode() {
return reasonCode;
}
public void setReasonCode(String reasonCode) {
this.reasonCode = reasonCode;
}
public String getVehicleId() {
return vehicleId;
}
public void setVehicleId(String vehicleId) {
this.vehicleId = vehicleId;
}
public long getStateVersion() {
return stateVersion;
}
public void setStateVersion(long stateVersion) {
this.stateVersion = stateVersion;
}
}

View File

@@ -0,0 +1,23 @@
package com.ruoyi.wcs.infrastructure.persistence.task;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface TransportOrderMapper {
TransportOrderRow selectByExternalRequestId(
@Param("externalRequestId") String externalRequestId);
TransportOrderRow selectByOrderId(@Param("orderId") String orderId);
int insertOrder(TransportOrderRow row);
int updateState(@Param("row") TransportOrderRow row,
@Param("expectedVersion") long expectedVersion);
int insertWorkflow(WorkflowRow row);
int insertTaskStep(TaskStepRow row);
int insertTaskDependency(TaskDependencyRow row);
}

View File

@@ -0,0 +1,173 @@
package com.ruoyi.wcs.infrastructure.persistence.task;
import com.ruoyi.wcs.domain.task.TransportOrder;
import com.ruoyi.wcs.domain.task.TransportOrderState;
import com.ruoyi.wcs.domain.task.TransportOrderType;
import java.time.Instant;
public final class TransportOrderRow {
private String orderId;
private String externalRequestId;
private String orderType;
private String loadId;
private String sourceCode;
private String destinationCode;
private int priority;
private String orderState;
private String workflowId;
private String resultCode;
private String stateReason;
private long stateVersion;
private Instant createdTime;
private Instant updatedTime;
public static TransportOrderRow fromDomain(TransportOrder order) {
TransportOrderRow row = new TransportOrderRow();
row.orderId = order.getOrderId();
row.externalRequestId = order.getExternalRequestId();
row.orderType = order.getType().name();
row.loadId = order.getLoadId();
row.sourceCode = order.getSourceCode();
row.destinationCode = order.getDestinationCode();
row.priority = order.getPriority();
row.orderState = order.getState().name();
row.workflowId = order.getWorkflowId();
row.resultCode = order.getResultCode();
row.stateReason = order.getStateReason();
row.stateVersion = order.getStateVersion();
row.createdTime = order.getCreatedAt();
row.updatedTime = order.getUpdatedAt();
return row;
}
public TransportOrder toDomain() {
return TransportOrder.restore(
orderId,
externalRequestId,
TransportOrderType.valueOf(orderType),
loadId,
sourceCode,
destinationCode,
priority,
TransportOrderState.valueOf(orderState),
workflowId,
resultCode,
stateReason,
stateVersion,
createdTime,
updatedTime);
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getExternalRequestId() {
return externalRequestId;
}
public void setExternalRequestId(String externalRequestId) {
this.externalRequestId = externalRequestId;
}
public String getOrderType() {
return orderType;
}
public void setOrderType(String orderType) {
this.orderType = orderType;
}
public String getLoadId() {
return loadId;
}
public void setLoadId(String loadId) {
this.loadId = loadId;
}
public String getSourceCode() {
return sourceCode;
}
public void setSourceCode(String sourceCode) {
this.sourceCode = sourceCode;
}
public String getDestinationCode() {
return destinationCode;
}
public void setDestinationCode(String destinationCode) {
this.destinationCode = destinationCode;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
public String getOrderState() {
return orderState;
}
public void setOrderState(String orderState) {
this.orderState = orderState;
}
public String getWorkflowId() {
return workflowId;
}
public void setWorkflowId(String workflowId) {
this.workflowId = workflowId;
}
public String getResultCode() {
return resultCode;
}
public void setResultCode(String resultCode) {
this.resultCode = resultCode;
}
public String getStateReason() {
return stateReason;
}
public void setStateReason(String stateReason) {
this.stateReason = stateReason;
}
public long getStateVersion() {
return stateVersion;
}
public void setStateVersion(long stateVersion) {
this.stateVersion = stateVersion;
}
public Instant getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Instant createdTime) {
this.createdTime = createdTime;
}
public Instant getUpdatedTime() {
return updatedTime;
}
public void setUpdatedTime(Instant updatedTime) {
this.updatedTime = updatedTime;
}
}

View File

@@ -0,0 +1,51 @@
package com.ruoyi.wcs.infrastructure.persistence.task;
import com.ruoyi.wcs.domain.task.WorkflowInstance;
public final class WorkflowRow {
private String workflowId;
private String orderId;
private String workflowState;
private long stateVersion;
public static WorkflowRow fromDomain(WorkflowInstance workflow) {
WorkflowRow row = new WorkflowRow();
row.workflowId = workflow.getWorkflowId();
row.orderId = workflow.getOrderId();
row.workflowState = "ACTIVE";
row.stateVersion = 0L;
return row;
}
public String getWorkflowId() {
return workflowId;
}
public void setWorkflowId(String workflowId) {
this.workflowId = workflowId;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getWorkflowState() {
return workflowState;
}
public void setWorkflowState(String workflowState) {
this.workflowState = workflowState;
}
public long getStateVersion() {
return stateVersion;
}
public void setStateVersion(long stateVersion) {
this.stateVersion = stateVersion;
}
}

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.wcs.infrastructure.persistence.task.TransportOrderMapper">
<resultMap id="TransportOrderRowMap"
type="com.ruoyi.wcs.infrastructure.persistence.task.TransportOrderRow">
<id property="orderId" column="order_id"/>
<result property="externalRequestId" column="external_request_id"/>
<result property="orderType" column="order_type"/>
<result property="loadId" column="load_id"/>
<result property="sourceCode" column="source_code"/>
<result property="destinationCode" column="destination_code"/>
<result property="priority" column="priority"/>
<result property="orderState" column="order_state"/>
<result property="workflowId" column="workflow_id"/>
<result property="resultCode" column="result_code"/>
<result property="stateReason" column="state_reason"/>
<result property="stateVersion" column="state_version"/>
<result property="createdTime" column="created_time"/>
<result property="updatedTime" column="updated_time"/>
</resultMap>
<sql id="OrderColumns">
order_id, external_request_id, order_type, load_id, source_code,
destination_code, priority, order_state, workflow_id, result_code,
state_reason, state_version, created_time, updated_time
</sql>
<select id="selectByExternalRequestId" resultMap="TransportOrderRowMap">
SELECT <include refid="OrderColumns"/>
FROM dbo.wcs_transport_order
WHERE external_request_id = #{externalRequestId}
</select>
<select id="selectByOrderId" resultMap="TransportOrderRowMap">
SELECT <include refid="OrderColumns"/>
FROM dbo.wcs_transport_order
WHERE order_id = #{orderId}
</select>
<insert id="insertOrder">
INSERT INTO dbo.wcs_transport_order (
order_id, external_request_id, order_type, load_id, source_code,
destination_code, priority, order_state, workflow_id, result_code,
state_reason, state_version, created_time, updated_time
) VALUES (
#{orderId}, #{externalRequestId}, #{orderType}, #{loadId}, #{sourceCode},
#{destinationCode}, #{priority}, #{orderState}, #{workflowId}, #{resultCode},
#{stateReason}, #{stateVersion}, #{createdTime}, #{updatedTime}
)
</insert>
<update id="updateState">
UPDATE dbo.wcs_transport_order
SET order_state = #{row.orderState},
workflow_id = #{row.workflowId},
result_code = #{row.resultCode},
state_reason = #{row.stateReason},
state_version = #{row.stateVersion},
updated_time = #{row.updatedTime}
WHERE order_id = #{row.orderId}
AND state_version = #{expectedVersion}
</update>
<insert id="insertWorkflow">
INSERT INTO dbo.wcs_workflow_instance (
workflow_id, order_id, workflow_state, state_version
) VALUES (
#{workflowId}, #{orderId}, #{workflowState}, #{stateVersion}
)
</insert>
<insert id="insertTaskStep">
INSERT INTO dbo.wcs_task_step (
step_id, workflow_id, step_key, step_type, required_flag,
step_state, reason_code, vehicle_id, state_version
) VALUES (
#{stepId}, #{workflowId}, #{stepKey}, #{stepType}, #{required},
#{stepState}, #{reasonCode}, #{vehicleId}, #{stateVersion}
)
</insert>
<insert id="insertTaskDependency">
INSERT INTO dbo.wcs_task_dependency (
workflow_id, step_key, depends_on_step_key
) VALUES (
#{workflowId}, #{stepKey}, #{dependsOnStepKey}
)
</insert>
</mapper>

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

View File

@@ -0,0 +1,85 @@
SET XACT_ABORT ON;
BEGIN TRANSACTION;
IF OBJECT_ID('dbo.wcs_transport_order', 'U') IS NULL
BEGIN
CREATE TABLE dbo.wcs_transport_order (
order_id varchar(64) NOT NULL PRIMARY KEY,
external_request_id varchar(128) NOT NULL,
order_type varchar(32) NOT NULL,
load_id varchar(128) NULL,
source_code varchar(128) NOT NULL,
destination_code varchar(128) NOT NULL,
priority int NOT NULL,
order_state varchar(32) NOT NULL,
workflow_id varchar(64) NULL,
result_code varchar(64) NULL,
state_reason nvarchar(512) NULL,
state_version bigint NOT NULL,
created_time datetime2(3) NOT NULL,
updated_time datetime2(3) NOT NULL,
CONSTRAINT uk_wcs_transport_order_external_request
UNIQUE (external_request_id)
);
END;
IF OBJECT_ID('dbo.wcs_workflow_instance', 'U') IS NULL
BEGIN
CREATE TABLE dbo.wcs_workflow_instance (
workflow_id varchar(64) NOT NULL PRIMARY KEY,
order_id varchar(64) NOT NULL,
workflow_state varchar(32) NOT NULL,
state_version bigint NOT NULL,
CONSTRAINT fk_wcs_workflow_order FOREIGN KEY (order_id)
REFERENCES dbo.wcs_transport_order(order_id),
CONSTRAINT uk_wcs_workflow_order UNIQUE (order_id)
);
END;
IF OBJECT_ID('dbo.wcs_task_step', 'U') IS NULL
BEGIN
CREATE TABLE dbo.wcs_task_step (
step_id varchar(128) NOT NULL PRIMARY KEY,
workflow_id varchar(64) NOT NULL,
step_key varchar(64) NOT NULL,
step_type varchar(32) NOT NULL,
required_flag bit NOT NULL,
step_state varchar(32) NOT NULL,
reason_code varchar(128) NULL,
vehicle_id varchar(64) NULL,
state_version bigint NOT NULL,
CONSTRAINT fk_wcs_task_step_workflow FOREIGN KEY (workflow_id)
REFERENCES dbo.wcs_workflow_instance(workflow_id),
CONSTRAINT uk_wcs_task_step_key UNIQUE (workflow_id, step_key)
);
END;
IF OBJECT_ID('dbo.wcs_task_dependency', 'U') IS NULL
BEGIN
CREATE TABLE dbo.wcs_task_dependency (
workflow_id varchar(64) NOT NULL,
step_key varchar(64) NOT NULL,
depends_on_step_key varchar(64) NOT NULL,
CONSTRAINT pk_wcs_task_dependency PRIMARY KEY (
workflow_id, step_key, depends_on_step_key),
CONSTRAINT fk_wcs_task_dependency_workflow FOREIGN KEY (workflow_id)
REFERENCES dbo.wcs_workflow_instance(workflow_id)
);
END;
IF OBJECT_ID('dbo.wcs_domain_event', 'U') IS NULL
BEGIN
CREATE TABLE dbo.wcs_domain_event (
event_id varchar(128) NOT NULL PRIMARY KEY,
aggregate_id varchar(64) NOT NULL,
event_sequence bigint NOT NULL,
event_type varchar(64) NOT NULL,
occurred_time datetime2(3) NOT NULL,
payload_json nvarchar(max) NULL,
published_flag bit NOT NULL CONSTRAINT df_wcs_domain_event_published DEFAULT (0),
CONSTRAINT uk_wcs_domain_event_sequence
UNIQUE (aggregate_id, event_sequence)
);
END;
COMMIT TRANSACTION;

View File

@@ -0,0 +1,19 @@
SET NOCOUNT ON;
SELECT name AS table_name
FROM sys.tables
WHERE name IN (
'wcs_transport_order',
'wcs_workflow_instance',
'wcs_task_step',
'wcs_task_dependency',
'wcs_domain_event'
)
ORDER BY name;
SELECT i.name AS unique_constraint_name
FROM sys.indexes i
JOIN sys.tables t ON t.object_id = i.object_id
WHERE t.name = 'wcs_transport_order'
AND i.name = 'uk_wcs_transport_order_external_request'
AND i.is_unique = 1;