fix(firstleg): make sailing copy idempotent
This commit is contained in:
+10
@@ -78,6 +78,16 @@ public class SailingCalendarRepository {
|
||||
sailing.getDeparturePortCode(), sailing.getDestinationPortCode(), sailing.getWarehouseCutoffAt());
|
||||
}
|
||||
public SailingEntity sailing(long id) { return sailings.selectById(id); }
|
||||
public SailingEntity sailingByBusinessKey(long warehouseId, String channelCode, String departurePortCode,
|
||||
String destinationPortCode, LocalDateTime etdAt, String voyageNo) {
|
||||
return sailings.selectOne(new LambdaQueryWrapper<SailingEntity>()
|
||||
.eq(SailingEntity::getWarehouseId, warehouseId)
|
||||
.eq(SailingEntity::getChannelCodeSnapshot, channelCode)
|
||||
.eq(SailingEntity::getDeparturePortCode, departurePortCode)
|
||||
.eq(SailingEntity::getDestinationPortCode, destinationPortCode)
|
||||
.eq(SailingEntity::getEtdAt, etdAt)
|
||||
.eq(SailingEntity::getVoyageNo, voyageNo));
|
||||
}
|
||||
public SailingEntity sailingByGeneratedKey(String key) {
|
||||
return sailings.selectOne(new LambdaQueryWrapper<SailingEntity>().eq(SailingEntity::getGeneratedKey, key));
|
||||
}
|
||||
|
||||
+28
-2
@@ -299,8 +299,23 @@ public class SailingCalendarService {
|
||||
}
|
||||
@Transactional
|
||||
public SailingDetail copy(long id, long operatorId, String operatorName, CopyRequest request) {
|
||||
SailingEntity source = requiredSailing(id); if (request.etdAt().isBefore(LocalDateTime.now(ZONE))) throw bad("复制船期不能早于当前时间");
|
||||
SailingEntity value = new SailingEntity(); value.setId(IdWorker.getId()); value.setSailingNo("SL-" + value.getId()); value.setTemplateId(source.getTemplateId()); value.setChannelId(source.getChannelId()); value.setChannelCodeSnapshot(source.getChannelCodeSnapshot()); value.setChannelNameSnapshot(source.getChannelNameSnapshot()); value.setWarehouseId(source.getWarehouseId()); value.setWarehouseNameSnapshot(source.getWarehouseNameSnapshot()); value.setDeparturePortCode(source.getDeparturePortCode()); value.setDestinationPortCode(source.getDestinationPortCode()); value.setVesselName(source.getVesselName()); value.setVoyageNo(source.getVoyageNo()); value.setEtdAt(request.etdAt()); value.setWarehouseCutoffAt(request.cutoffAt()); value.setEtaAt(source.getEtaAt()); value.setStatus("OPEN"); value.setSourceType("COPY"); value.setPlannedCapacityCbm(source.getPlannedCapacityCbm()); value.setVersion(0); value.setCreatedBy(operatorId); value.setCreatedByName(operatorName); value.setUpdatedBy(operatorId); value.setUpdatedByName(operatorName); now(value); repository.insert(value); recordHistory(value.getId(), "COPY", null, OPEN, "复制自" + source.getSailingNo(), operatorId, operatorName, value.getCreatedAt()); return detail(value.getId());
|
||||
SailingEntity source = requiredSailing(id);
|
||||
if (request.etdAt().isBefore(LocalDateTime.now(ZONE))) throw bad("复制船期不能早于当前时间");
|
||||
if (!request.cutoffAt().isBefore(request.etdAt())) throw bad("收货截止必须早于开船时间");
|
||||
String copyReason = "复制自" + source.getSailingNo();
|
||||
SailingEntity existing = repository.sailingByBusinessKey(source.getWarehouseId(), source.getChannelCodeSnapshot(),
|
||||
source.getDeparturePortCode(), source.getDestinationPortCode(), request.etdAt(), source.getVoyageNo());
|
||||
if (existing != null) return isCopyOf(existing, copyReason) ? detail(existing.getId())
|
||||
: throwCopyConflict(request.etdAt(), source.getVoyageNo());
|
||||
|
||||
SailingEntity value = new SailingEntity(); value.setId(IdWorker.getId()); value.setSailingNo("SL-" + value.getId()); value.setTemplateId(source.getTemplateId()); value.setChannelId(source.getChannelId()); value.setChannelCodeSnapshot(source.getChannelCodeSnapshot()); value.setChannelNameSnapshot(source.getChannelNameSnapshot()); value.setWarehouseId(source.getWarehouseId()); value.setWarehouseNameSnapshot(source.getWarehouseNameSnapshot()); value.setDeparturePortCode(source.getDeparturePortCode()); value.setDestinationPortCode(source.getDestinationPortCode()); value.setVesselName(source.getVesselName()); value.setVoyageNo(source.getVoyageNo()); value.setEtdAt(request.etdAt()); value.setWarehouseCutoffAt(request.cutoffAt()); value.setEtaAt(copyEta(source, request.etdAt())); value.setStatus("OPEN"); value.setSourceType("COPY"); value.setPlannedCapacityCbm(source.getPlannedCapacityCbm()); value.setVersion(0); value.setCreatedBy(operatorId); value.setCreatedByName(operatorName); value.setUpdatedBy(operatorId); value.setUpdatedByName(operatorName); now(value);
|
||||
try { repository.insert(value); } catch (DuplicateKeyException ex) {
|
||||
SailingEntity duplicate = repository.sailingByBusinessKey(source.getWarehouseId(), source.getChannelCodeSnapshot(),
|
||||
source.getDeparturePortCode(), source.getDestinationPortCode(), request.etdAt(), source.getVoyageNo());
|
||||
if (duplicate != null && isCopyOf(duplicate, copyReason)) return detail(duplicate.getId());
|
||||
throwCopyConflict(request.etdAt(), source.getVoyageNo());
|
||||
}
|
||||
recordHistory(value.getId(), "COPY", null, OPEN, copyReason, operatorId, operatorName, value.getCreatedAt()); return detail(value.getId());
|
||||
}
|
||||
public VolumeDetail volumeSummary(long id) {
|
||||
SailingEntity value = requiredSailing(id);
|
||||
@@ -459,6 +474,17 @@ public class SailingCalendarService {
|
||||
private SailingChannelEntity requiredChannel(long id) { SailingChannelEntity v = repository.channel(id); if (v == null) throw notFound("渠道不存在"); return v; }
|
||||
private SailingTemplateEntity requiredTemplate(long id) { SailingTemplateEntity v = repository.template(id); if (v == null) throw notFound("船期模板不存在"); return v; }
|
||||
private SailingEntity requiredSailing(long id) { SailingEntity v = repository.sailing(id); if (v == null) throw notFound("船期不存在"); return v; }
|
||||
private boolean isCopyOf(SailingEntity value, String reason) {
|
||||
return "COPY".equals(value.getSourceType()) && repository.histories(value.getId()).stream()
|
||||
.anyMatch(history -> "COPY".equals(history.getAction()) && Objects.equals(reason, history.getReason()));
|
||||
}
|
||||
private static LocalDateTime copyEta(SailingEntity source, LocalDateTime etdAt) {
|
||||
if (source.getEtdAt() == null || source.getEtaAt() == null) return null;
|
||||
return etdAt.plus(ChronoUnit.MILLIS.between(source.getEtdAt(), source.getEtaAt()), ChronoUnit.MILLIS);
|
||||
}
|
||||
private static SailingDetail throwCopyConflict(LocalDateTime etdAt, String voyageNo) {
|
||||
throw conflict("目标开船时间 " + etdAt + " 已存在航次 " + voyageNo + ",请勿重复复制");
|
||||
}
|
||||
private static LocalDateTime cutoff(SailingTemplateEntity t, LocalDateTime etd) { return etd.toLocalDate().minusDays(t.getCutoffDays()).atTime(t.getCutoffLocalTime()); }
|
||||
private static String voyage(SailingTemplateEntity t, LocalDate date) { return StringUtils.hasText(t.getVoyageRule()) ? t.getVoyageRule().replace("{date}", date.toString().replace("-", "")) : "V" + date.toString().replace("-", ""); }
|
||||
private static String key(SailingTemplateEntity t, LocalDateTime etd, String voyage) { return t.getId() + "|" + etd + "|" + voyage; }
|
||||
|
||||
Reference in New Issue
Block a user