diff --git a/backend/services/admin-service/src/api/controllers/user.controller.ts b/backend/services/admin-service/src/api/controllers/user.controller.ts index b93f3de2..6f3d49ee 100644 --- a/backend/services/admin-service/src/api/controllers/user.controller.ts +++ b/backend/services/admin-service/src/api/controllers/user.controller.ts @@ -150,19 +150,28 @@ export class UserController { private mapToListItem( item: UserQueryItem, totalTeamAdoptions: number, - realTimeStats?: { personalAdoptionCount: number; teamAddressCount: number; teamAdoptionCount: number }, + realTimeStats?: { + personalAdoptionCount: number; + teamAddressCount: number; + teamAdoptionCount: number; + provinceAdoptionCount: number; + cityAdoptionCount: number; + }, ): UserListItemDto { // 使用实时统计数据(如果有),否则使用预计算数据 const personalAdoptions = realTimeStats?.personalAdoptionCount ?? item.personalAdoptionCount; const teamAddresses = realTimeStats?.teamAddressCount ?? item.teamAddressCount; const teamAdoptions = realTimeStats?.teamAdoptionCount ?? item.teamAdoptionCount; + const provinceAdoptions = realTimeStats?.provinceAdoptionCount ?? item.provinceAdoptionCount; + const cityAdoptions = realTimeStats?.cityAdoptionCount ?? item.cityAdoptionCount; - // 计算省市认种百分比 - const provincePercentage = totalTeamAdoptions > 0 - ? Math.round((item.provinceAdoptionCount / totalTeamAdoptions) * 100) + // 计算省市认种百分比(相对于该用户的团队总认种量) + const userTeamAdoptions = teamAdoptions > 0 ? teamAdoptions : 1; + const provincePercentage = teamAdoptions > 0 + ? Math.round((provinceAdoptions / userTeamAdoptions) * 100) : 0; - const cityPercentage = totalTeamAdoptions > 0 - ? Math.round((item.cityAdoptionCount / totalTeamAdoptions) * 100) + const cityPercentage = teamAdoptions > 0 + ? Math.round((cityAdoptions / userTeamAdoptions) * 100) : 0; return { @@ -175,11 +184,11 @@ export class UserController { teamAddresses, teamAdoptions, provincialAdoptions: { - count: item.provinceAdoptionCount, + count: provinceAdoptions, percentage: provincePercentage, }, cityAdoptions: { - count: item.cityAdoptionCount, + count: cityAdoptions, percentage: cityPercentage, }, referrerId: item.inviterSequence, diff --git a/backend/services/admin-service/src/domain/repositories/user-detail-query.repository.ts b/backend/services/admin-service/src/domain/repositories/user-detail-query.repository.ts index f5de5b00..7b407052 100644 --- a/backend/services/admin-service/src/domain/repositories/user-detail-query.repository.ts +++ b/backend/services/admin-service/src/domain/repositories/user-detail-query.repository.ts @@ -270,5 +270,7 @@ export interface IUserDetailQueryRepository { personalAdoptionCount: number; teamAddressCount: number; teamAdoptionCount: number; + provinceAdoptionCount: number; + cityAdoptionCount: number; }>>; } diff --git a/backend/services/admin-service/src/infrastructure/persistence/repositories/user-detail-query.repository.impl.ts b/backend/services/admin-service/src/infrastructure/persistence/repositories/user-detail-query.repository.impl.ts index 1accf6ee..d03b862b 100644 --- a/backend/services/admin-service/src/infrastructure/persistence/repositories/user-detail-query.repository.impl.ts +++ b/backend/services/admin-service/src/infrastructure/persistence/repositories/user-detail-query.repository.impl.ts @@ -549,11 +549,15 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository personalAdoptionCount: number; teamAddressCount: number; teamAdoptionCount: number; + provinceAdoptionCount: number; + cityAdoptionCount: number; }>> { const result = new Map(); if (accountSequences.length === 0) return result; @@ -571,18 +575,40 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository personalAdoptionCounts.map(p => [p.accountSequence, p._count.id]) ); - // 2. 批量获取 userId 用于团队统计 + // 2. 批量获取用户的省市信息(从认种订单中获取第一个订单的省市) + const userProvinceCity = await this.prisma.plantingOrderQueryView.findMany({ + where: { + accountSequence: { in: accountSequences }, + status: 'MINING_ENABLED', + selectedProvince: { not: null }, + }, + select: { + accountSequence: true, + selectedProvince: true, + selectedCity: true, + }, + distinct: ['accountSequence'], + orderBy: { createdAt: 'asc' }, + }); + const userProvinceCityMap = new Map( + userProvinceCity.map(u => [u.accountSequence, { province: u.selectedProvince, city: u.selectedCity }]) + ); + + // 3. 批量获取 userId 用于团队统计 const referrals = await this.prisma.referralQueryView.findMany({ where: { accountSequence: { in: accountSequences } }, select: { accountSequence: true, userId: true }, }); const userIdMap = new Map(referrals.map(r => [r.accountSequence, r.userId])); - // 3. 对每个用户计算团队统计(这里需要单独查询,因为 PostgreSQL 数组查询不支持批量) + // 4. 对每个用户计算团队统计 for (const accountSequence of accountSequences) { const userId = userIdMap.get(accountSequence); + const userLocation = userProvinceCityMap.get(accountSequence); let teamAddressCount = 0; let teamAdoptionCount = 0; + let provinceAdoptionCount = 0; + let cityAdoptionCount = 0; if (userId) { // 获取团队成员 @@ -595,15 +621,41 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository teamAddressCount = teamMembers.length; - // 获取团队认种量 + // 获取团队认种量及省市统计 if (teamMembers.length > 0) { - const count = await this.prisma.plantingOrderQueryView.count({ + const teamAccountSequences = teamMembers.map(m => m.accountSequence); + + // 团队总认种量 + teamAdoptionCount = await this.prisma.plantingOrderQueryView.count({ where: { - accountSequence: { in: teamMembers.map(m => m.accountSequence) }, + accountSequence: { in: teamAccountSequences }, status: 'MINING_ENABLED', }, }); - teamAdoptionCount = count; + + // 如果用户有省市信息,统计同省同市的认种量 + if (userLocation?.province) { + // 同省认种量 + provinceAdoptionCount = await this.prisma.plantingOrderQueryView.count({ + where: { + accountSequence: { in: teamAccountSequences }, + status: 'MINING_ENABLED', + selectedProvince: userLocation.province, + }, + }); + + // 同市认种量 + if (userLocation.city) { + cityAdoptionCount = await this.prisma.plantingOrderQueryView.count({ + where: { + accountSequence: { in: teamAccountSequences }, + status: 'MINING_ENABLED', + selectedProvince: userLocation.province, + selectedCity: userLocation.city, + }, + }); + } + } } } @@ -611,6 +663,8 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository personalAdoptionCount: personalAdoptionMap.get(accountSequence) || 0, teamAddressCount, teamAdoptionCount, + provinceAdoptionCount, + cityAdoptionCount, }); }