fix: scope smart loading recommendations

This commit is contained in:
2026-09-14 17:22:09 +08:00
parent 8a1e587ee4
commit 64bddb1f81
10 changed files with 199 additions and 44 deletions
@@ -76,7 +76,8 @@ public final class SmartLoadingDtos { private SmartLoadingDtos(){}
public record RemoveGroupRequest(@NotNull @Min(0) Integer expectedPlanVersion,
@NotBlank @Size(max=128) String operationId) {}
public record RecommendRequest(@NotNull @Min(0) Integer expectedVersion,
@NotBlank @Size(max=128) String operationId) {}
@NotBlank @Size(max=128) String operationId,
@Positive Long customerId, @Size(max=64) String inboundCode) {}
public record AdjustRequest(@NotNull @Positive Integer packageCount,@NotNull @Min(0) Integer expectedPlanVersion,
@NotNull @Min(0) Integer expectedItemVersion,@NotBlank @Size(max=128) String operationId) {}
public record DeleteRequest(@NotNull @Min(0) Integer expectedPlanVersion,
@@ -106,9 +106,14 @@ public class SmartLoadingService {
return grouped.values().stream().flatMap(details -> inboundCandidates(details).stream()).toList();
}
private List<InboundCandidate> recommendationCandidates(ContainerBookingEntity booking) {
private List<InboundCandidate> recommendationCandidates(ContainerBookingEntity booking,
Long customerId, String inboundCode) {
List<Candidate> values = repository.candidates(null).stream()
.filter(candidate -> sameChannel(booking, candidate) && sameCustomerScope(booking, candidate)).toList();
.filter(candidate -> sameChannel(booking, candidate) && sameCustomerScope(booking, candidate))
.filter(candidate -> customerId == null || Objects.equals(customerId, candidate.accountId()))
.filter(candidate -> !StringUtils.hasText(inboundCode)
|| inboundCode.trim().equals(candidate.inboundCode()))
.toList();
Map<Long, List<Candidate>> grouped = enrichColoadCandidates(booking,
repository.planByBooking(booking.getId()), values).stream()
.collect(java.util.stream.Collectors.groupingBy(Candidate::inboundId, LinkedHashMap::new,
@@ -442,6 +447,11 @@ public class SmartLoadingService {
@BusinessAudit(event = "SMART_LOADING_PALLET_ASSIGN", resourceType = "LOADING_PLAN",
resourceId = "#planId", description = "按已封托托盘分配智能装柜明细")
public Plan assignPallet(long planId, AssignPalletRequest request, long operatorId, String operatorName) {
return assignPallet(planId, request, operatorId, operatorName, true);
}
private Plan assignPallet(long planId, AssignPalletRequest request,
long operatorId, String operatorName, boolean recalculateFees) {
String operation = operation(request.operationId());
String requestHash = sha256("ASSIGN_PALLET|" + planId + '|' + request.palletId() + '|'
+ request.expectedVersion());
@@ -475,7 +485,10 @@ public class SmartLoadingService {
json(values("palletId", request.palletId(), "version", request.expectedVersion())),
json(values("requestHash", requestHash, "palletNo", first.getPalletNo(), "lineCount", lines.size())),
operatorId, operatorName);
recalculateAfterRelationshipChange(assigned.getContainerBookingId(), Set.of(first.getAccountId()), operation);
if (recalculateFees) {
recalculateAfterRelationshipChange(assigned.getContainerBookingId(),
Set.of(first.getAccountId()), operation);
}
return view(assigned, requireBooking(assigned.getContainerBookingId()));
}
@@ -901,14 +914,18 @@ public class SmartLoadingService {
resourceId = "#planId", description = "执行智能装柜推荐")
public Plan recommend(long planId, RecommendRequest request, long operatorId, String operatorName) {
String operation = operation(request.operationId());
String requestHash = sha256("RECOMMEND|" + planId + '|' + request.expectedVersion());
String scopedInboundCode = trim(request.inboundCode());
String requestHash = sha256("RECOMMEND|" + planId + '|' + request.expectedVersion()
+ '|' + request.customerId() + '|' + scopedInboundCode);
Plan replay = replay(operation, planId, "SMART_RECOMMEND", null, requestHash);
if (replay != null) return replay;
LoadingPlanEntity plan = editing(planId, request.expectedVersion(), operatorId, operatorName);
ContainerBookingEntity booking = requireBooking(plan.getContainerBookingId());
Set<Long> visitedGroups = new HashSet<>();
Set<Long> visitedPallets = new HashSet<>();
for (InboundCandidate candidate : recommendationCandidates(booking)) {
Set<Long> affectedCustomers = new HashSet<>();
for (InboundCandidate candidate : recommendationCandidates(booking,
request.customerId(), scopedInboundCode)) {
if (candidate.coLoadGroupId() != null) {
if (!visitedGroups.add(candidate.coLoadGroupId()) || StringUtils.hasText(candidate.blockReason())) {
continue;
@@ -943,7 +960,8 @@ public class SmartLoadingService {
plan = repository.plan(planId);
assignPallet(planId, new AssignPalletRequest(candidate.palletId(), plan.getVersion(),
"REC-PAL-" + sha256(operation + '|' + candidate.palletId()).substring(0, 48)),
operatorId, operatorName);
operatorId, operatorName, false);
affectedCustomers.add(candidate.accountId());
continue;
}
int count = fittingBoxes(booking, repository.items(planId), candidate.availablePackageCount(),
@@ -952,12 +970,15 @@ public class SmartLoadingService {
plan = repository.plan(planId);
assignInbound(planId, new AssignInboundRequest(candidate.inboundId(), count,
plan.getVersion(), "REC-INB-" + sha256(operation + '|' + candidate.inboundId())
.substring(0, 48)), operatorId, operatorName);
.substring(0, 48)), operatorId, operatorName, false, null, false);
affectedCustomers.add(candidate.accountId());
}
}
recalculateAfterRelationshipChange(booking.getId(), affectedCustomers, operation);
LoadingPlanEntity recommended = repository.plan(planId);
history(planId, null, "SMART_RECOMMEND", plan.getPlanStatus(), recommended.getPlanStatus(), operation,
json(values("version", request.expectedVersion())),
json(values("version", request.expectedVersion(), "customerId", request.customerId(),
"inboundCode", scopedInboundCode)),
json(values("requestHash", requestHash, "version", recommended.getVersion())),
operatorId, operatorName);
return view(recommended, requireBooking(plan.getContainerBookingId()));