This commit is contained in:
2026-08-13 09:49:55 +08:00
parent f3366cb2fa
commit 95849f248f
69 changed files with 1224 additions and 221 deletions
@@ -1,6 +1,7 @@
package com.baiyue.finance;
import com.baiyue.finance.controller.PlatformFinanceController;
import com.baiyue.finance.controller.FinanceSettlementController;
import io.swagger.v3.oas.annotations.Parameter;
import org.junit.jupiter.api.Test;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -29,8 +30,8 @@ class FinancePlatformListContractTest {
Map<String, String> expected = Map.of(
"feePool", "hasAuthority('platform:finance:bill:view')",
"statements", "hasAuthority('platform:finance:bill:view')",
"receivableSettlements", "hasAuthority('platform:finance:bill:view')",
"payableSettlements", "hasAuthority('platform:finance:cost:view')");
"receivableSettlements", "hasAuthority('platform:finance:settlement-ar:view')",
"payableSettlements", "hasAuthority('platform:finance:settlement-ap:view')");
for (var entry : expected.entrySet()) {
assertThat(find(entry.getKey()).getAnnotation(PreAuthorize.class).value()).isEqualTo(entry.getValue());
}
@@ -46,7 +47,8 @@ class FinancePlatformListContractTest {
private static Method find(String name) throws Exception {
return switch (name) {
case "feePool" -> PlatformFinanceController.class.getMethod(name, Long.class, String.class);
default -> PlatformFinanceController.class.getMethod(name, Long.class);
case "statements" -> PlatformFinanceController.class.getMethod(name, Long.class);
default -> FinanceSettlementController.class.getMethod(name, Long.class);
};
}
}
@@ -4,11 +4,13 @@ import com.baiyue.common.security.AuthenticatedUser;
import com.baiyue.finance.client.FinanceFileClient;
import com.baiyue.finance.client.FirstlegCostSnapshotClient;
import com.baiyue.finance.controller.ClientFinanceController;
import com.baiyue.finance.controller.FinanceSettlementController;
import com.baiyue.finance.controller.PlatformFinanceController;
import com.baiyue.finance.dto.FinanceReadModels.ClientReceipt;
import com.baiyue.finance.dto.FinanceReadModels.ClientReceiptAllocation;
import com.baiyue.finance.service.FinanceQueryService;
import com.baiyue.finance.service.FinanceService;
import com.baiyue.finance.service.FinanceSettlementService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -31,6 +33,7 @@ class FinanceReadApiBehaviorTest {
@Mock FinanceQueryService queryService;
@Mock FinanceFileClient fileClient;
@Mock FirstlegCostSnapshotClient snapshotClient;
@Mock FinanceSettlementService settlementService;
@Test
void clientReceiptUsesAuthenticatedCustomerAndDoesNotExposeVoucherOrInternalFields() throws Exception {
@@ -63,34 +66,36 @@ class FinanceReadApiBehaviorTest {
}
@Test
void fivePlatformPageMethodsUseTypedQueryFacade() {
void fivePlatformPageMethodsUseTheirTypedQueryFacades() {
var controller = new PlatformFinanceController(service, fileClient, snapshotClient, queryService);
var settlementController = new FinanceSettlementController(settlementService);
controller.customerAccount(11L);
controller.statements(11L);
controller.receivableSettlements(11L);
controller.payableSettlements(11L);
settlementController.receivableSettlements(11L);
settlementController.payableSettlements(11L);
verify(queryService).customerAccount(11L);
verify(queryService).statements(11L, false);
verify(queryService).receivableSettlements(11L, false);
verify(queryService).payableSettlements(11L);
verify(settlementService).listSettlements("RECEIVABLE", 11L);
verify(settlementService).listSettlements("PAYABLE", 11L);
}
@Test
void platformListsDefaultToAllCustomersAndKeepOptionalFilter() {
var controller = new PlatformFinanceController(service, fileClient, snapshotClient, queryService);
var settlementController = new FinanceSettlementController(settlementService);
controller.feePool(null, null);
controller.statements(null);
controller.receivableSettlements(null);
controller.payableSettlements(null);
settlementController.receivableSettlements(null);
settlementController.payableSettlements(null);
controller.statements(31L);
verify(queryService).feePool(null, null);
verify(queryService).statements(null, false);
verify(queryService).receivableSettlements(null, false);
verify(queryService).payableSettlements(null);
verify(settlementService).listSettlements("RECEIVABLE", null);
verify(settlementService).listSettlements("PAYABLE", null);
verify(queryService).statements(31L, false);
}
@@ -64,6 +64,44 @@ class FinanceSettlementBehaviorTest {
user(Set.of("platform:finance:settlement-ar:view")), 88L)).isInstanceOf(AccessDeniedException.class);
}
@Test
void controllerListsFormalSettlementsByDirection() {
FinanceSettlementController controller = new FinanceSettlementController(controllerService);
SettlementSummary receivable = detail("RECEIVABLE", "ISSUED", BigDecimal.ZERO,
new BigDecimal("100.00"), 0).summary();
when(controllerService.listSettlements("RECEIVABLE", 10L)).thenReturn(List.of(receivable));
var response = controller.receivableSettlements(10L);
assertThat(response.code()).isEqualTo("200");
assertThat(response.data()).containsExactly(receivable);
verify(controllerService).listSettlements("RECEIVABLE", 10L);
}
@Test
void formalSettlementListUsesFinSettlementAndOptionalCustomerFilter() {
JdbcTemplate jdbc = org.mockito.Mockito.mock(JdbcTemplate.class);
Map<String, Object> row = settlementRow("PAYABLE", "ISSUED", BigDecimal.ZERO,
new BigDecimal("100.00"), 0);
when(jdbc.queryForList(anyString(), eq("PAYABLE"))).thenReturn(List.of(row));
when(jdbc.queryForList(anyString(), eq("PAYABLE"), eq(10L))).thenReturn(List.of(row));
FinanceSettlementService service = service(jdbc, org.mockito.Mockito.mock(FinanceFileClient.class));
var all = service.listSettlements("PAYABLE", null);
var scoped = service.listSettlements("PAYABLE", 10L);
assertThat(all).singleElement().extracting(SettlementSummary::settlementId).isEqualTo(88L);
assertThat(scoped).singleElement().extracting(SettlementSummary::customerId).isEqualTo(10L);
ArgumentCaptor<String> allSql = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<String> scopedSql = ArgumentCaptor.forClass(String.class);
verify(jdbc).queryForList(allSql.capture(), eq("PAYABLE"));
verify(jdbc).queryForList(scopedSql.capture(), eq("PAYABLE"), eq(10L));
assertThat(allSql.getValue()).contains("FROM fin_settlement WHERE direction=?")
.doesNotContain("FROM fin_bill", "FROM fin_cost", "tenant_id=?");
assertThat(scopedSql.getValue()).contains("FROM fin_settlement WHERE direction=?", "tenant_id=?")
.doesNotContain("FROM fin_bill", "FROM fin_cost");
}
@Test
void arCreationRejectsNonCnyBeforeDatabaseAccess() {
JdbcTemplate jdbc = org.mockito.Mockito.mock(JdbcTemplate.class);