Test #1

Merged
admin merged 1726 commits from test into main 2026-09-17 11:52:45 +08:00
6 changed files with 36 additions and 11 deletions
Showing only changes of commit edadca18c6 - Show all commits
@@ -46,6 +46,7 @@ public final class SmartLoadingDtos { private SmartLoadingDtos(){}
}
public record WorkbenchContainer(Long bookingId, String bookingNo, String cabinetNo, String containerNo,
Long warehouseId, String warehouseName, String channelCode, String channelName, String containerType,
BigDecimal capacityCbm, BigDecimal maxWeightKg,
Long planId, String planNo, String planStatus, Integer version) {}
public record Candidate(Long accountId, Long inboundId, Long inboundDetailId, Long orderId, String orderNo, String inboundCode,
Long warehouseId, String channelCode, String channelName, Long destinationWarehouseId, String destinationWarehouseCode, String destinationWarehouseName, String customerName, String goodsName, Integer availablePackageCount,
@@ -252,7 +252,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ba
<script>
SELECT b.id bookingId,b.booking_no bookingNo,b.cabinet_no cabinetNo,b.container_no containerNo,
b.warehouse_id warehouseId,b.warehouse_name warehouseName,b.channel_code channelCode,
b.channel_name channelName,b.container_type containerType,p.id planId,p.plan_no planNo,
b.channel_name channelName,b.container_type containerType,b.capacity_cbm capacityCbm,
b.max_weight_kg maxWeightKg,p.id planId,p.plan_no planNo,
CASE WHEN p.id IS NULL OR NOT EXISTS (SELECT 1 FROM fl_loading_plan_item state_item
WHERE state_item.loading_plan_id=p.id AND state_item.deleted=0)
THEN 'WAIT_LOADING' ELSE p.plan_status END planStatus,b.version version
@@ -559,6 +559,12 @@ public class SmartLoadingService {
private Plan assignInbound(long planId, AssignInboundRequest request,
long operatorId, String operatorName, boolean wholeGroupAssignment) {
return assignInbound(planId, request, operatorId, operatorName, wholeGroupAssignment, null);
}
private Plan assignInbound(long planId, AssignInboundRequest request,
long operatorId, String operatorName, boolean wholeGroupAssignment,
List<Candidate> frozenDetails) {
String operation = operation(request.operationId());
String requestHash = sha256("ASSIGN_INBOUND|" + planId + '|' + request.inboundId() + '|'
+ request.packageCount() + '|' + request.expectedVersion());
@@ -574,8 +580,10 @@ public class SmartLoadingService {
}
LoadingPlanEntity plan = editing(planId, request.expectedVersion(), operatorId, operatorName);
ContainerBookingEntity booking = requireBooking(plan.getContainerBookingId());
List<Candidate> details = candidates(plan.getContainerBookingId(), booking.getWarehouseId()).stream()
.filter(candidate -> Objects.equals(candidate.inboundId(), request.inboundId())).toList();
List<Candidate> details = frozenDetails == null
? candidates(plan.getContainerBookingId(), booking.getWarehouseId()).stream()
.filter(candidate -> Objects.equals(candidate.inboundId(), request.inboundId())).toList()
: List.copyOf(frozenDetails);
if (details.isEmpty()) throw conflict("入仓码不存在、已分配完或尚未完成入库");
Candidate first = details.get(0);
if (!wholeGroupAssignment) requireStandalone(first.accountId(), first.inboundId());
@@ -708,21 +716,21 @@ public class SmartLoadingService {
if (replay != null) return replay;
LoadingPlanEntity plan = editing(planId, request.expectedVersion(), operatorId, operatorName);
ContainerBookingEntity booking = requireBooking(plan.getContainerBookingId());
Candidate selected = candidates(plan.getContainerBookingId(), booking.getWarehouseId()).stream()
.filter(candidate -> Objects.equals(candidate.inboundId(), request.inboundId()))
.findFirst().orElseThrow(() -> conflict("同柜不可拆组成员当前不可分配"));
GroupSnapshot discovered = requiredGroup(selected.accountId(), selected.inboundId());
InboundOrderEntity selected = repository.inbound(request.inboundId());
if (selected == null) throw conflict("同柜不可拆组成员当前不可分配");
GroupSnapshot discovered = requiredGroup(selected.getTenantId(), selected.getId());
Map<Long, List<Candidate>> memberCandidates = requireWholeGroupCandidates(
planId, booking, discovered, repository.items(planId));
GroupSnapshot locked = coloadGroups.lockWholeGroupForAssignment(selected.accountId(),
selected.inboundId(), planId, "LOCK-" + sha256(operation).substring(0, 48),
GroupSnapshot locked = coloadGroups.lockWholeGroupForAssignment(selected.getTenantId(),
selected.getId(), planId, "LOCK-" + sha256(operation).substring(0, 48),
operatorId, operatorName);
requireSameGroup(discovered, locked, planId);
for (Member member : locked.members()) {
LoadingPlanEntity current = repository.plan(planId);
assignInbound(planId, new AssignInboundRequest(member.inboundId(), member.fullPackageCount(),
current.getVersion(), "GRP-INB-" + sha256(operation + '|' + member.inboundId())
.substring(0, 48)), operatorId, operatorName, true);
.substring(0, 48)), operatorId, operatorName, true,
memberCandidates.get(member.inboundId()));
}
LoadingPlanEntity assigned = repository.plan(planId);
history(planId, null, "ADD_COLOAD_GROUP", plan.getPlanStatus(), assigned.getPlanStatus(), operation,
@@ -2082,7 +2082,7 @@ components:
SmartLoadingWorkbenchContainer:
type: object
required: [bookingId, bookingNo, cabinetNo, warehouseId, warehouseName, channelCode,
channelName, containerType, planStatus, version]
channelName, containerType, capacityCbm, maxWeightKg, planStatus, version]
properties:
bookingId: {type: string, pattern: '^[1-9][0-9]*$', description: containerBookingId 技术关联}
bookingNo: {type: string, description: 货柜登记业务号}
@@ -2093,6 +2093,8 @@ components:
channelCode: {type: string}
channelName: {type: string}
containerType: {type: string}
capacityCbm: {type: number, nullable: true, description: 柜型基础容积;装柜上限按正式规则计算}
maxWeightKg: {type: number, nullable: true, description: 柜型基础载重;装柜上限按正式规则计算}
planId: {type: string, pattern: '^[1-9][0-9]*$', nullable: true}
planNo: {type: string, nullable: true}
planStatus: {type: string, enum: [WAIT_LOADING, EDITING, LOADING]}
@@ -21,6 +21,8 @@ class PlatformSmartLoadingControllerContractTest {
String yaml=java.nio.file.Files.readString(java.nio.file.Path.of("src/main/resources/openapi/firstleg-v1.yaml"));
assertThat(yaml).contains("/api/platform/v1/first-leg/smart-loading/containers:")
.contains("SmartLoadingWorkbenchContainerPageEnvelope:")
.contains("capacityCbm: {type: number, nullable: true, description: 柜型基础容积;装柜上限按正式规则计算}")
.contains("maxWeightKg: {type: number, nullable: true, description: 柜型基础载重;装柜上限按正式规则计算}")
.contains("只返回 WAIT_LOADING、EDITING 或 LOADING");
}
@Test void openApiFreezesGlobalPagedInboundCandidates() throws Exception {
@@ -441,6 +441,7 @@ class SmartLoadingServiceTest {
void pagesWorkbenchContainersWithTheSameGlobalFilters() {
WorkbenchContainer container = new WorkbenchContainer(99L, "CB-1", "CAB-1", null,
100L, "义乌仓", "MATSON_CLX_TRUCK", "美森正班海卡", "40HC",
new BigDecimal("68"), new BigDecimal("28000"),
1L, "LP-1", "EDITING", 2);
WorkbenchContainerQuery query = new WorkbenchContainerQuery(List.of("MATSON_CLX"), 100L,
9L, "IN-10", LocalDate.parse("2026-09-01"), LocalDate.parse("2026-09-30"),
@@ -585,6 +586,7 @@ class SmartLoadingServiceTest {
when(repository.plan(1L)).thenAnswer(invocation -> plan("EDITING", version.get()));
when(repository.planByBooking(99L)).thenAnswer(invocation -> plan("EDITING", version.get()));
when(repository.booking(99L)).thenReturn(booking());
when(repository.inbound(10L)).thenReturn(inbound(9L, 10L));
when(repository.candidates(100L)).thenReturn(List.of(first, second));
when(repository.candidate(1L, 100L, 10L, 20L)).thenReturn(first);
when(repository.candidate(1L, 100L, 11L, 21L)).thenReturn(second);
@@ -628,6 +630,7 @@ class SmartLoadingServiceTest {
org.mockito.ArgumentMatchers.startsWith("LOCK-"), eq(7L), eq("u"));
verify(loadingModelBridge, times(2)).bindIncrementalAllocation(eq(1L), anyLong(), anyLong(),
eq(7L), eq("u"), anyString());
verify(repository, times(1)).candidates(100L);
}
@Test
@@ -721,6 +724,7 @@ class SmartLoadingServiceTest {
when(repository.plan(1L)).thenReturn(plan("EDITING", 0));
when(repository.planByBooking(99L)).thenReturn(plan("EDITING", 0));
when(repository.booking(99L)).thenReturn(small);
when(repository.inbound(11L)).thenReturn(inbound(9L, 11L));
when(repository.items(1L)).thenReturn(List.of());
when(repository.candidates(100L)).thenReturn(List.of(first, second));
when(repository.activeItemsByInbound(anyLong(), anyLong())).thenReturn(List.of());
@@ -1457,6 +1461,13 @@ class SmartLoadingServiceTest {
return candidate(9L, 10L, 20L, 30L, boxes);
}
private static InboundOrderEntity inbound(long tenantId, long inboundId) {
InboundOrderEntity value = new InboundOrderEntity();
value.setTenantId(tenantId);
value.setId(inboundId);
return value;
}
private static Candidate candidate(long tenantId, long inboundId, long detailId, long orderId, int boxes) {
return candidate(tenantId, inboundId, detailId, orderId, boxes, "FBA1");
}