This commit is contained in:
2026-10-21 07:53:31 +08:00
parent af5cf1e3bd
commit f00d0e16cf
147 changed files with 1696 additions and 872 deletions
@@ -22,13 +22,13 @@ import static com.baiyue.lastleg.fleet.business.FleetPoolOrderModels.*;
public class FleetPoolOrderBridge {
@jakarta.annotation.Resource private FleetLooseboardDemandRepository demands;
private final FleetPoolOrderRepository repository;
private final FleetPoolOrderNumberGenerator numbers;
private final com.baiyue.lastleg.service.LastlegDocumentNumberGenerator numbers;
private final ObjectMapper json;
private final FleetPoolDeliveryEvidence deliveryEvidence;
private final com.baiyue.lastleg.appointmentplanning.AppointmentSourceLinker appointmentSources;
/** 注入同库商业委托仓储及履约协作服务。 */
public FleetPoolOrderBridge(FleetPoolOrderRepository repository,
FleetPoolOrderNumberGenerator numbers,ObjectMapper json,FleetPoolDeliveryEvidence deliveryEvidence,
com.baiyue.lastleg.service.LastlegDocumentNumberGenerator numbers,ObjectMapper json,FleetPoolDeliveryEvidence deliveryEvidence,
com.baiyue.lastleg.appointmentplanning.AppointmentSourceLinker appointmentSources) {
this.repository=repository;
this.numbers=numbers;this.json=json;this.deliveryEvidence=deliveryEvidence;
@@ -53,7 +53,7 @@ public class FleetPoolOrderBridge {
var now=LocalDateTime.now();var row=new FleetPoolOrderLink();
row.setId(IdWorker.getId());row.setBookingId(in.bookingId());row.setBookingNo(in.bookingNo());
row.setCustomerId(in.customerId());row.setBusinessType(in.businessType());row.setBusinessOrderId(source==null?IdWorker.getId():source.getId());
row.setBusinessOrderNo(source==null?numbers.next("US_TRUCK".equals(in.businessType())?"TRUCK":"LOOSEBOARD",in.destinationLabel(),row.getBusinessOrderId()):source.getOrderNo());
row.setBusinessOrderNo(source==null?numbers.next("US_TRUCK".equals(in.businessType())?"TR":"LB"):source.getOrderNo());
row.setWarehouseId(in.warehouseId());row.setDestinationKey(in.destinationKey());
row.setDeliveryMode(in.deliveryMode());row.setDeliveryInstructions(in.deliveryInstructions());
row.setFulfillmentStage("WAITING_INBOUND");row.setStageOccurredAt(now);row.setRecordVersion(0L);
@@ -1,23 +0,0 @@
package com.baiyue.lastleg.fleet.business;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.regex.Pattern;
/** Commercial pool orders own LB/TR numbering; no tail-leg order sequence is consumed. */
@Component
public class FleetPoolOrderNumberGenerator {
private static final Pattern LOCATION=Pattern.compile("LAX|ONT|NYC|JFK|LGB|EWR|SFO|SEA|DFW|ORD|ATL|MIA|PHX|LAS|YYZ|YVR|AMS|LHR|FRA|CDG|HKG|NRT|SYD");
public String next(String businessType,String destination,long uniqueId){
String prefix=switch(businessType){case "TRUCK"->"TR";case "LOOSEBOARD"->"LB";default->throw new IllegalArgumentException("Unsupported pool business type");};
return prefix+"-"+location(destination)+"-"+LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE)
+"-"+String.format(Locale.ROOT,"%06d",Math.floorMod(uniqueId,1_000_000));
}
private static String location(String value){
String code=value==null?"":value.trim().toUpperCase(Locale.ROOT);var match=LOCATION.matcher(code);
if(Pattern.matches(".*("+LOCATION.pattern()+").*",code)&&match.find())return match.group();
return code.matches("[A-Z0-9]{2,8}")?code:"WH";
}
}
@@ -22,6 +22,8 @@ import java.util.*;
@Service
public class VehicleCareService {
@jakarta.annotation.Resource private com.baiyue.lastleg.service.LastlegDocumentNumberGenerator documentNumbers;
private final VehicleCareMapper records;
private final VehicleCareHistoryMapper histories;
private final VehicleRepository vehicles;
@@ -41,6 +43,7 @@ public class VehicleCareService {
return new PageResult<>(page.getRecords().stream().map(this::response).toList(), page.getCurrent(), page.getSize(), page.getTotal(), page.getPages());
}
public VehicleCareModels.Response detail(long vehicleId, String number) { return response(required(vehicleId, number, false)); }
/** 建立车辆运维记录,首次建单分配 VR 编号。 */
@Transactional
public VehicleCareModels.Response create(long vehicleId, String operator, VehicleCareModels.Create request) {
// Serialize creation per vehicle; no empty-range locks or duplicate creation after a lost response.
@@ -53,7 +56,7 @@ public class VehicleCareService {
}
validate(request.details());
var row = new VehicleCareEntity(); row.setId(IdWorker.getId());
row.setRecordNo("VR-" + UUID.randomUUID().toString().replace("-", "").substring(0, 16).toUpperCase(Locale.ROOT));
row.setRecordNo(documentNumbers.next("VR"));
row.setVehicleId(vehicleId); row.setVehiclePlate(vehicle.plateNumber()); row.setRecordType(request.details().recordType());
row.setOccurredAt(utc(request.details().occurredAt())); row.setDetailsJson(write(request.details())); row.setFilesJson("[]");
row.setIdempotencyKey(request.idempotencyKey()); row.setRequestHash(fingerprint); row.setRecordVersion(0L);
@@ -26,6 +26,8 @@ import java.util.Objects;
@Service
public class FleetBookingService {
@jakarta.annotation.Resource private com.baiyue.lastleg.service.LastlegDocumentNumberGenerator documentNumbers;
private final FleetTripMapper trips;
private final FleetBookingMapper bookings;
private final FleetBookingReservations reservations;
@@ -56,13 +58,14 @@ public class FleetBookingService {
var context=quoteContext.resolve(trip(tripNo,false),query.customerId(),query.destinationKey(),query.currency(),query.priceBasis());
return quotes.candidates(new FleetQuoteCandidateModels.Query(context.customerId(),context.originCity(),context.destinationCode(),context.currency(),context.effectiveOn(),context.priceBasis(),query.page(),query.size()));
}
/** 在车次内登记预订,首次请求分配 FB 业务号。 */
@Transactional
public FleetBookingModels.Result create(String tripNo,String operator,FleetBookingModels.Create request){
var trip=trip(tripNo,true);String hash=hash(request);
var existing=bookings.created(trip.getId(),operator,request.idempotencyKey());
if(existing!=null){if(!hash.equals(existing.getRequestHash()))throw conflict("同一登记请求不能变更客户或货物内容");return result(trip,existing,true);}
requireTrip(trip,request.tripRecordVersion());var customer=customers.required(request.customerId());
var row=new FleetBookingEntity();row.setId(IdWorker.getId());row.setBookingNo("FB-"+row.getId());row.setTripId(trip.getId());row.setTripNo(tripNo);
var row=new FleetBookingEntity();row.setId(IdWorker.getId());row.setBookingNo(documentNumbers.next("FB"));row.setTripId(trip.getId());row.setTripNo(tripNo);
row.setCustomerId(customer.id());row.setCustomerCode(customer.code());row.setCustomerName(customer.name());row.setStatus("DRAFT");
apply(trip,row,request.cargo());applyQuote(trip,row,request.quoteRef());row.setIdempotencyKey(request.idempotencyKey());row.setRequestHash(hash);row.setRecordVersion(0L);
row.setCreatedBy(operator);row.setCreatedAt(now());row.setUpdatedBy(operator);row.setUpdatedAt(row.getCreatedAt());
@@ -25,6 +25,8 @@ import java.util.*;
@Service
public class FleetCapacityOfferService {
@jakarta.annotation.Resource private com.baiyue.lastleg.service.LastlegDocumentNumberGenerator documentNumbers;
@jakarta.annotation.Resource private com.baiyue.lastleg.fleet.business.FleetLooseboardDemandService demands;
public static final String LIST="platform:fleet:capacity-offer:list", MANAGE="platform:fleet:capacity-offer:manage";
private final FleetCapacityOfferMapper offers;
@@ -131,6 +133,7 @@ public class FleetCapacityOfferService {
var row=offer(number,false);requireVisible(row);customers.required(query.customerId());
return bookingService.quoteCandidates(row.getTripNo(),query);
}
/** 发布车队运力并分配 FO 日流水,重放复用原发布记录。 */
@Transactional
public FleetCapacityOfferModels.OfferResult create(FleetCapacityOfferModels.Write request){
requireManage();var trip=trip(request.tripNo(),true);
@@ -139,7 +142,7 @@ public class FleetCapacityOfferService {
tripVersion(trip,request.tripRecordVersion());
if(offers.byTrip(trip.getId())!=null)throw conflict("该车次已有发布,请从既有发布修改");
validateCutoff(trip,request.cutoffAt());
var row=new FleetCapacityOfferEntity();row.setId(IdWorker.getId());row.setOfferNo("FO-"+row.getId());row.setTripId(trip.getId());row.setTripNo(trip.getTripNo());
var row=new FleetCapacityOfferEntity();row.setId(IdWorker.getId());row.setOfferNo(documentNumbers.next("FO"));row.setTripId(trip.getId());row.setTripNo(trip.getTripNo());
row.setStatus("DRAFT");row.setRecordVersion(0L);row.setCreatedBy(operator());row.setCreatedAt(now());apply(row,request);
if(offers.insert(row)!=1)throw conflict("发布创建失败");
var result=new FleetCapacityOfferModels.OfferResult(project(row,trip),false);saveCommand(trip,"CREATE",trip.getTripNo(),request.idempotencyKey(),request,result);return result;
@@ -27,6 +27,8 @@ import java.util.*;
@Service
public class FleetTripService {
@jakarta.annotation.Resource private com.baiyue.lastleg.service.LastlegDocumentNumberGenerator documentNumbers;
@org.springframework.beans.factory.annotation.Autowired(required=false) private org.springframework.context.ApplicationEventPublisher transportEvents;
@jakarta.annotation.Resource private com.baiyue.lastleg.fleet.trip.work.AppointmentWorkTripChanges workChanges;
@jakarta.annotation.Resource private com.baiyue.lastleg.appointmentwork.AppointmentWorkDispatchPort appointmentAllocations;
@@ -114,7 +116,7 @@ public class FleetTripService {
var warehouse = warehouses.required(request.warehouseId(), "OVERSEAS");
var trip = new FleetTripEntity();
trip.setId(IdWorker.getId());
trip.setTripNo("TR-" + new java.math.BigInteger(UUID.randomUUID().toString().replace("-", ""), 16).toString(36).toUpperCase(Locale.ROOT));
trip.setTripNo(documentNumbers.next("CT"));
trip.setIdempotencyKey(request.idempotencyKey()); trip.setRequestHash(request.commandHash());
trip.setStatus("PLANNED"); trip.setTripPurpose("DELIVERY"); trip.setWarehouseId(request.warehouseId());
trip.setWarehouseCode(warehouse.code()); trip.setWarehouseName(warehouse.name());
@@ -390,7 +392,7 @@ public class FleetTripService {
var warehouse = warehouses.required(request.warehouseId(), "OVERSEAS");
var trip = new FleetTripEntity();
trip.setId(IdWorker.getId());
trip.setTripNo("TR-" + UUID.randomUUID().toString().replace("-", "").substring(0, 16).toUpperCase(Locale.ROOT));
trip.setTripNo(documentNumbers.next("CT"));
trip.setIdempotencyKey(request.idempotencyKey()); trip.setRequestHash(hash);
trip.setTripPurpose(purpose); trip.setStatus("PLANNED"); trip.setWarehouseId(Long.valueOf(warehouse.id()));
trip.setWarehouseCode(warehouse.code()); trip.setWarehouseName(warehouse.name());