fix(phase3): close loading and dispatch flows

This commit is contained in:
2026-08-10 17:42:17 +08:00
parent 074a95d82b
commit 84209bffd7
19 changed files with 221 additions and 43 deletions
@@ -43,6 +43,13 @@ public interface OrderFulfillmentSplitMapper extends BaseMapper<OrderFulfillment
@Select("SELECT COALESCE(MAX(split_sequence),0) FROM ord_order WHERE tenant_id=#{tenantId} AND (id=#{parentOrderId} OR parent_order_id=#{parentOrderId}) AND deleted=0")
Integer maxSplitSequence(@Param("tenantId") long tenantId, @Param("parentOrderId") long parentOrderId);
@Update("UPDATE ord_order SET version=version+1, updated_by=#{operatorId}, updated_at=CURRENT_TIMESTAMP "
+ "WHERE tenant_id=#{tenantId} AND id=#{parentOrderId} AND version=#{expectedVersion} AND deleted=0")
int incrementParentVersion(@Param("tenantId") long tenantId,
@Param("parentOrderId") long parentOrderId,
@Param("expectedVersion") int expectedVersion,
@Param("operatorId") long operatorId);
@Select("SELECT s.container_booking_id AS containerBookingId,s.tenant_id AS tenantId,s.parent_order_id AS parentOrderId,p.order_no AS parentOrderNo,s.child_order_id AS childOrderId,c.order_no AS childOrderNo,s.source_order_id AS sourceOrderId,so.order_no AS sourceOrderNo,s.split_role AS splitRole,CASE WHEN s.split_role='LOADED' THEN s.loaded_package_count ELSE s.remaining_package_count END AS packageCount,s.loading_plan_id AS loadingPlanId FROM ord_order_fulfillment_split s JOIN ord_order p ON p.id=s.parent_order_id AND p.tenant_id=s.tenant_id JOIN ord_order c ON c.id=s.child_order_id AND c.tenant_id=s.tenant_id LEFT JOIN ord_order so ON so.id=s.source_order_id AND so.tenant_id=s.tenant_id WHERE s.container_booking_id=#{containerBookingId} ORDER BY s.tenant_id,s.id")
List<OrderSplitDtos.ContainerSlice> findByContainer(@Param("containerBookingId") long containerBookingId);
@@ -7,7 +7,6 @@ import com.baiyue.order.entity.OrderEntity;
import com.baiyue.order.mapper.OrderFulfillmentSplitMapper;
import com.baiyue.order.mapper.OrderMapper;
import com.baiyue.order.entity.OrderFulfillmentSplitEntity;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
@@ -59,13 +58,9 @@ public class OrderFulfillmentSplitService {
OrderSplitDtos.Child remainder = req.remainingPackageCount() > 0
? createChild(req, parent, next, "REMAINDER", req.remainingPackageCount(), hash, operatorId, rows) : null;
for (OrderFulfillmentSplitEntity row : rows) splits.insert(row);
parent.setVersion(parentVersion + 1);
LambdaUpdateWrapper<OrderEntity> guard = new LambdaUpdateWrapper<OrderEntity>()
.eq(OrderEntity::getId, parent.getId())
.eq(OrderEntity::getTenantId, parent.getTenantId())
.eq(OrderEntity::getVersion, parentVersion);
int updated = orders.update(parent, guard);
int updated = splits.incrementParentVersion(parent.getTenantId(), parent.getId(), parentVersion, operatorId);
if (updated != 1) throw new BizException(ResultCode.CONFLICT, "父订单版本已变化,请刷新后重试");
parent.setVersion(parentVersion + 1);
return new OrderSplitDtos.Response(parent.getId(), parent.getOrderNo(), parent.getVersion(), loaded, remainder,
req.sourcePackageCount(), req.loadedPackageCount(), req.remainingPackageCount(), req.loadingPlanId(), req.containerBookingId(), req.idempotencyKey(), LocalDateTime.now());
}
@@ -1,13 +1,29 @@
package com.baiyue.order.service;
import com.baiyue.order.mapper.OrderFulfillmentSplitMapper;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import org.apache.ibatis.annotations.Update;
import java.nio.charset.StandardCharsets;
import static org.assertj.core.api.Assertions.assertThat;
class OrderFulfillmentSplitContractTest {
@Test
void parentVersionUsesOneAtomicCompareAndIncrementStatement() throws Exception {
Update update = OrderFulfillmentSplitMapper.class
.getMethod("incrementParentVersion", long.class, long.class, int.class, long.class)
.getAnnotation(Update.class);
assertThat(String.join(" ", update.value()))
.contains("SET version=version+1")
.contains("tenant_id=#{tenantId}")
.contains("id=#{parentOrderId}")
.contains("version=#{expectedVersion}")
.contains("deleted=0");
}
@Test
void schemaRequiresRealSourceOrderAndStableCabinetLedger() throws Exception {
String sql = new ClassPathResource("db/phase3-order-parent-child-schema.sql")
@@ -7,7 +7,6 @@ import com.baiyue.order.entity.OrderFulfillmentSplitEntity;
import com.baiyue.order.entity.OrderStatus;
import com.baiyue.order.mapper.OrderFulfillmentSplitMapper;
import com.baiyue.order.mapper.OrderMapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -46,10 +45,11 @@ class OrderFulfillmentSplitServiceTest {
when(splits.maxSplitSequence(10, 100)).thenReturn(0);
when(orders.insert(any(OrderEntity.class))).thenReturn(1);
when(splits.insert(any(OrderFulfillmentSplitEntity.class))).thenReturn(1);
when(orders.update(any(OrderEntity.class), any(Wrapper.class))).thenReturn(1);
when(splits.incrementParentVersion(10, 100, 3, 9)).thenReturn(1);
OrderSplitDtos.Response response = service.split(request(100L, 20, 8, 12, "key-1"), 9);
assertThat(response.parentVersion()).isEqualTo(4);
assertThat(response.loadedChild().sourceOrderId()).isEqualTo(100L);
assertThat(response.loadedChild().loadingPlanId()).isEqualTo(701L);
assertThat(response.remainderChild().containerBookingId()).isEqualTo(801L);
@@ -60,6 +60,25 @@ class OrderFulfillmentSplitServiceTest {
assertThat(row.getSourceDetailJson()).contains("sourceDetailId", "packageCount");
assertThat(row.getRequestHash()).hasSize(64);
});
verify(splits).incrementParentVersion(10, 100, 3, 9);
}
@Test
void rejectsConcurrentParentVersionChangeWithoutPublishingNextVersion() {
OrderEntity parent = parent();
when(splits.findByIdempotency(10, 100, "key-race")).thenReturn(List.of());
when(splits.lockOrder(10, 100)).thenReturn(parent);
when(splits.consumedSourceCount(10, 100, 100)).thenReturn(0);
when(splits.maxSplitSequence(10, 100)).thenReturn(0);
when(orders.insert(any(OrderEntity.class))).thenReturn(1);
when(splits.insert(any(OrderFulfillmentSplitEntity.class))).thenReturn(1);
when(splits.incrementParentVersion(10, 100, 3, 9)).thenReturn(0);
assertThatThrownBy(() -> service.split(request(100L, 20, 8, 12, "key-race"), 9))
.isInstanceOf(BizException.class).hasMessageContaining("父订单版本已变化");
assertThat(parent.getVersion()).isEqualTo(3);
verify(splits).incrementParentVersion(10, 100, 3, 9);
}
@Test