fix(lastleg): expose dispatch reviews and filter available capacity offers
This commit is contained in:
+33
-5
@@ -36,22 +36,50 @@ class FleetCapacityOfferServiceTest {
|
||||
@AfterEach void clear(){SecurityContextHolder.clearContext();}
|
||||
@Test void ordinaryPageOnlyCalculatesRequestedDatabasePageAndPreservesAccurateTotal(){
|
||||
var rows=List.of(offer(1),offer(2));var page=new Page<FleetCapacityOfferEntity>(3,20,2000);page.setRecords(rows);
|
||||
when(offers.pageOffers(any(),any(),eq(true))).thenReturn(page);
|
||||
when(offers.pageOffers(any(),any(),eq(true),any())).thenReturn(page);
|
||||
for(var row:rows){var trip=trip(row.getTripNo());when(trips.findByNo(row.getTripNo())).thenReturn(trip);
|
||||
when(reservations.stops(trip)).thenReturn(List.of(new FleetTripModels.Stop("起点",null,null),new FleetTripModels.Stop("ONT8",null,"FBA:ONT8")));
|
||||
when(execution.capacityPlan(row.getTripNo())).thenReturn(new FleetExecutionModels.Planning(row.getTripNo(),1L,List.of(),"hash",null,false,"计划待核对",List.of()));}
|
||||
var query=new FleetCapacityOfferModels.Query(3,20,"FO",null,null,null,null,null,null,null);
|
||||
var query=new FleetCapacityOfferModels.Query(3,20,"FO",null,null,null,null,null,null,null,null);
|
||||
var result=service.page(query);
|
||||
assertThat(result.total()).isEqualTo(2000);assertThat(result.page()).isEqualTo(3);assertThat(result.records()).hasSize(2);
|
||||
var captor=ArgumentCaptor.forClass(Page.class);verify(offers).pageOffers(captor.capture(),same(query),eq(true));
|
||||
assertThat(result.records()).allMatch(FleetCapacityOfferModels.Offer::canCreateInquiry).allMatch(value -> !value.canConfirm());
|
||||
var captor=ArgumentCaptor.forClass(Page.class);verify(offers).pageOffers(captor.capture(),same(query),eq(true),eq(LocalDateTime.ofInstant(now,ZoneOffset.UTC)));
|
||||
assertThat(captor.getValue().getCurrent()).isEqualTo(3);assertThat(captor.getValue().getSize()).isEqualTo(20);
|
||||
verify(execution,times(2)).capacityPlan(anyString());verify(trips,times(2)).findByNo(anyString());verifyNoMoreInteractions(execution,trips);
|
||||
}
|
||||
@Test void broadDynamicAvailabilityFilterRejectsBeforeAnyPerTripCapacityQuery(){
|
||||
when(offers.pageOffers(any(),any(),eq(true))).thenReturn(new Page<>(1,100,101));
|
||||
assertThatThrownBy(()->service.page(new FleetCapacityOfferModels.Query(1,20,null,null,null,null,null,null,true,null))).hasMessageContaining("100");
|
||||
when(offers.pageOffers(any(),any(),eq(true),any())).thenReturn(new Page<>(1,100,101));
|
||||
assertThatThrownBy(()->service.page(new FleetCapacityOfferModels.Query(1,20,null,null,null,null,null,null,true,null,null))).hasMessageContaining("100");
|
||||
verifyNoInteractions(execution,trips);
|
||||
}
|
||||
@Test void historyScopeRequiresManagementAndDefaultScopeIsAvailable(){
|
||||
var query=new FleetCapacityOfferModels.Query(1,20,null,null,null,null,null,null,null,null,null);
|
||||
assertThat(query.scope()).isEqualTo("AVAILABLE");
|
||||
var history=new FleetCapacityOfferModels.Query(1,20,null,null,null,null,null,null,null,null,"ALL");
|
||||
assertThatThrownBy(()->service.page(history)).isInstanceOf(org.springframework.web.server.ResponseStatusException.class);
|
||||
verifyNoInteractions(offers);
|
||||
var user=new AuthenticatedUser(101L,0L,0L,101L,"admin","ADMIN","WEB",Set.of(FleetCapacityOfferService.MANAGE));
|
||||
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user,"",List.of(new SimpleGrantedAuthority(FleetCapacityOfferService.MANAGE))));
|
||||
when(offers.pageOffers(any(),same(query),eq(true),any())).thenReturn(new Page<>(1,20,0));
|
||||
service.page(query);
|
||||
verify(offers).pageOffers(any(),same(query),eq(true),eq(LocalDateTime.ofInstant(now,ZoneOffset.UTC)));
|
||||
when(offers.pageOffers(any(),same(history),eq(false),any())).thenReturn(new Page<>(1,20,0));
|
||||
assertThat(service.page(history).records()).isEmpty();
|
||||
verify(offers).pageOffers(any(),same(history),eq(false),eq(LocalDateTime.ofInstant(now,ZoneOffset.UTC)));
|
||||
}
|
||||
@Test void availableSqlFiltersBeforePaginationWithoutRequiringKnownCapacity() throws Exception {
|
||||
var method=Arrays.stream(FleetCapacityOfferMapper.class.getMethods()).filter(m->m.getName().equals("pageOffers")).findFirst().orElseThrow();
|
||||
String script=String.join("\n",method.getAnnotation(org.apache.ibatis.annotations.Select.class).value());
|
||||
var sql=new org.apache.ibatis.scripting.xmltags.XMLLanguageDriver().createSqlSource(new org.apache.ibatis.session.Configuration(),script,Map.class);
|
||||
var query=new FleetCapacityOfferModels.Query(1,20,null,null,null,null,null,null,null,null,null);
|
||||
var parameters=new HashMap<String,Object>();parameters.put("q",query);parameters.put("availableOnly",true);parameters.put("now",LocalDateTime.ofInstant(now,ZoneOffset.UTC));
|
||||
String active=sql.getBoundSql(parameters).getSql().replaceAll("\\s+"," ");
|
||||
assertThat(active).contains("o.status='OPEN'", "t.status='PLANNED'", "o.cutoff_at > ?", "o.cutoff_at <= t.planned_departure_at");
|
||||
assertThat(active).doesNotContain("planned_load_json", "remaining", "NOW()");
|
||||
parameters.put("availableOnly",false);
|
||||
assertThat(sql.getBoundSql(parameters).getSql()).doesNotContain("o.cutoff_at", "t.status='PLANNED'");
|
||||
}
|
||||
@Test void cutoffUsesStrictServerInstantAndRechecksChangedDeparture(){
|
||||
var trip=trip("FT-A");var offer=offer(1);offer.setStatus("OPEN");
|
||||
assertThat(FleetCapacityOfferService.openBlock(offer,trip,now)).isNull();
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.baiyue.lastleg.fleet.trip;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.util.List;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
|
||||
class FleetDispatchWorkflowQueryTest {
|
||||
@Test
|
||||
void batchesReviewsAndKeepsCustomerDispatchDecisionsIndependent() {
|
||||
var bindings = mock(FleetTripDispatchMapper.class);
|
||||
var trips = mock(FleetTripMapper.class);
|
||||
var first = new FleetTripDispatch(); first.setDispatchNo("PC-A"); first.setTripId(1L); first.setTripNo("TR-A");
|
||||
first.setReviewJson("{\"status\":\"REJECTED\",\"reason\":\"货物需重新核对\"}");
|
||||
var second = new FleetTripDispatch(); second.setDispatchNo("PC-B"); second.setTripId(1L); second.setTripNo("TR-A");
|
||||
var trip = new FleetTripEntity(); trip.setId(1L);
|
||||
trip.setLoadingReviewJson("{\"status\":\"APPROVED\",\"reason\":\"审核通过\"}");
|
||||
when(bindings.findByDispatchNos(List.of("PC-A", "PC-B", "PC-C"))).thenReturn(List.of(first, second));
|
||||
when(trips.selectBatchIds(any())).thenReturn(List.of(trip));
|
||||
var query = new FleetDispatchWorkflowQuery(bindings, trips, new ObjectMapper().findAndRegisterModules());
|
||||
var result = query.read(List.of("PC-A", "PC-B", "PC-C"));
|
||||
assertEquals("REJECTED", result.get("PC-A").dispatchReview().status());
|
||||
assertEquals("货物需重新核对", result.get("PC-A").dispatchReview().reason());
|
||||
assertEquals("PENDING", result.get("PC-B").dispatchReview().status());
|
||||
assertEquals("APPROVED", result.get("PC-B").loadingReview().status());
|
||||
assertFalse(result.containsKey("PC-C"));
|
||||
verify(trips).selectBatchIds(List.of(1L));
|
||||
verifyNoMoreInteractions(trips);
|
||||
assertTrue(query.read(List.of()).isEmpty());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user