fix(phase3): close loading and dispatch flows
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user