装柜修改
This commit is contained in:
+2
-2
@@ -43,7 +43,7 @@ public class PlatformContainerController {
|
||||
}
|
||||
@PutMapping("/{id}") @PreAuthorize("hasAuthority('platform:firstleg:container:edit')")
|
||||
@BusinessAudit(event="CONTAINER_UPDATE", resourceType="CONTAINER", resourceId="#id", description="完整编辑货柜基础资料")
|
||||
@Operation(summary="完整编辑货柜", description="仅无装柜计划或计划为EDITING时允许")
|
||||
@Operation(summary="完整编辑货柜", description="仅待装柜或预装柜中时允许")
|
||||
public ApiResponse<ContainerBookingEntity> updateDraft(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@PathVariable @Positive long id,
|
||||
@Valid @RequestBody FullUpdateRequest request) {
|
||||
@@ -53,7 +53,7 @@ public class PlatformContainerController {
|
||||
@PatchMapping("/{id}/transport") @PreAuthorize("hasAuthority('platform:firstleg:container:edit')")
|
||||
@BusinessAudit(event="CONTAINER_TRANSPORT_UPDATE", resourceType="CONTAINER", resourceId="#id",
|
||||
description="修改货柜集装箱号和航次")
|
||||
@Operation(summary="修改集装箱号和航次", description="LOADING仅开放此白名单;COMPLETED全部只读")
|
||||
@Operation(summary="修改集装箱号和航次", description="PDA拣货中仅开放此白名单;装柜完成后全部只读")
|
||||
public ApiResponse<ContainerBookingEntity> updateTransport(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@PathVariable @Positive long id,
|
||||
@Valid @RequestBody TransportUpdateRequest request) {
|
||||
|
||||
@@ -2,9 +2,11 @@ package com.baiyue.firstleg.domain;
|
||||
|
||||
/** 智能装柜状态,只允许沿此方向推进。 */
|
||||
public enum SmartLoadingStatus {
|
||||
/** 编辑中:可以维护货柜信息并增删未拣货明细。 */
|
||||
/** 待装柜:货柜已创建但尚未创建装柜计划,仅作为货柜聚合查询的派生状态,不写入装柜计划表。 */
|
||||
WAIT_LOADING,
|
||||
/** 预装柜中:可以维护货柜信息并增删未拣货明细。 */
|
||||
EDITING,
|
||||
/** 装柜中:已生成 PDA 拣货任务,仍可追加或调整未拣货明细。 */
|
||||
/** 拣货中:已生成拣货任务,仍可追加或调整未拣货明细。 */
|
||||
LOADING,
|
||||
/** 装柜完成:PDA 拣货完成后自动完成,计划、明细和货柜信息只读。 */
|
||||
COMPLETED;
|
||||
@@ -12,7 +14,7 @@ public enum SmartLoadingStatus {
|
||||
/** 装柜完成前可维护集装箱号和航次。 */
|
||||
public boolean allowsTransportUpdate() { return this != COMPLETED; }
|
||||
|
||||
/** 编辑中和装柜中均允许维护尚未拣货的明细。 */
|
||||
/** 待装柜、预装柜中和拣货中均允许维护尚未拣货的明细。 */
|
||||
public boolean mutable() { return this != COMPLETED; }
|
||||
|
||||
public boolean canAdvanceTo(SmartLoadingStatus next) {
|
||||
|
||||
@@ -73,14 +73,14 @@ public final class ContainerDtos {
|
||||
LocalDateTime actualWarehouseArrivalAt
|
||||
) {}
|
||||
|
||||
@Schema(name = "ContainerFullUpdateRequest", description = "智能装柜 EDITING 阶段完整编辑")
|
||||
@Schema(name = "ContainerFullUpdateRequest", description = "待装柜或预装柜中阶段完整编辑")
|
||||
public record FullUpdateRequest(
|
||||
@NotNull @PositiveOrZero Integer version,
|
||||
@Valid @NotNull SaveRequest data
|
||||
) {}
|
||||
|
||||
@Schema(name = "ContainerTransportUpdateRequest",
|
||||
description = "LOADING 阶段只允许修改集装箱号和航次")
|
||||
description = "拣货中阶段只允许修改集装箱号和航次")
|
||||
public record TransportUpdateRequest(
|
||||
@Size(max = 64) String containerNo,
|
||||
@Size(max = 64) String voyageNo,
|
||||
|
||||
@@ -13,7 +13,13 @@ import org.apache.ibatis.annotations.Update;
|
||||
public interface ContainerBookingMapper extends BaseMapper<ContainerBookingEntity> {
|
||||
@Select("""
|
||||
<script>
|
||||
SELECT b.*, p.id AS plan_id, COALESCE(p.plan_status, 'EDITING') AS plan_status
|
||||
SELECT b.*, p.id AS plan_id,
|
||||
CASE WHEN p.id IS NULL THEN 'WAIT_LOADING'
|
||||
WHEN p.plan_status != 'COMPLETED' AND NOT EXISTS (
|
||||
SELECT 1 FROM fl_loading_plan_item active_item
|
||||
WHERE active_item.loading_plan_id = p.id AND active_item.deleted = 0
|
||||
) THEN 'WAIT_LOADING'
|
||||
ELSE p.plan_status END AS plan_status
|
||||
FROM fl_container_booking b LEFT JOIN fl_loading_plan p ON p.container_booking_id = b.id
|
||||
WHERE (#{query.containerNo} IS NULL OR b.container_no = #{query.containerNo})
|
||||
AND (#{query.cabinetNo} IS NULL OR b.cabinet_no = #{query.cabinetNo})
|
||||
@@ -24,7 +30,12 @@ public interface ContainerBookingMapper extends BaseMapper<ContainerBookingEntit
|
||||
AND (#{query.departurePortCode} IS NULL OR b.departure_port_code = #{query.departurePortCode})
|
||||
AND (#{query.destinationPortCode} IS NULL OR b.destination_port_code = #{query.destinationPortCode})
|
||||
AND (#{query.destinationWarehouseCode} IS NULL OR b.destination_warehouse_code = #{query.destinationWarehouseCode})
|
||||
AND (#{query.status} IS NULL OR COALESCE(p.plan_status, 'EDITING') = #{query.status})
|
||||
AND (#{query.status} IS NULL OR CASE WHEN p.id IS NULL THEN 'WAIT_LOADING'
|
||||
WHEN p.plan_status != 'COMPLETED' AND NOT EXISTS (
|
||||
SELECT 1 FROM fl_loading_plan_item active_item
|
||||
WHERE active_item.loading_plan_id = p.id AND active_item.deleted = 0
|
||||
) THEN 'WAIT_LOADING'
|
||||
ELSE p.plan_status END = #{query.status})
|
||||
AND (#{query.estimatedSailingFrom} IS NULL OR b.estimated_sailing_at >= #{query.estimatedSailingFrom})
|
||||
AND (#{query.estimatedSailingTo} IS NULL OR b.estimated_sailing_at <= #{query.estimatedSailingTo})
|
||||
AND (#{query.keyword} IS NULL OR b.container_no LIKE CONCAT('%', #{query.keyword}, '%')
|
||||
@@ -38,7 +49,13 @@ public interface ContainerBookingMapper extends BaseMapper<ContainerBookingEntit
|
||||
Page<ContainerBookingEntity> selectBookingPage(Page<ContainerBookingEntity> page, @Param("query") Query query);
|
||||
|
||||
@Select("""
|
||||
SELECT b.*, p.id AS plan_id, COALESCE(p.plan_status, 'EDITING') AS plan_status
|
||||
SELECT b.*, p.id AS plan_id,
|
||||
CASE WHEN p.id IS NULL THEN 'WAIT_LOADING'
|
||||
WHEN p.plan_status != 'COMPLETED' AND NOT EXISTS (
|
||||
SELECT 1 FROM fl_loading_plan_item active_item
|
||||
WHERE active_item.loading_plan_id = p.id AND active_item.deleted = 0
|
||||
) THEN 'WAIT_LOADING'
|
||||
ELSE p.plan_status END AS plan_status
|
||||
FROM fl_container_booking b LEFT JOIN fl_loading_plan p ON p.container_booking_id = b.id
|
||||
WHERE b.id = #{id}
|
||||
""")
|
||||
|
||||
@@ -45,7 +45,10 @@ public interface LoadingListQueryMapper {
|
||||
FROM fl_loading_plan p
|
||||
JOIN fl_container_booking b ON b.id = p.container_booking_id
|
||||
LEFT JOIN fl_loading_plan_item i ON i.loading_plan_id = p.id AND i.deleted = 0
|
||||
WHERE p.plan_status = 'LOADING'
|
||||
WHERE p.preload_pushed_at IS NOT NULL
|
||||
AND p.plan_status IN ('EDITING', 'LOADING')
|
||||
AND EXISTS (SELECT 1 FROM fl_loading_plan_item active_item
|
||||
WHERE active_item.loading_plan_id = p.id AND active_item.deleted = 0)
|
||||
<if test="query.status != null and query.status != ''">AND p.plan_status = #{query.status}</if>
|
||||
<if test="query.keyword != null and query.keyword != ''">
|
||||
AND (p.plan_no LIKE CONCAT('%', #{query.keyword}, '%')
|
||||
|
||||
@@ -8,10 +8,28 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baiyue.firstl
|
||||
""")
|
||||
int touchEditing(@Param("planId") long planId,@Param("expectedVersion") int expectedVersion,@Param("operatorId") long operatorId,@Param("operatorName") String operatorName);
|
||||
@Update("""
|
||||
UPDATE fl_loading_plan SET plan_status='LOADING',push_idempotency_key=#{key},
|
||||
UPDATE fl_loading_plan SET push_idempotency_key=#{key},
|
||||
preload_pushed_by=#{operatorId},preload_pushed_by_name=#{operatorName},preload_pushed_at=CURRENT_TIMESTAMP(3),
|
||||
updated_by=#{operatorId},updated_by_name=#{operatorName},updated_at=CURRENT_TIMESTAMP(3),version=version+1
|
||||
WHERE id=#{planId} AND plan_status='EDITING' AND version=#{expectedVersion}
|
||||
WHERE id=#{planId} AND plan_status='EDITING' AND push_idempotency_key IS NULL AND version=#{expectedVersion}
|
||||
""")
|
||||
int pushPreload(@Param("planId") long planId,@Param("key") String key,@Param("expectedVersion") int expectedVersion,@Param("operatorId") long operatorId,@Param("operatorName") String operatorName);
|
||||
@Update("""
|
||||
UPDATE fl_loading_plan SET plan_status='LOADING',updated_by=#{operatorId},updated_by_name=#{operatorName},
|
||||
updated_at=CURRENT_TIMESTAMP(3),version=version+1
|
||||
WHERE id=#{planId} AND plan_status='EDITING' AND preload_pushed_at IS NOT NULL
|
||||
""")
|
||||
int startPicking(@Param("planId") long planId,@Param("operatorId") long operatorId,@Param("operatorName") String operatorName);
|
||||
@Update("""
|
||||
UPDATE fl_loading_plan plan SET plan_status='EDITING',updated_by=#{operatorId},updated_by_name=#{operatorName},
|
||||
updated_at=CURRENT_TIMESTAMP(3),version=version+1
|
||||
WHERE plan.id=#{planId} AND plan.plan_status='LOADING' AND plan.version=#{expectedVersion}
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM fl_loading_pick_item pick_item
|
||||
JOIN fl_loading_pick_task pick_task ON pick_task.id=pick_item.pick_task_id
|
||||
WHERE pick_task.loading_plan_id=plan.id AND pick_item.picked_package_count>0
|
||||
)
|
||||
""")
|
||||
int recycleUnpicked(@Param("planId") long planId,@Param("expectedVersion") int expectedVersion,
|
||||
@Param("operatorId") long operatorId,@Param("operatorName") String operatorName);
|
||||
}
|
||||
|
||||
@@ -72,4 +72,10 @@ public class SmartLoadingRepository {
|
||||
public int push(long planId, String key, int version, long operatorId, String operatorName) {
|
||||
return plans.pushPreload(planId, key, version, operatorId, operatorName);
|
||||
}
|
||||
public int startPicking(long planId, long operatorId, String operatorName) {
|
||||
return plans.startPicking(planId, operatorId, operatorName);
|
||||
}
|
||||
public int recycleUnpicked(long planId, int expectedVersion, long operatorId, String operatorName) {
|
||||
return plans.recycleUnpicked(planId, expectedVersion, operatorId, operatorName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public class ContainerService {
|
||||
value.setCreatedBy(operatorId); value.setCreatedByName(operatorName);
|
||||
value.setUpdatedBy(operatorId); value.setUpdatedByName(operatorName); value.setVersion(0);
|
||||
try { repository.insert(value); } catch (DuplicateKeyException ex) { throw duplicate(); }
|
||||
value.setPlanStatus(SmartLoadingStatus.EDITING.name());
|
||||
value.setPlanStatus(SmartLoadingStatus.WAIT_LOADING.name());
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public class ContainerService {
|
||||
FullUpdateRequest request) {
|
||||
ContainerBookingEntity current = required(id);
|
||||
requireVersion(current, request.version());
|
||||
if (status(current) != SmartLoadingStatus.EDITING) {
|
||||
if (status(current) != SmartLoadingStatus.WAIT_LOADING && status(current) != SmartLoadingStatus.EDITING) {
|
||||
throw conflict("货柜已进入预装流程,只能修改集装箱号和航次");
|
||||
}
|
||||
validateSchedule(request.data());
|
||||
|
||||
@@ -32,7 +32,7 @@ import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class LoadingListService {
|
||||
private static final Set<String> LOADING_STATUSES = Set.of("LOADING");
|
||||
private static final Set<String> PRELOAD_STATUSES = Set.of("EDITING", "LOADING");
|
||||
@Resource
|
||||
private LoadingListRepository repository;
|
||||
@Resource
|
||||
@@ -43,8 +43,8 @@ public class LoadingListService {
|
||||
public PageResult<PreloadSummary> preloadPage(PreloadQuery query) {
|
||||
validateRange(query.createdFrom(), query.createdTo());
|
||||
String status = trim(query.status());
|
||||
if (StringUtils.hasText(status) && !LOADING_STATUSES.contains(status)) {
|
||||
throw badRequest("装柜清单状态只能是 LOADING");
|
||||
if (StringUtils.hasText(status) && !PRELOAD_STATUSES.contains(status)) {
|
||||
throw badRequest("预装清单状态只能是 EDITING 或 LOADING");
|
||||
}
|
||||
return repository.preloadPage(new PreloadQuery(trim(query.keyword()), status,
|
||||
query.warehouseId(), query.createdFrom(), query.createdTo(), query.page(), query.size()));
|
||||
@@ -59,8 +59,8 @@ public class LoadingListService {
|
||||
public PreloadDetail preloadDetail(long planId, long warehouseId) {
|
||||
AggregateRow row = requirePlan(planId);
|
||||
requireWarehouse(row, warehouseId);
|
||||
if (!LOADING_STATUSES.contains(row.getPlanStatus())) {
|
||||
throw notFound("装柜中清单不存在");
|
||||
if (!PRELOAD_STATUSES.contains(row.getPlanStatus())) {
|
||||
throw notFound("预装清单不存在");
|
||||
}
|
||||
return repository.preloadDetail(row);
|
||||
}
|
||||
@@ -160,8 +160,8 @@ public class LoadingListService {
|
||||
}
|
||||
|
||||
private static void requireLoadingMutable(AggregateRow row) {
|
||||
if (!LOADING_STATUSES.contains(row.getPlanStatus())) {
|
||||
throw conflict("仅装柜中可修改集装箱号与航次");
|
||||
if (!PRELOAD_STATUSES.contains(row.getPlanStatus())) {
|
||||
throw conflict("仅预装柜中或拣货中可修改集装箱号与航次");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ import com.baiyue.firstleg.entity.LoadingPickOperationEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPickReviewEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPickTaskEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPlanItemEntity;
|
||||
import com.baiyue.firstleg.entity.LoadingPlanHistoryEntity;
|
||||
import com.baiyue.firstleg.repository.LoadingPickRepository;
|
||||
import com.baiyue.firstleg.repository.SmartLoadingRepository;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -48,6 +50,8 @@ public class LoadingPickService {
|
||||
@Resource
|
||||
private LoadingPickRepository repository;
|
||||
@Resource
|
||||
private SmartLoadingRepository smartLoadingRepository;
|
||||
@Resource
|
||||
private LoadingListService loadingListService;
|
||||
@Resource
|
||||
private PdaPickSettlementService settlementService;
|
||||
@@ -182,6 +186,7 @@ public class LoadingPickService {
|
||||
history.setOperatorId(operatorId);
|
||||
history.setOperatorName(operatorName);
|
||||
repository.insert(history);
|
||||
startPickingIfNeeded(task, operatorId, operatorName);
|
||||
touchSettleFinishAndSync(task, operatorId, operatorName);
|
||||
return view(requiredTask(taskId), request.warehouseId());
|
||||
}
|
||||
@@ -207,6 +212,7 @@ public class LoadingPickService {
|
||||
}
|
||||
insertHistory(taskId, itemId, operationId, requestHash, "PARTIAL",
|
||||
request.finalPickedPackageCount(), null, operatorId, operatorName);
|
||||
startPickingIfNeeded(task, operatorId, operatorName);
|
||||
touchSettleFinishAndSync(task, operatorId, operatorName);
|
||||
return view(requiredTask(taskId), request.warehouseId());
|
||||
}
|
||||
@@ -271,6 +277,9 @@ public class LoadingPickService {
|
||||
for (BatchLine line : lines) {
|
||||
applyBatchLine(taskId, line, operationId, operatorId, operatorName);
|
||||
}
|
||||
if (lines.stream().anyMatch(line -> "PICK".equals(line.action()) || "PARTIAL".equals(line.action()))) {
|
||||
startPickingIfNeeded(task, operatorId, operatorName);
|
||||
}
|
||||
if (!repository.allTaskItemsResolved(taskId)) {
|
||||
throw conflict("整批提交必须处理本任务全部待拣明细");
|
||||
}
|
||||
@@ -375,6 +384,25 @@ public class LoadingPickService {
|
||||
}
|
||||
}
|
||||
|
||||
/** 首笔实际拣货才将预装柜计划推进为拣货中;领取、跳拣和任务创建均不改变货柜状态。 */
|
||||
private void startPickingIfNeeded(LoadingPickTaskEntity task, long operatorId, String operatorName) {
|
||||
if (smartLoadingRepository.startPicking(task.getLoadingPlanId(), operatorId, operatorName) != 1) {
|
||||
return;
|
||||
}
|
||||
LoadingPlanHistoryEntity history = new LoadingPlanHistoryEntity();
|
||||
history.setLoadingPlanId(task.getLoadingPlanId());
|
||||
history.setOperationId("PDA-START-" + task.getId());
|
||||
history.setActionType("START_LOADING");
|
||||
history.setFromStatus("EDITING");
|
||||
history.setToStatus("LOADING");
|
||||
history.setBeforeSnapshot("{\"source\":\"PDA\"}");
|
||||
history.setAfterSnapshot("{\"source\":\"PDA\",\"status\":\"LOADING\"}");
|
||||
history.setOperatorId(operatorId);
|
||||
history.setOperatorName(operatorName);
|
||||
history.setOperationReason("首笔实际拣货");
|
||||
smartLoadingRepository.insert(history);
|
||||
}
|
||||
|
||||
private LoadingPickTaskEntity requiredTask(long taskId) {
|
||||
LoadingPickTaskEntity task = repository.task(taskId);
|
||||
if (task == null) {
|
||||
|
||||
@@ -89,7 +89,7 @@ public class SmartLoadingService {
|
||||
+ '|' + request.expectedVersion());
|
||||
Plan replay = replay(operation, planId, "ADD_ITEM", null, requestHash);
|
||||
if (replay != null) return replay;
|
||||
LoadingPlanEntity plan = editing(planId, request.expectedVersion());
|
||||
LoadingPlanEntity plan = editing(planId, request.expectedVersion(), operatorId, operatorName);
|
||||
ContainerBookingEntity booking = requireBooking(plan.getContainerBookingId());
|
||||
Candidate candidate = repository.candidate(booking.getWarehouseId(), request.inboundId(), request.inboundDetailId());
|
||||
if (candidate == null) throw conflict("入库货物不存在或尚未完成入库");
|
||||
@@ -131,7 +131,7 @@ public class SmartLoadingService {
|
||||
String requestHash = sha256("RECOMMEND|" + planId + '|' + request.expectedVersion());
|
||||
Plan replay = replay(operation, planId, "SMART_RECOMMEND", null, requestHash);
|
||||
if (replay != null) return replay;
|
||||
LoadingPlanEntity plan = editing(planId, request.expectedVersion());
|
||||
LoadingPlanEntity plan = editing(planId, request.expectedVersion(), operatorId, operatorName);
|
||||
ContainerBookingEntity booking = requireBooking(plan.getContainerBookingId());
|
||||
for (Candidate candidate : candidates(booking.getWarehouseId())) {
|
||||
if (!Objects.equals(candidate.warehouseId(), booking.getWarehouseId())) continue;
|
||||
@@ -163,7 +163,7 @@ public class SmartLoadingService {
|
||||
+ '|' + request.expectedItemVersion());
|
||||
Plan replay = replay(operation, planId, "ADJUST_ITEM", itemId, requestHash);
|
||||
if (replay != null) return replay;
|
||||
LoadingPlanEntity plan = editing(planId, request.expectedPlanVersion());
|
||||
LoadingPlanEntity plan = editing(planId, request.expectedPlanVersion(), operatorId, operatorName);
|
||||
LoadingPlanItemEntity item = repository.item(planId, itemId);
|
||||
if (item == null) throw new BizException(ResultCode.NOT_FOUND, "装柜明细不存在");
|
||||
if (!Objects.equals(item.getVersion(), request.expectedItemVersion())) throw conflict("装柜明细版本冲突");
|
||||
@@ -197,7 +197,7 @@ public class SmartLoadingService {
|
||||
+ request.expectedPlanVersion() + '|' + request.expectedItemVersion());
|
||||
Plan replay = replay(operation, planId, "REMOVE_ITEM", itemId, requestHash);
|
||||
if (replay != null) return replay;
|
||||
LoadingPlanEntity plan = editing(planId, request.expectedPlanVersion());
|
||||
LoadingPlanEntity plan = editing(planId, request.expectedPlanVersion(), operatorId, operatorName);
|
||||
LoadingPlanItemEntity item = repository.item(planId, itemId);
|
||||
if (item == null) throw new BizException(ResultCode.NOT_FOUND, "装柜明细不存在");
|
||||
loadingPickService.remove(itemId);
|
||||
@@ -216,7 +216,8 @@ public class SmartLoadingService {
|
||||
String key = trim(idempotencyKey);
|
||||
if (!StringUtils.hasText(key) || key.length() > 128) throw new BizException(ResultCode.BAD_REQUEST, "必须提供有效 Idempotency-Key");
|
||||
LoadingPlanEntity plan = repository.plan(planId); if (plan == null) throw new BizException(ResultCode.NOT_FOUND, "装柜计划不存在");
|
||||
if ("LOADING".equals(plan.getPlanStatus()) && key.equals(plan.getPushIdempotencyKey())) return view(plan, requireBooking(plan.getContainerBookingId()));
|
||||
if (key.equals(plan.getPushIdempotencyKey())) return view(plan, requireBooking(plan.getContainerBookingId()));
|
||||
if (StringUtils.hasText(plan.getPushIdempotencyKey())) throw conflict("预装清单已推送,不能使用新的幂等键重复推送");
|
||||
if (!"EDITING".equals(plan.getPlanStatus()) || !Objects.equals(plan.getVersion(), request.expectedVersion())) throw conflict("仅编辑中的计划可开始装柜且版本必须匹配");
|
||||
ContainerBookingEntity booking = requireBooking(plan.getContainerBookingId());
|
||||
List<LoadingPlanItemEntity> items = repository.items(planId);
|
||||
@@ -228,7 +229,7 @@ public class SmartLoadingService {
|
||||
try {
|
||||
if (repository.push(planId, key, request.expectedVersion(), operatorId, operatorName) != 1) throw conflict("装柜计划已被并发修改");
|
||||
} catch (DuplicateKeyException e) { throw conflict("幂等键已用于其他推送"); }
|
||||
history(planId, null, "START_LOADING", "EDITING", "LOADING", key, operatorId, operatorName);
|
||||
history(planId, null, "PUSH_PRELOAD", "EDITING", "EDITING", key, operatorId, operatorName);
|
||||
LoadingPlanEntity pushed = repository.plan(planId);
|
||||
return view(pushed, requireBooking(pushed.getContainerBookingId()));
|
||||
}
|
||||
@@ -329,7 +330,19 @@ public class SmartLoadingService {
|
||||
}
|
||||
return view(plan, booking);
|
||||
}
|
||||
private LoadingPlanEntity editing(long id,int version){LoadingPlanEntity p=repository.plan(id);if(p==null)throw new BizException(ResultCode.NOT_FOUND,"装柜计划不存在");if(!Set.of("EDITING", "LOADING").contains(p.getPlanStatus())||!Objects.equals(p.getVersion(),version))throw conflict("计划已完成或版本冲突");return p;}
|
||||
private LoadingPlanEntity editing(long id, int version, long operatorId, String operatorName) {
|
||||
LoadingPlanEntity p = repository.plan(id);
|
||||
if (p == null) throw new BizException(ResultCode.NOT_FOUND, "装柜计划不存在");
|
||||
if (!Set.of("EDITING", "LOADING").contains(p.getPlanStatus()) || !Objects.equals(p.getVersion(), version)) {
|
||||
throw conflict("计划已完成或版本冲突");
|
||||
}
|
||||
if ("LOADING".equals(p.getPlanStatus())
|
||||
&& repository.recycleUnpicked(id, version, operatorId, operatorName) == 1) {
|
||||
p.setPlanStatus("EDITING");
|
||||
p.setVersion(version + 1);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
private ContainerBookingEntity requireBooking(long id){ContainerBookingEntity b=repository.booking(id);if(b==null)throw new BizException(ResultCode.NOT_FOUND,"货柜预订不存在");return b;}
|
||||
private void touch(LoadingPlanEntity p,long op,String name){if(repository.touch(p.getId(),p.getVersion(),op,name)!=1)throw conflict("计划已被并发修改");}
|
||||
private Plan replay(String operation, long planId, String action, Long itemId, String requestHash) {
|
||||
|
||||
Reference in New Issue
Block a user