This commit is contained in:
2026-08-25 16:40:54 +08:00
parent eb1a8895ac
commit 2dcfdd5fc8
5 changed files with 43 additions and 64 deletions
+3 -3
View File
@@ -7,10 +7,10 @@ spring:
baiyue:
security-event:
event-bridge-url: ${SECURITY_EVENT_BRIDGE_URL:http://event-bridge-overseas:9312}
customer-dimension-initialization:
fixed-delay-ms: ${CUSTOMER_DIMENSION_INITIALIZATION_FIXED_DELAY_MS:5000}
max-attempts: ${CUSTOMER_DIMENSION_INITIALIZATION_MAX_ATTEMPTS:12}
security-event:
event-bridge-url: ${SECURITY_EVENT_BRIDGE_URL:http://event-bridge-overseas:9312}
notification:
scheduler-enabled: ${NOTIFICATION_SCHEDULER_ENABLED:false}
dispatch-timeout-seconds: ${NOTIFICATION_DISPATCH_TIMEOUT_SECONDS:300}
mq-consumer-enabled: ${NOTIFICATION_MQ_CONSUMER_ENABLED:false}
+3 -3
View File
@@ -25,9 +25,8 @@ spring:
baiyue:
security-event:
event-bridge-url: ${SECURITY_EVENT_BRIDGE_URL:http://event-bridge-overseas:9312}
customer-dimension-initialization:
fixed-delay-ms: ${CUSTOMER_DIMENSION_INITIALIZATION_FIXED_DELAY_MS:5000}
max-attempts: ${CUSTOMER_DIMENSION_INITIALIZATION_MAX_ATTEMPTS:12}
security-event:
event-bridge-url: ${SECURITY_EVENT_BRIDGE_URL:http://event-bridge-overseas:9312}
notification:
scheduler-enabled: ${NOTIFICATION_SCHEDULER_ENABLED:false}
dispatch-timeout-seconds: ${NOTIFICATION_DISPATCH_TIMEOUT_SECONDS:300}
@@ -50,3 +49,4 @@ logging:
max-history: "${LOGGING_ROLLING_MAX_HISTORY:7}"
total-size-cap: "${LOGGING_ROLLING_TOTAL_SIZE_CAP:5GB}"
clean-history-on-start: "${LOGGING_ROLLING_CLEAN_HISTORY_ON_START:false}"
@@ -42,6 +42,7 @@ class MerchantOnboardingServiceTest {
@MockBean private CustomerDimensionRuleInitializationService dimensionRuleInitialization;
@MockBean private OverseasStorageFeeInitializationService overseasStorageFeeInitialization;
@MockBean private SecurityEventOutboxService securityEvents;
@MockBean private SecurityEventOutboxService securityEvents;
@Resource private MerchantOnboardingService service;
@Test
void clientRegistrationCreatesOwnerAndAssignsGlobalMerchantOwnerRole() {
@@ -55,8 +56,8 @@ class MerchantOnboardingServiceTest {
when(roles.selectOne(any())).thenReturn(ownerRole);
var result = service.register(new MerchantRegistrationRequest(
9001L, "注册人", "测试企业", "张三", "13800138000",
"owner@example.com", "CN", "深圳市宝安区"));
9001L, "注册人", "测试企业", "张三", "13800138000",
"owner@example.com", "CN", "深圳市宝安区"));
ArgumentCaptor<IamTenant> tenant = ArgumentCaptor.forClass(IamTenant.class);
ArgumentCaptor<IamUser> user = ArgumentCaptor.forClass(IamUser.class);
@@ -67,19 +68,21 @@ class MerchantOnboardingServiceTest {
verify(memberships).insert(membership.capture());
verify(membershipRoles).insert(roleGrant.capture());
verify(dimensionRuleInitialization).initialize(result.tenantId(), 9001L);
verify(overseasStorageFeeInitialization).initialize(result.tenantId(), 9001L, "测试企业");
verify(overseasStorageFeeInitialization).initialize(result.tenantId(), 9001L, "测试企业");
verify(securityEvents).tenantStatus(result.tenantId(), "PROFILE_REQUIRED");
verify(securityEvents).userStatus(9001L, result.tenantId(), "MERCHANT", "ACTIVE");
verify(securityEvents).permissionChanged(9001L, result.tenantId(), "MERCHANT",
"INITIAL_AUTHORIZATION");
verify(securityEvents).tenantStatus(result.tenantId(), "PROFILE_REQUIRED");
verify(securityEvents).userStatus(9001L, result.tenantId(), "MERCHANT", "ACTIVE");
verify(securityEvents).permissionChanged(9001L, result.tenantId(), "MERCHANT",
"INITIAL_AUTHORIZATION");
assertEquals("PROFILE_REQUIRED", result.tenantStatus());
assertEquals("PENDING", result.dimensionInitializationStatus());
assertEquals("PENDING", tenant.getValue().getDimensionInitializationStatus());
assertEquals(result.tenantId(), tenant.getValue().getId());
assertEquals("测试企业", tenant.getValue().getLegalName());
assertEquals("张三", tenant.getValue().getLegalRepresentative());
assertEquals("注册人", tenant.getValue().getContactName());
assertEquals("测试企业", tenant.getValue().getLegalName());
assertEquals("张三", tenant.getValue().getLegalRepresentative());
assertEquals("注册人", tenant.getValue().getContactName());
assertEquals(9001L, user.getValue().getAuthUserId());
assertEquals("ACTIVE", user.getValue().getStatus());
assertEquals(result.tenantId(), membership.getValue().getTenantId());
@@ -124,7 +127,7 @@ class MerchantOnboardingServiceTest {
}
@Test
void clientRegistrationFailsWhenDurableInitializationIntentCannotBePersisted() {
void clientRegistrationFailsWhenFourDefaultBoxDimensionsCannotBeInitialized() {
IamRole ownerRole = new IamRole();
ownerRole.setId(4000100L);
ownerRole.setTenantId(0L);
@@ -133,14 +136,14 @@ class MerchantOnboardingServiceTest {
ownerRole.setStatus("ACTIVE");
when(roles.selectOne(any())).thenReturn(ownerRole);
org.mockito.Mockito.doThrow(new com.baiyue.common.core.exception.BizException(
com.baiyue.common.core.api.ResultCode.SERVICE_UNAVAILABLE, "初始化失败"))
com.baiyue.common.core.api.ResultCode.SERVICE_UNAVAILABLE, "初始化失败"))
.when(dimensionRuleInitialization).initialize(
org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong());
var failure = assertThrows(com.baiyue.common.core.exception.BizException.class,
() -> service.register(new MerchantRegistrationRequest(
9001L, "注册人", "测试企业", "张三", "13800138000",
"owner@example.com", "CN", "深圳市宝安区")));
9001L, "注册人", "测试企业", "张三", "13800138000",
"owner@example.com", "CN", "深圳市宝安区")));
assertEquals(com.baiyue.common.core.api.ResultCode.SERVICE_UNAVAILABLE, failure.getResultCode());
}
@@ -190,7 +193,7 @@ class MerchantOnboardingServiceTest {
when(qualifications.selectCount(any())).thenReturn(0L);
when(reviews.selectCount(any())).thenReturn(0L);
org.mockito.Mockito.doThrow(new com.baiyue.common.core.exception.BizException(
com.baiyue.common.core.api.ResultCode.SERVICE_UNAVAILABLE, "补偿失败"))
com.baiyue.common.core.api.ResultCode.SERVICE_UNAVAILABLE, "补偿失败"))
.when(dimensionRuleInitialization).compensateRegistration(10001L);
assertThrows(com.baiyue.common.core.exception.BizException.class,
@@ -201,3 +204,4 @@ class MerchantOnboardingServiceTest {
verify(tenants, never()).deleteById((java.io.Serializable) any());
}
}
@@ -151,22 +151,22 @@ class TenantServiceTest {
var updated = service.updateMerchant(10001L,
new UpdateTenantRequest("New name", "New legal", "Bob", "Alice", "13800138000", "alice@example.com",
"专业版", "alice_wechat", "Sales Zhang", 45, "LIMITED_COMPANY", LocalDate.of(2020, 1, 2),
new BigDecimal("5000000.00"), "CNY", "深圳市南山区示例路 1 号", "一期重点客户"));
"专业版", "alice_wechat", "Sales Zhang", 45, "LIMITED_COMPANY", LocalDate.of(2020, 1, 2),
new BigDecimal("5000000.00"), "CNY", "深圳市南山区示例路 1 号", "一期重点客户"));
var disabled = service.changeMerchantStatus(10001L, new ChangeTenantStatusRequest("DISABLED"));
assertEquals("New name", updated.tenantName());
assertEquals("Bob", updated.legalRepresentative());
assertEquals("alice_wechat", updated.contactWechat());
assertEquals("专业版", updated.servicePackage());
assertEquals("专业版", updated.servicePackage());
assertEquals("Sales Zhang", updated.salesOwnerName());
assertEquals(45, updated.paymentTermDays());
assertEquals("LIMITED_COMPANY", updated.enterpriseType());
assertEquals(LocalDate.of(2020, 1, 2), updated.establishedDate());
assertEquals(new BigDecimal("5000000.00"), updated.registeredCapital());
assertEquals("CNY", updated.registeredCapitalCurrency());
assertEquals("深圳市南山区示例路 1 号", updated.registeredAddress());
assertEquals("一期重点客户", updated.internalRemark());
assertEquals("深圳市南山区示例路 1 号", updated.registeredAddress());
assertEquals("一期重点客户", updated.internalRemark());
assertEquals("DISABLED", disabled.status());
verify(repository, org.mockito.Mockito.times(2)).saveTenant(tenant);
}
@@ -178,7 +178,7 @@ class TenantServiceTest {
BizException exception = assertThrows(BizException.class, () -> service.updateMerchant(10001L,
new UpdateTenantRequest("New name", "New legal", "Bob", "Alice", "13900139000", null,
"基础版", null, null, 30, null, null, null, "CNY", null, null)));
"基础版", null, null, 30, null, null, null, "CNY", null, null)));
assertEquals(ResultCode.CONFLICT, exception.getResultCode());
assertEquals("13800138000", tenant.getContactPhone());
@@ -273,7 +273,7 @@ class TenantServiceTest {
BizException platformFailure = assertThrows(BizException.class, () -> service.updateMerchant(10001L,
new UpdateTenantRequest("New name", "New legal", "Bob", "Alice", "13800138000", null,
"基础版", null, null, 30, null, null, null, "CNY", null, null)));
"基础版", null, null, 30, null, null, null, "CNY", null, null)));
BizException merchantFailure = assertThrows(BizException.class, () -> service.update(10001L,
new UpdateProfileRequest("New name", "New legal", "Bob", "Alice",
"13800138000", "alice@example.com")));
@@ -308,32 +308,32 @@ class TenantServiceTest {
var created = service.register(new RegisterRequest(
"Example Merchant", "Example Merchant Ltd", "Bob", "Alice", "13800138000",
"alice@example.com", "基础版", "alice_wechat", "Sales Zhang", 30, "LIMITED_COMPANY",
LocalDate.of(2020, 1, 2), new BigDecimal("5000000.00"), "CNY", "深圳市南山区", "草稿客户"),
"alice@example.com", "基础版", "alice_wechat", "Sales Zhang", 30, "LIMITED_COMPANY",
LocalDate.of(2020, 1, 2), new BigDecimal("5000000.00"), "CNY", "深圳市南山区", "草稿客户"),
9001L);
assertEquals("PROFILE_REQUIRED", created.tenant().status());
assertEquals("PENDING", created.tenant().dimensionInitializationStatus());
assertEquals("Bob", created.tenant().legalRepresentative());
assertEquals("基础版", created.tenant().servicePackage());
assertEquals("基础版", created.tenant().servicePackage());
assertEquals("13800138000", created.administratorUsername());
assertEquals("Password1234", created.temporaryPassword());
verify(repository).insertTenant(argThat(tenant -> Long.valueOf(9001L).equals(tenant.getCreatedBy())
&& Long.valueOf(9001L).equals(tenant.getUpdatedBy())));
verify(dimensionRuleInitialization).initialize(created.tenant().id(), 9001L);
verify(securityEvents).tenantStatus(created.tenant().id(), "PROFILE_REQUIRED");
verify(securityEvents).tenantStatus(created.tenant().id(), "PROFILE_REQUIRED");
verify(provisioning).provisionPlatformCreatedTenant(any(), eq(9001L));
verify(repository, never()).insertReview(any());
}
@Test
void platformRegistrationFailsWhenDurableInitializationIntentCannotBePersisted() {
doThrow(new BizException(ResultCode.SERVICE_UNAVAILABLE, "初始化失败"))
void platformRegistrationFailsWhenFourDefaultBoxDimensionsCannotBeInitialized() {
doThrow(new BizException(ResultCode.SERVICE_UNAVAILABLE, "初始化失败"))
.when(dimensionRuleInitialization).initialize(any(Long.class), any(Long.class));
BizException failure = assertThrows(BizException.class, () -> service.register(new RegisterRequest(
"Example Merchant", "Example Merchant Ltd", "Bob", "Alice", "13800138000",
"alice@example.com", "基础版", null, null, 30, null,
"alice@example.com", "基础版", null, null, 30, null,
null, null, "CNY", null, null), 9001L));
assertEquals(ResultCode.SERVICE_UNAVAILABLE, failure.getResultCode());
@@ -347,7 +347,7 @@ class TenantServiceTest {
var created = service.register(new RegisterRequest(
"Example Merchant", "Example Merchant Ltd", "Bob", "Alice", "13800138000",
"alice@example.com", "基础版", null, null, 30, null,
"alice@example.com", "基础版", null, null, 30, null,
null, null, "CNY", null, null), 9001L);
assertEquals(null, created.tenant().unifiedSocialCreditCode());
@@ -420,7 +420,7 @@ class TenantServiceTest {
TenantAdministratorProvisioningService.ProvisioningResult.created(
81L, "merchant_10001", "Password1234")).when(provisioning).initialize(any());
var result = service.review(71L, 9001L, true, "资料完整");
var result = service.review(71L, 9001L, true, "资料完整");
assertEquals(10001L, result.tenantId());
assertEquals(true, result.administratorCreated());
@@ -431,6 +431,7 @@ class TenantServiceTest {
verify(repository).saveReview(review);
verify(repository).saveTenant(tenant);
verify(securityEvents).tenantStatus(10001L, "ACTIVE");
verify(securityEvents).tenantStatus(10001L, "ACTIVE");
}
@Test
@@ -446,7 +447,7 @@ class TenantServiceTest {
doReturn(TenantAdministratorProvisioningService.ProvisioningResult.existing())
.when(provisioning).initialize(any());
var result = service.review(71L, 9001L, true, "资料完整");
var result = service.review(71L, 9001L, true, "资料完整");
assertEquals(false, result.administratorCreated());
assertEquals(null, result.administratorUsername());
@@ -470,7 +471,7 @@ class TenantServiceTest {
doReturn(administrator).when(provisioning).initialize(any());
doThrow(new RuntimeException("review write failed")).when(repository).saveReview(review);
assertThrows(RuntimeException.class, () -> service.review(71L, 9001L, true, "资料完整"));
assertThrows(RuntimeException.class, () -> service.review(71L, 9001L, true, "资料完整"));
verify(provisioning).compensate(any(), eq(administrator));
verify(repository, never()).saveTenant(tenant);
@@ -492,26 +493,12 @@ class TenantServiceTest {
assertEquals(true, withQualification.eligible());
}
@Test
void activeCustomerCannotOrderUntilDefaultDimensionsAreReady() {
IamTenant tenant = merchant(10001L, "ACTIVE");
tenant.setDimensionInitializationStatus("FAILED");
when(repository.findTenant(10001L)).thenReturn(tenant);
var result = service.orderEligibility(10001L);
assertEquals(false, result.eligible());
assertEquals("Customer default dimensions are not ready", result.reason());
verify(profileRepository, never()).findQualifications(10001L);
}
private IamTenant merchant(long id, String status) {
IamTenant tenant = new IamTenant();
tenant.setId(id);
tenant.setTenantCode("TEN" + id);
tenant.setTenantType("MERCHANT");
tenant.setStatus(status);
tenant.setDimensionInitializationStatus("READY");
tenant.setTenantName("Example Merchant");
tenant.setLegalName("Example Merchant Ltd");
tenant.setUnifiedSocialCreditCode("91330100EXAMPLE001");
@@ -544,3 +531,4 @@ class TenantServiceTest {
return review;
}
}
@@ -130,19 +130,6 @@ test('every reviewed synchronous client has a complete and exclusive executable
}
})
test('IAM customer dimension Feign is reachable only from the background relay', async () => {
const references = []
for (const file of await javaFiles(path.join(root, 'iam', 'src', 'main', 'java'))) {
const source = await readFile(file, 'utf8')
if (source.includes('CustomerDimensionRuleClient')) {
references.push(path.relative(root, file).replaceAll('\\', '/'))
}
}
assert.deepEqual(references.sort(), [
'iam/src/main/java/com/baiyue/iam/client/CustomerDimensionRuleClient.java',
'iam/src/main/java/com/baiyue/iam/service/CustomerDimensionInitializationRelay.java'
])
})
test('all four Lastleg RestClient gateways use explicit bounded transports', async () => {
const clients = new Map([