From 1621b75a4781af6dbc5e9155e54523f57d79ec2e Mon Sep 17 00:00:00 2001 From: hailin Date: Mon, 2 Mar 2026 19:16:02 -0800 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=E5=BC=95=E8=8D=90=E5=85=B3?= =?UTF-8?q?=E7=B3=BB=E6=A0=91=E8=8A=82=E7=82=B9=E5=A2=9E=E5=8A=A0=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA/=E5=9B=A2=E9=98=9F=E9=A2=84=E7=A7=8D=E4=BB=BD?= =?UTF-8?q?=E6=95=B0=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端: - ReferralNodeDto 新增 selfPrePlantingPortions, teamPrePlantingPortions - user-detail.controller: getReferralTree 中并行调用 ReferralProxyService 批量获取所有节点的预种统计 (当前用户用 getPrePlantingStats,祖先+下级用 batchGetPrePlantingStats) 前端: - ReferralNode 类型新增两个预种字段 - 引荐关系树节点(祖先链 + 递归展开节点)在"本人认种/团队认种" 下方新增一行"个人预种: X份 / 团队预种: Y份" Co-Authored-By: Claude Opus 4.6 --- .../api/controllers/user-detail.controller.ts | 39 +++++++++++++++---- .../src/api/dto/response/user-detail.dto.ts | 2 + .../src/app/(dashboard)/users/[id]/page.tsx | 6 +++ .../admin-web/src/types/userDetail.types.ts | 2 + 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/backend/services/admin-service/src/api/controllers/user-detail.controller.ts b/backend/services/admin-service/src/api/controllers/user-detail.controller.ts index 5f778432..01f11640 100644 --- a/backend/services/admin-service/src/api/controllers/user-detail.controller.ts +++ b/backend/services/admin-service/src/api/controllers/user-detail.controller.ts @@ -138,11 +138,12 @@ export class UserDetailController { } // 获取引荐信息和实时统计 - const [referralInfo, personalAdoptionCount, directReferralCount, teamStats] = await Promise.all([ + const [referralInfo, personalAdoptionCount, directReferralCount, teamStats, prePlantingStats] = await Promise.all([ this.userDetailRepository.getReferralInfo(accountSequence), this.userDetailRepository.getPersonalAdoptionCount(accountSequence), this.userDetailRepository.getDirectReferralCount(accountSequence), this.userDetailRepository.getBatchUserStats([accountSequence]), + this.referralProxyService.getPrePlantingStats(accountSequence), ]); const currentUserStats = teamStats.get(accountSequence); @@ -153,6 +154,8 @@ export class UserDetailController { avatar: user.avatarUrl, personalAdoptions: personalAdoptionCount, teamAdoptions: currentUserStats?.teamAdoptionCount || 0, + selfPrePlantingPortions: prePlantingStats.selfPrePlantingPortions, + teamPrePlantingPortions: prePlantingStats.teamPrePlantingPortions, depth: referralInfo?.depth || 0, directReferralCount: directReferralCount, isCurrentUser: true, @@ -161,34 +164,56 @@ export class UserDetailController { let ancestors: ReferralNodeDto[] = []; let directReferrals: ReferralNodeDto[] = []; + // 收集所有需要查预种的 accountSequences + const allNodeSeqs: string[] = []; + // 向上查询 + let ancestorNodes: typeof ancestors extends (infer T)[] ? any[] : never = []; if (query.direction === 'up' || query.direction === 'both') { - const ancestorNodes = await this.userDetailRepository.getAncestors( + ancestorNodes = await this.userDetailRepository.getAncestors( accountSequence, query.depth || 1, ); - ancestors = ancestorNodes.map((node) => ({ + allNodeSeqs.push(...ancestorNodes.map((n: any) => n.accountSequence)); + } + + // 向下查询 + let referralNodes: typeof directReferrals extends (infer T)[] ? any[] : never = []; + if (query.direction === 'down' || query.direction === 'both') { + referralNodes = await this.userDetailRepository.getDirectReferrals(accountSequence); + allNodeSeqs.push(...referralNodes.map((n: any) => n.accountSequence)); + } + + // 批量获取所有节点的预种统计 + const batchPrePlanting = allNodeSeqs.length > 0 + ? await this.referralProxyService.batchGetPrePlantingStats(allNodeSeqs) + : {}; + + if (ancestorNodes.length > 0) { + ancestors = ancestorNodes.map((node: any) => ({ accountSequence: node.accountSequence, userId: node.userId.toString(), nickname: node.nickname, avatar: node.avatarUrl, personalAdoptions: node.personalAdoptionCount, teamAdoptions: node.teamAdoptionCount, + selfPrePlantingPortions: batchPrePlanting[node.accountSequence]?.selfPrePlantingPortions ?? 0, + teamPrePlantingPortions: batchPrePlanting[node.accountSequence]?.teamPrePlantingPortions ?? 0, depth: node.depth, directReferralCount: node.directReferralCount, })); } - // 向下查询 - if (query.direction === 'down' || query.direction === 'both') { - const referralNodes = await this.userDetailRepository.getDirectReferrals(accountSequence); - directReferrals = referralNodes.map((node) => ({ + if (referralNodes.length > 0) { + directReferrals = referralNodes.map((node: any) => ({ accountSequence: node.accountSequence, userId: node.userId.toString(), nickname: node.nickname, avatar: node.avatarUrl, personalAdoptions: node.personalAdoptionCount, teamAdoptions: node.teamAdoptionCount, + selfPrePlantingPortions: batchPrePlanting[node.accountSequence]?.selfPrePlantingPortions ?? 0, + teamPrePlantingPortions: batchPrePlanting[node.accountSequence]?.teamPrePlantingPortions ?? 0, depth: node.depth, directReferralCount: node.directReferralCount, })); diff --git a/backend/services/admin-service/src/api/dto/response/user-detail.dto.ts b/backend/services/admin-service/src/api/dto/response/user-detail.dto.ts index 37ea0707..2e544b28 100644 --- a/backend/services/admin-service/src/api/dto/response/user-detail.dto.ts +++ b/backend/services/admin-service/src/api/dto/response/user-detail.dto.ts @@ -74,6 +74,8 @@ export class ReferralNodeDto { avatar!: string | null; personalAdoptions!: number; teamAdoptions!: number; // 团队认种量 + selfPrePlantingPortions!: number; // 个人预种份数 + teamPrePlantingPortions!: number; // 团队预种份数 depth!: number; directReferralCount!: number; isCurrentUser?: boolean; diff --git a/frontend/admin-web/src/app/(dashboard)/users/[id]/page.tsx b/frontend/admin-web/src/app/(dashboard)/users/[id]/page.tsx index a19b13a0..318c4fec 100644 --- a/frontend/admin-web/src/app/(dashboard)/users/[id]/page.tsx +++ b/frontend/admin-web/src/app/(dashboard)/users/[id]/page.tsx @@ -490,6 +490,9 @@ export default function UserDetailPage() { 本人认种: {formatNumber(ancestor.personalAdoptions)} / 团队认种: {formatNumber(ancestor.teamAdoptions)} + + 个人预种: {formatNumber(ancestor.selfPrePlantingPortions)}份 / 团队预种: {formatNumber(ancestor.teamPrePlantingPortions)}份 + {index < referralTree.ancestors.length - 1 && (
@@ -1198,6 +1201,9 @@ function ReferralNodeItem({ 本人认种: {formatNumber(node.personalAdoptions)} / 团队认种: {formatNumber(node.teamAdoptions)} + + 个人预种: {formatNumber(node.selfPrePlantingPortions)}份 / 团队预种: {formatNumber(node.teamPrePlantingPortions)}份 + {node.directReferralCount > 0 && ( 引荐: {formatNumber(node.directReferralCount)} diff --git a/frontend/admin-web/src/types/userDetail.types.ts b/frontend/admin-web/src/types/userDetail.types.ts index 97427761..2bbd346d 100644 --- a/frontend/admin-web/src/types/userDetail.types.ts +++ b/frontend/admin-web/src/types/userDetail.types.ts @@ -64,6 +64,8 @@ export interface ReferralNode { avatar: string | null; personalAdoptions: number; teamAdoptions: number; // 团队认种量 + selfPrePlantingPortions: number; // 个人预种份数 + teamPrePlantingPortions: number; // 团队预种份数 depth: number; directReferralCount: number; isCurrentUser?: boolean;