feat(appointments): discover external booking candidates through fleet boundary
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
package com.baiyue.lastleg.appointmentplanning;
|
||||
|
||||
import com.baiyue.common.core.api.*;
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/platform/v1/last-leg/appointment-planning/external-candidates")
|
||||
public class AppointmentExternalCandidateController {
|
||||
private final AppointmentExternalCandidateService service;
|
||||
public AppointmentExternalCandidateController(AppointmentExternalCandidateService service){this.service=service;}
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAuthority('platform:appointment:list') and hasAuthority('platform:overseas-warehouse:last-mile-order:list')")
|
||||
public ApiResponse<PageResult<AppointmentExternalCandidateService.Row>> page(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@RequestParam(defaultValue="1") int page,@RequestParam(defaultValue="20") int size,
|
||||
@RequestParam(required=false) String warehouseId,@RequestParam(required=false) String destination,@RequestParam(required=false) String keyword) {
|
||||
AppointmentPlanningController.requirePlatform(user);
|
||||
return ApiResponse.success(service.page(page,size,warehouseId,destination,keyword));
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package com.baiyue.lastleg.appointmentplanning;
|
||||
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class AppointmentExternalCandidateService {
|
||||
public record Row(String customerName,String tripNo,AppointmentSourceService.Source source,
|
||||
AppointmentCoverageService.Coverage coverage) { }
|
||||
private final AppointmentExternalSourceGateway external;
|
||||
private final AppointmentWarehouseClient warehouses;
|
||||
private final AppointmentSourceService sources;
|
||||
private final AppointmentCoverageService coverage;
|
||||
private final String token;
|
||||
public AppointmentExternalCandidateService(AppointmentExternalSourceGateway external,AppointmentWarehouseClient warehouses,
|
||||
AppointmentSourceService sources,AppointmentCoverageService coverage,
|
||||
@Value("${baiyue.security.internal-service-token:}") String token) {
|
||||
this.external=external;this.warehouses=warehouses;this.sources=sources;this.coverage=coverage;this.token=token;
|
||||
}
|
||||
public PageResult<Row> page(int page,int size,String warehouseId,String destination,String keyword) {
|
||||
if(page<1 || size<1 || size>100 || keyword!=null && keyword.length()>128) throw bad("候选分页或关键词无效");
|
||||
warehouseId=clean(warehouseId);destination=clean(destination);keyword=clean(keyword);
|
||||
List<AppointmentWarehouseClient.Warehouse> catalog;
|
||||
try {
|
||||
var response=warehouses.catalog(token);
|
||||
if(response==null || !response.successful() || response.data()==null) throw new IllegalStateException();
|
||||
catalog=response.data();
|
||||
} catch(RuntimeException error) {throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE,"仓库目录暂不可用,无法核实外接货范围",error);}
|
||||
var us=catalog.stream().filter(w->w!=null && w.id()!=null && w.id()>0 && "US".equals(w.countryCode())).toList();
|
||||
var origins=us.stream().filter(w->Set.of("OVERSEAS","OVERSEAS_PARTNER").contains(Objects.toString(w.warehouseType(),""))).map(w->w.id().toString()).distinct().toList();
|
||||
var destinations=us.stream().filter(w->"FBA".equals(w.warehouseType()) && clean(w.warehouseCode())!=null).map(AppointmentWarehouseClient.Warehouse::warehouseCode).distinct().toList();
|
||||
if(warehouseId!=null && !origins.contains(warehouseId) || destination!=null && !destinations.contains(destination)) throw bad("请选择已启用的美国接仓点及FBA目的仓");
|
||||
if(origins.isEmpty() || destinations.isEmpty()) return new PageResult<>(List.of(),page,size,0,0);
|
||||
var found=external.candidates(new AppointmentExternalSourceGateway.CandidateQuery(page,size,origins,destinations,warehouseId,destination,keyword));
|
||||
if(found.records().isEmpty()) return new PageResult<>(List.of(),page,size,found.total(),found.pages());
|
||||
var refs=found.records().stream().map(r->new AppointmentCoverageService.SourceRef(r.customerId(),"BOOKING:"+r.bookingNo(),r.destinationKey())).toList();
|
||||
var resolved=sources.resolveBatch(refs).stream().collect(Collectors.toMap(r->r.customerId()+"\n"+r.sourceKey(),Function.identity()));
|
||||
var covered=coverage.query(refs).stream().collect(Collectors.toMap(r->r.customerId()+"\n"+r.sourceKey(),Function.identity()));
|
||||
var rows=found.records().stream().map(r->{
|
||||
String key=r.customerId()+"\nBOOKING:"+r.bookingNo();
|
||||
return new Row(r.customerName(),r.tripNo(),Objects.requireNonNull(resolved.get(key)),Objects.requireNonNull(covered.get(key)));
|
||||
}).toList();
|
||||
return new PageResult<>(rows,page,size,found.total(),found.pages());
|
||||
}
|
||||
private static String clean(String value){return value==null || value.isBlank()?null:value.trim();}
|
||||
private static ResponseStatusException bad(String message){return new ResponseStatusException(HttpStatus.BAD_REQUEST,message);}
|
||||
}
|
||||
+4
@@ -4,6 +4,10 @@ import java.util.List;
|
||||
|
||||
/** Implemented by the external-booking owner; planning must not query Fleet's persistence directly. */
|
||||
public interface AppointmentExternalSourceGateway {
|
||||
record Candidate(String customerId, String customerName, String bookingNo, String tripNo, String destinationKey) { }
|
||||
record CandidateQuery(int page, int size, List<String> originIds, List<String> destinationCodes,
|
||||
String warehouseId, String destination, String keyword) { }
|
||||
com.baiyue.common.core.api.PageResult<Candidate> candidates(CandidateQuery query);
|
||||
record SourceRef(String customerId, String bookingNo) { }
|
||||
record OrderRef(String customerId, Long orderId) { }
|
||||
record Source(String customerId, String bookingNo, String tripNo, String bookingStatus,
|
||||
|
||||
Reference in New Issue
Block a user