优化提交

This commit is contained in:
2026-09-14 09:58:30 +08:00
parent 5ffd6c0118
commit 8cf7c4b9c9
93 changed files with 2945 additions and 188 deletions
@@ -30,6 +30,7 @@ import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.util.HtmlUtils;
import org.springframework.web.server.ResponseStatusException;
import java.nio.charset.StandardCharsets;
@@ -77,6 +78,30 @@ public class PdaPalletOperationService {
return content(identity.overseasWarehouseId(), pallet);
}
public PalletOperationResponses.PrintablePallet createRelabelPallet(PdaWarehouseIdentity identity,
PalletOperationRequests.NewRelabelPallet request) {
String relabelNo = required(request.relabelNo(), "换标单号不能为空");
RelabelApplication application = relabelApplications.findAnyByRelabelNo(relabelNo);
if (application == null || !"READY_FOR_OUTBOUND".equals(application.getStatus())) {
throw conflict("换标单尚未完成客户确认,不能生成新托盘");
}
if (!Objects.equals(application.getOverseasWarehouseId(), String.valueOf(identity.overseasWarehouseId()))) {
throw conflict("换标单不属于 Token 冻结的海外仓");
}
String key = required(request.idempotencyKey(), "幂等键不能为空");
PalletPhysicalGateway.PalletReference pallet = physical.ensureRelabelPallet(identity.overseasWarehouseId(),
relabelNo + ":" + key);
String code = HtmlUtils.htmlEscape(pallet.palletCode());
String order = HtmlUtils.htmlEscape(relabelNo);
String html = "<!doctype html><html><head><meta charset=\"UTF-8\"><title>" + code
+ "</title><style>@page{size:100mm 70mm;margin:4mm}body{font-family:Arial,sans-serif;text-align:center;margin:0}"
+ "h1{font-size:24px;margin:8mm 0 4mm;letter-spacing:0}.code{font-size:18px;font-weight:700;word-break:break-all}"
+ ".ref{font-size:12px;margin-top:6mm}</style></head><body><h1>托盘标签</h1><div class=\"code\">"
+ code + "</div><div class=\"ref\">换标单:" + order + "</div></body></html>";
return new PalletOperationResponses.PrintablePallet(pallet.palletId(), pallet.palletCode(), pallet.version(),
relabelNo, html);
}
public PalletOperationResponses.Operation assemble(PdaWarehouseIdentity identity,
PalletOperationRequests.Assemble request) {
PalletOperationResponses.Operation replay = replayIfPresent(identity, "PALLET_ASSEMBLE",
@@ -175,8 +200,10 @@ public class PdaPalletOperationService {
Plan base = plan(identity.overseasWarehouseId(), pallets, moves, Set.of(target.palletId()), false);
Plan relabelPlan = new Plan(base.pallets(), base.moves(), base.changes(),
new RelabelMutation(application, itemsByPosition));
String reason = "换标拆托" + (request.reason() == null || request.reason().isBlank()
? "" : ":" + request.reason().trim());
return execute(identity, "RELABEL_PALLET_DISASSEMBLE", request.idempotencyKey(), request,
request.reason(), relabelPlan);
reason, relabelPlan);
}
public PalletOperationResponses.Operation swap(PdaWarehouseIdentity identity,
@@ -563,9 +590,10 @@ public class PdaPalletOperationService {
.digest((operator + "|" + key + "|" + palletId).getBytes(StandardCharsets.UTF_8))); }
catch (NoSuchAlgorithmException exception) { throw new IllegalStateException(exception); }
}
private String locationCode(long warehouseId, long locationId) {
private String locationCode(long warehouseId, Long locationId) {
if (locationId == null) return null;
PalletPhysicalGateway.LocationReference location = physical.resolveLocation(warehouseId, locationId);
if (location == null || location.locationId() != locationId || "INACTIVE".equals(location.status())) {
if (location == null || location.locationId() != locationId.longValue() || "INACTIVE".equals(location.status())) {
throw conflict("托盘库位不存在或已停用");
}
return location.locationCode();
@@ -622,10 +650,10 @@ public class PdaPalletOperationService {
private record PlannedMove(long positionId, long packages, long expectedVersion,
long sourcePalletId, String sourcePalletCode,
long targetPalletId, String targetPalletCode, long targetLocationId,
long targetPalletId, String targetPalletCode, Long targetLocationId,
String targetLocationCode, InventoryBucket bucket) { }
private record PalletChange(PalletPhysicalGateway.PalletReference pallet, String targetStatus,
long targetLocationId) { }
Long targetLocationId) { }
private record Plan(Map<String, PalletPhysicalGateway.PalletReference> pallets,
List<PlannedMove> moves, List<PalletChange> changes,
RelabelMutation relabelMutation) { }