feat(fleet): integrate costs quotes operations and in-transit handovers
This commit is contained in:
+26
@@ -0,0 +1,26 @@
|
||||
package com.baiyue.lastleg.fleet.quote;
|
||||
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.lastleg.fleet.customer.FleetCustomerDirectory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.util.List;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class FleetQuoteDirectoryTest {
|
||||
@Test void saleUsesCustomerAndPurchaseRequiresAnActiveOverseasTruckingSupplier() {
|
||||
var customers=mock(FleetCustomerDirectory.class);var suppliers=mock(FleetQuoteSupplierClient.class);
|
||||
var directory=new FleetQuoteDirectory(customers,suppliers,"test-only-token");
|
||||
when(customers.required(1)).thenReturn(new FleetCustomerDirectory.Customer("1","C001","委托客户",null,null));
|
||||
assertThat(directory.name("SALE",1)).isEqualTo("委托客户");verifyNoInteractions(suppliers);
|
||||
when(suppliers.get("test-only-token",2,true,"TRUCKING")).thenReturn(ApiResponse.success(new FleetQuoteSupplierClient.Supplier(2,"国内拖车","TRUCKING","ACTIVE",List.of(),"DOMESTIC")));
|
||||
assertThatThrownBy(()->directory.name("PURCHASE",2)).hasMessageContaining("海外车队");
|
||||
when(suppliers.get("test-only-token",2,true,"TRUCKING")).thenReturn(ApiResponse.success(new FleetQuoteSupplierClient.Supplier(2,"海外车队","TRUCKING","ACTIVE",List.of(),"OVERSEAS")));
|
||||
assertThat(directory.name("PURCHASE",2)).isEqualTo("海外车队");
|
||||
}
|
||||
@Test void unavailableDirectoryDoesNotCreateFakeNames() {
|
||||
var suppliers=mock(FleetQuoteSupplierClient.class);var directory=new FleetQuoteDirectory(mock(FleetCustomerDirectory.class),suppliers,"test-only-token");
|
||||
when(suppliers.get(anyString(),anyLong(),anyBoolean(),anyString())).thenThrow(new IllegalStateException("unavailable"));
|
||||
assertThatThrownBy(()->directory.name("PURCHASE",2)).hasMessageContaining("目录暂不可用");
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package com.baiyue.lastleg.fleet.quote;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.*;
|
||||
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.*;
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class FleetQuotePersistenceTest {
|
||||
private JdbcTemplate jdbc; private FleetQuoteService service;
|
||||
@BeforeEach void setup() throws Exception {
|
||||
var source=new DriverManagerDataSource("jdbc:h2:mem:quotes-"+UUID.randomUUID()+";MODE=MySQL;DB_CLOSE_DELAY=-1","sa","");
|
||||
jdbc=new JdbcTemplate(source);
|
||||
String schema=Files.readString(Path.of("src/main/resources/db/lastleg.sql"));
|
||||
for(String table:List.of("ll_fleet_quote","ll_fleet_quote_history")) {
|
||||
var match=Pattern.compile("(?s)CREATE TABLE "+table+" \\(.*?;").matcher(schema);
|
||||
assertThat(match.find()).as("总控集成表结构 %s",table).isTrue();jdbc.execute(match.group().replace(" JSON"," LONGTEXT"));
|
||||
}
|
||||
var directory=mock(FleetQuoteDirectory.class);when(directory.name(anyString(),anyLong())).thenReturn("正式往来单位");
|
||||
var target=new FleetQuoteService(new FleetQuoteRepository(jdbc),directory,new ObjectMapper().registerModule(new JavaTimeModule()));
|
||||
var proxy=new ProxyFactory(target);proxy.setProxyTargetClass(true);
|
||||
proxy.addAdvice(new TransactionInterceptor(new DataSourceTransactionManager(source),new AnnotationTransactionAttributeSource()));
|
||||
service=(FleetQuoteService)proxy.getProxy();
|
||||
}
|
||||
private FleetQuoteModels.Draft draft(String kind,String city,BigDecimal price) {
|
||||
return new FleetQuoteModels.Draft(kind,1L,city,"ont8",new BigDecimal("10.0"),"USD",price,null,null,LocalDate.of(2026,1,1),LocalDate.of(2026,12,31),null);
|
||||
}
|
||||
@Test void nullPriceDiffersFromZeroAndSalesNeverAliasPurchase() {
|
||||
assertThatThrownBy(()->service.create("1",new FleetQuoteModels.Create("invalid-key",draft("SALE","LA",null)))).hasMessageContaining("至少填写");
|
||||
var sale=service.create("1",new FleetQuoteModels.Create("sale-key",draft("SALE","LA",BigDecimal.ZERO)));
|
||||
var purchase=service.create("1",new FleetQuoteModels.Create("purchase-key",draft("PURCHASE","LA",BigDecimal.TEN)));
|
||||
assertThat(sale.details().selfPickupPrice()).isEqualByComparingTo("0");assertThat(sale.details().fullDeliveryPrice()).isNull();
|
||||
assertThat(purchase.quoteNo()).isNotEqualTo(sale.quoteNo());assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ll_fleet_quote",Integer.class)).isEqualTo(2);
|
||||
}
|
||||
@Test void replayAndCorrectionKeepVersionsAndRejectStaleWrites() {
|
||||
var request=new FleetQuoteModels.Create("stable-key",draft("SALE","LA",BigDecimal.TEN));var first=service.create("1",request);
|
||||
assertThat(service.create("1",request).quoteNo()).isEqualTo(first.quoteNo());
|
||||
assertThatThrownBy(()->service.create("1",new FleetQuoteModels.Create("stable-key",draft("SALE","LA",BigDecimal.ONE)))).hasMessageContaining("不同报价");
|
||||
service.update(first.quoteNo(),"1",new FleetQuoteModels.Update(0L,draft("SALE","LA",BigDecimal.ONE)));
|
||||
assertThat(service.create("1",request).details().selfPickupPrice()).isEqualByComparingTo("10");
|
||||
assertThat(service.history(first.quoteNo())).hasSize(2);assertThat(service.history(first.quoteNo()).get(1).snapshot().details().selfPickupPrice()).isEqualByComparingTo("10");
|
||||
assertThatThrownBy(()->service.status(first.quoteNo(),"1",new FleetQuoteModels.Status(0L,"INACTIVE"))).hasMessageContaining("已变化");
|
||||
}
|
||||
@Test void failedAtomicImportLeavesNoRowsAndChangedBatchCannotReuseKey() {
|
||||
var row=draft("SALE","LA",BigDecimal.ONE);
|
||||
assertThatThrownBy(()->service.importRows("1",new FleetQuoteModels.Import("batch-key",List.of(row,row)))).hasMessageContaining("重复");
|
||||
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ll_fleet_quote",Integer.class)).isZero();
|
||||
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ll_fleet_quote_history",Integer.class)).isZero();
|
||||
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() {
|
||||
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");
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user