This commit is contained in:
2026-08-12 17:29:30 +08:00
parent 4562e3f1d1
commit b5649cf8ae
42 changed files with 1205 additions and 78 deletions
@@ -59,7 +59,7 @@ class FinanceFeePoolDirectionBehaviorTest {
}
private static FeePoolItem item(String direction, String sourceType, long sourceId) {
return new FeePoolItem(direction + ":" + sourceId, direction, sourceType, sourceId, 1,
return new FeePoolItem(direction + ":" + sourceId, 7L, direction, sourceType, sourceId, 1,
11L, 21L, null, "清关费", new BigDecimal("100.00"), "CNY", "AVAILABLE",
LocalDateTime.of(2026, 8, 11, 1, 0));
}
@@ -0,0 +1,52 @@
package com.baiyue.finance;
import com.baiyue.finance.controller.PlatformFinanceController;
import io.swagger.v3.oas.annotations.Parameter;
import org.junit.jupiter.api.Test;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestParam;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Arrays;
import static org.assertj.core.api.Assertions.assertThat;
class FinancePlatformListContractTest {
@Test
void platformCustomerFiltersAreOptionalAndDocumentAllCustomerDefault() throws Exception {
for (String methodName : new String[]{"feePool", "statements", "receivableSettlements", "payableSettlements"}) {
Method method = find(methodName);
var customer = method.getParameters()[0];
assertThat(customer.getType()).isEqualTo(Long.class);
assertThat(customer.getAnnotation(RequestParam.class).required()).isFalse();
assertThat(customer.getAnnotation(Parameter.class).description()).contains("省略时查询全部客户");
}
}
@Test
void platformListPermissionsRemainSeparatedByReceivableAndPayableData() throws Exception {
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')");
for (var entry : expected.entrySet()) {
assertThat(find(entry.getKey()).getAnnotation(PreAuthorize.class).value()).isEqualTo(entry.getValue());
}
}
@Test
void allCustomerFeePoolResponseCarriesCustomerIdentity() {
assertThat(Arrays.stream(com.baiyue.finance.dto.FinanceReadModels.FeePoolItem.class
.getRecordComponents()).map(java.lang.reflect.RecordComponent::getName))
.contains("customerId");
}
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);
};
}
}
@@ -0,0 +1,67 @@
package com.baiyue.finance;
import com.baiyue.finance.service.FinanceQueryService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
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 FinancePlatformOptionalCustomerQueryTest {
@Test
void allCustomerQueriesOmitCustomerPredicateAndArguments() {
RecordingJdbc jdbc = new RecordingJdbc();
FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper());
service.feePool(null, null);
service.statements(null, false);
service.receivableSettlements(null, false);
service.payableSettlements(null);
assertThat(jdbc.calls).hasSize(5).allSatisfy(call -> {
assertThat(call.sql()).doesNotContain("customer_id=?", "tenant_id=?");
assertThat(call.args()).isEmpty();
});
}
@Test
void customerFilterAddsPredicateAndRealCustomerArgument() {
RecordingJdbc jdbc = new RecordingJdbc();
FinanceQueryService service = new FinanceQueryService(jdbc, new ObjectMapper());
service.feePool(31L, null);
service.statements(31L, false);
service.receivableSettlements(31L, false);
service.payableSettlements(31L);
assertThat(jdbc.calls).hasSize(5).allSatisfy(call -> {
assertThat(call.sql()).containsAnyOf("customer_id=?", "tenant_id=?");
assertThat(call.args()).containsExactly(31L);
});
}
@Test
void optionalCustomerNeverTreatsZeroAsAllCustomers() {
FinanceQueryService service = new FinanceQueryService(new RecordingJdbc(), new ObjectMapper());
assertThatThrownBy(() -> service.statements(0L, false)).hasMessageContaining("客户范围无效");
assertThatThrownBy(() -> service.feePool(-1L, null)).hasMessageContaining("客户范围无效");
}
private record Call(String sql, List<Object> args) {}
private static final class RecordingJdbc extends JdbcTemplate {
private final List<Call> calls = new ArrayList<>();
@Override
public <T> List<T> query(String sql, RowMapper<T> mapper, Object... args) {
calls.add(new Call(sql, List.of(args)));
return List.of();
}
}
}
@@ -22,6 +22,7 @@ import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
@@ -75,4 +76,35 @@ class FinanceReadApiBehaviorTest {
verify(queryService).receivableSettlements(11L, false);
verify(queryService).payableSettlements(11L);
}
@Test
void platformListsDefaultToAllCustomersAndKeepOptionalFilter() {
var controller = new PlatformFinanceController(service, fileClient, snapshotClient, queryService);
controller.feePool(null, null);
controller.statements(null);
controller.receivableSettlements(null);
controller.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(queryService).statements(31L, false);
}
@Test
void clientListsAlwaysUseAuthenticatedCustomerScope() {
AuthenticatedUser user = new AuthenticatedUser(7L, 23L, null, null, "client", "MERCHANT", "WEB", Set.of());
var controller = new ClientFinanceController(service, queryService);
controller.bills(user);
controller.statements(user);
controller.receivableSettlements(user);
verify(queryService, org.mockito.Mockito.times(2)).statements(23L, true);
verify(queryService).receivableSettlements(23L, true);
verifyNoMoreInteractions(queryService);
}
}