diff --git a/auth/src/test/java/com/baiyue/auth/service/MerchantRegistrationServiceTest.java b/auth/src/test/java/com/baiyue/auth/service/MerchantRegistrationServiceTest.java index fd61c5925..b09d43d33 100644 --- a/auth/src/test/java/com/baiyue/auth/service/MerchantRegistrationServiceTest.java +++ b/auth/src/test/java/com/baiyue/auth/service/MerchantRegistrationServiceTest.java @@ -54,6 +54,8 @@ class MerchantRegistrationServiceTest { assertEquals(30001L, storedAccount.getValue().getDefaultMembershipId()); assertEquals("MERCHANT", storedAccount.getValue().getDefaultAppCode()); assertEquals("ACTIVE", storedAccount.getValue().getAccountStatus()); + assertEquals("13800138000", storedAccount.getValue().getPhone()); + assertEquals("13800138000", onboardingRequest.getValue().administratorPhone()); assertEquals("encoded-password", storedAccount.getValue().getPasswordHash()); assertEquals("LOGIN_AND_COMPLETE_QUALIFICATIONS", result.nextStep()); assertEquals("PENDING", result.dimensionInitializationStatus()); diff --git a/iam/src/main/java/com/baiyue/iam/dto/MemberDtos.java b/iam/src/main/java/com/baiyue/iam/dto/MemberDtos.java index e8cb9ce4b..77b6fd492 100644 --- a/iam/src/main/java/com/baiyue/iam/dto/MemberDtos.java +++ b/iam/src/main/java/com/baiyue/iam/dto/MemberDtos.java @@ -1,5 +1,6 @@ package com.baiyue.iam.dto; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotEmpty; @@ -18,6 +19,7 @@ public final class MemberDtos { @NotBlank @Size(min = 8, max = 64) @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d).+$", message = "密码必须同时包含字母和数字") String password, @NotBlank @Size(max = 64) String displayName, + @Schema(description = "成员登录手机号;同步 Auth 且与公司联系电话独立") @Pattern(regexp = "^1[3-9]\\d{9}$") String phone, @Email @Size(max = 128) String email, @NotBlank String memberType, @@ -30,6 +32,7 @@ public final class MemberDtos { public record UpdateMemberRequest( @NotBlank @Size(max = 64) String displayName, + @Schema(description = "成员登录手机号;变更时同步 Auth、撤销该账号全部会话并审计") @Pattern(regexp = "^1[3-9]\\d{9}$") String phone, @Email @Size(max = 128) String email, @NotBlank String memberType, diff --git a/iam/src/main/java/com/baiyue/iam/dto/PlatformUserManagementDtos.java b/iam/src/main/java/com/baiyue/iam/dto/PlatformUserManagementDtos.java index 1056a6519..a90f46028 100644 --- a/iam/src/main/java/com/baiyue/iam/dto/PlatformUserManagementDtos.java +++ b/iam/src/main/java/com/baiyue/iam/dto/PlatformUserManagementDtos.java @@ -1,5 +1,6 @@ package com.baiyue.iam.dto; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -16,6 +17,7 @@ public final class PlatformUserManagementDtos { public record CreateUserRequest( @NotBlank @Pattern(regexp = "^[A-Za-z][A-Za-z0-9_.@-]{3,63}$") String username, @NotBlank @Size(max = 64) String displayName, + @Schema(description = "平台账号登录手机号;与企业联系电话独立") @NotBlank @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") String phone, @Email @Size(max = 128) String email, @NotNull Long tenantId, @@ -32,6 +34,7 @@ public final class PlatformUserManagementDtos { public record UpdateUserRequest( @NotBlank @Size(max = 64) String displayName, + @Schema(description = "平台账号登录手机号;变更时同步 Auth、撤销该账号全部会话并审计") @NotBlank @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") String phone, @Email @Size(max = 128) String email, @NotNull Long tenantId, diff --git a/iam/src/test/java/com/baiyue/iam/service/TenantAdministratorProvisioningServiceTest.java b/iam/src/test/java/com/baiyue/iam/service/TenantAdministratorProvisioningServiceTest.java index 8e76d8a1d..bb874cce6 100644 --- a/iam/src/test/java/com/baiyue/iam/service/TenantAdministratorProvisioningServiceTest.java +++ b/iam/src/test/java/com/baiyue/iam/service/TenantAdministratorProvisioningServiceTest.java @@ -88,7 +88,7 @@ class TenantAdministratorProvisioningServiceTest { var result = service.initialize(context()); assertTrue(result.created()); - assertEquals("13800138000", result.username()); + assertEquals("merchant_" + result.authUserId(), result.username()); assertNotNull(result.temporaryPassword()); assertEquals(16, result.temporaryPassword().length()); assertTrue(result.temporaryPassword().matches("^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z0-9]+$")); @@ -97,6 +97,7 @@ class TenantAdministratorProvisioningServiceTest { ArgumentCaptor.forClass(AuthAccountClient.CreateAccountRequest.class); verify(auth).create(eq("internal-token"), account.capture()); assertEquals(result.username(), account.getValue().username()); + assertEquals("13800138000", account.getValue().phone()); assertEquals(result.temporaryPassword(), account.getValue().password()); assertEquals("MERCHANT", account.getValue().appCode()); assertEquals(tenant.getId(), account.getValue().tenantId()); @@ -243,7 +244,7 @@ class TenantAdministratorProvisioningServiceTest { } @Test - void usernameConflictStopsProvisioningInsteadOfChangingTheContactPhoneUsername() { + void loginPhoneConflictStopsProvisioningWithoutCouplingCompanyContactToUsername() { org.mockito.Mockito.doReturn(ApiResponse.failure(com.baiyue.common.core.api.ResultCode.CONFLICT, "登录账号已存在", null, null)).when(auth).create(anyString(), 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 6233f0642..e25b98c87 100644 --- a/iam/src/test/java/com/baiyue/iam/service/TenantServiceTest.java +++ b/iam/src/test/java/com/baiyue/iam/service/TenantServiceTest.java @@ -19,6 +19,7 @@ import com.baiyue.iam.repository.MemberRepository; import com.baiyue.iam.mapper.IamRoleMapper; import com.baiyue.iam.client.AuthAccountClient; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.TestPropertySource; @@ -60,6 +61,12 @@ class TenantServiceTest { @SpyBean private TenantAdministratorProvisioningService provisioning; @Resource private TenantService service; + @BeforeEach + void allowUnusedAdministratorPhone() { + when(authClient.availability(eq("internal-token"), any())) + .thenReturn(com.baiyue.common.core.api.ApiResponse.success(true)); + } + @Test void pageMerchantsMapsTenantEntitiesToApiResponses() { @@ -172,25 +179,25 @@ class TenantServiceTest { } @Test - void activeCustomerCannotChangeTheContactPhoneUsedAsItsLoginUsername() { + void activeCustomerCanChangeCompanyContactPhoneWithoutChangingAnyLoginIdentity() { IamTenant tenant = merchant(10001L, "ACTIVE"); when(repository.findTenant(10001L)).thenReturn(tenant); - BizException exception = assertThrows(BizException.class, () -> service.updateMerchant(10001L, + var updated = 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()); - verify(repository, never()).saveTenant(tenant); + assertEquals("13900139000", updated.contactPhone()); + verify(repository).saveTenant(tenant); + verify(authClient, never()).phone(any(), any(), any()); } @Test - void contactPhoneAvailabilityAlsoChecksTheAuthUsernameFactSource() { + void administratorPhoneAvailabilityChecksOnlyTheAuthLoginFactSource() { when(authClient.availability("internal-token", "13800138000")) .thenReturn(com.baiyue.common.core.api.ApiResponse.success(false)); - assertEquals(false, service.contactPhoneAndUsernameAvailable("13800138000", null)); + assertEquals(false, service.administratorPhoneAvailable("13800138000")); verify(authClient).availability("internal-token", "13800138000"); } @@ -303,7 +310,7 @@ class TenantServiceTest { @Test void platformRegistrationCreatesDraftWithoutCreatingReviewTicket() { doReturn(TenantAdministratorProvisioningService.ProvisioningResult.created( - 81L, "13800138000", "Password1234")) + 81L, "merchant_81", "Password1234")) .when(provisioning).provisionPlatformCreatedTenant(any(), eq(9001L)); var created = service.register(new RegisterRequest( @@ -316,7 +323,7 @@ class TenantServiceTest { assertEquals("PENDING", created.tenant().dimensionInitializationStatus()); assertEquals("Bob", created.tenant().legalRepresentative()); assertEquals("基础版", created.tenant().servicePackage()); - assertEquals("13800138000", created.administratorUsername()); + assertEquals("merchant_81", created.administratorUsername()); assertEquals("Password1234", created.temporaryPassword()); verify(repository).insertTenant(argThat(tenant -> Long.valueOf(9001L).equals(tenant.getCreatedBy()) && Long.valueOf(9001L).equals(tenant.getUpdatedBy()))); @@ -342,7 +349,7 @@ class TenantServiceTest { @Test void platformRegistrationAlwaysCreatesTheDraftWithoutACreditCode() { doReturn(TenantAdministratorProvisioningService.ProvisioningResult.created( - 81L, "13800138000", "Password1234")) + 81L, "merchant_81", "Password1234")) .when(provisioning).provisionPlatformCreatedTenant(any(), eq(9001L)); var created = service.register(new RegisterRequest(