优化
This commit is contained in:
+2
-1
@@ -114,9 +114,10 @@ public class PlatformContainerController {
|
||||
@Operation(summary="修改集装箱号和航次", description="PDA拣货中仅开放此白名单;装柜完成后全部只读")
|
||||
public ApiResponse<ContainerBookingEntity> updateTransport(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@PathVariable @Positive long id,
|
||||
@RequestHeader("Idempotency-Key") String key,
|
||||
@Valid @RequestBody TransportUpdateRequest request) {
|
||||
FirstlegScopeGuard.platform(user);
|
||||
return ApiResponse.success(service.updateTransport(id, user.userId(), user.username(), request));
|
||||
return ApiResponse.success(service.updateTransport(id, user.userId(), user.username(), key, request));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/clearance")
|
||||
|
||||
@@ -16,6 +16,8 @@ public class ContainerTransportHistoryEntity {
|
||||
private String afterContainerNo;
|
||||
private String beforeVoyageNo;
|
||||
private String afterVoyageNo;
|
||||
private String operationId;
|
||||
private String requestHash;
|
||||
private String reason;
|
||||
private Long operatorId;
|
||||
private String operatorName;
|
||||
|
||||
+8
-1
@@ -3,6 +3,13 @@ package com.baiyue.firstleg.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baiyue.firstleg.entity.ContainerTransportHistoryEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
@Mapper
|
||||
public interface ContainerTransportHistoryMapper extends BaseMapper<ContainerTransportHistoryEntity> {}
|
||||
public interface ContainerTransportHistoryMapper extends BaseMapper<ContainerTransportHistoryEntity> {
|
||||
@Select("SELECT * FROM fl_container_transport_history WHERE container_booking_id=#{bookingId} "
|
||||
+ "AND operator_id=#{operatorId} AND operation_id=#{operationId} LIMIT 1")
|
||||
ContainerTransportHistoryEntity selectOperation(@Param("bookingId") long bookingId,
|
||||
@Param("operatorId") long operatorId, @Param("operationId") String operationId);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Objects;
|
||||
|
||||
@Service
|
||||
public class ContainerService {
|
||||
@@ -151,31 +155,83 @@ public class ContainerService {
|
||||
|
||||
@Transactional
|
||||
public ContainerBookingEntity updateTransport(long id, long operatorId, String operatorName,
|
||||
TransportUpdateRequest request) {
|
||||
ContainerBookingEntity current = required(id);
|
||||
String idempotencyKey, TransportUpdateRequest request) {
|
||||
String operationId = transportOperationId(id, idempotencyKey);
|
||||
String containerNo = trim(request.containerNo());
|
||||
String voyageNo = trim(request.voyageNo());
|
||||
String requestHash = sha256("TRANSPORT|" + id + '|' + request.version() + '|'
|
||||
+ Objects.toString(containerNo, "") + '|' + Objects.toString(voyageNo, "") + '|'
|
||||
+ request.reason().trim());
|
||||
ContainerTransportHistoryEntity replay = transportHistories == null ? null
|
||||
: transportHistories.selectOperation(id, operatorId, operationId);
|
||||
if (replay != null) {
|
||||
if (!requestHash.equals(replay.getRequestHash())) {
|
||||
throw conflict("Idempotency-Key已用于不同运输字段请求");
|
||||
}
|
||||
return required(id);
|
||||
}
|
||||
ContainerBookingEntity current = repository.findForUpdate(id);
|
||||
if (current == null || !"ACTIVE".equals(current.getRecordStatus())) {
|
||||
throw new BizException(ResultCode.NOT_FOUND, "货柜不存在");
|
||||
}
|
||||
requireVersion(current, request.version());
|
||||
if (status(current) == SmartLoadingStatus.COMPLETED) throw conflict("装柜完成后货柜全部只读,不能修改");
|
||||
String containerNo = trim(request.containerNo());
|
||||
replay = transportHistories == null ? null
|
||||
: transportHistories.selectOperation(id, operatorId, operationId);
|
||||
if (replay != null) {
|
||||
if (!requestHash.equals(replay.getRequestHash())) {
|
||||
throw conflict("Idempotency-Key已用于不同运输字段请求");
|
||||
}
|
||||
return current;
|
||||
}
|
||||
if (Objects.equals(current.getContainerNo(), containerNo)
|
||||
&& Objects.equals(current.getVoyageNo(), voyageNo)) {
|
||||
return current;
|
||||
}
|
||||
LoadingPlanEntity plan = smartLoadingRepository.planByBooking(id);
|
||||
if (containerNo == null && plan != null && loadingPickRepository.taskByPlan(plan.getId()) != null) {
|
||||
throw conflict("PDA拣货任务已创建,不能清空真实集装箱号");
|
||||
}
|
||||
if (repository.containerNoExists(containerNo, id)) throw conflict("集装箱号已存在");
|
||||
try {
|
||||
if (repository.updateTransport(id, containerNo, trim(request.voyageNo()), request.version(),
|
||||
if (repository.updateTransport(id, containerNo, voyageNo, request.version(),
|
||||
operatorId, operatorName) != 1) throw conflict("货柜状态或版本已变化,请刷新后重试");
|
||||
} catch (DuplicateKeyException ex) { throw conflict("集装箱号已存在"); }
|
||||
ContainerTransportHistoryEntity history = new ContainerTransportHistoryEntity();
|
||||
history.setContainerBookingId(id); history.setBeforeContainerNo(current.getContainerNo());
|
||||
history.setAfterContainerNo(containerNo); history.setBeforeVoyageNo(current.getVoyageNo());
|
||||
history.setAfterVoyageNo(trim(request.voyageNo())); history.setReason(request.reason().trim());
|
||||
history.setAfterVoyageNo(voyageNo); history.setOperationId(operationId);
|
||||
history.setRequestHash(requestHash); history.setReason(request.reason().trim());
|
||||
history.setOperatorId(operatorId); history.setOperatorName(operatorName);
|
||||
if (transportHistories != null) transportHistories.insert(history);
|
||||
try {
|
||||
if (transportHistories != null) transportHistories.insert(history);
|
||||
} catch (DuplicateKeyException exception) {
|
||||
ContainerTransportHistoryEntity concurrent = transportHistories.selectOperation(id, operatorId, operationId);
|
||||
if (concurrent == null || !requestHash.equals(concurrent.getRequestHash())) {
|
||||
throw conflict("Idempotency-Key已被并发用于不同运输字段请求");
|
||||
}
|
||||
}
|
||||
ContainerBookingEntity updated = required(id);
|
||||
loadingPickService.beginIfReady(updated.getId(), operatorId, operatorName);
|
||||
return updated;
|
||||
}
|
||||
|
||||
private static String transportOperationId(long id, String value) {
|
||||
String key = trim(value);
|
||||
if (key == null) throw new BizException(ResultCode.BAD_REQUEST, "Idempotency-Key不能为空");
|
||||
if (key.length() > 128) throw new BizException(ResultCode.BAD_REQUEST, "Idempotency-Key长度不能超过128");
|
||||
return sha256("CONTAINER_TRANSPORT|" + id + '|' + key);
|
||||
}
|
||||
|
||||
private static String sha256(String value) {
|
||||
try {
|
||||
return java.util.HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256")
|
||||
.digest(value.getBytes(StandardCharsets.UTF_8)));
|
||||
} catch (NoSuchAlgorithmException exception) {
|
||||
throw new IllegalStateException("SHA-256不可用", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private ContainerBookingEntity required(long id) {
|
||||
ContainerBookingEntity value = repository.find(id);
|
||||
if (value == null) throw new BizException(ResultCode.NOT_FOUND, "货柜不存在");
|
||||
|
||||
Reference in New Issue
Block a user