This commit is contained in:
2026-09-15 14:18:18 +08:00
parent 6a2075fe4b
commit a474ca4d93
35 changed files with 751 additions and 228 deletions
@@ -122,20 +122,6 @@ public class PlatformTerminalDrayageController {
containerBookingId, user.userId(), user.username(), key, request), user.permissions()));
}
@PostMapping("/{containerBookingId}/pickup")
@PreAuthorize("hasAuthority('platform:firstleg:terminal-drayage:pickup:confirm')")
@Operation(summary = "确认目的港实际提柜")
@BusinessAudit(event = "TERMINAL_DRAYAGE_RECORD_PICKUP", resourceType = "CONTAINER",
resourceId = "#containerBookingId", description = "确认目的港实际提柜")
public ApiResponse<View> recordPickup(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable @Positive long containerBookingId,
@RequestHeader("Idempotency-Key") String key,
@Valid @RequestBody ActualTimeRequest request) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.authorize(service.recordPickup(
containerBookingId, user.userId(), user.username(), key, request), user.permissions()));
}
@PostMapping("/{containerBookingId}/empty-return")
@PreAuthorize("hasAuthority('platform:firstleg:terminal-drayage:empty-return:confirm')")
@Operation(summary = "确认实际还空柜")
@@ -17,7 +17,6 @@ public class TerminalDrayageEntity {
private Long fleetSupplierId;
private String fleetSupplierName;
private LocalDateTime pickupAppointmentAt;
private LocalDateTime actualPickupAt;
private LocalDateTime estimatedEmptyReturnAt;
private LocalDateTime actualEmptyReturnAt;
private Integer version;
@@ -69,7 +69,7 @@ public interface TerminalDrayageMapper extends BaseMapper<TerminalDrayageEntity>
@Update("""
UPDATE fl_terminal_drayage SET fleet_supplier_id=#{value.fleetSupplierId},
fleet_supplier_name=#{value.fleetSupplierName},pickup_appointment_at=#{value.pickupAppointmentAt},
actual_pickup_at=#{value.actualPickupAt},estimated_empty_return_at=#{value.estimatedEmptyReturnAt},
estimated_empty_return_at=#{value.estimatedEmptyReturnAt},
actual_empty_return_at=#{value.actualEmptyReturnAt},updated_by=#{operatorId},
updated_by_name=#{operatorName},updated_at=CURRENT_TIMESTAMP(3),version=version+1
WHERE container_booking_id=#{value.containerBookingId} AND version=#{expectedVersion}
@@ -135,35 +135,6 @@ public class TerminalDrayageService {
request.reason(), operatorId, operatorName);
}
@Transactional
public View recordPickup(long containerBookingId, long operatorId, String operatorName, String key,
ActualTimeRequest request) {
String action = "RECORD_PICKUP";
WorkContext context = requiredLockedContext(containerBookingId);
View replay = replay(context, action, key, request);
if (replay != null) return replay;
requireAtLeast(context, CarrierScheduleNode.PICKUP_READY, "货柜尚未清关放行,不允许确认实际提柜");
TerminalDrayageEntity value = requiredAggregate(containerBookingId);
requireVersion(value, request.expectedVersion());
if (value.getFleetSupplierId() == null || value.getPickupAppointmentAt() == null) {
throw conflict("选择码头车队并预约提柜后才能确认实际提柜");
}
if (actualPickupAt(context, value) != null) throw conflict("实际提柜时间已确认");
if (context.facts() != null && context.facts().latestActualAt() != null
&& request.actualAt().isBefore(context.facts().latestActualAt())) {
throw badRequest("实际提柜时间不能早于当前实际船节点时间");
}
advanceNode(context, CarrierScheduleNode.PICKED_UP, request.actualAt(), action, key,
request.remark(), operatorId, operatorName);
int fromVersion = version(value);
String fromStatus = status(context, value);
value.setActualPickupAt(request.actualAt());
applyAuthoritativeDeadline(context, value);
persist(value, fromVersion, operatorId, operatorName);
return finish(requiredContext(containerBookingId), value, action, key, request, fromStatus,
fromVersion, request.remark(), operatorId, operatorName);
}
@Transactional
public View recordEmptyReturn(long containerBookingId, long operatorId, String operatorName, String key,
ActualTimeRequest request) {
@@ -297,9 +268,6 @@ public class TerminalDrayageService {
result.add("SELECT_FLEET");
if (value != null && value.getFleetSupplierId() != null) result.add("SCHEDULE_PICKUP");
}
if (current != null && current.sequence() >= CarrierScheduleNode.PICKUP_READY.sequence()
&& value != null && value.getFleetSupplierId() != null && value.getPickupAppointmentAt() != null
&& actualPickupAt(context, value) == null) result.add("RECORD_PICKUP");
if (context.base().unloadingCompletedAt() != null
&& actualEmptyReturnAt(context, value) == null) result.add("RECORD_EMPTY_RETURN");
return List.copyOf(result);
@@ -333,8 +301,8 @@ public class TerminalDrayageService {
}
private LocalDateTime actualPickupAt(WorkContext context, TerminalDrayageEntity value) {
return value != null && value.getActualPickupAt() != null ? value.getActualPickupAt()
: context.facts() == null ? null : context.facts().pickedUpAt();
if (context.facts() != null && context.facts().pickedUpAt() != null) return context.facts().pickedUpAt();
return context.base().unloadingCompletedAt();
}
private LocalDateTime actualEmptyReturnAt(WorkContext context, TerminalDrayageEntity value) {
@@ -400,7 +368,6 @@ public class TerminalDrayageService {
case "SELECT_FLEET" -> "platform:firstleg:terminal-drayage:fleet:edit";
case "SEND_PICKUP_EMAIL" -> "platform:firstleg:terminal-drayage:fleet:edit";
case "SCHEDULE_PICKUP" -> "platform:firstleg:terminal-drayage:pickup-appointment:edit";
case "RECORD_PICKUP" -> "platform:firstleg:terminal-drayage:pickup:confirm";
case "RECORD_EMPTY_RETURN" -> "platform:firstleg:terminal-drayage:empty-return:confirm";
default -> "";
};