Merge remote-tracking branch 'origin/test' into dev

This commit is contained in:
2026-09-03 20:59:58 +08:00
12 changed files with 318 additions and 13 deletions
@@ -67,7 +67,17 @@ public final class SmartLoadingDtos { private SmartLoadingDtos(){}
public record WarehouseRemarkRequest(@Size(max=1000) String warehouseRemark,
@NotNull @Min(0) Integer expectedPlanVersion, @NotNull @Min(0) Integer expectedItemVersion,
@NotBlank @Size(max=128) String operationId) {}
public record PlanCoLoadGroup(Long groupId,String groupNo,Long lockedPlanId,Integer memberOrderCount,
Integer inboundFragmentCount,Integer fullPackageCount,List<Long> memberInboundIds) {
public PlanCoLoadGroup { memberInboundIds=memberInboundIds==null?List.of():List.copyOf(memberInboundIds); }
}
public record Plan(Long id,String planNo,String status,Long bookingId,String containerNo,String voyageNo,
Integer version,Boolean canAssign,Boolean canEdit,String blockedReason,String finalCompletionStatus,
Long pdaTaskId,String pdaTaskStatus,Integer pdaTaskVersion,List<LoadingPlanItemEntity> items) {}
Long pdaTaskId,String pdaTaskStatus,Integer pdaTaskVersion,List<PlanCoLoadGroup> coLoadGroups,
List<LoadingPlanItemEntity> items) {
public Plan {
coLoadGroups=coLoadGroups==null?List.of():List.copyOf(coLoadGroups);
items=items==null?List.of():List.copyOf(items);
}
}
}
@@ -12,6 +12,22 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ba
""")
List<ChannelContainerCountRow> channelContainerCounts(@Param("warehouseId") long warehouseId);
@Select("""
SELECT DISTINCT group_row.id groupId,group_row.group_no groupNo,
group_row.locked_plan_id lockedPlanId,member.order_id orderId,
member.inbound_id inboundId,member.full_package_count fullPackageCount
FROM fl_loading_plan_item plan_item
JOIN fl_coload_group_member member
ON member.tenant_id=plan_item.account_id AND member.inbound_id=plan_item.inbound_id
AND member.active_flag=1
JOIN fl_coload_group group_row ON group_row.id=member.group_id
WHERE plan_item.loading_plan_id=#{planId} AND plan_item.deleted=0
AND group_row.locked_plan_id=#{planId}
AND group_row.group_status IN ('LOCKED','CONSUMED')
ORDER BY group_row.id,member.order_id,member.inbound_id
""")
List<PlanColoadMemberRow> planColoadMembers(@Param("planId") long planId);
@Select("""
SELECT o.tenant_id accountId,o.id inboundId,i.inbound_detail_id inboundDetailId,
o.order_id orderId,o.order_no orderNo,o.inbound_code inboundCode,
@@ -142,10 +158,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ba
AND SUM(CASE WHEN LOWER(eligible.inboundCode) LIKE CONCAT('%',LOWER(#{query.inboundCode}),'%') THEN 1 ELSE 0 END)&gt;0
</if>
<if test="query.expectedArrivalStart != null or query.expectedArrivalEnd != null">
AND SUM(CASE WHEN eligible.expectedArrivalAt IS NOT NULL
<if test="query.expectedArrivalStart != null">AND DATE(eligible.expectedArrivalAt)&gt;=#{query.expectedArrivalStart}</if>
<if test="query.expectedArrivalEnd != null">AND DATE(eligible.expectedArrivalAt)&lt;=#{query.expectedArrivalEnd}</if>
THEN 1 ELSE 0 END)&gt;0
AND MIN(eligible.expectedArrivalAt) IS NOT NULL
<if test="query.expectedArrivalStart != null">AND DATE(MIN(eligible.expectedArrivalAt))&gt;=#{query.expectedArrivalStart}</if>
<if test="query.expectedArrivalEnd != null">AND DATE(MIN(eligible.expectedArrivalAt))&lt;=#{query.expectedArrivalEnd}</if>
</if>
ORDER BY sortArrivalAt IS NULL,sortArrivalAt,sortInboundId
</script>
@@ -328,4 +343,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ba
}
record CandidateUnit(Long groupId,Long accountId,Long inboundId,
LocalDateTime sortArrivalAt,Long sortInboundId) {}
record PlanColoadMemberRow(Long groupId,String groupNo,Long lockedPlanId,
Long orderId,Long inboundId,Integer fullPackageCount) {}
}
@@ -19,6 +19,7 @@ import com.baiyue.firstleg.mapper.LoadingPlanMapper;
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper;
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper.ChannelContainerCountRow;
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper.CandidateUnit;
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper.PlanColoadMemberRow;
import com.baiyue.firstleg.mapper.InboundOrderMapper;
import org.springframework.stereotype.Repository;
import jakarta.annotation.Resource;
@@ -56,6 +57,9 @@ public class SmartLoadingRepository {
public List<ChannelContainerCountRow> channelContainerCounts(long warehouseId) {
return candidates.channelContainerCounts(warehouseId);
}
public List<PlanColoadMemberRow> planColoadMembers(long planId) {
return candidates.planColoadMembers(planId);
}
public Candidate candidate(long planId, long warehouseId, long inboundId, long detailId) {
return candidates.candidate(planId, warehouseId, inboundId, detailId);
}
@@ -14,6 +14,7 @@ import com.baiyue.firstleg.loading.coload.model.ColoadGroupModels.GroupSnapshot;
import com.baiyue.firstleg.loading.coload.model.ColoadGroupModels.Member;
import com.baiyue.firstleg.loading.coload.port.ColoadGroupAssignmentPort;
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper.CandidateUnit;
import com.baiyue.firstleg.mapper.SmartLoadingCandidateMapper.PlanColoadMemberRow;
import com.baiyue.firstleg.pricebookquery.application.port.in.PriceBookQueryUseCase;
import com.baiyue.firstleg.split.service.SplitPreloadService;
import org.springframework.dao.DuplicateKeyException;
@@ -235,12 +236,14 @@ public class SmartLoadingService {
return actual;
}
@Transactional(readOnly = true)
public Plan draftByBooking(long bookingId) {
ContainerBookingEntity booking = requireBooking(bookingId);
LoadingPlanEntity plan = repository.planByBooking(bookingId);
return draftView(plan, booking);
}
@Transactional(readOnly = true)
public Plan detail(long planId) {
LoadingPlanEntity plan = repository.plan(planId);
if (plan == null) throw new BizException(ResultCode.NOT_FOUND, "装柜计划不存在");
@@ -964,7 +967,22 @@ public class SmartLoadingService {
: "COMPLETED".equals(task.taskStatus()) ? "COMPLETED" : "IN_PROGRESS";
return new Plan(p.getId(),p.getPlanNo(),p.getPlanStatus(),p.getContainerBookingId(),
b.getContainerNo(),b.getVoyageNo(),p.getVersion(),mutable,mutable,blockedReason,finalStatus,
task.taskId(),task.taskStatus(),task.taskVersion(),items);
task.taskId(),task.taskStatus(),task.taskVersion(),planCoLoadGroups(p.getId()),items);
}
private List<PlanCoLoadGroup> planCoLoadGroups(long planId) {
Map<Long,List<PlanColoadMemberRow>> grouped=repository.planColoadMembers(planId).stream()
.collect(java.util.stream.Collectors.groupingBy(PlanColoadMemberRow::groupId,
LinkedHashMap::new,java.util.stream.Collectors.toList()));
return grouped.values().stream().map(rows->{
PlanColoadMemberRow first=rows.get(0);
LinkedHashMap<Long,Integer> packagesByInbound=new LinkedHashMap<>();
rows.forEach(row->packagesByInbound.putIfAbsent(row.inboundId(),row.fullPackageCount()));
int orderCount=(int)rows.stream().map(PlanColoadMemberRow::orderId).distinct().count();
int packages=packagesByInbound.values().stream().filter(Objects::nonNull)
.mapToInt(Integer::intValue).sum();
return new PlanCoLoadGroup(first.groupId(),first.groupNo(),first.lockedPlanId(),orderCount,
packagesByInbound.size(),packages,List.copyOf(packagesByInbound.keySet()));
}).toList();
}
private Plan draftView(LoadingPlanEntity plan, ContainerBookingEntity booking) {
if (plan == null) throw new BizException(ResultCode.NOT_FOUND, "该货柜尚未创建智能装柜草稿");
@@ -2035,7 +2035,7 @@ components:
version: {type: integer, minimum: 0, description: 柜次版本}
SmartLoadingPlan:
type: object
required: [id, planNo, status, bookingId, version, canAssign, canEdit, finalCompletionStatus, items]
required: [id, planNo, status, bookingId, version, canAssign, canEdit, finalCompletionStatus, coLoadGroups, items]
properties:
id: {type: string}
planNo: {type: string, pattern: '^LP-[0-9]{6}-[0-9]{4}$', example: LP-260818-0001, description: 预装与确认装柜清单共用的稳定编号}
@@ -2051,9 +2051,28 @@ components:
pdaTaskId: {type: string, nullable: true}
pdaTaskStatus: {type: string, nullable: true, enum: [LOADING, COMPLETED], description: LOADING task 在增量后保持原 taskId;COMPLETED 永久拒绝增量}
pdaTaskVersion: {type: integer, minimum: 0, nullable: true, description: 服务端同步后最新 taskVersion;Admin 写操作不提交该值}
coLoadGroups:
type: array
description: 当前计划内同柜不可拆组的完整冻结快照;随计划一次返回,调用方不得按 inboundId 扇出重建
items: {$ref: '#/components/schemas/SmartLoadingPlanCoLoadGroup'}
items:
type: array
items: {$ref: '#/components/schemas/SmartLoadingPlanItem'}
SmartLoadingPlanCoLoadGroup:
type: object
required: [groupId, groupNo, lockedPlanId, memberOrderCount, inboundFragmentCount, fullPackageCount, memberInboundIds]
properties:
groupId: {type: string, pattern: '^[1-9][0-9]*$'}
groupNo: {type: string, minLength: 1}
lockedPlanId: {type: string, pattern: '^[1-9][0-9]*$'}
memberOrderCount: {type: integer, minimum: 1, description: 组内去重订单数}
inboundFragmentCount: {type: integer, minimum: 1, description: 组内去重当前有效入仓片段数}
fullPackageCount: {type: integer, minimum: 1, description: 全部成员冻结满箱数之和}
memberInboundIds:
type: array
minItems: 1
uniqueItems: true
items: {type: string, pattern: '^[1-9][0-9]*$'}
SmartLoadingPlanItem:
type: object
required: [id, packageCount, version, pickedPackageCount, minPackageCount, canAdjust, canRemove]