From 505d448850428ec70ce5a9e5e4bfc534fa932828 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Tue, 25 Aug 2026 16:41:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/nacos/cn/iam-service.yml | 6 +-- iam/src/main/resources/application.yml | 6 +-- .../MerchantOnboardingServiceTest.java | 30 +++++------ .../baiyue/iam/service/TenantServiceTest.java | 52 ++++++++++++------- .../synchronous-dependency-contract.test.mjs | 13 +++++ 5 files changed, 64 insertions(+), 43 deletions(-) diff --git a/config/nacos/cn/iam-service.yml b/config/nacos/cn/iam-service.yml index 52b571129..f735164e7 100644 --- a/config/nacos/cn/iam-service.yml +++ b/config/nacos/cn/iam-service.yml @@ -7,10 +7,10 @@ spring: baiyue: security-event: event-bridge-url: ${SECURITY_EVENT_BRIDGE_URL:http://event-bridge-overseas:9312} - 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} notification: scheduler-enabled: ${NOTIFICATION_SCHEDULER_ENABLED:false} dispatch-timeout-seconds: ${NOTIFICATION_DISPATCH_TIMEOUT_SECONDS:300} mq-consumer-enabled: ${NOTIFICATION_MQ_CONSUMER_ENABLED:false} - diff --git a/iam/src/main/resources/application.yml b/iam/src/main/resources/application.yml index c01cc552b..5faf2522f 100644 --- a/iam/src/main/resources/application.yml +++ b/iam/src/main/resources/application.yml @@ -25,8 +25,9 @@ spring: baiyue: security-event: event-bridge-url: ${SECURITY_EVENT_BRIDGE_URL:http://event-bridge-overseas:9312} - 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} notification: scheduler-enabled: ${NOTIFICATION_SCHEDULER_ENABLED:false} dispatch-timeout-seconds: ${NOTIFICATION_DISPATCH_TIMEOUT_SECONDS:300} @@ -49,4 +50,3 @@ 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}" - diff --git a/iam/src/test/java/com/baiyue/iam/service/MerchantOnboardingServiceTest.java b/iam/src/test/java/com/baiyue/iam/service/MerchantOnboardingServiceTest.java index c0d166639..2b48686ef 100644 --- a/iam/src/test/java/com/baiyue/iam/service/MerchantOnboardingServiceTest.java +++ b/iam/src/test/java/com/baiyue/iam/service/MerchantOnboardingServiceTest.java @@ -42,7 +42,6 @@ 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() { @@ -56,8 +55,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 tenant = ArgumentCaptor.forClass(IamTenant.class); ArgumentCaptor user = ArgumentCaptor.forClass(IamUser.class); @@ -68,21 +67,19 @@ 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(securityEvents).tenantStatus(result.tenantId(), "PROFILE_REQUIRED"); - verify(securityEvents).userStatus(9001L, result.tenantId(), "MERCHANT", "ACTIVE"); - verify(securityEvents).permissionChanged(9001L, result.tenantId(), "MERCHANT", - "INITIAL_AUTHORIZATION"); + 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"); 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()); @@ -127,7 +124,7 @@ class MerchantOnboardingServiceTest { } @Test - void clientRegistrationFailsWhenFourDefaultBoxDimensionsCannotBeInitialized() { + void clientRegistrationFailsWhenDurableInitializationIntentCannotBePersisted() { IamRole ownerRole = new IamRole(); ownerRole.setId(4000100L); ownerRole.setTenantId(0L); @@ -136,14 +133,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()); } @@ -193,7 +190,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, @@ -204,4 +201,3 @@ class MerchantOnboardingServiceTest { verify(tenants, never()).deleteById((java.io.Serializable) any()); } } - diff --git a/iam/src/test/java/com/baiyue/iam/service/TenantServiceTest.java b/iam/src/test/java/com/baiyue/iam/service/TenantServiceTest.java index a7886a304..eb7bb54dc 100644 --- a/iam/src/test/java/com/baiyue/iam/service/TenantServiceTest.java +++ b/iam/src/test/java/com/baiyue/iam/service/TenantServiceTest.java @@ -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 platformRegistrationFailsWhenFourDefaultBoxDimensionsCannotBeInitialized() { - doThrow(new BizException(ResultCode.SERVICE_UNAVAILABLE, "初始化失败")) + void platformRegistrationFailsWhenDurableInitializationIntentCannotBePersisted() { + 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,7 +431,6 @@ class TenantServiceTest { verify(repository).saveReview(review); verify(repository).saveTenant(tenant); verify(securityEvents).tenantStatus(10001L, "ACTIVE"); - verify(securityEvents).tenantStatus(10001L, "ACTIVE"); } @Test @@ -447,7 +446,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()); @@ -471,7 +470,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); @@ -493,12 +492,26 @@ 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"); @@ -531,4 +544,3 @@ class TenantServiceTest { return review; } } - diff --git a/scripts/synchronous-dependency-contract.test.mjs b/scripts/synchronous-dependency-contract.test.mjs index a9b69530f..fa082a7fd 100644 --- a/scripts/synchronous-dependency-contract.test.mjs +++ b/scripts/synchronous-dependency-contract.test.mjs @@ -130,6 +130,19 @@ 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([