fix(mining-admin): 用户列表API添加认种统计和推荐人信息

- 后端getUsers添加批量查询认种统计和推荐人信息
- 后端formatUserListItem返回adoption和referral字段
- 前端transformUserOverview映射新字段

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-12 03:42:54 -08:00
parent 3074748d15
commit 2025c6ce36
2 changed files with 144 additions and 5 deletions

View File

@ -36,6 +36,7 @@ export class UsersService {
{ phone: { contains: search } },
{ accountSequence: { contains: search } },
{ realName: { contains: search } },
{ nickname: { contains: search } },
];
}
@ -63,13 +64,31 @@ export class UsersService {
contributionAccount: true,
miningAccount: true,
tradingAccount: true,
referral: true,
},
}),
this.prisma.syncedUser.count({ where }),
]);
// 批量获取认种统计
const accountSequences = users.map((u) => u.accountSequence);
const adoptionStats = await this.getAdoptionStatsForUsers(accountSequences);
// 批量获取推荐人信息
const referrerAccountSequences = users
.map((u) => u.referral?.referrerAccountSequence)
.filter((s): s is string => !!s);
const referrers = await this.getReferrersInfo(referrerAccountSequences);
return {
data: users.map((user) => this.formatUserListItem(user)),
data: users.map((user) =>
this.formatUserListItem(user, {
adoptionStats: adoptionStats.get(user.accountSequence),
referrerInfo: user.referral?.referrerAccountSequence
? referrers.get(user.referral.referrerAccountSequence)
: null,
}),
),
pagination: {
page,
pageSize,
@ -79,6 +98,102 @@ export class UsersService {
};
}
/**
*
*/
private async getAdoptionStatsForUsers(
accountSequences: string[],
): Promise<Map<string, { personalCount: number; teamCount: number }>> {
const result = new Map<
string,
{ personalCount: number; teamCount: number }
>();
if (accountSequences.length === 0) return result;
// 获取每个用户的个人认种数量
const personalAdoptions = await this.prisma.syncedAdoption.groupBy({
by: ['accountSequence'],
where: { accountSequence: { in: accountSequences } },
_sum: { treeCount: true },
});
for (const stat of personalAdoptions) {
result.set(stat.accountSequence, {
personalCount: stat._sum.treeCount || 0,
teamCount: 0,
});
}
// 确保所有用户都有记录
for (const seq of accountSequences) {
if (!result.has(seq)) {
result.set(seq, { personalCount: 0, teamCount: 0 });
}
}
// 获取团队认种数量(通过 referral 的 originalUserId 和 ancestorPath
const referrals = await this.prisma.syncedReferral.findMany({
where: { accountSequence: { in: accountSequences } },
select: { accountSequence: true, originalUserId: true },
});
for (const ref of referrals) {
if (!ref.originalUserId) continue;
// 找所有下级
const teamMembers = await this.prisma.syncedReferral.findMany({
where: {
ancestorPath: { contains: ref.originalUserId.toString() },
},
select: { accountSequence: true },
});
if (teamMembers.length > 0) {
const teamAdoptionStats = await this.prisma.syncedAdoption.aggregate({
where: {
accountSequence: { in: teamMembers.map((m) => m.accountSequence) },
},
_sum: { treeCount: true },
});
const stats = result.get(ref.accountSequence);
if (stats) {
stats.teamCount = teamAdoptionStats._sum.treeCount || 0;
}
}
}
return result;
}
/**
*
*/
private async getReferrersInfo(
accountSequences: string[],
): Promise<Map<string, { nickname: string | null; phone: string }>> {
const result = new Map<
string,
{ nickname: string | null; phone: string }
>();
if (accountSequences.length === 0) return result;
const referrers = await this.prisma.syncedUser.findMany({
where: { accountSequence: { in: accountSequences } },
select: { accountSequence: true, nickname: true, phone: true },
});
for (const ref of referrers) {
result.set(ref.accountSequence, {
nickname: ref.nickname,
phone: ref.phone,
});
}
return result;
}
/**
*
*/
@ -430,7 +545,13 @@ export class UsersService {
// 辅助方法
// ===========================================================================
private formatUserListItem(user: any) {
private formatUserListItem(
user: any,
extra?: {
adoptionStats?: { personalCount: number; teamCount: number };
referrerInfo?: { nickname: string | null; phone: string } | null;
},
) {
return {
accountSequence: user.accountSequence,
phone: this.maskPhone(user.phone),
@ -440,6 +561,22 @@ export class UsersService {
realName: user.realName,
isLegacyUser: user.isLegacyUser,
createdAt: user.createdAt,
// 认种统计
adoption: {
personalAdoptionCount: extra?.adoptionStats?.personalCount || 0,
teamAdoptions: extra?.adoptionStats?.teamCount || 0,
},
// 推荐人信息
referral: user.referral
? {
referrerAccountSequence: user.referral.referrerAccountSequence,
referrerNickname:
extra?.referrerInfo?.nickname ||
(extra?.referrerInfo?.phone
? this.maskPhone(extra.referrerInfo.phone)
: null),
}
: null,
contribution: user.contributionAccount
? {
totalContribution:

View File

@ -25,9 +25,11 @@ function transformUserOverview(backendUser: any): UserOverview {
miningBalance: backendUser.mining?.availableBalance || '0',
tradingBalance: backendUser.trading?.shareBalance || '0',
frozenBalance: '0',
personalAdoptions: 0,
teamAdoptions: 0,
referrerId: null,
// 认种数据 - 从后端 adoption 字段获取
personalAdoptions: backendUser.adoption?.personalAdoptionCount || 0,
teamAdoptions: backendUser.adoption?.teamAdoptions || 0,
// 推荐人
referrerId: backendUser.referral?.referrerAccountSequence || null,
status: backendUser.status?.toLowerCase() as 'active' | 'frozen' | 'deactivated',
isOnline: false,
createdAt: backendUser.createdAt,