feat(lastleg): add overseas container fee sources
This commit is contained in:
+67
@@ -0,0 +1,67 @@
|
||||
package com.baiyue.lastleg.financefee.service;
|
||||
|
||||
import com.baiyue.lastleg.financefee.dto.OverseasFeeSourceDtos.SaveRequest;
|
||||
import com.baiyue.lastleg.financefee.entity.OverseasFeeSource;
|
||||
import com.baiyue.lastleg.financefee.mapper.OverseasFeeCommandMapper;
|
||||
import com.baiyue.lastleg.financefee.mapper.OverseasFeeSourceMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class OverseasContainerFeeSourceServiceTest {
|
||||
private final OverseasFeeSourceMapper mapper = mock(OverseasFeeSourceMapper.class);
|
||||
private final OverseasFeeCommandMapper commands = mock(OverseasFeeCommandMapper.class);
|
||||
private final OverseasFeeBusinessGuard guard = mock(OverseasFeeBusinessGuard.class);
|
||||
private final OverseasFeeOutboxService outbox = mock(OverseasFeeOutboxService.class);
|
||||
private final OverseasFeeSourceService service = new OverseasFeeSourceService(
|
||||
mapper, commands, guard, outbox, new ObjectMapper().findAndRegisterModules());
|
||||
|
||||
@Test
|
||||
void createsContainerFeeUsingTheRealContainerAsItsStableBusinessReference() {
|
||||
SaveRequest request = OverseasFeeBusinessGuardTest.containerFee("customer-a", 701L, "MSCU1234567");
|
||||
when(commands.find("operator-a", "CREATE", "create-container-fee")).thenReturn(null);
|
||||
OverseasFeeSource result = service.create("operator-a", "create-container-fee", request);
|
||||
|
||||
verify(guard).requireOwned(request);
|
||||
ArgumentCaptor<OverseasFeeSource> inserted = ArgumentCaptor.forClass(OverseasFeeSource.class);
|
||||
verify(mapper).insert(inserted.capture());
|
||||
assertThat(result).isSameAs(inserted.getValue());
|
||||
assertThat(result.getSourceType()).isEqualTo("OVERSEAS_CONTAINER_FEE");
|
||||
assertThat(result.getBusinessType()).isEqualTo("OVERSEAS_CONTAINER");
|
||||
assertThat(result.getBusinessId()).isEqualTo(701L);
|
||||
assertThat(result.getContainerBookingId()).isEqualTo(701L);
|
||||
assertThat(result.getSourceNo()).isEqualTo("MSCU1234567");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsUnsupportedContainerFeeItems() {
|
||||
SaveRequest source = OverseasFeeBusinessGuardTest.containerFee("customer-a", 701L, "MSCU1234567");
|
||||
SaveRequest request = new SaveRequest(source.customerId(), source.customerName(), source.sourceType(),
|
||||
source.businessType(), source.businessId(), source.businessNo(), source.orderId(), source.inboundId(),
|
||||
source.inboundCode(), source.containerId(), source.containerBookingId(), source.containerNo(),
|
||||
source.warehouseId(), "CUSTOM_FEE", "自定义费", source.direction(), source.supplierId(),
|
||||
source.supplierName(), source.supplierCategory(), source.settlementMode(), source.paidAt(),
|
||||
source.paymentMethod(), source.paymentReferenceNo(), source.paymentVoucherFileIds(),
|
||||
source.businessDate(), source.currency(), source.amount());
|
||||
when(commands.find("operator-a", "CREATE", "bad-container-fee")).thenReturn(null);
|
||||
|
||||
assertThatThrownBy(() -> service.create("operator-a", "bad-container-fee", request))
|
||||
.isInstanceOf(ResponseStatusException.class)
|
||||
.hasMessageContaining("海外货柜费用项目不受支持");
|
||||
}
|
||||
|
||||
@Test
|
||||
void canListAllCustomerFeesForAContainerInOneQuery() {
|
||||
service.list(null, "OVERSEAS_CONTAINER", null, 701L);
|
||||
|
||||
verify(mapper).selectList(any());
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
package com.baiyue.lastleg.financefee.service;
|
||||
|
||||
import com.baiyue.lastleg.financefee.dto.OverseasFeeSourceDtos.SaveRequest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
class OverseasFeeBusinessGuardTest {
|
||||
|
||||
@Test
|
||||
void acceptsOnlyARealContainerOwnedByTheCustomer() {
|
||||
StubJdbc jdbc = new StubJdbc(3, 0, 1);
|
||||
new OverseasFeeBusinessGuard(jdbc).requireOwned(containerFee("customer-a", 701L, "MSCU1234567"));
|
||||
|
||||
assertThat(jdbc.sql()).hasSize(3);
|
||||
assertThat(jdbc.sql().get(0)).contains("source_container_booking_id");
|
||||
assertThat(jdbc.sql().get(1)).contains("container_no");
|
||||
assertThat(jdbc.sql().get(2)).contains("customer_id");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsAContainerThatDoesNotContainTheClaimedCustomer() {
|
||||
StubJdbc jdbc = new StubJdbc(3, 0, 0);
|
||||
|
||||
assertThatThrownBy(() -> new OverseasFeeBusinessGuard(jdbc)
|
||||
.requireOwned(containerFee("other-customer", 701L, "MSCU1234567")))
|
||||
.isInstanceOf(ResponseStatusException.class)
|
||||
.hasMessageContaining("该客户不属于费用关联的真实货柜");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsAContainerWhoseOrdersDoNotShareTheClaimedContainerNumber() {
|
||||
StubJdbc jdbc = new StubJdbc(3, 1);
|
||||
|
||||
assertThatThrownBy(() -> new OverseasFeeBusinessGuard(jdbc)
|
||||
.requireOwned(containerFee("customer-a", 701L, "MSCU1234567")))
|
||||
.isInstanceOf(ResponseStatusException.class)
|
||||
.hasMessageContaining("真实货柜的集装箱号缺失或不一致");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsDifferentBusinessAndContainerBookingIdentifiersBeforeQuerying() {
|
||||
SaveRequest source = containerFee("customer-a", 701L, "MSCU1234567");
|
||||
SaveRequest inconsistent = new SaveRequest(source.customerId(), source.customerName(), source.sourceType(),
|
||||
source.businessType(), 702L, source.businessNo(), source.orderId(), source.inboundId(),
|
||||
source.inboundCode(), source.containerId(), source.containerBookingId(), source.containerNo(),
|
||||
source.warehouseId(), source.feeItemCode(), source.feeItemName(), source.direction(), source.supplierId(),
|
||||
source.supplierName(), source.supplierCategory(), source.settlementMode(), source.paidAt(),
|
||||
source.paymentMethod(), source.paymentReferenceNo(), source.paymentVoucherFileIds(),
|
||||
source.businessDate(), source.currency(), source.amount());
|
||||
StubJdbc jdbc = new StubJdbc();
|
||||
|
||||
assertThatThrownBy(() -> new OverseasFeeBusinessGuard(jdbc).requireOwned(inconsistent))
|
||||
.isInstanceOf(ResponseStatusException.class)
|
||||
.hasMessageContaining("真实来源柜ID");
|
||||
assertThat(jdbc.sql()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void listsAllContainerCustomersWithOneGroupedQuery() {
|
||||
StubJdbc jdbc = new StubJdbc();
|
||||
jdbc.queryResults = List.of(
|
||||
new com.baiyue.lastleg.financefee.dto.OverseasFeeSourceDtos.ContainerCustomerOption("customer-a", "客户A"),
|
||||
new com.baiyue.lastleg.financefee.dto.OverseasFeeSourceDtos.ContainerCustomerOption("customer-b", "客户B"));
|
||||
|
||||
var result = new OverseasFeeBusinessGuard(jdbc).listContainerCustomers(701L);
|
||||
|
||||
assertThat(result).extracting("customerId").containsExactly("customer-a", "customer-b");
|
||||
assertThat(jdbc.sql()).singleElement().asString().contains("GROUP BY customer_id");
|
||||
}
|
||||
|
||||
static SaveRequest containerFee(String customerId, Long bookingId, String containerNo) {
|
||||
return new SaveRequest(customerId, "客户A", "OVERSEAS_CONTAINER_FEE", "OVERSEAS_CONTAINER",
|
||||
bookingId, containerNo, null, null, null, null, bookingId, containerNo, 91L,
|
||||
"PORT_FEE", "港口费", "RECEIVABLE", null, null, null, null, null, null, null,
|
||||
List.of(), LocalDate.of(2026, 9, 8), "CNY", new BigDecimal("88.00"));
|
||||
}
|
||||
|
||||
private static final class StubJdbc extends JdbcTemplate {
|
||||
private final ArrayDeque<Integer> results = new ArrayDeque<>();
|
||||
private final List<String> sql = new ArrayList<>();
|
||||
private List<?> queryResults = List.of();
|
||||
|
||||
private StubJdbc(Integer... values) {
|
||||
results.addAll(List.of(values));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T queryForObject(String statement, Class<T> requiredType, Object... args) {
|
||||
sql.add(statement);
|
||||
return requiredType.cast(results.removeFirst());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> List<T> query(String statement, RowMapper<T> rowMapper, Object... args) {
|
||||
sql.add(statement);
|
||||
return (List<T>) queryResults;
|
||||
}
|
||||
|
||||
private List<String> sql() {
|
||||
return sql;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user