优化
This commit is contained in:
@@ -16,6 +16,8 @@ import com.baiyue.iam.service.MerchantOnboardingService;
|
||||
import com.baiyue.iam.service.ProfileResourceService;
|
||||
import com.baiyue.iam.service.TenantService;
|
||||
import com.baiyue.iam.dto.CustomerExportDtos.CustomerExportBatch;
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.iam.dto.TenantDtos.FinanceCustomerSummary;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
@@ -106,6 +108,18 @@ public class InternalTenantController {
|
||||
return ApiResponse.success(tenantService.orderEligibility(tenantId));
|
||||
}
|
||||
|
||||
@GetMapping("/finance-customers")
|
||||
public ApiResponse<PageResult<FinanceCustomerSummary>> financeCustomers(
|
||||
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token,
|
||||
@RequestParam(defaultValue = "1") @Min(1) long page,
|
||||
@RequestParam(defaultValue = "20") @Min(1) @Max(200) long size,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) @Pattern(regexp = "ACTIVE|DISABLED") String status,
|
||||
@RequestParam(required = false) Long customerId) {
|
||||
verify(token);
|
||||
return ApiResponse.success(tenantService.pageFinanceCustomers(page, size, keyword, status, customerId));
|
||||
}
|
||||
|
||||
@GetMapping("/exports/customers")
|
||||
public ApiResponse<CustomerExportBatch> exportCustomers(
|
||||
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token,
|
||||
|
||||
@@ -49,7 +49,7 @@ public class PlatformTenantController {
|
||||
|
||||
@GetMapping("/tenants")
|
||||
@PreAuthorize("hasAuthority('platform:customer:list')")
|
||||
@Operation(summary = "查询企业列表", description = "平台端分页查询商户企业,支持企业名称、统一社会信用代码、联系人和状态筛选。")
|
||||
@Operation(summary = "查询企业列表", description = "平台端分页查询商户企业,支持客户名称和状态筛选。客户编号、联系人等仅作展示信息,不作为搜索条件。")
|
||||
@ApiResponses({
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "企业列表查询成功"),
|
||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "分页参数类型无效"),
|
||||
|
||||
@@ -124,4 +124,8 @@ public final class TenantDtos {
|
||||
LocalDate establishedDate, BigDecimal registeredCapital,
|
||||
String registeredCapitalCurrency, String registeredAddress,
|
||||
String internalRemark, LocalDateTime createdAt, LocalDateTime approvedAt) { }
|
||||
|
||||
/** Full customer contact summary for trusted internal finance read models. */
|
||||
public record FinanceCustomerSummary(Long id, String tenantCode, String tenantName, String status,
|
||||
String contactName, String contactPhone) { }
|
||||
}
|
||||
|
||||
+1
-8
@@ -44,14 +44,7 @@ public class CustomerListExportProviderRepository {
|
||||
.eq(IamTenant::getTenantType, "MERCHANT")
|
||||
.eq(StringUtils.hasText(status), IamTenant::getStatus, status);
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
wrapper.and(condition -> condition
|
||||
.like(IamTenant::getTenantCode, keyword)
|
||||
.or().like(IamTenant::getTenantName, keyword)
|
||||
.or().like(IamTenant::getLegalName, keyword)
|
||||
.or().like(IamTenant::getUnifiedSocialCreditCode, keyword)
|
||||
.or().like(IamTenant::getContactName, keyword)
|
||||
.or().like(IamTenant::getContactPhone, keyword)
|
||||
.or().like(IamTenant::getSalesOwnerName, keyword));
|
||||
wrapper.like(IamTenant::getTenantName, keyword);
|
||||
}
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
@@ -91,15 +91,28 @@ public class TenantRepository {
|
||||
.eq(IamTenant::getTenantType, "MERCHANT")
|
||||
.eq(StringUtils.hasText(status), IamTenant::getStatus, status)
|
||||
.orderByDesc(IamTenant::getCreatedAt);
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
wrapper.like(IamTenant::getTenantName, keyword);
|
||||
}
|
||||
return PageResult.from(tenantMapper.selectPage(new Page<>(page, size), wrapper));
|
||||
}
|
||||
|
||||
public PageResult<IamTenant> pageFinanceCustomers(long page, long size, String keyword, String status, Long customerId) {
|
||||
LambdaQueryWrapper<IamTenant> wrapper = new LambdaQueryWrapper<IamTenant>()
|
||||
.eq(IamTenant::getTenantType, "MERCHANT")
|
||||
.eq(customerId != null, IamTenant::getId, customerId)
|
||||
.in(!StringUtils.hasText(status), IamTenant::getStatus, List.of("ACTIVE", "DISABLED"))
|
||||
.eq(StringUtils.hasText(status), IamTenant::getStatus, status)
|
||||
.orderByDesc(IamTenant::getCreatedAt);
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
wrapper.and(condition -> condition
|
||||
.like(IamTenant::getTenantCode, keyword)
|
||||
.or().like(IamTenant::getTenantName, keyword)
|
||||
.or().like(IamTenant::getLegalName, keyword)
|
||||
.or().like(IamTenant::getUnifiedSocialCreditCode, keyword)
|
||||
.or().like(IamTenant::getContactName, keyword)
|
||||
.or().like(IamTenant::getContactPhone, keyword)
|
||||
.or().like(IamTenant::getSalesOwnerName, keyword));
|
||||
.like(IamTenant::getTenantCode, keyword)
|
||||
.or().like(IamTenant::getTenantName, keyword)
|
||||
.or().like(IamTenant::getLegalName, keyword)
|
||||
.or().like(IamTenant::getUnifiedSocialCreditCode, keyword)
|
||||
.or().like(IamTenant::getContactName, keyword)
|
||||
.or().like(IamTenant::getContactPhone, keyword)
|
||||
.or().like(IamTenant::getSalesOwnerName, keyword));
|
||||
}
|
||||
return PageResult.from(tenantMapper.selectPage(new Page<>(page, size), wrapper));
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.baiyue.iam.dto.OnboardingDtos.OrderEligibilityResponse;
|
||||
|
||||
import com.baiyue.iam.dto.TenantDtos.RegisterRequest;
|
||||
import com.baiyue.iam.dto.TenantDtos.TenantResponse;
|
||||
import com.baiyue.iam.dto.TenantDtos.FinanceCustomerSummary;
|
||||
import com.baiyue.iam.dto.TenantDtos.UpdateProfileRequest;
|
||||
import com.baiyue.iam.dto.TenantDtos.UpdateTenantRequest;
|
||||
import com.baiyue.iam.dto.TenantDtos.ChangeTenantStatusRequest;
|
||||
@@ -198,6 +199,14 @@ public class TenantService {
|
||||
source.page(), source.size(), source.total(), source.pages());
|
||||
}
|
||||
|
||||
public PageResult<FinanceCustomerSummary> pageFinanceCustomers(long page, long size, String keyword, String status, Long customerId) {
|
||||
PageResult<IamTenant> source = repository.pageFinanceCustomers(page, size, keyword, status, customerId);
|
||||
return new PageResult<>(source.records().stream().map(tenant -> new FinanceCustomerSummary(
|
||||
tenant.getId(), tenant.getTenantCode(), tenant.getTenantName(), tenant.getStatus(),
|
||||
tenant.getContactName(), tenant.getContactPhone())).toList(),
|
||||
source.page(), source.size(), source.total(), source.pages());
|
||||
}
|
||||
|
||||
public CustomerExportBatch exportMerchants(Long cursor, int limit, String keyword, String status) {
|
||||
int boundedLimit = Math.min(Math.max(limit, 1), 500);
|
||||
List<IamTenant> source = repository.exportMerchants(cursor, boundedLimit, keyword, status);
|
||||
|
||||
@@ -44,7 +44,7 @@ paths:
|
||||
parameters:
|
||||
- {name: page, in: query, required: false, schema: {type: integer, minimum: 1, default: 1}}
|
||||
- {name: size, in: query, required: false, schema: {type: integer, minimum: 1, maximum: 200, default: 20}}
|
||||
- {name: keyword, in: query, required: false, schema: {type: string}, description: 企业编码、名称、信用代码或联系人关键字}
|
||||
- {name: keyword, in: query, required: false, schema: {type: string}, description: 客户名称关键字,不支持客户编号、信用代码或联系人搜索}
|
||||
- {name: status, in: query, required: false, schema: {type: string, enum: [PROFILE_REQUIRED, PENDING, ACTIVE, REJECTED, DISABLED]}}
|
||||
responses:
|
||||
'200':
|
||||
@@ -740,6 +740,39 @@ paths:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/RequiredQualificationPackageApiResponse'}
|
||||
'403': {$ref: '#/components/responses/Forbidden'}
|
||||
/internal/iam/finance-customers:
|
||||
get:
|
||||
tags: [IAM Resources / IAM 资源]
|
||||
summary: 提供财务客户主数据摘要
|
||||
description: 仅供 Finance Service 调用;只返回正常/停用客户及财务账户所需的客户编号、名称和联系方式,不依赖财务表是否已有记录。
|
||||
security: []
|
||||
parameters:
|
||||
- {$ref: '#/components/parameters/InternalServiceToken'}
|
||||
- {name: page, in: query, required: false, schema: {type: integer, minimum: 1, default: 1}}
|
||||
- {name: size, in: query, required: false, schema: {type: integer, minimum: 1, maximum: 200, default: 20}}
|
||||
- {name: keyword, in: query, required: false, schema: {type: string}}
|
||||
- {name: status, in: query, required: false, schema: {type: string, enum: [ACTIVE, DISABLED]}}
|
||||
- {name: customerId, in: query, required: false, schema: {type: integer, format: int64, minimum: 1}}
|
||||
responses:
|
||||
'200':
|
||||
description: 财务客户主数据分页摘要
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- {$ref: '#/components/schemas/ApiResponse'}
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
type: object
|
||||
required: [records, page, size, total, pages]
|
||||
properties:
|
||||
records: {type: array, items: {$ref: '#/components/schemas/FinanceCustomerSummary'}}
|
||||
page: {type: integer}
|
||||
size: {type: integer}
|
||||
total: {type: integer, format: int64}
|
||||
pages: {type: integer}
|
||||
'403': {$ref: '#/components/responses/Forbidden'}
|
||||
/internal/exports/v1/CUSTOMER_LIST/batches:
|
||||
post:
|
||||
tags: [Internal Export Provider]
|
||||
@@ -971,6 +1004,16 @@ components:
|
||||
internalRemark: {type: string, maxLength: 1000, description: 仅平台端可见}
|
||||
createdAt: {type: string, format: date-time, description: 入驻时间}
|
||||
approvedAt: {type: string, format: date-time, description: 审批通过时间}
|
||||
FinanceCustomerSummary:
|
||||
type: object
|
||||
required: [id, tenantCode, tenantName, status]
|
||||
properties:
|
||||
id: {type: string, pattern: '^\d+$'}
|
||||
tenantCode: {type: string}
|
||||
tenantName: {type: string}
|
||||
status: {type: string, enum: [ACTIVE, DISABLED]}
|
||||
contactName: {type: string, nullable: true}
|
||||
contactPhone: {type: string, nullable: true}
|
||||
TenantAddressListApiResponse:
|
||||
allOf:
|
||||
- {$ref: '#/components/schemas/ApiResponse'}
|
||||
|
||||
@@ -40,4 +40,17 @@ class InternalCustomerExportContractTest {
|
||||
assertEquals(ResultCode.SUCCESS.code(), response.code());
|
||||
verify(service).exportMerchants(null, 100, "柏悦", "ACTIVE");
|
||||
}
|
||||
|
||||
@Test
|
||||
void exposesFinanceCustomerMasterDataOnlyWithInternalToken() {
|
||||
BizException denied = assertThrows(BizException.class,
|
||||
() -> controller.financeCustomers(null, 1, 20, "Alice", "ACTIVE"));
|
||||
assertEquals(ResultCode.FORBIDDEN, denied.getResultCode());
|
||||
|
||||
when(service.pageFinanceCustomers(1, 20, "Alice", "ACTIVE", null))
|
||||
.thenReturn(new com.baiyue.common.core.api.PageResult<>(List.of(), 1, 20, 0, 0));
|
||||
var response = controller.financeCustomers("test-internal-service-token-at-least-32-bytes", 1, 20, "Alice", "ACTIVE");
|
||||
assertEquals(ResultCode.SUCCESS.code(), response.code());
|
||||
verify(service).pageFinanceCustomers(1, 20, "Alice", "ACTIVE", null);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,8 +40,8 @@ class CustomerListExportProviderRepositoryTest {
|
||||
assertThat(query.getSqlSegment())
|
||||
.contains("tenant_type")
|
||||
.contains("status")
|
||||
.contains("tenant_code")
|
||||
.contains("tenant_name")
|
||||
.doesNotContain("tenant_code")
|
||||
.contains("id >")
|
||||
.contains("id <=")
|
||||
.contains("ORDER BY id ASC")
|
||||
|
||||
@@ -93,6 +93,24 @@ class TenantServiceTest {
|
||||
verify(repository).pageMerchants(1, 10, "Example", "ACTIVE");
|
||||
}
|
||||
|
||||
@Test
|
||||
void pageFinanceCustomersKeepsFullContactSummaryForInternalFinance() {
|
||||
IamTenant tenant = merchant(10001L, "ACTIVE");
|
||||
tenant.setTenantCode("TEN10001");
|
||||
tenant.setTenantName("Example Merchant");
|
||||
tenant.setContactName("Alice");
|
||||
tenant.setContactPhone("13800138000");
|
||||
when(repository.pageFinanceCustomers(1, 10, "Alice", "ACTIVE", null))
|
||||
.thenReturn(new PageResult<>(List.of(tenant), 1, 10, 1, 1));
|
||||
|
||||
var result = service.pageFinanceCustomers(1, 10, "Alice", "ACTIVE", null);
|
||||
|
||||
assertEquals("TEN10001", result.records().get(0).tenantCode());
|
||||
assertEquals("Alice", result.records().get(0).contactName());
|
||||
assertEquals("13800138000", result.records().get(0).contactPhone());
|
||||
verify(repository).pageFinanceCustomers(1, 10, "Alice", "ACTIVE", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void platformCanMaintainAndDisableOnlyMerchantTenant() {
|
||||
IamTenant tenant = merchant(10001L, "ACTIVE");
|
||||
|
||||
Reference in New Issue
Block a user