feat(fleet): enforce reviewed trips and original fee coverage for dispatch

This commit is contained in:
2026-10-03 04:18:52 +08:00
parent c50e9f97d1
commit 5e683da3b9
162 changed files with 9642 additions and 218 deletions
@@ -0,0 +1,66 @@
package com.baiyue.lastleg.transfer.fleet;
import com.baiyue.lastleg.fleet.trip.FleetBookingIncomingGateway;
import com.baiyue.lastleg.fleet.trip.FleetDispatchGateway;
import com.baiyue.lastleg.transfer.service.WarehouseTransferService;
import com.baiyue.lastleg.transfer.dto.WarehouseTransferRequests;
import com.baiyue.lastleg.transfer.dto.WarehouseTransferResponses;
import com.baiyue.lastleg.transfer.mapper.WarehouseTransferOrderMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;
@Component
public class FleetIncomingAdapter implements FleetBookingIncomingGateway {
private final WarehouseTransferService service;
private final WarehouseTransferOrderMapper orders;
private final ObjectMapper json;
public FleetIncomingAdapter(WarehouseTransferService service,WarehouseTransferOrderMapper orders,ObjectMapper json){this.service=service;this.orders=orders;this.json=json;}
@Override @Transactional(propagation=Propagation.MANDATORY)
public Evidence create(String operator,Incoming input){
boolean fba=input.cargo().destinationKey().startsWith("FBA:");
String code=fba?input.cargo().destinationKey().substring(4):null;
String key=FleetDispatchGateway.destinationKey(fba?"FBA":"PRIVATE",code,code,input.privateAddress(),input.postalCode());
if(!Objects.equals(key,input.cargo().destinationKey()))throw conflict("收货目的地与已确认外接卸货站不一致");
var request=new WarehouseTransferRequests.Create(null,List.of(),input.warehouseId(),code,
fba?"FBA":"PRIVATE",null,code,code,input.privateAddress(),input.postalCode(),input.recipientName(),input.recipientPhone(),
input.deliveryInstructions()==null||input.deliveryInstructions().isBlank()?"客户自送至"+input.warehouseName():input.deliveryInstructions(),
input.cargo().contactName(),input.cargo().contactPhone(),input.cargo().packageCount(),input.cargo().volumeCbm(),"CUSTOMER_DELIVERY",input.estimatedArrivalAt(),null,
null,null,null,"外接预订 "+input.bookingNo(),false,null,null,null,null,null,"FLEET:"+input.bookingNo()+":INCOMING",
List.of(new WarehouseTransferRequests.Item(null,input.cargo().cargoName(),input.cargo().packageCount(),null)),null);
var created=service.createAndSubmit(input.customerId(),operator,request);
var approved=service.review(input.customerId(),created.transferNo(),operator,new WarehouseTransferRequests.Review("APPROVE","外接承运已确认,按实际来货收货"));
return evidence(approved);
}
@Override @Transactional(propagation=Propagation.MANDATORY)
public Evidence inspect(String customerId,String incomingNo,boolean lock){
var row=lock?orders.lockNo(customerId,incomingNo):orders.findNo(customerId,incomingNo);
if(row==null)throw new ResponseStatusException(HttpStatus.NOT_FOUND,"当前客户无此外接来货单");
return evidence(service.detail(customerId,incomingNo));
}
@Override @Transactional(propagation=Propagation.MANDATORY)
public Evidence confirm(String customerId,String incomingNo,String operator,String key){
var row=orders.lockNo(customerId,incomingNo);
if(row==null)throw new ResponseStatusException(HttpStatus.NOT_FOUND,"外接来货单不存在");
if("READY_FOR_DISPATCH".equals(row.getStatus()))return evidence(service.detail(customerId,incomingNo));
return evidence(service.confirmMeasurement(customerId,incomingNo,operator,new WarehouseTransferRequests.CustomerConfirm(key,"运营代委托方确认外接实测货量")));
}
private Evidence evidence(WarehouseTransferResponses.Detail value){
var pallets=value.pallets().stream().filter(row->"COMPLETED".equals(row.status())).map(row->new Pallet(
row.warehousePalletId()==null?null:row.warehousePalletId().toString(),row.palletNo(),row.cartonCount(),row.grossWeightKg(),row.volumeCbm()))
.sorted(Comparator.comparing(row->Objects.requireNonNullElse(row.warehousePalletId(),""))).toList();
String destination=FleetDispatchGateway.destinationKey(value.destinationType(),value.destinationWarehouseCode(),value.fbaWarehouseCode(),value.privateDestinationAddress(),value.privateDestinationPostalCode());
int packages=pallets.stream().mapToInt(Pallet::packages).sum();
try{
String hash=HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(json.writeValueAsString(Arrays.asList(value.transferNo(),value.inboundCode(),value.operatingWarehouseId(),destination,pallets)).getBytes(StandardCharsets.UTF_8)));
return new Evidence(value.transferNo(),value.inboundCode(),value.operatingWarehouseId(),destination,value.status(),hash,pallets,packages);
}catch(Exception error){throw new IllegalStateException(error);}
}
private static ResponseStatusException conflict(String reason){return new ResponseStatusException(HttpStatus.CONFLICT,reason);}
}
@@ -953,7 +953,7 @@ public class WarehouseTransferService {
String operationKey = "WAREHOUSE_TRANSFER_STOCK_IN:" + transfer.getTransferNo() + ":"
+ pallet.getSequenceNo() + ":" + item.getId();
InventoryCommandResponse result = inventory.stockIn(customerId, operatorId,
new InventoryCommandRequests.StockIn(customerId, "客户-" + customerId,
new InventoryCommandRequests.StockIn(customerId, "—",
transfer.getOperatingWarehouseId(), warehouse.warehouseCode(), warehouse.warehouseName(),
null, trim(transfer.getFbaWarehouseCode()), trim(transfer.getFbaWarehouseCode()),
transfer.getInboundOrderId(), transfer.getTransferNo(), transfer.getTransferNo(),