fix(firstleg): align coload group eligibility
This commit is contained in:
+1
-1
@@ -33,7 +33,7 @@ import java.util.List;
|
||||
@Validated
|
||||
@RestController
|
||||
@RequestMapping("/api/platform/v1/first-leg/coload-groups")
|
||||
@Tag(name = "平台同柜不可拆组", description = "跨客户、同仓同渠道同目的仓港的整组装柜约束")
|
||||
@Tag(name = "平台同柜不可拆组", description = "跨客户、同国内仓同渠道的整组装柜约束")
|
||||
public class PlatformColoadGroupController {
|
||||
@Resource
|
||||
private ColoadGroupService service;
|
||||
|
||||
+2
-8
@@ -43,9 +43,6 @@ public final class ColoadGroupModels {
|
||||
String status,
|
||||
long warehouseId,
|
||||
String channelCode,
|
||||
String destinationType,
|
||||
Long destinationWarehouseId,
|
||||
String destinationCode,
|
||||
Long lockedPlanId,
|
||||
int version,
|
||||
List<Member> members,
|
||||
@@ -60,11 +57,9 @@ public final class ColoadGroupModels {
|
||||
}
|
||||
|
||||
public GroupSnapshot(long groupId, String groupNo, String status, long warehouseId,
|
||||
String channelCode, String destinationType, Long destinationWarehouseId,
|
||||
String destinationCode, Long lockedPlanId, int version,
|
||||
String channelCode, Long lockedPlanId, int version,
|
||||
List<Member> members, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this(groupId, groupNo, status, warehouseId, channelCode, destinationType,
|
||||
destinationWarehouseId, destinationCode, lockedPlanId, version, members,
|
||||
this(groupId, groupNo, status, warehouseId, channelCode, lockedPlanId, version, members,
|
||||
members == null ? 0 : members.stream().mapToInt(Member::fullPackageCount).sum(),
|
||||
createdAt, updatedAt);
|
||||
}
|
||||
@@ -72,7 +67,6 @@ public final class ColoadGroupModels {
|
||||
|
||||
public record OrderGroupInfo(
|
||||
long orderId,
|
||||
long inboundId,
|
||||
GroupSnapshot group
|
||||
) {
|
||||
}
|
||||
|
||||
+10
-29
@@ -15,21 +15,11 @@ import static com.baiyue.firstleg.loading.coload.persistence.ColoadPersistenceRo
|
||||
public interface ColoadGroupMapper {
|
||||
@Select({"<script>", """
|
||||
SELECT o.tenant_id tenantId,o.order_id orderId,o.id inboundId,
|
||||
o.process_status processStatus,o.inbound_role inboundRole,
|
||||
o.split_status splitStatus,o.split_purpose splitPurpose,
|
||||
o.parent_inbound_id parentInboundId,o.warehouse_id warehouseId,
|
||||
o.channel_code channelCode,o.destination_type destinationType,
|
||||
o.destination_warehouse_id destinationWarehouseId,
|
||||
o.destination_code destinationCode,o.total_package_count fullPackageCount,
|
||||
o.process_status processStatus,o.warehouse_id warehouseId,
|
||||
o.channel_code channelCode,o.total_package_count fullPackageCount,
|
||||
(SELECT COUNT(*) FROM fl_loading_plan_item item
|
||||
WHERE item.account_id=o.tenant_id AND item.inbound_id=o.id AND item.deleted=0)
|
||||
activeAllocationCount,
|
||||
(SELECT COUNT(*) FROM fl_inbound_order child
|
||||
WHERE child.tenant_id=o.tenant_id AND child.parent_inbound_id=o.id AND child.deleted=0)
|
||||
childCount,
|
||||
(SELECT COUNT(*) FROM fl_inbound_split_batch batch
|
||||
WHERE batch.tenant_id=o.tenant_id AND batch.parent_inbound_id=o.id
|
||||
AND batch.status IN ('PREPARED','APPLIED')) splitBatchCount,
|
||||
(SELECT member.group_id FROM fl_coload_group_member member
|
||||
WHERE member.tenant_id=o.tenant_id AND member.inbound_id=o.id
|
||||
AND member.active_flag=1 LIMIT 1) activeGroupId
|
||||
@@ -41,20 +31,15 @@ public interface ColoadGroupMapper {
|
||||
|
||||
@Insert("""
|
||||
INSERT INTO fl_coload_group
|
||||
(id,group_no,group_status,warehouse_id,channel_code,destination_type,
|
||||
destination_warehouse_id,destination_code,locked_plan_id,version,
|
||||
(id,group_no,group_status,warehouse_id,channel_code,locked_plan_id,version,
|
||||
created_by,created_by_name,created_at,updated_at)
|
||||
VALUES
|
||||
(#{id},#{groupNo},'ACTIVE',#{warehouseId},#{channelCode},#{destinationType},
|
||||
#{destinationWarehouseId},#{destinationCode},NULL,0,
|
||||
(#{id},#{groupNo},'ACTIVE',#{warehouseId},#{channelCode},NULL,0,
|
||||
#{operatorId},#{operatorName},CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3))
|
||||
""")
|
||||
int insertGroup(@Param("id") long id, @Param("groupNo") String groupNo,
|
||||
@Param("warehouseId") long warehouseId,
|
||||
@Param("channelCode") String channelCode,
|
||||
@Param("destinationType") String destinationType,
|
||||
@Param("destinationWarehouseId") Long destinationWarehouseId,
|
||||
@Param("destinationCode") String destinationCode,
|
||||
@Param("operatorId") long operatorId,
|
||||
@Param("operatorName") String operatorName);
|
||||
|
||||
@@ -70,16 +55,14 @@ public interface ColoadGroupMapper {
|
||||
@Param("fullPackageCount") int fullPackageCount);
|
||||
|
||||
@Select("SELECT id,group_no groupNo,group_status groupStatus,warehouse_id warehouseId,"
|
||||
+ "channel_code channelCode,destination_type destinationType,"
|
||||
+ "destination_warehouse_id destinationWarehouseId,destination_code destinationCode,"
|
||||
+ "locked_plan_id lockedPlanId,version,created_at createdAt,updated_at updatedAt "
|
||||
+ "channel_code channelCode,locked_plan_id lockedPlanId,version,"
|
||||
+ "created_at createdAt,updated_at updatedAt "
|
||||
+ "FROM fl_coload_group WHERE id=#{groupId}")
|
||||
GroupRow group(@Param("groupId") long groupId);
|
||||
|
||||
@Select("SELECT id,group_no groupNo,group_status groupStatus,warehouse_id warehouseId,"
|
||||
+ "channel_code channelCode,destination_type destinationType,"
|
||||
+ "destination_warehouse_id destinationWarehouseId,destination_code destinationCode,"
|
||||
+ "locked_plan_id lockedPlanId,version,created_at createdAt,updated_at updatedAt "
|
||||
+ "channel_code channelCode,locked_plan_id lockedPlanId,version,"
|
||||
+ "created_at createdAt,updated_at updatedAt "
|
||||
+ "FROM fl_coload_group WHERE id=#{groupId} FOR UPDATE")
|
||||
GroupRow lockGroup(@Param("groupId") long groupId);
|
||||
|
||||
@@ -91,9 +74,7 @@ public interface ColoadGroupMapper {
|
||||
@Select("""
|
||||
SELECT group_row.id,group_row.group_no groupNo,group_row.group_status groupStatus,
|
||||
group_row.warehouse_id warehouseId,group_row.channel_code channelCode,
|
||||
group_row.destination_type destinationType,
|
||||
group_row.destination_warehouse_id destinationWarehouseId,
|
||||
group_row.destination_code destinationCode,group_row.locked_plan_id lockedPlanId,
|
||||
group_row.locked_plan_id lockedPlanId,
|
||||
group_row.version,group_row.created_at createdAt,group_row.updated_at updatedAt
|
||||
FROM fl_coload_group_member member
|
||||
JOIN fl_coload_group group_row ON group_row.id=member.group_id
|
||||
@@ -105,7 +86,7 @@ public interface ColoadGroupMapper {
|
||||
@Param("inboundId") long inboundId);
|
||||
|
||||
@Select({"<script>", """
|
||||
SELECT member.order_id orderId,member.inbound_id inboundId,member.group_id groupId
|
||||
SELECT member.order_id orderId,member.group_id groupId
|
||||
FROM fl_coload_group_member member
|
||||
JOIN fl_coload_group group_row ON group_row.id=member.group_id
|
||||
WHERE member.order_id IN
|
||||
|
||||
-13
@@ -17,19 +17,10 @@ public final class ColoadPersistenceRows {
|
||||
private Long orderId;
|
||||
private Long inboundId;
|
||||
private String processStatus;
|
||||
private String inboundRole;
|
||||
private String splitStatus;
|
||||
private String splitPurpose;
|
||||
private Long parentInboundId;
|
||||
private Long warehouseId;
|
||||
private String channelCode;
|
||||
private String destinationType;
|
||||
private Long destinationWarehouseId;
|
||||
private String destinationCode;
|
||||
private Integer fullPackageCount;
|
||||
private Integer activeAllocationCount;
|
||||
private Integer childCount;
|
||||
private Integer splitBatchCount;
|
||||
private Long activeGroupId;
|
||||
}
|
||||
|
||||
@@ -41,9 +32,6 @@ public final class ColoadPersistenceRows {
|
||||
private String groupStatus;
|
||||
private Long warehouseId;
|
||||
private String channelCode;
|
||||
private String destinationType;
|
||||
private Long destinationWarehouseId;
|
||||
private String destinationCode;
|
||||
private Long lockedPlanId;
|
||||
private Integer version;
|
||||
private LocalDateTime createdAt;
|
||||
@@ -63,7 +51,6 @@ public final class ColoadPersistenceRows {
|
||||
@Setter
|
||||
public static class OrderGroupLinkRow {
|
||||
private Long orderId;
|
||||
private Long inboundId;
|
||||
private Long groupId;
|
||||
}
|
||||
|
||||
|
||||
+18
-35
@@ -52,8 +52,7 @@ public class ColoadGroupService implements ColoadGroupAssignmentPort {
|
||||
String groupNo = "CLG-" + groupId;
|
||||
InboundCandidateRow first = candidates.get(0);
|
||||
mapper.insertGroup(groupId, groupNo, first.getWarehouseId(), first.getChannelCode().trim(),
|
||||
normalized(first.getDestinationType()), first.getDestinationWarehouseId(),
|
||||
trim(first.getDestinationCode()), operatorId, trim(operatorName));
|
||||
operatorId, trim(operatorName));
|
||||
for (InboundCandidateRow candidate : candidates) {
|
||||
mapper.insertMember(IdWorker.getId(), groupId, candidate.getTenantId(), candidate.getOrderId(),
|
||||
candidate.getInboundId(), candidate.getFullPackageCount());
|
||||
@@ -98,7 +97,7 @@ public class ColoadGroupService implements ColoadGroupAssignmentPort {
|
||||
if (latest.containsKey(link.getOrderId())) continue;
|
||||
GroupSnapshot group = groups.computeIfAbsent(link.getGroupId(), this::requiredSnapshot);
|
||||
if (!CURRENT_GROUP_STATUSES.contains(group.status())) continue;
|
||||
latest.put(link.getOrderId(), new OrderGroupInfo(link.getOrderId(), link.getInboundId(), group));
|
||||
latest.put(link.getOrderId(), new OrderGroupInfo(link.getOrderId(), group));
|
||||
}
|
||||
return orderIds.stream().map(latest::get).filter(Objects::nonNull).toList();
|
||||
}
|
||||
@@ -188,10 +187,10 @@ public class ColoadGroupService implements ColoadGroupAssignmentPort {
|
||||
for (Long orderId : orderIds) {
|
||||
List<InboundCandidateRow> eligible = byOrder.getOrDefault(orderId, List.of()).stream()
|
||||
.filter(row -> eligible(row, allowedGroupId)).toList();
|
||||
if (eligible.size() != 1) {
|
||||
throw conflict("订单 " + orderId + " 没有唯一可创建同柜不可拆组的当前入库单");
|
||||
if (eligible.isEmpty()) {
|
||||
throw conflict("订单 " + orderId + " 不存在可创建同柜不可拆组的可用入库片段");
|
||||
}
|
||||
resolved.add(eligible.get(0));
|
||||
resolved.addAll(eligible);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
@@ -200,46 +199,39 @@ public class ColoadGroupService implements ColoadGroupAssignmentPort {
|
||||
return row.getTenantId() != null && row.getTenantId() > 0
|
||||
&& row.getInboundId() != null && row.getInboundId() > 0
|
||||
&& "WAIT_SPLIT".equals(row.getProcessStatus())
|
||||
&& "MASTER".equals(row.getInboundRole())
|
||||
&& "NOT_SPLIT".equals(row.getSplitStatus())
|
||||
&& "MASTER".equals(row.getSplitPurpose())
|
||||
&& row.getParentInboundId() == null
|
||||
&& row.getFullPackageCount() != null && row.getFullPackageCount() > 0
|
||||
&& Objects.equals(row.getActiveAllocationCount(), 0)
|
||||
&& Objects.equals(row.getChildCount(), 0)
|
||||
&& Objects.equals(row.getSplitBatchCount(), 0)
|
||||
&& (row.getActiveGroupId() == null || Objects.equals(row.getActiveGroupId(), allowedGroupId));
|
||||
}
|
||||
|
||||
private void validateSameRoute(List<InboundCandidateRow> rows) {
|
||||
InboundCandidateRow first = rows.get(0);
|
||||
if (first.getWarehouseId() == null || first.getWarehouseId() <= 0
|
||||
|| !StringUtils.hasText(first.getChannelCode())
|
||||
|| !StringUtils.hasText(first.getDestinationType())
|
||||
|| destinationKey(first) == null) {
|
||||
throw conflict("订单缺少仓库、渠道或目的仓港权威快照");
|
||||
|| !StringUtils.hasText(first.getChannelCode())) {
|
||||
throw conflict("订单缺少国内仓或渠道权威快照");
|
||||
}
|
||||
for (InboundCandidateRow row : rows) {
|
||||
if (!Objects.equals(first.getWarehouseId(), row.getWarehouseId())
|
||||
|| !normalized(first.getChannelCode()).equals(normalized(row.getChannelCode()))
|
||||
|| !normalized(first.getDestinationType()).equals(normalized(row.getDestinationType()))
|
||||
|| !Objects.equals(destinationKey(first), destinationKey(row))) {
|
||||
throw conflict("同柜不可拆组成员必须属于同一国内仓、渠道和目的仓港");
|
||||
|| !normalized(first.getChannelCode()).equals(normalized(row.getChannelCode()))) {
|
||||
throw conflict("同柜不可拆组成员必须属于同一国内仓和渠道");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateStillAssignable(GroupSnapshot group) {
|
||||
List<Long> orderIds = group.members().stream().map(Member::orderId).toList();
|
||||
List<Long> orderIds = group.members().stream().map(Member::orderId).distinct().sorted().toList();
|
||||
List<InboundCandidateRow> current = authoritativeCandidates(orderIds, group.groupId());
|
||||
Map<Long, Member> frozen = new LinkedHashMap<>();
|
||||
group.members().forEach(member -> frozen.put(member.orderId(), member));
|
||||
group.members().forEach(member -> frozen.put(member.inboundId(), member));
|
||||
if (current.size() != frozen.size()) {
|
||||
throw conflict("同柜不可拆组的可用入库片段集合已变化");
|
||||
}
|
||||
for (InboundCandidateRow row : current) {
|
||||
Member member = frozen.get(row.getOrderId());
|
||||
Member member = frozen.get(row.getInboundId());
|
||||
if (member == null || member.tenantId() != row.getTenantId()
|
||||
|| member.inboundId() != row.getInboundId()
|
||||
|| member.orderId() != row.getOrderId()
|
||||
|| member.fullPackageCount() != row.getFullPackageCount()) {
|
||||
throw conflict("同柜不可拆组成员的当前入库单或满箱数已变化");
|
||||
throw conflict("同柜不可拆组的入库片段或满箱数已变化");
|
||||
}
|
||||
}
|
||||
validateSameRoute(current);
|
||||
@@ -257,8 +249,7 @@ public class ColoadGroupService implements ColoadGroupAssignmentPort {
|
||||
value.getFullPackageCount()))
|
||||
.toList();
|
||||
return new GroupSnapshot(row.getId(), row.getGroupNo(), row.getGroupStatus(), row.getWarehouseId(),
|
||||
row.getChannelCode(), row.getDestinationType(), row.getDestinationWarehouseId(),
|
||||
row.getDestinationCode(), row.getLockedPlanId(), row.getVersion(), members,
|
||||
row.getChannelCode(), row.getLockedPlanId(), row.getVersion(), members,
|
||||
members.stream().mapToInt(Member::fullPackageCount).sum(),
|
||||
row.getCreatedAt(), row.getUpdatedAt());
|
||||
}
|
||||
@@ -303,14 +294,6 @@ public class ColoadGroupService implements ColoadGroupAssignmentPort {
|
||||
operationId, trim(remark), operatorId, trim(operatorName));
|
||||
}
|
||||
|
||||
private static String destinationKey(InboundCandidateRow row) {
|
||||
if (row.getDestinationWarehouseId() != null && row.getDestinationWarehouseId() > 0) {
|
||||
return "ID:" + row.getDestinationWarehouseId();
|
||||
}
|
||||
String code = trim(row.getDestinationCode());
|
||||
return code == null ? null : "CODE:" + code.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private static List<Long> normalizedOrderIds(List<Long> source, int minimum, int maximum) {
|
||||
if (source == null) throw badRequest("orderIds不能为空");
|
||||
List<Long> values = source.stream().filter(Objects::nonNull).distinct().sorted().toList();
|
||||
|
||||
Reference in New Issue
Block a user