优化
This commit is contained in:
-22
@@ -123,31 +123,9 @@ class LastlegOrderControllerTest {
|
||||
verify(service).containerProgressDetail("BOOKING-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void exportBuildsCsvBeforeReturningTheResponse() {
|
||||
byte[] csv = "csv".getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
when(service.exportCsv("200", query)).thenReturn(csv);
|
||||
|
||||
var response = controller.export(user(0L, "PLATFORM"), "200", query);
|
||||
|
||||
assertArrayEquals(csv, response.getBody());
|
||||
verify(service).exportCsv("200", query);
|
||||
}
|
||||
|
||||
@Test
|
||||
void platformOperatorCanExportAllCustomersWithoutFilters() {
|
||||
byte[] csv = "all customers".getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
LastlegOrderRequests.Query emptyQuery = new LastlegOrderRequests.Query(
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
when(service.exportCsv(null, emptyQuery)).thenReturn(csv);
|
||||
|
||||
var response = controller.export(user(0L, "PLATFORM"), null, emptyQuery);
|
||||
|
||||
assertEquals(Long.valueOf(1), emptyQuery.page());
|
||||
assertEquals(Long.valueOf(20), emptyQuery.size());
|
||||
assertArrayEquals(csv, response.getBody());
|
||||
verify(service).exportCsv(isNull(), eq(emptyQuery));
|
||||
}
|
||||
|
||||
@Test
|
||||
void iamSecurityEventsAreRejectedBeforeLoadingCompletedConsumer() {
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
package com.baiyue.lastleg.export;
|
||||
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.common.core.export.ExportContracts.ExportBatchRequest;
|
||||
import com.baiyue.lastleg.dto.LastlegOrderRequests;
|
||||
import com.baiyue.lastleg.service.LastlegOrderService;
|
||||
import com.baiyue.lastleg.inventory.query.dto.InventoryQueryRequests;
|
||||
import com.baiyue.lastleg.inventory.query.service.InventoryQueryService;
|
||||
import com.baiyue.lastleg.masterdata.exchange.dto.MonthlyExchangeRateDtos;
|
||||
import com.baiyue.lastleg.masterdata.exchange.service.MonthlyExchangeRateService;
|
||||
import com.baiyue.lastleg.fleet.reporting.TripOperationsModels;
|
||||
import com.baiyue.lastleg.fleet.reporting.TripOperationsService;
|
||||
import com.baiyue.lastleg.security.InternalServiceTokenGuard;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import jakarta.validation.Validation;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class LastlegExportProviderServiceTest {
|
||||
private LastlegExportProviderService service;
|
||||
private LastlegOrderService orders;
|
||||
private InventoryQueryService inventory;
|
||||
private MonthlyExchangeRateService rates;
|
||||
private TripOperationsService operations;
|
||||
private jakarta.validation.ValidatorFactory validators;
|
||||
|
||||
@BeforeEach void setup() {
|
||||
service = new LastlegExportProviderService();
|
||||
orders = mock(LastlegOrderService.class); inventory = mock(InventoryQueryService.class);
|
||||
rates = mock(MonthlyExchangeRateService.class); operations = mock(TripOperationsService.class);
|
||||
ReflectionTestUtils.setField(service, "orders", orders);
|
||||
ReflectionTestUtils.setField(service, "inventory", inventory);
|
||||
ReflectionTestUtils.setField(service, "rates", rates);
|
||||
ReflectionTestUtils.setField(service, "operations", operations);
|
||||
ReflectionTestUtils.setField(service, "json", new ObjectMapper().findAndRegisterModules().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS));
|
||||
validators = Validation.buildDefaultValidatorFactory();
|
||||
ReflectionTestUtils.setField(service, "validator", validators.getValidator());
|
||||
ReflectionTestUtils.setField(service, "signingKey", "test-internal-token-with-at-least-32-bytes");
|
||||
}
|
||||
@AfterEach void close() { validators.close(); }
|
||||
|
||||
@Test void batchesAllRowsAndBindsSnapshotToFiltersRequesterAndAttempt() {
|
||||
var row = new MonthlyExchangeRateDtos.Response("2026-10", "USD", "CNY", new BigDecimal("7.12"), "ACTIVE", 1, "operator", LocalDateTime.of(2026,10,8,12,0), null);
|
||||
when(rates.page(any())).thenAnswer(call -> {
|
||||
MonthlyExchangeRateDtos.Query q = call.getArgument(0);
|
||||
return new PageResult<>(List.of(row), q.page(), q.size(), 2, 2);
|
||||
});
|
||||
var first = service.batch("MONTHLY_EXCHANGE_RATE_LIST", request(Map.of("status", "ACTIVE"), null, null));
|
||||
assertTrue(first.hasMore()); assertEquals(2L, first.totalRows());
|
||||
assertEquals(new BigDecimal("7.12"), first.rows().get(0).get("usdToCnyAverage"));
|
||||
assertEquals("2026-10-08T12:00:00", first.rows().get(0).get("updatedAt"));
|
||||
var second = service.batch("MONTHLY_EXCHANGE_RATE_LIST", request(Map.of("status", "ACTIVE"), first.nextCursor(), first.snapshotToken()));
|
||||
assertFalse(second.hasMore()); assertTrue(second.columns().isEmpty()); assertEquals(first.schemaHash(), second.schemaHash());
|
||||
verify(rates).page(new MonthlyExchangeRateDtos.Query(2,1,null,null,"ACTIVE"));
|
||||
assertThrows(ResponseStatusException.class, () -> service.batch("MONTHLY_EXCHANGE_RATE_LIST", request(Map.of("status", "INACTIVE"), "1", first.snapshotToken())));
|
||||
assertThrows(ResponseStatusException.class, () -> service.batch("MONTHLY_EXCHANGE_RATE_LIST", new ExportBatchRequest("t","0","PLATFORM","other",1,Map.of("status","ACTIVE"),"1",first.snapshotToken(),1)));
|
||||
assertThrows(ResponseStatusException.class, () -> service.batch("MONTHLY_EXCHANGE_RATE_LIST", new ExportBatchRequest("t","0","PLATFORM","u",2,Map.of("status","ACTIVE"),"1",first.snapshotToken(),1)));
|
||||
}
|
||||
|
||||
@Test void retainsOrderStageFalseAndRelabelFiltersWithoutExportingInternalIds() {
|
||||
var order = new ObjectMapper().findAndRegisterModules().convertValue(Map.of("id", "9007199254740993", "orderNo", "LL-1", "currentStatus", "PENDING_ASSIGNMENT", "abnormal", false, "detained", true), com.baiyue.lastleg.dto.LastlegOrderSummary.class);
|
||||
when(orders.page(isNull(), any(), eq("RL-1"))).thenReturn(new PageResult<>(List.of(order),1,1,1,1));
|
||||
var result = service.batch("LAST_MILE_ORDER_LIST", request(Map.of("detained",false,"sailingStage","BEFORE_DEPARTURE","relabelNo","RL-1"),null,null));
|
||||
verify(orders).page(isNull(), argThat(q -> Boolean.FALSE.equals(q.detained()) && q.sailingStage() == LastlegOrderRequests.SailingStage.BEFORE_DEPARTURE), eq("RL-1"));
|
||||
assertEquals("否", result.rows().get(0).get("abnormal"));
|
||||
assertEquals("是", result.rows().get(0).get("detained"));
|
||||
assertEquals("待分配", result.rows().get(0).get("currentStatus"));
|
||||
assertTrue(result.columns().stream().noneMatch(c -> c.key().equals("id") || c.key().equals("customerId")));
|
||||
}
|
||||
|
||||
@Test void keepsCustomerScopeAndLedgerCustomerFilter() {
|
||||
when(inventory.ledgers(eq("200"), any())).thenReturn(new PageResult<>(List.of(),1,1,0,0));
|
||||
service.batch("OVERSEAS_INVENTORY_LEDGER", request(Map.of("customerId","200","customerName","客户"),null,null));
|
||||
verify(inventory).ledgers(eq("200"), argThat(q -> "客户".equals(q.customerName())));
|
||||
}
|
||||
|
||||
@Test void defaultsCostVisibilityClosed() {
|
||||
when(operations.report(any(), eq(false))).thenReturn(new TripOperationsModels.Result(new PageResult<>(List.of(),1,1,0,0),null,null));
|
||||
service.batch("TRIP_OPERATIONS_LIST",request(Map.of("from","2026-10-01T00:00:00Z","to","2026-10-08T00:00:00Z"),null,null));
|
||||
verify(operations).report(any(), eq(false));
|
||||
}
|
||||
|
||||
@Test void rejectsScopeInvalidParametersAndUntrustedInternalCalls() {
|
||||
assertThrows(ResponseStatusException.class, () -> service.batch("LAST_MILE_ORDER_LIST",new ExportBatchRequest("t","1","MERCHANT","u",1,Map.of(),null,null,1)));
|
||||
assertThrows(ResponseStatusException.class, () -> service.batch("LAST_MILE_ORDER_LIST",request(Map.of("unknown","x"),null,null)));
|
||||
assertThrows(ResponseStatusException.class, () -> service.batch("LAST_MILE_ORDER_LIST",request(Map.of(),"1",null)));
|
||||
assertThrows(ResponseStatusException.class, () -> new LastlegExportProviderController(new InternalServiceTokenGuard("expected"),service).batch("wrong","LAST_MILE_ORDER_LIST",request(Map.of(),null,null)));
|
||||
verifyNoInteractions(orders);
|
||||
}
|
||||
|
||||
private ExportBatchRequest request(Map<String,Object> p,String cursor,String snapshot) {
|
||||
return new ExportBatchRequest("t","0","PLATFORM","u",1,p,cursor,snapshot,1);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -59,9 +59,9 @@ class FleetQuotePersistenceTest {
|
||||
service.importRows("1",new FleetQuoteModels.Import("batch-key",List.of(row)));
|
||||
assertThatThrownBy(()->service.importRows("1",new FleetQuoteModels.Import("batch-key",List.of(row,draft("SALE","SF",BigDecimal.ONE))))).hasMessageContaining("不同报价");
|
||||
}
|
||||
@Test void deletePreservesHistoryAndExportEscapesSpreadsheetFormulas() {
|
||||
@Test void deletePreservesHistoryAndBusinessText() {
|
||||
var created=service.create("1",new FleetQuoteModels.Create("formula-key",draft("SALE","=2+2",BigDecimal.ZERO)));
|
||||
assertThat(new String(service.exportCsv(new FleetQuoteModels.Query(1,20,null,null,null,null)),java.nio.charset.StandardCharsets.UTF_8)).contains("'=2+2");
|
||||
assertThat(service.page(new FleetQuoteModels.Query(1,20,null,null,null,null)).records().get(0).details().originCity()).isEqualTo("=2+2");
|
||||
service.delete(created.quoteNo(),"1",0);assertThat(service.page(new FleetQuoteModels.Query(1,20,null,null,null,null)).total()).isZero();
|
||||
assertThat(service.history(created.quoteNo())).hasSize(2);
|
||||
}
|
||||
|
||||
-23
@@ -60,32 +60,9 @@ class InventoryQueryControllerTest {
|
||||
verify(service).dashboard(eq(null), eq(query));
|
||||
}
|
||||
|
||||
@Test
|
||||
void balanceExportIsMaterializedBeforeReturningResponse() {
|
||||
byte[] csv = "\uFEFFcustomerId\r\n10001\r\n".getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
when(service.exportBalances("10001", query)).thenReturn(csv);
|
||||
|
||||
ResponseEntity<byte[]> response = controller.exportBalances(user(0L, "PLATFORM"), "10001", query);
|
||||
|
||||
org.junit.jupiter.api.Assertions.assertArrayEquals(csv, response.getBody());
|
||||
org.junit.jupiter.api.Assertions.assertEquals("text/csv;charset=UTF-8",
|
||||
response.getHeaders().getContentType().toString());
|
||||
verify(service).exportBalances("10001", query);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ledgerExportIsMaterializedBeforeReturningResponse() {
|
||||
InventoryQueryRequests.LedgerQuery ledgerQuery = new InventoryQueryRequests.LedgerQuery(
|
||||
1L, 20L, null, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
byte[] csv = "\uFEFFoccurredAt\r\n".getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
when(service.exportLedgers("10001", ledgerQuery)).thenReturn(csv);
|
||||
|
||||
ResponseEntity<byte[]> response = controller.exportLedgers(
|
||||
user(0L, "PLATFORM"), "10001", ledgerQuery);
|
||||
|
||||
org.junit.jupiter.api.Assertions.assertArrayEquals(csv, response.getBody());
|
||||
verify(service).exportLedgers("10001", ledgerQuery);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unauthenticatedPositionDetailIsRejected() {
|
||||
|
||||
+10
-10
@@ -198,21 +198,21 @@ class InventoryQueryServiceTest {
|
||||
InventoryQueryRequests.LedgerQuery query = new InventoryQueryRequests.LedgerQuery(
|
||||
1L, 20L, null, null, null, null, null, null, null, null, null, null, null,
|
||||
java.time.LocalDateTime.of(2026, 8, 18, 0, 0),
|
||||
java.time.LocalDateTime.of(2026, 8, 17, 0, 0));
|
||||
java.time.LocalDateTime.of(2026, 8, 17, 0, 0), null);
|
||||
|
||||
assertThrows(ResponseStatusException.class, () -> service.ledgers("100", query));
|
||||
verify(mapper, never()).countLedgers(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void balanceExportUsesInboundOrderPackageColumnsOnly() {
|
||||
InventoryQueryRequests.BalanceQuery query = balanceQuery(null);
|
||||
when(mapper.sourceBalances("100", query, 0, 10_000)).thenReturn(List.of());
|
||||
|
||||
String csv = new String(service.exportBalances("100", query), StandardCharsets.UTF_8);
|
||||
|
||||
assertTrue(csv.contains("inboundOrderNo,inboundCode,skuCode,productNameZh,productNameEn,totalPackages"));
|
||||
assertFalse(csv.contains("Quantity"));
|
||||
void balanceExportPagesSourceRowsInsteadOfGroupedBalances() {
|
||||
var query = balanceQuery(null);
|
||||
when(mapper.countSourceBalances("100", query)).thenReturn(201L);
|
||||
when(mapper.sourceBalances("100", query, 0, 20)).thenReturn(List.of());
|
||||
var page = service.exportBalancePage("100", query);
|
||||
assertEquals(201L, page.total());
|
||||
verify(mapper).sourceBalances("100", query, 0, 20);
|
||||
verify(mapper, never()).countBalances(any(), any());
|
||||
}
|
||||
|
||||
private static InventoryQueryRequests.BalanceQuery balanceQuery(String status) {
|
||||
@@ -224,7 +224,7 @@ class InventoryQueryServiceTest {
|
||||
InventoryQueryRequests.BalanceQuery balances = new InventoryQueryRequests.BalanceQuery(
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
InventoryQueryRequests.LedgerQuery ledgers = new InventoryQueryRequests.LedgerQuery(
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
|
||||
assertEquals(1L, balances.page());
|
||||
assertEquals(20L, balances.size());
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ class PlatformOverseasQuoteControllerContractTest {
|
||||
assertThat(authorities.get("create")).contains("platform:overseas-master-data:quote:create");
|
||||
assertThat(authorities.get("update")).contains("platform:overseas-master-data:quote:edit");
|
||||
assertThat(authorities.get("status")).contains("platform:overseas-master-data:quote:status");
|
||||
assertThat(authorities.get("export")).contains("platform:overseas-master-data:quote:export");
|
||||
assertThat(authorities).doesNotContainKey("export");
|
||||
|
||||
Method options = Stream.of(PlatformOverseasQuoteController.class.getDeclaredMethods())
|
||||
.filter(method -> method.getName().equals("warehouseOptions"))
|
||||
|
||||
+1
-1
@@ -23,6 +23,6 @@ class PlatformVehicleControllerContractTest {
|
||||
assertThat(authorities.get("create")).contains("platform:overseas-master-data:vehicle:create");
|
||||
assertThat(authorities.get("update")).contains("platform:overseas-master-data:vehicle:edit");
|
||||
assertThat(authorities.get("status")).contains("platform:overseas-master-data:vehicle:status");
|
||||
assertThat(authorities.get("export")).contains("platform:overseas-master-data:vehicle:export");
|
||||
assertThat(authorities).doesNotContainKey("export");
|
||||
}
|
||||
}
|
||||
|
||||
+1
-6
@@ -63,12 +63,7 @@ class VehicleApplicationServiceTest {
|
||||
org.mockito.Mockito.verify(repository, org.mockito.Mockito.never()).create(org.mockito.ArgumentMatchers.any());
|
||||
}
|
||||
|
||||
@Test void exportUsesPlateAsBusinessIdentifierAndIncludesAllCapacityDimensions() {
|
||||
when(repository.list(org.mockito.ArgumentMatchers.any())).thenReturn(java.util.List.of(vehicle(3, 1000)));
|
||||
String csv = new String(service.exportCsv(new VehicleModels.Query(1, 20, null, null, null, null)), java.nio.charset.StandardCharsets.UTF_8);
|
||||
assertThat(csv).contains("最大托位,最大载重(kg),可用容积(m³)").contains("\"30\",\"18000\",\"90\"")
|
||||
.doesNotContain("车辆ID");
|
||||
}
|
||||
|
||||
|
||||
private static VehicleModels.Update update(long version, long mileage) {
|
||||
return new VehicleModels.Update(version, "ABC-001", "Box Truck", new BigDecimal("12.50"), mileage,
|
||||
|
||||
@@ -389,51 +389,9 @@ class LastlegOrderServiceTest {
|
||||
assertEquals(25, page.getValue().getSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
void exportUsesChineseBooleanAndMainStatusLabels() {
|
||||
LastlegOrder order = order(11L, "100", "PENDING_ASSIGNMENT", null);
|
||||
order.setAbnormal(false);
|
||||
order.setDetained(true);
|
||||
var resultPage = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<LastlegOrder>(1, 1000);
|
||||
resultPage.setRecords(List.of(order));
|
||||
resultPage.setTotal(1);
|
||||
when(orders.selectPage(any(), any())).thenReturn(resultPage);
|
||||
|
||||
String csv = new String(service.exportCsv(null, new LastlegOrderRequests.Query(
|
||||
1L, 20L, null, null, null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null)), StandardCharsets.UTF_8);
|
||||
|
||||
assertTrue(csv.contains("\"否\",\"是\""));
|
||||
assertTrue(csv.contains("\"待分配\""));
|
||||
assertFalse(csv.contains("\"false\""));
|
||||
assertFalse(csv.contains("\"PENDING_ASSIGNMENT\""));
|
||||
assertEquals("草稿", LastlegOrderStatus.labelOf("DRAFT"));
|
||||
assertEquals("审核通过", LastlegOrderStatus.labelOf("APPROVED"));
|
||||
assertEquals("已分配目的仓", LastlegOrderStatus.labelOf("DESTINATION_ASSIGNED"));
|
||||
assertEquals("已生成入库计划", LastlegOrderStatus.labelOf("INBOUND_PLANNED"));
|
||||
assertEquals("处理中", LastlegOrderStatus.labelOf("PROCESSING"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void exportContinuesAfterThePaginationInterceptorCapsTheBatchSize() {
|
||||
when(orders.selectPage(any(), any())).thenAnswer(invocation -> {
|
||||
com.baomidou.mybatisplus.extension.plugins.pagination.Page<LastlegOrder> page = invocation.getArgument(0);
|
||||
page.setSize(500);
|
||||
page.setTotal(501);
|
||||
long start = (page.getCurrent() - 1) * page.getSize() + 1;
|
||||
page.setRecords(java.util.stream.LongStream.rangeClosed(start, Math.min(start + 499, 501))
|
||||
.mapToObj(id -> order(id, "100", "PENDING_ASSIGNMENT", null)).toList());
|
||||
return page;
|
||||
});
|
||||
var query = new LastlegOrderRequests.Query(1L, 20L, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null, null, LastlegOrderRequests.SailingStage.BEFORE_DEPARTURE);
|
||||
|
||||
String csv = new String(service.exportCsv("100", query), StandardCharsets.UTF_8);
|
||||
|
||||
assertEquals(502, csv.lines().count());
|
||||
assertTrue(csv.contains("\"LM-501\""));
|
||||
verify(orders, org.mockito.Mockito.times(2)).selectPage(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void pageWarehouseFilterMatchesFrozenWarehouseName() {
|
||||
|
||||
Reference in New Issue
Block a user