优化
This commit is contained in:
+2
-2
@@ -72,7 +72,7 @@ public class PlatformLoadingListController {
|
||||
return ApiResponse.success(service.lifecycle(planId, warehouseId));
|
||||
}
|
||||
|
||||
@Operation(summary = "修改装柜运输字段", description = "已推送且仍为 EDITING 的预装清单可补录或修改集装箱号与航次;LOADING 阶段按稳定柜次身份规则维护,不能清空已创建 PDA 任务的柜号。")
|
||||
@Operation(summary = "修改装柜运输字段", description = "已推送且仍为 EDITING、PDA尚未领取或产生物理作业事实时,可首次补录或纠正集装箱号并维护航次;已有柜号的纠正必须提供原因和版本,柜号不能清空。")
|
||||
@PatchMapping("/loading-plans/{planId}/transport")
|
||||
@PreAuthorize("hasAuthority('platform:firstleg:preload:edit')")
|
||||
public ApiResponse<PreloadDetail> updateTransport(
|
||||
@@ -86,7 +86,7 @@ public class PlatformLoadingListController {
|
||||
planId, warehouseId, user.userId(), user.username(), idempotencyKey, request));
|
||||
}
|
||||
|
||||
@Operation(summary = "确认整份预装计划", description = "按 loadingPlanId 原子确认全部关联入库预装票据;要求已推送、真实集装箱号已录入、关联订舱已放舱。成功后唯一创建待领取 PDA 任务,claim 后才进入 LOADING。")
|
||||
@Operation(summary = "确认整份预装计划", description = "按 loadingPlanId 原子确认全部关联入库预装票据;要求已推送且关联订舱已放舱,真实集装箱号可在PDA确认装柜完成前补录。成功后唯一创建待领取 PDA 任务,claim 后才进入 LOADING。")
|
||||
@PostMapping("/loading-plans/{planId}/confirm")
|
||||
@PreAuthorize("hasAuthority('platform:firstleg:preload:edit')")
|
||||
public ApiResponse<PreloadConfirmationResponse> confirmPreload(
|
||||
|
||||
@@ -51,7 +51,7 @@ public final class LoadingListDtos {
|
||||
@Size(max = 64) String containerNo,
|
||||
@Size(max = 64) String voyageNo,
|
||||
@NotNull @Min(0) Integer expectedVersion,
|
||||
@NotBlank @Size(max = 500) String operationReason
|
||||
@Size(max = 500) String operationReason
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
@@ -250,7 +250,6 @@ public interface LoadingListQueryMapper {
|
||||
b.updated_by = #{operatorId}, b.updated_by_name = #{operatorName},
|
||||
b.updated_at = CURRENT_TIMESTAMP(3), b.version = b.version + 1
|
||||
WHERE p.id = #{planId} AND p.plan_status='EDITING' AND p.preload_pushed_at IS NOT NULL
|
||||
AND #{containerNo} IS NOT NULL
|
||||
AND NOT EXISTS (SELECT 1 FROM fl_loading_pick_task task
|
||||
JOIN fl_loading_pick_assignment assignment ON assignment.pick_task_id=task.id
|
||||
WHERE task.loading_plan_id=p.id)
|
||||
|
||||
@@ -151,13 +151,12 @@ public class LoadingListRepository {
|
||||
java.util.ArrayList<String> blockers = new java.util.ArrayList<>();
|
||||
if (!pushed) blockers.add("PRELOAD_NOT_PUSHED");
|
||||
if (!editing) blockers.add("LOADING_PLAN_NOT_EDITING");
|
||||
if (!containerReady) blockers.add("CONTAINER_NO_MISSING");
|
||||
if (linkCount == 0) blockers.add("PRELOAD_LINK_MISSING");
|
||||
if (!oceanLinked) blockers.add("OCEAN_BOOKING_NOT_LINKED");
|
||||
else if (!released) blockers.add("OCEAN_BOOKING_NOT_RELEASED");
|
||||
if (taskCreated) blockers.add("PDA_TASK_ALREADY_CREATED");
|
||||
boolean allConfirmed = linkCount > 0 && confirmedCount == linkCount;
|
||||
boolean prerequisitesReady = pushed && editing && containerReady && linkCount > 0 && released;
|
||||
boolean prerequisitesReady = pushed && editing && linkCount > 0 && released;
|
||||
boolean canConfirm = prerequisitesReady && !taskCreated && !allConfirmed;
|
||||
if (allConfirmed) blockers.add("PRELOAD_ALREADY_CONFIRMED");
|
||||
String confirmationStatus = allConfirmed ? "CONFIRMED"
|
||||
|
||||
@@ -423,7 +423,6 @@ public class LoadingListService {
|
||||
AggregateRow before = requirePlan(planId);
|
||||
requireWarehouse(before, warehouseId);
|
||||
String reason = trim(request.operationReason());
|
||||
if (!StringUtils.hasText(reason)) throw badRequest("纠错原因不能为空");
|
||||
String operationId = operationId("TRANSPORT", planId, idempotencyKey);
|
||||
HistoryRow replay = repository.history(operationId);
|
||||
if (replay != null) {
|
||||
@@ -434,15 +433,21 @@ public class LoadingListService {
|
||||
return repository.preloadDetail(before);
|
||||
}
|
||||
|
||||
requireTransportCorrectionMutable(before);
|
||||
requireTransportMutable(before);
|
||||
String containerNo = targetContainerNo(before, request.containerNo());
|
||||
String voyageNo = nullableTrim(request.voyageNo());
|
||||
boolean correctingContainerNo = StringUtils.hasText(before.getContainerNo())
|
||||
&& !java.util.Objects.equals(before.getContainerNo(), containerNo);
|
||||
if (correctingContainerNo && !StringUtils.hasText(reason)) {
|
||||
throw badRequest("纠正已有真实集装箱号时纠错原因不能为空");
|
||||
}
|
||||
String requestHash = transportHash(planId, request.expectedVersion(), containerNo, voyageNo, reason);
|
||||
if (java.util.Objects.equals(before.getContainerNo(), containerNo)
|
||||
&& java.util.Objects.equals(before.getVoyageNo(), voyageNo)) {
|
||||
return repository.preloadDetail(before);
|
||||
}
|
||||
if (containerRepository.containerNoExists(containerNo, before.getContainerBookingId())) {
|
||||
if (containerNo != null
|
||||
&& containerRepository.containerNoExists(containerNo, before.getContainerBookingId())) {
|
||||
throw conflict("集装箱号已存在");
|
||||
}
|
||||
try {
|
||||
@@ -464,8 +469,9 @@ public class LoadingListService {
|
||||
}
|
||||
|
||||
private static String targetContainerNo(AggregateRow before, String requested) {
|
||||
return requested == null ? Iso6346ContainerNumber.require(before.getContainerNo())
|
||||
: Iso6346ContainerNumber.require(requested);
|
||||
if (requested != null) return Iso6346ContainerNumber.require(requested);
|
||||
return StringUtils.hasText(before.getContainerNo())
|
||||
? Iso6346ContainerNumber.require(before.getContainerNo()) : null;
|
||||
}
|
||||
|
||||
private static String transportHash(long planId, int version, String containerNo,
|
||||
@@ -577,14 +583,11 @@ public class LoadingListService {
|
||||
}
|
||||
}
|
||||
|
||||
private void requireTransportCorrectionMutable(AggregateRow row) {
|
||||
private void requireTransportMutable(AggregateRow row) {
|
||||
if (!"EDITING".equals(row.getPlanStatus())) {
|
||||
throw conflict("仅已推送且PDA尚未开始的预装计划可纠错真实集装箱号");
|
||||
}
|
||||
if (row.getPreloadPushedAt() == null) throw conflict("仅已推送的预装清单可维护运输字段");
|
||||
if (!StringUtils.hasText(row.getContainerNo())) {
|
||||
throw conflict("真实集装箱号只能在关联已放舱订舱时首次写入");
|
||||
}
|
||||
if (repository.hasPdaWorkStarted(row.getPlanId())) {
|
||||
throw conflict("PDA任务已领取、已开始作业或已有物理作业事实,不能修改真实集装箱号");
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.baiyue.firstleg.dto.LoadingPickDtos.InventoryResource;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.InboundInventory;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.InventorySnapshot;
|
||||
import com.baiyue.firstleg.dto.LoadingPickDtos.PendingTask;
|
||||
import com.baiyue.firstleg.domain.Iso6346ContainerNumber;
|
||||
import com.baiyue.firstleg.entity.LoadingPickAssignmentEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPickHistoryEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPickItemEntity;
|
||||
@@ -113,11 +114,11 @@ public class LoadingPickService {
|
||||
@Value("${baiyue.security.internal-service-token:test-internal-service-token}")
|
||||
private String internalToken;
|
||||
|
||||
/** Opens the field task only after planning, transport identity and ocean release have converged. */
|
||||
/** Opens the field task after planning and ocean release; the real container number may be filled later. */
|
||||
@Transactional
|
||||
public void beginIfReady(long containerBookingId, long operatorId, String operatorName) {
|
||||
var booking = smartLoadingRepository.booking(containerBookingId);
|
||||
if (booking == null || !StringUtils.hasText(booking.getContainerNo())) return;
|
||||
if (booking == null) return;
|
||||
var plan = smartLoadingRepository.planByBooking(containerBookingId);
|
||||
if (plan == null || plan.getPreloadPushedAt() == null || !"EDITING".equals(plan.getPlanStatus())) return;
|
||||
var links = loadingModelBridge.byLoadingPlan(plan.getId());
|
||||
@@ -137,10 +138,6 @@ public class LoadingPickService {
|
||||
if (!Set.of("PENDING", "CONFIRMING").contains(link.preloadStatus())) {
|
||||
throw conflict("预装清单已确认或当前状态不允许确认");
|
||||
}
|
||||
var booking = smartLoadingRepository.booking(link.containerBookingId());
|
||||
if (booking == null || !StringUtils.hasText(booking.getContainerNo())) {
|
||||
throw conflict("确认预装前必须录入真实集装箱号");
|
||||
}
|
||||
var allocation = oceanBookingRepository.allocationByContainer(link.containerBookingId());
|
||||
if (allocation == null) throw conflict("确认预装前必须关联真实订舱柜");
|
||||
var oceanBooking = oceanBookingRepository.find(allocation.getOceanBookingId());
|
||||
@@ -462,6 +459,7 @@ public class LoadingPickService {
|
||||
requireSameCompletionCommand(replay, taskId, requestHash, operatorId);
|
||||
if ("FAILED".equals(replay.getCommandStatus())) {
|
||||
LoadingPickTaskEntity failed = requiredTaskForUpdate(taskId);
|
||||
requireCompletionContainerNumber(taskId);
|
||||
if (!"COMPLETION_FAILED".equals(failed.getTaskStatus())
|
||||
|| !completionCommands.resume(replay.getId()) || repository.resumeCompletion(taskId) != 1) {
|
||||
throw conflict("装柜完成命令状态已变化,请刷新后重试");
|
||||
@@ -485,6 +483,7 @@ public class LoadingPickService {
|
||||
if (!request.expectedTaskVersion().equals(task.getVersion())) {
|
||||
throw conflict("PDA任务版本已变化,请刷新后复核");
|
||||
}
|
||||
requireCompletionContainerNumber(taskId);
|
||||
if (!repository.allTaskItemsResolved(taskId)) {
|
||||
throw conflict("存在尚未处理的PDA拣货明细,不能确认装柜完成");
|
||||
}
|
||||
@@ -514,6 +513,18 @@ public class LoadingPickService {
|
||||
return view(requiredTask(taskId), null);
|
||||
}
|
||||
|
||||
private void requireCompletionContainerNumber(long taskId) {
|
||||
ContainerBookingEntity booking = repository.activeBookingByTask(taskId);
|
||||
if (booking == null || !StringUtils.hasText(booking.getContainerNo())) {
|
||||
throw conflict("PDA确认装柜完成前必须录入真实集装箱号");
|
||||
}
|
||||
try {
|
||||
Iso6346ContainerNumber.require(booking.getContainerNo());
|
||||
} catch (BizException exception) {
|
||||
throw conflict("PDA确认装柜完成前必须录入符合ISO 6346的真实集装箱号");
|
||||
}
|
||||
}
|
||||
|
||||
/** Called only by the completion worker, after its durable lease has committed. */
|
||||
public void processCompletion(LoadingCompletionCommandEntity command, String worker) {
|
||||
long taskId = command.getPickTaskId();
|
||||
|
||||
Reference in New Issue
Block a user