fix(admin-service): 引荐关系树实时统计本人认种和直推数量

问题:
- 引荐节点的"本人认种"和"引荐"显示为0
- user_query_view.personal_adoption_count 和
  referral_query_view.direct_referral_count 没有被CDC更新

解决方案:
- getAncestors: 实时统计每个祖先的认种订单数和直推数
- getDirectReferrals: 实时统计每个下级的认种订单数和直推数
- getReferralTree API: 实时获取当前用户的统计数据
- 新增 getDirectReferralCount 方法

实时统计方式:
- 本人认种 = planting_order_query_view 中状态为 MINING_ENABLED 的订单数
- 直推数量 = referral_query_view 中 referrer_id 等于该用户的记录数

🤖 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 22:30:38 -08:00
parent 3ed72499a0
commit d303cf076b
3 changed files with 84 additions and 20 deletions

View File

@ -131,16 +131,21 @@ export class UserDetailController {
throw new NotFoundException(`用户 ${accountSequence} 不存在`); throw new NotFoundException(`用户 ${accountSequence} 不存在`);
} }
const referralInfo = await this.userDetailRepository.getReferralInfo(accountSequence); // 获取引荐信息和实时统计
const [referralInfo, personalAdoptionCount, directReferralCount] = await Promise.all([
this.userDetailRepository.getReferralInfo(accountSequence),
this.userDetailRepository.getPersonalAdoptionCount(accountSequence),
this.userDetailRepository.getDirectReferralCount(accountSequence),
]);
const currentUser: ReferralNodeDto = { const currentUser: ReferralNodeDto = {
accountSequence: user.accountSequence, accountSequence: user.accountSequence,
userId: user.userId.toString(), userId: user.userId.toString(),
nickname: user.nickname, nickname: user.nickname,
avatar: user.avatarUrl, avatar: user.avatarUrl,
personalAdoptions: user.personalAdoptionCount, personalAdoptions: personalAdoptionCount,
depth: referralInfo?.depth || 0, depth: referralInfo?.depth || 0,
directReferralCount: referralInfo?.directReferralCount || 0, directReferralCount: directReferralCount,
isCurrentUser: true, isCurrentUser: true,
}; };

View File

