From 4478351f89e5dc07db5647d3877a8260b794f57e Mon Sep 17 00:00:00 2001 From: hailin Date: Sun, 14 Dec 2025 00:15:09 -0800 Subject: [PATCH] =?UTF-8?q?fix(authorization):=20=E4=BF=AE=E5=A4=8D=20gran?= =?UTF-8?q?tProvinceCompany=20=E4=B8=9A=E5=8A=A1=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加省区域/市区域互斥检查:同一用户不能同时拥有两种身份 - 添加省区域全局唯一性检查:整个系统只允许一个省区域角色被授权 - 添加 findAnyProvinceCompany 仓储方法用于全局唯一性校验 - 移除错误的 validateAuthorizationRequest 调用(该方法只适用于团队角色) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../authorization-application.service.ts | 45 +++++++++++++------ .../authorization-role.repository.ts | 5 +++ .../authorization-role.repository.impl.ts | 10 +++++ 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/backend/services/authorization-service/src/application/services/authorization-application.service.ts b/backend/services/authorization-service/src/application/services/authorization-application.service.ts index f77b1623..0823152b 100644 --- a/backend/services/authorization-service/src/application/services/authorization-application.service.ts +++ b/backend/services/authorization-service/src/application/services/authorization-application.service.ts @@ -268,27 +268,46 @@ export class AuthorizationApplicationService { /** * 管理员授权正式省公司(省区域) - * 需要验证团队内唯一性:同一推荐链上不能有重复的相同省份授权 + * + * 业务规则: + * - 同一个用户不能同时拥有省区域和市区域两种身份 + * - 整个系统中只允许一个省区域角色被授权(全局唯一) */ async grantProvinceCompany(command: GrantProvinceCompanyCommand): Promise { const userId = UserId.create(command.userId, command.accountSequence) const adminId = AdminUserId.create(command.adminId, command.adminAccountSequence) - const regionCode = RegionCode.create(command.provinceCode) - // 1. 验证团队内唯一性(同一推荐链上不能有重复的相同省份授权) - const validation = await this.validatorService.validateAuthorizationRequest( - userId, - RoleType.PROVINCE_COMPANY, - regionCode, - this.referralRepository, - this.authorizationRepository, + // 1. 检查用户是否已有市区域授权(省区域和市区域互斥) + const existingCityCompany = await this.authorizationRepository.findByAccountSequenceAndRoleType( + command.accountSequence, + RoleType.CITY_COMPANY, ) - - if (!validation.isValid) { - throw new ApplicationError(validation.errorMessage!) + if (existingCityCompany && existingCityCompany.status !== AuthorizationStatus.REVOKED) { + throw new ApplicationError( + `用户 ${command.accountSequence} 已拥有市区域角色「${existingCityCompany.regionName}」,不能同时拥有省区域角色`, + ) } - // 2. 创建授权 + // 2. 检查用户是否已有省区域授权(一个用户只能有一个省区域) + const existingProvinceCompany = await this.authorizationRepository.findByAccountSequenceAndRoleType( + command.accountSequence, + RoleType.PROVINCE_COMPANY, + ) + if (existingProvinceCompany && existingProvinceCompany.status !== AuthorizationStatus.REVOKED) { + throw new ApplicationError( + `用户 ${command.accountSequence} 已拥有省区域角色「${existingProvinceCompany.regionName}」,不能重复授权`, + ) + } + + // 3. 检查系统中是否已有省区域授权(整个系统全局唯一) + const existingAnyProvince = await this.authorizationRepository.findAnyProvinceCompany() + if (existingAnyProvince) { + throw new ApplicationError( + `系统中已有省区域角色授权给用户 ${existingAnyProvince.userId.accountSequence}(${existingAnyProvince.regionName}),整个系统只允许一个省区域角色`, + ) + } + + // 4. 创建授权 const authorization = AuthorizationRole.createProvinceCompany({ userId, provinceCode: command.provinceCode, diff --git a/backend/services/authorization-service/src/domain/repositories/authorization-role.repository.ts b/backend/services/authorization-service/src/domain/repositories/authorization-role.repository.ts index 548fd7fa..5669d781 100644 --- a/backend/services/authorization-service/src/domain/repositories/authorization-role.repository.ts +++ b/backend/services/authorization-service/src/domain/repositories/authorization-role.repository.ts @@ -96,4 +96,9 @@ export interface IAuthorizationRoleRepository { * 根据社区名称查找社区授权(用于社区名称全局唯一性校验) */ findCommunityByName(communityName: string): Promise + /** + * 查找系统中任何已授权的省区域角色(用于省区域全局唯一性校验) + * 整个系统只允许一个省区域角色被授权 + */ + findAnyProvinceCompany(): Promise } diff --git a/backend/services/authorization-service/src/infrastructure/persistence/repositories/authorization-role.repository.impl.ts b/backend/services/authorization-service/src/infrastructure/persistence/repositories/authorization-role.repository.impl.ts index d4069a58..1096e1d7 100644 --- a/backend/services/authorization-service/src/infrastructure/persistence/repositories/authorization-role.repository.impl.ts +++ b/backend/services/authorization-service/src/infrastructure/persistence/repositories/authorization-role.repository.impl.ts @@ -399,6 +399,16 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi return record ? this.toDomain(record) : null } + async findAnyProvinceCompany(): Promise { + const record = await this.prisma.authorizationRole.findFirst({ + where: { + roleType: RoleType.PROVINCE_COMPANY, + status: AuthorizationStatus.AUTHORIZED, + }, + }) + return record ? this.toDomain(record) : null + } + private toDomain(record: any): AuthorizationRole { const props: AuthorizationRoleProps = { authorizationId: AuthorizationId.create(record.id),