优化
This commit is contained in:
+1
-2
@@ -45,12 +45,11 @@ public class PlatformLoadingPdaController {
|
||||
@GetMapping("/tasks")
|
||||
@PreAuthorize("hasAuthority('platform:firstleg:pda:view')")
|
||||
public ApiResponse<Task> task(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@RequestParam(required = false) String containerNo,
|
||||
@RequestParam(required = false) String cabinetNo,
|
||||
@RequestParam(required = false) Long loadingPlanId,
|
||||
@RequestParam @Positive long warehouseId) {
|
||||
FirstlegScopeGuard.platform(user);
|
||||
return ApiResponse.success(service.task(containerNo, cabinetNo, loadingPlanId, warehouseId));
|
||||
return ApiResponse.success(service.task(cabinetNo, loadingPlanId, warehouseId));
|
||||
}
|
||||
|
||||
@GetMapping("/tasks/pending")
|
||||
|
||||
@@ -60,17 +60,10 @@ public final class LoadingPickDtos {
|
||||
@NotNull @Min(1) Long warehouseId,
|
||||
@NotNull @Min(1) Long loadingPlanId,
|
||||
@NotNull @Min(1) Long containerBookingId,
|
||||
@Size(max = 64) String containerNo,
|
||||
@NotNull @Min(0) Integer expectedVersion,
|
||||
@NotBlank @Size(max = 64) String inventorySnapshotToken,
|
||||
@NotEmpty @Size(max = 500) List<@Valid BatchItemRequest> items
|
||||
) {
|
||||
@Deprecated
|
||||
public BatchRequest(Long warehouseId, String containerNo, Integer expectedVersion,
|
||||
String inventorySnapshotToken, List<BatchItemRequest> items) {
|
||||
this(warehouseId, null, null, containerNo, expectedVersion, inventorySnapshotToken, items);
|
||||
}
|
||||
}
|
||||
) {}
|
||||
|
||||
public record BatchItemRequest(
|
||||
@NotNull @Min(1) Long itemId,
|
||||
|
||||
+4
@@ -59,6 +59,10 @@ public class OceanBookingRepository {
|
||||
public List<OceanBookingPartyEntity> parties(long id) { return parties.selectList(new LambdaQueryWrapper<OceanBookingPartyEntity>().eq(OceanBookingPartyEntity::getOceanBookingId, id).orderByAsc(OceanBookingPartyEntity::getId)); }
|
||||
public List<OceanBookingContainerRequirementEntity> requirements(long id) { return requirements.selectList(new LambdaQueryWrapper<OceanBookingContainerRequirementEntity>().eq(OceanBookingContainerRequirementEntity::getOceanBookingId, id).orderByAsc(OceanBookingContainerRequirementEntity::getSortNo)); }
|
||||
public List<OceanBookingContainerAllocationEntity> allocations(long id) { return allocations.selectList(new LambdaQueryWrapper<OceanBookingContainerAllocationEntity>().eq(OceanBookingContainerAllocationEntity::getOceanBookingId, id).orderByAsc(OceanBookingContainerAllocationEntity::getAllocatedAt)); }
|
||||
public OceanBookingContainerAllocationEntity allocationByContainer(long containerBookingId) {
|
||||
return allocations.selectOne(new LambdaQueryWrapper<OceanBookingContainerAllocationEntity>()
|
||||
.eq(OceanBookingContainerAllocationEntity::getContainerBookingId, containerBookingId));
|
||||
}
|
||||
public List<OceanBookingStatusHistoryEntity> histories(long id) { return histories.selectList(new LambdaQueryWrapper<OceanBookingStatusHistoryEntity>().eq(OceanBookingStatusHistoryEntity::getOceanBookingId, id).orderByAsc(OceanBookingStatusHistoryEntity::getCreatedAt)); }
|
||||
public void replaceParties(long id, List<OceanBookingPartyEntity> values) { parties.delete(new LambdaQueryWrapper<OceanBookingPartyEntity>().eq(OceanBookingPartyEntity::getOceanBookingId, id)); values.forEach(parties::insert); }
|
||||
public void replaceRequirements(long id, List<OceanBookingContainerRequirementEntity> values) { requirements.delete(new LambdaQueryWrapper<OceanBookingContainerRequirementEntity>().eq(OceanBookingContainerRequirementEntity::getOceanBookingId, id)); values.forEach(requirements::insert); }
|
||||
|
||||
+4
@@ -10,6 +10,7 @@ import com.baiyue.firstleg.oceanbooking.domain.OceanBookingStatus;
|
||||
import com.baiyue.firstleg.oceanbooking.dto.OceanBookingDtos.*;
|
||||
import com.baiyue.firstleg.oceanbooking.entity.*;
|
||||
import com.baiyue.firstleg.oceanbooking.repository.OceanBookingRepository;
|
||||
import com.baiyue.firstleg.service.LoadingPickService;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -31,6 +32,7 @@ public class OceanBookingService {
|
||||
private static final Set<String> PARTY_TYPES = Set.of("SHIPPER", "CONSIGNEE", "NOTIFY_PARTY");
|
||||
@Resource private OceanBookingRepository repository;
|
||||
@Resource private ObjectMapper objectMapper;
|
||||
@Resource private LoadingPickService loadingPickService;
|
||||
|
||||
public PageResult<OceanBookingEntity> page(Query query) {
|
||||
String status = trim(query.status());
|
||||
@@ -106,6 +108,8 @@ public class OceanBookingService {
|
||||
requireStatus(before, OceanBookingStatus.CONFIRMED);
|
||||
if (repository.release(id, request, operatorId, name) != 1) throw concurrent();
|
||||
repository.syncReleasedTransport(id, request, operatorId, name);
|
||||
repository.allocations(id).forEach(allocation ->
|
||||
loadingPickService.beginIfReady(allocation.getContainerBookingId(), operatorId, name));
|
||||
OceanBookingEntity after = required(id);
|
||||
addHistory(id, before.getStatus(), after.getStatus(), action, request.reason(), before, after,
|
||||
operatorId, name);
|
||||
|
||||
@@ -104,23 +104,10 @@ public class ContainerService {
|
||||
history.setOperatorId(operatorId); history.setOperatorName(operatorName);
|
||||
if (transportHistories != null) transportHistories.insert(history);
|
||||
ContainerBookingEntity updated = required(id);
|
||||
beginPdaTaskAfterContainerBound(updated, containerNo, operatorId, operatorName);
|
||||
loadingPickService.beginIfReady(updated.getId(), operatorId, operatorName);
|
||||
return updated;
|
||||
}
|
||||
|
||||
/** 预装清单先推送、后录入集装箱号时,补建对应的 PDA 拣货任务。 */
|
||||
private void beginPdaTaskAfterContainerBound(ContainerBookingEntity booking, String containerNo,
|
||||
long operatorId, String operatorName) {
|
||||
if (containerNo == null) return;
|
||||
LoadingPlanEntity plan = smartLoadingRepository.planByBooking(booking.getId());
|
||||
if (plan == null || plan.getPreloadPushedAt() == null || !"EDITING".equals(plan.getPlanStatus())) return;
|
||||
// The PDA task and its item versions are stable work identities. A later transport-number
|
||||
// correction only changes the booking display value and must not resynchronize the task.
|
||||
if (loadingPickRepository.taskByPlan(plan.getId()) != null) return;
|
||||
loadingPickService.begin(plan.getId(), containerNo, operatorId, operatorName,
|
||||
smartLoadingRepository.items(plan.getId()));
|
||||
}
|
||||
|
||||
private ContainerBookingEntity required(long id) {
|
||||
ContainerBookingEntity value = repository.find(id);
|
||||
if (value == null) throw new BizException(ResultCode.NOT_FOUND, "货柜不存在");
|
||||
|
||||
@@ -174,8 +174,12 @@ public class InboundMeasurementService {
|
||||
InboundOrderEntity inbound = requireInbound(tenantId, inboundId);
|
||||
InboundWeightResultEntity weight = requireWeight(tenantId, inboundId, resultId);
|
||||
requireVersion(request.version(), weight.getVersion());
|
||||
boolean wasConfirmed = WEIGHT_CONFIRMED.equals(weight.getRecognitionStatus());
|
||||
applyWeightCorrection(weight, request.version(), request.adjustedWeightValue(),
|
||||
request.adjustedWeightUnit(), request.adjustmentReason(), operatorId, operatorName);
|
||||
if (wasConfirmed) {
|
||||
recalculateConfirmedWeight(inbound, operatorId);
|
||||
}
|
||||
return toWeight(weight);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.baiyue.firstleg.entity.LoadingPlanHistoryEntity;
|
||||
import com.baiyue.firstleg.mapper.LoadingPickItemMapper.CartonLabelRow;
|
||||
import com.baiyue.firstleg.repository.LoadingPickRepository;
|
||||
import com.baiyue.firstleg.repository.SmartLoadingRepository;
|
||||
import com.baiyue.firstleg.oceanbooking.repository.OceanBookingRepository;
|
||||
import jakarta.annotation.Resource;
|
||||
import feign.FeignException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -65,6 +66,8 @@ public class LoadingPickService {
|
||||
@Resource
|
||||
private SmartLoadingRepository smartLoadingRepository;
|
||||
@Resource
|
||||
private OceanBookingRepository oceanBookingRepository;
|
||||
@Resource
|
||||
private LoadingListService loadingListService;
|
||||
@Resource
|
||||
private PdaPickSettlementService settlementService;
|
||||
@@ -73,6 +76,22 @@ 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. */
|
||||
@Transactional
|
||||
public void beginIfReady(long containerBookingId, long operatorId, String operatorName) {
|
||||
var booking = smartLoadingRepository.booking(containerBookingId);
|
||||
if (booking == null || !StringUtils.hasText(booking.getContainerNo())) return;
|
||||
var plan = smartLoadingRepository.planByBooking(containerBookingId);
|
||||
if (plan == null || plan.getPreloadPushedAt() == null || !"EDITING".equals(plan.getPlanStatus())) return;
|
||||
var allocation = oceanBookingRepository.allocationByContainer(containerBookingId);
|
||||
if (allocation == null) return;
|
||||
var oceanBooking = oceanBookingRepository.find(allocation.getOceanBookingId());
|
||||
if (oceanBooking == null || !"RELEASED".equals(oceanBooking.getStatus())) return;
|
||||
if (repository.taskByPlan(plan.getId()) != null) return;
|
||||
begin(plan.getId(), booking.getContainerNo(), operatorId, operatorName,
|
||||
smartLoadingRepository.items(plan.getId()));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void begin(long planId, String containerNo, long operatorId, String operatorName,
|
||||
List<LoadingPlanItemEntity> planItems) {
|
||||
@@ -111,29 +130,14 @@ public class LoadingPickService {
|
||||
}
|
||||
}
|
||||
|
||||
public Task task(String containerNo, long warehouseId) {
|
||||
if (!StringUtils.hasText(containerNo)) {
|
||||
throw new BizException(ResultCode.BAD_REQUEST, "集装箱号不能为空");
|
||||
}
|
||||
if (warehouseId <= 0) {
|
||||
throw new BizException(ResultCode.BAD_REQUEST, "warehouseId必须大于0");
|
||||
}
|
||||
LoadingPickTaskEntity task = repository.taskByContainer(containerNo.trim(), warehouseId);
|
||||
if (task == null) {
|
||||
throw new BizException(ResultCode.NOT_FOUND, "未找到装柜中的PDA拣货任务");
|
||||
}
|
||||
return view(task, warehouseId);
|
||||
}
|
||||
|
||||
public Task task(String containerNo, String cabinetNo, Long loadingPlanId, long warehouseId) {
|
||||
int selectors = (StringUtils.hasText(containerNo) ? 1 : 0) + (StringUtils.hasText(cabinetNo) ? 1 : 0)
|
||||
public Task task(String cabinetNo, Long loadingPlanId, long warehouseId) {
|
||||
int selectors = (StringUtils.hasText(cabinetNo) ? 1 : 0)
|
||||
+ (loadingPlanId != null ? 1 : 0);
|
||||
if (selectors != 1) throw new BizException(ResultCode.BAD_REQUEST,
|
||||
"containerNo、cabinetNo、loadingPlanId必须且只能提供一个");
|
||||
"cabinetNo、loadingPlanId必须且只能提供一个");
|
||||
LoadingPickTaskEntity task = StringUtils.hasText(cabinetNo)
|
||||
? repository.taskByCabinet(cabinetNo.trim(), warehouseId)
|
||||
: loadingPlanId != null ? repository.taskByPlan(loadingPlanId, warehouseId)
|
||||
: repository.taskByContainer(containerNo.trim(), warehouseId);
|
||||
: repository.taskByPlan(loadingPlanId, warehouseId);
|
||||
if (task == null) throw new BizException(ResultCode.NOT_FOUND, "未找到装柜中的PDA拣货任务");
|
||||
return view(task, warehouseId);
|
||||
}
|
||||
@@ -854,6 +858,7 @@ public class LoadingPickService {
|
||||
}
|
||||
StringBuilder canonical = new StringBuilder("BATCH|").append(taskId).append('|')
|
||||
.append(request.warehouseId()).append('|').append(request.loadingPlanId()).append('|')
|
||||
.append(request.containerBookingId()).append('|')
|
||||
.append(request.expectedVersion()).append('|').append(request.inventorySnapshotToken().trim());
|
||||
request.items().stream().sorted(Comparator.comparing(BatchItemRequest::itemId,
|
||||
Comparator.nullsFirst(Long::compareTo)))
|
||||
|
||||
@@ -49,7 +49,7 @@ public class MerchantPriceBookService {
|
||||
}
|
||||
|
||||
public Response requireAvailableChannel(long merchantTenantId, long originWarehouseId, String channelCode,
|
||||
String destinationType) {
|
||||
String destinationType) {
|
||||
requireMerchant(merchantTenantId);
|
||||
if (originWarehouseId <= 0) throw validation("请选择有效起运仓");
|
||||
Long versionId = repository.activeVersionId();
|
||||
@@ -59,7 +59,7 @@ public class MerchantPriceBookService {
|
||||
}
|
||||
var prefix = repository.channelPrefix(normalized);
|
||||
if (prefix == null || !StringUtils.hasText(prefix.inboundCodePrefix())) {
|
||||
throw new BizException(ResultCode.CONFLICT, "所选渠道未配置入仓码前缀");
|
||||
throw new BizException(ResultCode.CONFLICT, "所选渠道未配置进仓编码");
|
||||
}
|
||||
String selected = "OVERSEAS".equals(destinationType) && StringUtils.hasText(prefix.overseasInboundCodePrefix())
|
||||
? prefix.overseasInboundCodePrefix() : prefix.inboundCodePrefix();
|
||||
|
||||
@@ -225,21 +225,22 @@ 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 (key.equals(plan.getPushIdempotencyKey())) return view(plan, requireBooking(plan.getContainerBookingId()));
|
||||
if (key.equals(plan.getPushIdempotencyKey())) {
|
||||
loadingPickService.beginIfReady(plan.getContainerBookingId(), operatorId, operatorName);
|
||||
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);
|
||||
if (items.isEmpty()) throw validation("装柜明细不能为空");
|
||||
checkPlanCapacity(booking, items);
|
||||
if (StringUtils.hasText(booking.getContainerNo())) {
|
||||
loadingPickService.begin(planId, booking.getContainerNo(), operatorId, operatorName, items);
|
||||
}
|
||||
try {
|
||||
if (repository.push(planId, key, request.expectedVersion(), operatorId, operatorName) != 1) throw conflict("装柜计划已被并发修改");
|
||||
} catch (DuplicateKeyException e) { throw conflict("幂等键已用于其他推送"); }
|
||||
history(planId, null, "PUSH_PRELOAD", "EDITING", "EDITING", key, operatorId, operatorName);
|
||||
LoadingPlanEntity pushed = repository.plan(planId);
|
||||
loadingPickService.beginIfReady(pushed.getContainerBookingId(), operatorId, operatorName);
|
||||
return view(pushed, requireBooking(pushed.getContainerBookingId()));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user