diff --git a/backend/services/authorization-service/src/api/controllers/authorization.controller.ts b/backend/services/authorization-service/src/api/controllers/authorization.controller.ts index 9593dcdd..ab826ca0 100644 --- a/backend/services/authorization-service/src/api/controllers/authorization.controller.ts +++ b/backend/services/authorization-service/src/api/controllers/authorization.controller.ts @@ -38,6 +38,7 @@ import { AuthorizationResponse, ApplyAuthorizationResponse, StickmanRankingResponse, + CommunityHierarchyResponse, } from '@/api/dto/response' import { CurrentUser } from '@/shared/decorators' import { JwtAuthGuard } from '@/shared/guards' @@ -97,6 +98,15 @@ export class AuthorizationController { return await this.applicationService.getUserAuthorizations(user.accountSequence) } + @Get('my/community-hierarchy') + @ApiOperation({ summary: '获取我的社区层级(上级社区和下级社区)' }) + @ApiResponse({ status: 200, type: CommunityHierarchyResponse }) + async getMyCommunityHierarchy( + @CurrentUser() user: { userId: string; accountSequence: number }, + ): Promise { + return await this.applicationService.getCommunityHierarchy(user.accountSequence) + } + @Get(':id') @ApiOperation({ summary: '获取授权详情' }) @ApiParam({ name: 'id', description: '授权ID' }) diff --git a/backend/services/authorization-service/src/api/dto/response/community-hierarchy.response.ts b/backend/services/authorization-service/src/api/dto/response/community-hierarchy.response.ts new file mode 100644 index 00000000..2edf66c6 --- /dev/null +++ b/backend/services/authorization-service/src/api/dto/response/community-hierarchy.response.ts @@ -0,0 +1,52 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' + +/** + * 社区简要信息 + */ +export class CommunityInfo { + @ApiProperty({ description: '授权ID' }) + authorizationId: string + + @ApiProperty({ description: '账户序列号' }) + accountSequence: number + + @ApiProperty({ description: '社区名称' }) + communityName: string + + @ApiPropertyOptional({ description: '用户ID' }) + userId?: string + + @ApiProperty({ description: '是否为总部社区' }) + isHeadquarters: boolean +} + +/** + * 社区层级响应 + */ +export class CommunityHierarchyResponse { + @ApiPropertyOptional({ description: '我的社区授权(如果有)', type: CommunityInfo }) + myCommunity: CommunityInfo | null + + @ApiProperty({ description: '上级社区(最近的,如果没有则为总部社区)', type: CommunityInfo }) + parentCommunity: CommunityInfo + + @ApiProperty({ description: '下级社区列表(最近的,按accountSequence排序)', type: [CommunityInfo] }) + childCommunities: CommunityInfo[] + + @ApiProperty({ description: '是否有上级社区(非总部)' }) + hasParentCommunity: boolean + + @ApiProperty({ description: '下级社区数量' }) + childCommunityCount: number +} + +/** + * 总部社区常量 + */ +export const HEADQUARTERS_COMMUNITY: CommunityInfo = { + authorizationId: 'headquarters', + accountSequence: 0, + communityName: '总部社区', + userId: undefined, + isHeadquarters: true, +} diff --git a/backend/services/authorization-service/src/api/dto/response/index.ts b/backend/services/authorization-service/src/api/dto/response/index.ts index 893ed171..9154a88b 100644 --- a/backend/services/authorization-service/src/api/dto/response/index.ts +++ b/backend/services/authorization-service/src/api/dto/response/index.ts @@ -1 +1,2 @@ export * from './authorization.response' +export * from './community-hierarchy.response' diff --git a/backend/services/authorization-service/src/application/dto/community-hierarchy.dto.ts b/backend/services/authorization-service/src/application/dto/community-hierarchy.dto.ts new file mode 100644 index 00000000..12a105c1 --- /dev/null +++ b/backend/services/authorization-service/src/application/dto/community-hierarchy.dto.ts @@ -0,0 +1,26 @@ +/** + * 社区简要信息 + */ +export interface CommunityInfoDTO { + authorizationId: string + accountSequence: number + communityName: string + userId?: string + isHeadquarters: boolean +} + +/** + * 社区层级响应 DTO + */ +export interface CommunityHierarchyDTO { + /** 我的社区授权(如果有) */ + myCommunity: CommunityInfoDTO | null + /** 上级社区(最近的,如果没有则为总部社区) */ + parentCommunity: CommunityInfoDTO + /** 下级社区列表(最近的) */ + childCommunities: CommunityInfoDTO[] + /** 是否有上级社区(非总部) */ + hasParentCommunity: boolean + /** 下级社区数量 */ + childCommunityCount: number +} diff --git a/backend/services/authorization-service/src/application/dto/index.ts b/backend/services/authorization-service/src/application/dto/index.ts index 87829299..b1333f86 100644 --- a/backend/services/authorization-service/src/application/dto/index.ts +++ b/backend/services/authorization-service/src/application/dto/index.ts @@ -1 +1,2 @@ export * from './authorization.dto' +export * from './community-hierarchy.dto' 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 796aecf4..b3ffd349 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 @@ -22,6 +22,7 @@ import { TeamStatistics, } from '@/domain/services' import { EventPublisherService } from '@/infrastructure/kafka' +import { ReferralServiceClient } from '@/infrastructure/external' import { ApplicationError, NotFoundError } from '@/shared/exceptions' import { ApplyCommunityAuthCommand, @@ -37,7 +38,7 @@ import { GrantMonthlyBypassCommand, ExemptLocalPercentageCheckCommand, } from '@/application/commands' -import { AuthorizationDTO, StickmanRankingDTO } from '@/application/dto' +import { AuthorizationDTO, StickmanRankingDTO, CommunityHierarchyDTO } from '@/application/dto' export const REFERRAL_REPOSITORY = Symbol('IReferralRepository') export const TEAM_STATISTICS_REPOSITORY = Symbol('ITeamStatisticsRepository') @@ -57,6 +58,7 @@ export class AuthorizationApplicationService { @Inject(TEAM_STATISTICS_REPOSITORY) private readonly statsRepository: ITeamStatisticsRepository, private readonly eventPublisher: EventPublisherService, + private readonly referralServiceClient: ReferralServiceClient, ) {} /** @@ -493,4 +495,130 @@ export class AuthorizationApplicationService { updatedAt: auth.updatedAt, } } + + /** + * 获取用户的社区层级信息 + * - myCommunity: 我的社区授权(如果有) + * - parentCommunity: 上级社区(沿推荐链往上找最近的,如果没有则返回总部社区) + * - childCommunities: 下级社区(在我的团队中找最近的社区) + */ + async getCommunityHierarchy(accountSequence: number): Promise { + this.logger.debug(`[getCommunityHierarchy] accountSequence=${accountSequence}`) + + // 1. 查询我的社区授权 + const myCommunity = await this.authorizationRepository.findByAccountSequenceAndRoleType( + BigInt(accountSequence), + RoleType.COMMUNITY, + ) + + // 2. 获取我的祖先链(推荐链) + const ancestorAccountSequences = await this.referralServiceClient.getReferralChain(BigInt(accountSequence)) + this.logger.debug(`[getCommunityHierarchy] ancestorPath: ${ancestorAccountSequences.join(',')}`) + + // 3. 查找上级社区(在祖先链中找最近的有社区授权的用户) + let parentCommunityAuth: AuthorizationRole | null = null + if (ancestorAccountSequences.length > 0) { + const ancestorCommunities = await this.authorizationRepository.findActiveCommunityByAccountSequences( + ancestorAccountSequences.map((seq) => BigInt(seq)), + ) + + // 找最近的(ancestorAccountSequences 是从直接推荐人到根节点的顺序) + if (ancestorCommunities.length > 0) { + // 按祖先链顺序找第一个匹配的 + for (const ancestorSeq of ancestorAccountSequences) { + const found = ancestorCommunities.find( + (auth) => Number(auth.userId.accountSequence) === ancestorSeq, + ) + if (found) { + parentCommunityAuth = found + break + } + } + } + } + + // 4. 获取我的团队成员 + const teamMemberAccountSequences = await this.referralServiceClient.getTeamMembers(BigInt(accountSequence)) + this.logger.debug(`[getCommunityHierarchy] teamMembers count: ${teamMemberAccountSequences.length}`) + + // 5. 查找下级社区(在团队成员中找最近的有社区授权的用户) + // "最近" 的定义:直接下级优先,然后是下级的下级,以此类推 + // 由于 getTeamMembers 返回的是广度优先遍历结果,可以直接使用顺序 + let childCommunityAuths: AuthorizationRole[] = [] + if (teamMemberAccountSequences.length > 0) { + const teamCommunities = await this.authorizationRepository.findActiveCommunityByAccountSequences( + teamMemberAccountSequences.map((seq) => BigInt(seq)), + ) + + // 只保留"最近的"下级社区 + // 如果一个社区的上级不在我的直接团队成员中,或者其上级就是我,则它是"最近的" + // 简化实现:返回所有团队中的社区,前端可以根据需要过滤 + // 但按用户要求"只计算最近的那个",这里需要做过滤 + // 算法:如果某个社区 A 的祖先中有另一个社区 B 也在团队中,则 A 不是最近的 + + const communityAccountSeqs = new Set(teamCommunities.map((c) => Number(c.userId.accountSequence))) + + for (const comm of teamCommunities) { + // 获取这个社区成员的祖先链 + const commAncestors = await this.referralServiceClient.getReferralChain(comm.userId.accountSequence) + + // 检查这个社区是否有"更近"的祖先社区 + let hasCloserAncestorCommunity = false + for (const ancestorSeq of commAncestors) { + // 如果祖先是我,停止检查 + if (ancestorSeq === accountSequence) { + break + } + // 如果祖先也是社区且在我的团队中,则当前社区不是最近的 + if (communityAccountSeqs.has(ancestorSeq)) { + hasCloserAncestorCommunity = true + break + } + } + + if (!hasCloserAncestorCommunity) { + childCommunityAuths.push(comm) + } + } + } + + // 6. 构建响应 + const HEADQUARTERS_COMMUNITY = { + authorizationId: 'headquarters', + accountSequence: 0, + communityName: '总部社区', + userId: undefined, + isHeadquarters: true, + } + + return { + myCommunity: myCommunity && myCommunity.status === AuthorizationStatus.AUTHORIZED + ? { + authorizationId: myCommunity.authorizationId.value, + accountSequence: Number(myCommunity.userId.accountSequence), + communityName: myCommunity.displayTitle, + userId: myCommunity.userId.value, + isHeadquarters: false, + } + : null, + parentCommunity: parentCommunityAuth + ? { + authorizationId: parentCommunityAuth.authorizationId.value, + accountSequence: Number(parentCommunityAuth.userId.accountSequence), + communityName: parentCommunityAuth.displayTitle, + userId: parentCommunityAuth.userId.value, + isHeadquarters: false, + } + : HEADQUARTERS_COMMUNITY, + childCommunities: childCommunityAuths.map((auth) => ({ + authorizationId: auth.authorizationId.value, + accountSequence: Number(auth.userId.accountSequence), + communityName: auth.displayTitle, + userId: auth.userId.value, + isHeadquarters: false, + })), + hasParentCommunity: parentCommunityAuth !== null, + childCommunityCount: childCommunityAuths.length, + } + } } 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 c7745e37..d18be749 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 @@ -24,4 +24,8 @@ export interface IAuthorizationRoleRepository { findPendingByUserId(userId: UserId): Promise findByStatus(status: AuthorizationStatus): Promise delete(authorizationId: AuthorizationId): Promise + /** + * 批量查询指定 accountSequence 列表中具有活跃社区授权的用户 + */ + findActiveCommunityByAccountSequences(accountSequences: bigint[]): Promise } diff --git a/backend/services/authorization-service/src/infrastructure/external/referral-service.client.ts b/backend/services/authorization-service/src/infrastructure/external/referral-service.client.ts index e138d4e7..e926b392 100644 --- a/backend/services/authorization-service/src/infrastructure/external/referral-service.client.ts +++ b/backend/services/authorization-service/src/infrastructure/external/referral-service.client.ts @@ -16,6 +16,24 @@ interface ReferralTeamStatsResponse { provinceCityDistribution: Record> | null; } +/** + * 推荐链数据接口 + */ +interface ReferralChainResponse { + accountSequence: number; + userId: string | null; + ancestorPath: string[]; + referrerId: string | null; +} + +/** + * 团队成员数据接口 + */ +interface TeamMembersResponse { + accountSequence: number; + teamMembers: number[]; +} + /** * 适配器类:将 referral-service 返回的数据转换为 authorization-service 需要的格式 */ @@ -159,4 +177,61 @@ export class ReferralServiceClient implements ITeamStatisticsRepository, OnModul private createEmptyStats(userId: string, accountSequence: bigint): TeamStatistics { return new TeamStatisticsAdapter(userId, accountSequence, 0, null); } + + /** + * 获取用户的祖先链(推荐链) + * 返回从直接推荐人到根节点的 accountSequence 列表 + */ + async getReferralChain(accountSequence: bigint): Promise { + if (!this.enabled) { + this.logger.debug('[DISABLED] Referral service integration is disabled'); + return []; + } + + try { + this.logger.debug(`[HTTP] GET /internal/referral-chain/${accountSequence}`); + + const response = await this.httpClient.get( + `/api/v1/internal/referral-chain/${accountSequence}`, + ); + + if (!response.data || !response.data.ancestorPath) { + return []; + } + + // ancestorPath 存储的是 userId (bigint string),我们需要映射到 accountSequence + // 由于 referral-service 中 userId = BigInt(accountSequence),可以直接转换 + return response.data.ancestorPath.map((id) => Number(id)); + } catch (error) { + this.logger.error(`[HTTP] Failed to get referral chain for accountSequence ${accountSequence}:`, error); + return []; + } + } + + /** + * 获取用户的团队成员 accountSequence 列表 + */ + async getTeamMembers(accountSequence: bigint): Promise { + if (!this.enabled) { + this.logger.debug('[DISABLED] Referral service integration is disabled'); + return []; + } + + try { + this.logger.debug(`[HTTP] GET /internal/referral-chain/${accountSequence}/team-members`); + + const response = await this.httpClient.get( + `/api/v1/internal/referral-chain/${accountSequence}/team-members`, + ); + + if (!response.data || !response.data.teamMembers) { + return []; + } + + return response.data.teamMembers; + } catch (error) { + this.logger.error(`[HTTP] Failed to get team members for accountSequence ${accountSequence}:`, error); + return []; + } + } } 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 ab133826..edbf1a9f 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 @@ -176,6 +176,25 @@ export class AuthorizationRoleRepositoryImpl implements IAuthorizationRoleReposi }) } + async findActiveCommunityByAccountSequences( + accountSequences: bigint[], + ): Promise { + if (accountSequences.length === 0) { + return [] + } + + const records = await this.prisma.authorizationRole.findMany({ + where: { + accountSequence: { in: accountSequences }, + roleType: RoleType.COMMUNITY, + status: AuthorizationStatus.AUTHORIZED, + benefitActive: true, + }, + orderBy: { accountSequence: 'asc' }, + }) + return records.map((record) => this.toDomain(record)) + } + private toDomain(record: any): AuthorizationRole { const props: AuthorizationRoleProps = { authorizationId: AuthorizationId.create(record.id), diff --git a/backend/services/authorization-service/test/domain-services.integration-spec.ts b/backend/services/authorization-service/test/domain-services.integration-spec.ts index b7589165..bdedb722 100644 --- a/backend/services/authorization-service/test/domain-services.integration-spec.ts +++ b/backend/services/authorization-service/test/domain-services.integration-spec.ts @@ -29,6 +29,7 @@ describe('Domain Services Integration Tests', () => { delete: jest.fn(), findByAccountSequenceAndRoleType: jest.fn(), findByAccountSequence: jest.fn(), + findActiveCommunityByAccountSequences: jest.fn(), } const mockMonthlyAssessmentRepository: jest.Mocked = { diff --git a/backend/services/referral-service/src/api/controllers/index.ts b/backend/services/referral-service/src/api/controllers/index.ts index 73ce4ef2..1f005e58 100644 --- a/backend/services/referral-service/src/api/controllers/index.ts +++ b/backend/services/referral-service/src/api/controllers/index.ts @@ -1,4 +1,5 @@ export * from './referral.controller'; export * from './team-statistics.controller'; export * from './internal-team-statistics.controller'; +export * from './internal-referral-chain.controller'; export * from './health.controller'; diff --git a/backend/services/referral-service/src/api/controllers/internal-referral-chain.controller.ts b/backend/services/referral-service/src/api/controllers/internal-referral-chain.controller.ts new file mode 100644 index 00000000..a561dc9a --- /dev/null +++ b/backend/services/referral-service/src/api/controllers/internal-referral-chain.controller.ts @@ -0,0 +1,163 @@ +import { Controller, Get, Param, Logger, Inject, NotFoundException } from '@nestjs/common'; +import { ApiTags, ApiOperation, ApiParam, ApiResponse } from '@nestjs/swagger'; +import { + REFERRAL_RELATIONSHIP_REPOSITORY, + IReferralRelationshipRepository, +} from '../../domain'; + +/** + * 内部推荐链API - 供其他微服务调用 + * 无需JWT认证(服务间内部通信) + */ +@ApiTags('Internal Referral Chain API') +@Controller('internal/referral-chain') +export class InternalReferralChainController { + private readonly logger = new Logger(InternalReferralChainController.name); + + constructor( + @Inject(REFERRAL_RELATIONSHIP_REPOSITORY) + private readonly referralRepo: IReferralRelationshipRepository, + ) {} + + /** + * 获取用户的祖先链(从直接推荐人到根节点) + */ + @Get(':accountSequence') + @ApiOperation({ summary: '获取用户推荐链(内部API)' }) + @ApiParam({ name: 'accountSequence', description: '账户序列号' }) + @ApiResponse({ + status: 200, + description: '推荐链数据', + schema: { + type: 'object', + properties: { + accountSequence: { type: 'number' }, + userId: { type: 'string' }, + ancestorPath: { + type: 'array', + items: { type: 'string' }, + description: '祖先链(从直接推荐人到根节点的accountSequence列表)', + }, + referrerId: { type: 'string', nullable: true }, + }, + }, + }) + async getReferralChain(@Param('accountSequence') accountSequence: string) { + this.logger.debug(`[INTERNAL] getReferralChain: accountSequence=${accountSequence}`); + + const relationship = await this.referralRepo.findByAccountSequence(Number(accountSequence)); + + if (!relationship) { + this.logger.debug(`[INTERNAL] No referral found for accountSequence: ${accountSequence}`); + // 返回空的祖先链而不是抛出错误 + return { + accountSequence: Number(accountSequence), + userId: null, + ancestorPath: [], + referrerId: null, + }; + } + + // ancestorPath 存储的是 userId (bigint),需要转换为字符串 + const ancestorPath = relationship.referralChain.map((id) => id.toString()); + + return { + accountSequence: relationship.accountSequence, + userId: relationship.userId.toString(), + ancestorPath, + referrerId: relationship.referrerId?.toString() ?? null, + }; + } + + /** + * 批量获取用户的祖先链 + */ + @Get('batch/:accountSequences') + @ApiOperation({ summary: '批量获取用户推荐链(内部API)' }) + @ApiParam({ name: 'accountSequences', description: '账户序列号列表,逗号分隔' }) + @ApiResponse({ + status: 200, + description: '批量推荐链数据', + }) + async getBatchReferralChains(@Param('accountSequences') accountSequences: string) { + const sequences = accountSequences.split(',').map((s) => Number(s.trim())); + this.logger.debug(`[INTERNAL] getBatchReferralChains: ${sequences.length} accounts`); + + const results: Record = {}; + + for (const seq of sequences) { + const relationship = await this.referralRepo.findByAccountSequence(seq); + if (relationship) { + results[seq] = { + userId: relationship.userId.toString(), + ancestorPath: relationship.referralChain.map((id) => id.toString()), + referrerId: relationship.referrerId?.toString() ?? null, + }; + } else { + results[seq] = { + userId: null, + ancestorPath: [], + referrerId: null, + }; + } + } + + return results; + } + + /** + * 获取用户的团队成员(下级用户)的accountSequence列表 + * 用于查找下级社区 + */ + @Get(':accountSequence/team-members') + @ApiOperation({ summary: '获取团队成员accountSequence列表(内部API)' }) + @ApiParam({ name: 'accountSequence', description: '账户序列号' }) + @ApiResponse({ + status: 200, + description: '团队成员列表', + schema: { + type: 'object', + properties: { + accountSequence: { type: 'number' }, + teamMembers: { + type: 'array', + items: { type: 'number' }, + description: '团队成员accountSequence列表(直接和间接下级)', + }, + }, + }, + }) + async getTeamMembers(@Param('accountSequence') accountSequence: string) { + this.logger.debug(`[INTERNAL] getTeamMembers: accountSequence=${accountSequence}`); + + const relationship = await this.referralRepo.findByAccountSequence(Number(accountSequence)); + + if (!relationship) { + return { + accountSequence: Number(accountSequence), + teamMembers: [], + }; + } + + // 获取所有直推用户 + const directReferrals = await this.referralRepo.findDirectReferrals(relationship.userId); + + // 递归获取所有下级成员的accountSequence + const teamMembers: number[] = []; + const queue = [...directReferrals]; + + while (queue.length > 0) { + const current = queue.shift()!; + teamMembers.push(current.accountSequence); + + // 获取当前用户的直推 + const subReferrals = await this.referralRepo.findDirectReferrals(current.userId); + queue.push(...subReferrals); + } + + return { + accountSequence: Number(accountSequence), + teamMembers, + }; + } +} diff --git a/backend/services/referral-service/src/modules/api.module.ts b/backend/services/referral-service/src/modules/api.module.ts index b04e3569..4b06477a 100644 --- a/backend/services/referral-service/src/modules/api.module.ts +++ b/backend/services/referral-service/src/modules/api.module.ts @@ -6,6 +6,7 @@ import { ReferralController, TeamStatisticsController, InternalTeamStatisticsController, + InternalReferralChainController, HealthController, } from '../api'; import { InternalReferralController } from '../api/controllers/referral.controller'; @@ -16,6 +17,7 @@ import { InternalReferralController } from '../api/controllers/referral.controll ReferralController, TeamStatisticsController, InternalTeamStatisticsController, + InternalReferralChainController, HealthController, InternalReferralController, ], diff --git a/frontend/mobile-app/lib/core/constants/api_endpoints.dart b/frontend/mobile-app/lib/core/constants/api_endpoints.dart index d1e2b6af..a990ffe4 100644 --- a/frontend/mobile-app/lib/core/constants/api_endpoints.dart +++ b/frontend/mobile-app/lib/core/constants/api_endpoints.dart @@ -78,6 +78,7 @@ class ApiEndpoints { // Authorization (-> Authorization Service) static const String authorizations = '/authorizations'; static const String myAuthorizations = '$authorizations/my'; // 获取我的授权列表 + static const String myCommunityHierarchy = '$authorizations/my/community-hierarchy'; // 获取社区层级 // Telemetry (-> Reporting Service) static const String telemetry = '/telemetry'; diff --git a/frontend/mobile-app/lib/core/services/authorization_service.dart b/frontend/mobile-app/lib/core/services/authorization_service.dart index b40f77ac..16c2f4b5 100644 --- a/frontend/mobile-app/lib/core/services/authorization_service.dart +++ b/frontend/mobile-app/lib/core/services/authorization_service.dart @@ -208,6 +208,69 @@ class UserAuthorizationSummary { } } +/// 社区简要信息 +class CommunityInfo { + final String authorizationId; + final int accountSequence; + final String communityName; + final String? userId; + final bool isHeadquarters; + + CommunityInfo({ + required this.authorizationId, + required this.accountSequence, + required this.communityName, + this.userId, + required this.isHeadquarters, + }); + + factory CommunityInfo.fromJson(Map json) { + return CommunityInfo( + authorizationId: json['authorizationId'] ?? '', + accountSequence: json['accountSequence'] ?? 0, + communityName: json['communityName'] ?? '', + userId: json['userId'], + isHeadquarters: json['isHeadquarters'] ?? false, + ); + } +} + +/// 社区层级信息 +class CommunityHierarchy { + /// 我的社区授权(如果有) + final CommunityInfo? myCommunity; + /// 上级社区(最近的,如果没有则为总部社区) + final CommunityInfo parentCommunity; + /// 下级社区列表 + final List childCommunities; + /// 是否有上级社区(非总部) + final bool hasParentCommunity; + /// 下级社区数量 + final int childCommunityCount; + + CommunityHierarchy({ + this.myCommunity, + required this.parentCommunity, + required this.childCommunities, + required this.hasParentCommunity, + required this.childCommunityCount, + }); + + factory CommunityHierarchy.fromJson(Map json) { + return CommunityHierarchy( + myCommunity: json['myCommunity'] != null + ? CommunityInfo.fromJson(json['myCommunity']) + : null, + parentCommunity: CommunityInfo.fromJson(json['parentCommunity']), + childCommunities: (json['childCommunities'] as List?) + ?.map((e) => CommunityInfo.fromJson(e as Map)) + .toList() ?? [], + hasParentCommunity: json['hasParentCommunity'] ?? false, + childCommunityCount: json['childCommunityCount'] ?? 0, + ); + } +} + /// 授权服务 /// /// 处理用户授权相关功能: @@ -261,4 +324,40 @@ class AuthorizationService { final authorizations = await getMyAuthorizations(); return UserAuthorizationSummary.fromList(authorizations); } + + /// 获取我的社区层级信息 + /// + /// 返回上级社区(如果没有则为总部社区)和下级社区列表 + /// 调用 GET /authorizations/my/community-hierarchy + Future getMyCommunityHierarchy() async { + try { + debugPrint('获取社区层级...'); + final response = await _apiClient.get(ApiEndpoints.myCommunityHierarchy); + + if (response.statusCode == 200) { + final responseData = response.data; + // API 返回格式: {"success": true, "data": {...}} + Map? data; + if (responseData is Map) { + if (responseData.containsKey('data')) { + data = responseData['data'] as Map?; + } else { + data = responseData; + } + } + + if (data != null) { + final hierarchy = CommunityHierarchy.fromJson(data); + debugPrint('社区层级获取成功: 上级=${hierarchy.parentCommunity.communityName}, 下级数量=${hierarchy.childCommunityCount}'); + return hierarchy; + } + throw Exception('社区层级数据格式错误'); + } + + throw Exception('获取社区层级失败'); + } catch (e) { + debugPrint('获取社区层级失败: $e'); + rethrow; + } + } }