From 68182fd0a3ed06cb7de0b0a44df2310fa75d2425 Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 10 Dec 2025 05:57:46 -0800 Subject: [PATCH] feat(referral): add personal/team planting counts to direct referral list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Backend: batch query TeamStatistics to get each direct referral's personalPlantingCount and teamPlantingCount - Updated DTO and query interfaces with new fields - Frontend: parse and display "个人/团队" counts in profile page 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .claude/settings.local.json | 79 ++++++------------- .../src/api/dto/referral.dto.ts | 5 +- .../queries/get-direct-referrals.query.ts | 3 +- .../application/services/referral.service.ts | 34 +++++--- .../team-statistics.repository.ts | 14 +++- .../lib/core/services/referral_service.dart | 9 ++- .../presentation/pages/profile_page.dart | 4 +- 7 files changed, 74 insertions(+), 74 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index b69ecab7..eac5afae 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -1,65 +1,30 @@ { "permissions": { "allow": [ - "Bash(__NEW_LINE__ echo \"=== Test /api/v1/referral/me (referral-service) ===\")", - "Bash(git -C \"c:\\Users\\dong\\Desktop\\rwadurian\" push)", - "Bash(__NEW_LINE__ echo \"=== Test /api/v1/authorizations/my (authorization-service on port 3002) ===\")", - "Bash(__NEW_LINE__ echo \"=== Test /api/v1/authorizations/my (authorization-service on port 3009) ===\")", - "Bash(backend/api-gateway/kong.yml )", - "Bash(frontend/mobile-app/lib/core/constants/api_endpoints.dart )", - "Bash(frontend/mobile-app/lib/core/di/injection_container.dart )", - "Bash(frontend/mobile-app/lib/core/services/referral_service.dart )", - "Bash(frontend/mobile-app/lib/core/services/authorization_service.dart )", - "Bash(frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart )", - "Bash(backend/services/authorization-service/package.json )", - "Bash(backend/services/authorization-service/package-lock.json )", - "Bash(backend/services/authorization-service/src/shared/strategies/jwt.strategy.ts )", - "Bash(backend/services/authorization-service/src/infrastructure/persistence/repositories/system-account.repository.impl.ts)", - "Bash(DATABASE_URL=\"postgresql://rwauser:rwapass123@localhost:5432/rwa_authorization\" npx prisma migrate:*)", - "Bash(backend/services/authorization-service/.gitignore )", - "Bash(backend/services/authorization-service/prisma/migrations/00000000000000_init/migration.sql )", - "Bash(git fetch:*)", - "Bash(backend/services/blockchain-service/package.json )", - "Bash(backend/services/blockchain-service/package-lock.json )", - "Bash(backend/services/blockchain-service/src/api/api.module.ts )", - "Bash(backend/services/blockchain-service/src/api/controllers/index.ts )", - "Bash(backend/services/blockchain-service/src/api/controllers/deposit.controller.ts )", - "Bash(backend/services/blockchain-service/src/shared/index.ts )", - "Bash(backend/services/blockchain-service/src/shared/decorators/ )", - "Bash(backend/services/blockchain-service/src/shared/guards/ )", - "Bash(backend/services/blockchain-service/src/shared/strategies/ )", - "Bash(backend/services/wallet-service/src/api/api.module.ts )", - "Bash(backend/services/wallet-service/src/infrastructure/kafka/ )", - "Bash(backend/services/wallet-service/src/application/event-handlers/ )", - "Bash(backend/services/docker-compose.yml )", - "Bash(frontend/mobile-app/lib/core/services/deposit_service.dart)", - "Bash(backend/services/authorization-service/prisma/schema.prisma )", - "Bash(backend/services/authorization-service/src/application/services/ )", - "Bash(backend/services/authorization-service/src/domain/ )", - "Bash(backend/services/authorization-service/src/infrastructure/persistence/repositories/ )", - "Bash(backend/services/planting-service/src/application/services/planting-application.service.ts )", - "Bash(backend/services/planting-service/src/domain/services/fund-allocation.service.ts )", - "Bash(backend/services/planting-service/src/infrastructure/external/ )", - "Bash(backend/services/planting-service/src/infrastructure/infrastructure.module.ts )", - "Bash(backend/services/planting-service/src/infrastructure/persistence/prisma/prisma.service.ts )", - "Bash(backend/services/planting-service/src/infrastructure/persistence/unit-of-work.ts )", - "Bash(backend/services/referral-service/src/modules/api.module.ts )", - "Bash(backend/services/reward-service/src/infrastructure/external/authorization-service/authorization-service.client.ts )", - "Bash(backend/services/wallet-service/package.json )", - "Bash(backend/services/wallet-service/prisma/schema.prisma )", - "Bash(backend/services/wallet-service/src/)", - "Bash(git status:*)", - "Bash(git check-ignore:*)", - "Bash(docker inspect:*)", - "Bash(python -m json.tool:*)", - "Bash(docker logs:*)", - "Bash(curl:*)", + "Bash(git -C \"c:\\Users\\dong\\Desktop\\rwadurian\" add frontend/mobile-app/lib/features/home/presentation/pages/home_shell_page.dart)", + "Bash(git -C \"c:\\Users\\dong\\Desktop\\rwadurian\" commit -m \"$(cat <<''EOF''\nfix(mobile): change update check interval from 24h to 30-90s random\n\nAllows faster detection of urgent updates while preventing excessive\nAPI calls with random cooldown period.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n)\")", + "Bash(git -C \"c:\\Users\\dong\\Desktop\\rwadurian\" commit -m \"$(cat <<''EOF''\nfix(mobile): change update check interval to 90-300s random\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n)\")", + "Bash(git -C \"c:/Users/dong/Desktop/rwadurian\" status --short backend/services/blockchain-service/)", + "Bash(git -C \"c:/Users/dong/Desktop/rwadurian\" log --oneline -5)", + "Bash(git -C \"c:/Users/dong/Desktop/rwadurian\" status backend/services/blockchain-service/)", + "Bash(git -C \"c:/Users/dong/Desktop/rwadurian\" log --oneline --all -- backend/services/blockchain-service/src/api/controllers/deposit-repair.controller.ts)", + "Bash(git -C \"c:/Users/dong/Desktop/rwadurian\" log --oneline origin/main -10)", + "Bash(npx tsc:*)", + "Bash(flutter analyze:*)", "Bash(git add:*)", "Bash(git commit:*)", - "Bash(git push)", - "Bash(cat:*)", - "Bash(git revert:*)", - "Bash(git stash:*)" + "Bash(git push:*)", + "Bash(echo:*)", + "Bash(git -C \"c:\\Users\\dong\\Desktop\\rwadurian\" add frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart)", + "Bash(git -C \"c:\\Users\\dong\\Desktop\\rwadurian\" commit -m \"$(cat <<''EOF''\nfix(mobile): reduce direct referral list item spacing\n\n- Row gap: 8px → 4px\n- Vertical padding: 12px → 8px\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n)\")", + "Bash(git -C \"c:\\Users\\dong\\Desktop\\rwadurian\" push)", + "Bash(git checkout:*)", + "Bash(find:*)", + "Bash(docker exec:*)", + "Bash(npx prisma migrate status)", + "Bash(DATABASE_URL=\"postgresql://rwa_user:your_secure_password_here@localhost:5432/rwa_referral\" npx prisma migrate status:*)", + "Bash(DATABASE_URL=\"postgresql://rwa_user:your_secure_password_here@localhost:5432/rwa_referral\" npx prisma migrate deploy:*)", + "Bash(DATABASE_URL=\"postgresql://rwa_user:your_secure_password_here@localhost:5432/rwa_referral\" npx prisma migrate resolve:*)" ], "deny": [], "ask": [] diff --git a/backend/services/referral-service/src/api/dto/referral.dto.ts b/backend/services/referral-service/src/api/dto/referral.dto.ts index c59aa618..329be2b4 100644 --- a/backend/services/referral-service/src/api/dto/referral.dto.ts +++ b/backend/services/referral-service/src/api/dto/referral.dto.ts @@ -90,8 +90,11 @@ export class DirectReferralResponseDto { @ApiProperty({ description: '推荐码' }) referralCode: string; + @ApiProperty({ description: '个人认种量' }) + personalPlantingCount: number; + @ApiProperty({ description: '团队认种量' }) - teamCount: number; + teamPlantingCount: number; @ApiProperty({ description: '加入时间' }) joinedAt: Date; diff --git a/backend/services/referral-service/src/application/queries/get-direct-referrals.query.ts b/backend/services/referral-service/src/application/queries/get-direct-referrals.query.ts index 599c89c3..7854fd0e 100644 --- a/backend/services/referral-service/src/application/queries/get-direct-referrals.query.ts +++ b/backend/services/referral-service/src/application/queries/get-direct-referrals.query.ts @@ -10,7 +10,8 @@ export interface DirectReferralResult { userId: string; accountSequence: number; // 8位账户序列号,显示用 referralCode: string; - teamCount: number; + personalPlantingCount: number; // 个人认种量 + teamPlantingCount: number; // 团队认种量 joinedAt: Date; } diff --git a/backend/services/referral-service/src/application/services/referral.service.ts b/backend/services/referral-service/src/application/services/referral.service.ts index c3e0135e..d903adea 100644 --- a/backend/services/referral-service/src/application/services/referral.service.ts +++ b/backend/services/referral-service/src/application/services/referral.service.ts @@ -154,20 +154,36 @@ export class ReferralService { */ async getDirectReferrals(query: GetDirectReferralsQuery): Promise { const relationships = await this.referralRepo.findDirectReferrals(query.userId); - const teamStats = await this.teamStatsRepo.findByUserId(query.userId); - const directStats = teamStats?.getDirectReferralStats() ?? new Map(); // 分页 const total = relationships.length; const paginated = relationships.slice(query.offset, query.offset + query.limit); - const referrals = paginated.map((r) => ({ - userId: r.userId.toString(), - accountSequence: r.accountSequence, // 8位账户序列号,用于前端显示 - referralCode: r.referralCode, - teamCount: directStats.get(r.userId) ?? 0, - joinedAt: r.createdAt, - })); + // 批量查询直推用户的认种统计 + const directUserIds = paginated.map((r) => r.userId); + const directStatsMap = new Map(); + + if (directUserIds.length > 0) { + const directStats = await this.teamStatsRepo.findByUserIds(directUserIds); + for (const stats of directStats) { + directStatsMap.set(stats.userId.toString(), { + personal: stats.personalPlantingCount, + team: stats.teamPlantingCount, + }); + } + } + + const referrals = paginated.map((r) => { + const stats = directStatsMap.get(r.userId.toString()); + return { + userId: r.userId.toString(), + accountSequence: r.accountSequence, + referralCode: r.referralCode, + personalPlantingCount: stats?.personal ?? 0, + teamPlantingCount: stats?.team ?? 0, + joinedAt: r.createdAt, + }; + }); return { referrals, diff --git a/backend/services/referral-service/src/infrastructure/repositories/team-statistics.repository.ts b/backend/services/referral-service/src/infrastructure/repositories/team-statistics.repository.ts index db3f07be..6e683b5d 100644 --- a/backend/services/referral-service/src/infrastructure/repositories/team-statistics.repository.ts +++ b/backend/services/referral-service/src/infrastructure/repositories/team-statistics.repository.ts @@ -46,7 +46,19 @@ export class TeamStatisticsRepository implements ITeamStatisticsRepository { // 更新直推统计明细 if (data.directReferrals && data.directReferrals.length > 0) { + // 批量查询直推用户的 accountSequence + const referralIds = data.directReferrals.map((dr) => dr.referralId); + const referralRelations = await tx.referralRelationship.findMany({ + where: { userId: { in: referralIds } }, + select: { userId: true, accountSequence: true }, + }); + const sequenceMap = new Map(); + for (const rel of referralRelations) { + sequenceMap.set(rel.userId, rel.accountSequence); + } + for (const dr of data.directReferrals) { + const accountSequence = sequenceMap.get(dr.referralId) ?? Number(dr.referralId); await tx.directReferral.upsert({ where: { uk_referrer_referral: { @@ -61,7 +73,7 @@ export class TeamStatisticsRepository implements ITeamStatisticsRepository { create: { referrerId: data.userId, referralId: dr.referralId, - referralSequence: dr.referralId, + referralSequence: BigInt(accountSequence), teamPlantingCount: dr.teamCount, }, }); diff --git a/frontend/mobile-app/lib/core/services/referral_service.dart b/frontend/mobile-app/lib/core/services/referral_service.dart index 027b8112..84287c70 100644 --- a/frontend/mobile-app/lib/core/services/referral_service.dart +++ b/frontend/mobile-app/lib/core/services/referral_service.dart @@ -54,14 +54,16 @@ class DirectReferralInfo { final String userId; final int accountSequence; // 8位账户序列号,用于显示 final String referralCode; - final int teamCount; + final int personalPlantingCount; // 个人认种量 + final int teamPlantingCount; // 团队认种量 final DateTime joinedAt; DirectReferralInfo({ required this.userId, required this.accountSequence, required this.referralCode, - required this.teamCount, + required this.personalPlantingCount, + required this.teamPlantingCount, required this.joinedAt, }); @@ -70,7 +72,8 @@ class DirectReferralInfo { userId: json['userId']?.toString() ?? '', accountSequence: json['accountSequence'] ?? 0, referralCode: json['referralCode'] ?? '', - teamCount: json['teamCount'] ?? 0, + personalPlantingCount: json['personalPlantingCount'] ?? 0, + teamPlantingCount: json['teamPlantingCount'] ?? 0, joinedAt: json['joinedAt'] != null ? DateTime.parse(json['joinedAt']) : DateTime.now(), diff --git a/frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart b/frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart index 52bee234..c92ee17d 100644 --- a/frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart +++ b/frontend/mobile-app/lib/features/profile/presentation/pages/profile_page.dart @@ -268,8 +268,8 @@ class _ProfilePageState extends ConsumerState { // 转换直推列表格式 _referrals = directReferrals.referrals.map((r) => { 'serial': r.accountSequence, // 使用8位账户序列号显示 - 'personal': 0, // API暂未返回个人认种量 - 'team': r.teamCount, + 'personal': r.personalPlantingCount, // 个人认种量 + 'team': r.teamPlantingCount, // 团队认种量 }).toList(); }); }