feat(admin-service): 实现团队本省/本市认种量实时统计

- getBatchUserStats 新增 provinceAdoptionCount 和 cityAdoptionCount
- 根据用户认种订单中的省市信息,统计团队成员同省/同市的认种量
- 百分比改为相对于该用户团队总认种量计算

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-07 23:07:33 -08:00
parent 8298f2e371
commit d1e04152dc
3 changed files with 79 additions and 14 deletions

View File

@ -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,

View File

@ -270,5 +270,7 @@ export interface IUserDetailQueryRepository {
personalAdoptionCount: number;
teamAddressCount: number;
teamAdoptionCount: number;
provinceAdoptionCount: number;
cityAdoptionCount: number;
}>>;
}

View File

@ -549,11 +549,15 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository
personalAdoptionCount: number;
teamAddressCount: number;
teamAdoptionCount: number;
provinceAdoptionCount: number;
cityAdoptionCount: number;
}>> {
const result = new Map<string, {
personalAdoptionCount: number;
teamAddressCount: number;
teamAdoptionCount: number;
provinceAdoptionCount: number;
cityAdoptionCount: number;
}>();
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,
});
}