@ -251,6 +251,11 @@ export interface IUserDetailQueryRepository {
*/ */
getPersonalAdoptionCount(accountSequence: string): Promise<number>; getPersonalAdoptionCount(accountSequence: string): Promise<number>;
/**
*
*/
getDirectReferralCount(accountSequence: string): Promise<number>;
/** /**
* *
* *

View File

@ -60,7 +60,7 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository
const ancestorIds = referral.ancestorPath.slice(0, depth); const ancestorIds = referral.ancestorPath.slice(0, depth);
if (ancestorIds.length === 0) return []; if (ancestorIds.length === 0) return [];
// 获取祖先用户信息 // 获取祖先用户信息和引荐关系
const [users, referrals] = await Promise.all([ const [users, referrals] = await Promise.all([
this.prisma.userQueryView.findMany({ this.prisma.userQueryView.findMany({
where: { userId: { in: ancestorIds } }, where: { userId: { in: ancestorIds } },
@ -69,19 +69,41 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository
accountSequence: true, accountSequence: true,
nickname: true, nickname: true,
avatarUrl: true, avatarUrl: true,
personalAdoptionCount: true,
}, },
}), }),
this.prisma.referralQueryView.findMany({ this.prisma.referralQueryView.findMany({
where: { userId: { in: ancestorIds } }, where: { userId: { in: ancestorIds } },
select: { select: {
userId: true, userId: true,
accountSequence: true,
depth: true, depth: true,
directReferralCount: true,
}, },
}), }),
]); ]);
// 实时统计:获取每个祖先的认种数量和直推数量
const accountSequences = users.map(u => u.accountSequence);
const [adoptionCounts, directReferralCounts] = await Promise.all([
// 统计每个用户的认种订单数量(状态为 MINING_ENABLED
this.prisma.plantingOrderQueryView.groupBy({
by: ['accountSequence'],
where: {
accountSequence: { in: accountSequences },
status: 'MINING_ENABLED',
},
_count: { id: true },
}),
// 统计每个用户的直推数量
this.prisma.referralQueryView.groupBy({
by: ['referrerId'],
where: { referrerId: { in: ancestorIds } },
_count: { userId: true },
}),
]);
const adoptionCountMap = new Map(adoptionCounts.map(a => [a.accountSequence, a._count.id]));
const directCountMap = new Map(directReferralCounts.map(d => [d.referrerId.toString(), d._count.userId]));
// 合并数据 // 合并数据
const referralMap = new Map(referrals.map((r) => [r.userId.toString(), r])); const referralMap = new Map(referrals.map((r) => [r.userId.toString(), r]));
const userMap = new Map(users.map((u) => [u.userId.toString(), u])); const userMap = new Map(users.map((u) => [u.userId.toString(), u]));
@ -94,9 +116,9 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository
accountSequence: user?.accountSequence || '', accountSequence: user?.accountSequence || '',
nickname: user?.nickname || null, nickname: user?.nickname || null,
avatarUrl: user?.avatarUrl || null, avatarUrl: user?.avatarUrl || null,
personalAdoptionCount: user?.personalAdoptionCount || 0, personalAdoptionCount: adoptionCountMap.get(user?.accountSequence || '') || 0,
depth: ref?.depth || index, depth: ref?.depth || index,
directReferralCount: ref?.directReferralCount || 0, directReferralCount: directCountMap.get(id.toString()) || 0,
}; };
}); });
} }
@ -115,24 +137,45 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository
userId: true, userId: true,
accountSequence: true, accountSequence: true,
depth: true, depth: true,
directReferralCount: true,
}, },
}); });
if (directReferrals.length === 0) return []; if (directReferrals.length === 0) return [];
// 获取用户信息 // 获取用户信息
const userIds = directReferrals.map((r) => r.userId);
const users = await this.prisma.userQueryView.findMany({ const users = await this.prisma.userQueryView.findMany({
where: { userId: { in: directReferrals.map((r) => r.userId) } }, where: { userId: { in: userIds } },
select: { select: {
userId: true, userId: true,
accountSequence: true, accountSequence: true,
nickname: true, nickname: true,
avatarUrl: true, avatarUrl: true,
personalAdoptionCount: true,
}, },
}); });
// 实时统计:获取每个用户的认种数量和直推数量
const userAccountSequences = directReferrals.map(r => r.accountSequence);
const [adoptionCounts, directReferralCounts] = await Promise.all([
// 统计每个用户的认种订单数量(状态为 MINING_ENABLED
this.prisma.plantingOrderQueryView.groupBy({
by: ['accountSequence'],
where: {
accountSequence: { in: userAccountSequences },
status: 'MINING_ENABLED',
},
_count: { id: true },
}),
// 统计每个用户的直推数量
this.prisma.referralQueryView.groupBy({
by: ['referrerId'],
where: { referrerId: { in: userIds } },
_count: { userId: true },
}),
]);
const adoptionCountMap = new Map(adoptionCounts.map(a => [a.accountSequence, a._count.id]));
const directCountMap = new Map(directReferralCounts.map(d => [d.referrerId.toString(), d._count.userId]));
const userMap = new Map(users.map((u) => [u.userId.toString(), u])); const userMap = new Map(users.map((u) => [u.userId.toString(), u]));
return directReferrals.map((ref) => { return directReferrals.map((ref) => {
@ -142,9 +185,9 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository
accountSequence: ref.accountSequence, accountSequence: ref.accountSequence,
nickname: user?.nickname || null, nickname: user?.nickname || null,
avatarUrl: user?.avatarUrl || null, avatarUrl: user?.avatarUrl || null,
personalAdoptionCount: user?.personalAdoptionCount || 0, personalAdoptionCount: adoptionCountMap.get(ref.accountSequence) || 0,
depth: ref.depth, depth: ref.depth,
directReferralCount: ref.directReferralCount, directReferralCount: directCountMap.get(ref.userId.toString()) || 0,
}; };
}); });
} }
@ -428,21 +471,32 @@ export class UserDetailQueryRepositoryImpl implements IUserDetailQueryRepository
} }
async getPersonalAdoptionCount(accountSequence: string): Promise<number> { async getPersonalAdoptionCount(accountSequence: string): Promise<number> {
// 统计用户的认种订单数量(状态为 MINING_ENABLED
const count = await this.prisma.plantingOrderQueryView.count({
where: {
accountSequence,
status: 'MINING_ENABLED',
},
});
return count;
}
async getDirectReferralCount(accountSequence: string): Promise<number> {
// 先获取用户的 userId // 先获取用户的 userId
const user = await this.prisma.userQueryView.findUnique({ const referral = await this.prisma.referralQueryView.findUnique({
where: { accountSequence }, where: { accountSequence },
select: { userId: true }, select: { userId: true },
}); });
if (!user) return 0; if (!referral) return 0;
// 从 PlantingPositionQueryView 获取有效认种树数 // 统计以该用户为 referrerId 的用户数量
const position = await this.prisma.plantingPositionQueryView.findUnique({ const count = await this.prisma.referralQueryView.count({
where: { userId: user.userId }, where: { referrerId: referral.userId },
select: { effectiveTreeCount: true },
}); });
return position?.effectiveTreeCount || 0; return count;
} }
async getTeamStats(accountSequence: string): Promise<{ teamAddressCount: number; teamAdoptionCount: number }> { async getTeamStats(accountSequence: string): Promise<{ teamAddressCount: number; teamAdoptionCount: number }> {