feat(mining-admin): 重构算力构成展示,添加解锁状态

- 后端添加 unlockedBonusTiers 字段同步
- 前端算力构成卡片展示层级解锁(L1-15)和团队奖励解锁(3档)状态
- 移除无用的系统运营/省级/市级字段

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-12 04:59:51 -08:00
parent 4ca4fc9135
commit 180e5ad057
8 changed files with 131 additions and 29 deletions

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "synced_contribution_accounts" ADD COLUMN "unlockedBonusTiers" INTEGER NOT NULL DEFAULT 0;

View File

@ -191,6 +191,7 @@ model SyncedContributionAccount {
hasAdopted Boolean @default(false) hasAdopted Boolean @default(false)
directReferralCount Int @default(0) directReferralCount Int @default(0)
unlockedLevelDepth Int @default(0) unlockedLevelDepth Int @default(0)
unlockedBonusTiers Int @default(0)
syncedAt DateTime @default(now()) syncedAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt

View File

@ -174,6 +174,7 @@ export class InitializationService {
hasAdopted: account.hasAdopted || false, hasAdopted: account.hasAdopted || false,
directReferralCount: account.directReferralAdoptedCount || 0, directReferralCount: account.directReferralAdoptedCount || 0,
unlockedLevelDepth: account.unlockedLevelDepth || 0, unlockedLevelDepth: account.unlockedLevelDepth || 0,
unlockedBonusTiers: account.unlockedBonusTiers || 0,
}, },
update: { update: {
personalContribution: account.personalContribution, personalContribution: account.personalContribution,
@ -184,6 +185,7 @@ export class InitializationService {
hasAdopted: account.hasAdopted, hasAdopted: account.hasAdopted,
directReferralCount: account.directReferralAdoptedCount, directReferralCount: account.directReferralAdoptedCount,
unlockedLevelDepth: account.unlockedLevelDepth, unlockedLevelDepth: account.unlockedLevelDepth,
unlockedBonusTiers: account.unlockedBonusTiers,
}, },
}); });
syncedCount++; syncedCount++;

View File

@ -325,6 +325,9 @@ export class UsersService {
totalContribution: '0', totalContribution: '0',
effectiveContribution: '0', effectiveContribution: '0',
hasAdopted: false, hasAdopted: false,
directReferralCount: 0,
unlockedLevelDepth: 0,
unlockedBonusTiers: 0,
}, },
records: [], records: [],
pagination: { page, pageSize, total: 0, totalPages: 0 }, pagination: { page, pageSize, total: 0, totalPages: 0 },
@ -342,6 +345,7 @@ export class UsersService {
hasAdopted: contribution.hasAdopted, hasAdopted: contribution.hasAdopted,
directReferralCount: contribution.directReferralCount, directReferralCount: contribution.directReferralCount,
unlockedLevelDepth: contribution.unlockedLevelDepth, unlockedLevelDepth: contribution.unlockedLevelDepth,
unlockedBonusTiers: contribution.unlockedBonusTiers,
}, },
// 详细流水需要从 contribution-service 获取 // 详细流水需要从 contribution-service 获取
records: [], records: [],
@ -883,6 +887,7 @@ export class UsersService {
hasAdopted: user.contributionAccount.hasAdopted, hasAdopted: user.contributionAccount.hasAdopted,
directReferralCount: user.contributionAccount.directReferralCount, directReferralCount: user.contributionAccount.directReferralCount,
unlockedLevelDepth: user.contributionAccount.unlockedLevelDepth, unlockedLevelDepth: user.contributionAccount.unlockedLevelDepth,
unlockedBonusTiers: user.contributionAccount.unlockedBonusTiers,
} }
: null, : null,
mining: user.miningAccount mining: user.miningAccount

View File

@ -447,6 +447,7 @@ export class CdcSyncService implements OnModuleInit {
hasAdopted: payload.hasAdopted || false, hasAdopted: payload.hasAdopted || false,
directReferralCount: payload.directReferralAdoptedCount || 0, directReferralCount: payload.directReferralAdoptedCount || 0,
unlockedLevelDepth: payload.unlockedLevelDepth || 0, unlockedLevelDepth: payload.unlockedLevelDepth || 0,
unlockedBonusTiers: payload.unlockedBonusTiers || 0,
}, },
update: { update: {
personalContribution: payload.personalContribution, personalContribution: payload.personalContribution,
@ -457,6 +458,7 @@ export class CdcSyncService implements OnModuleInit {
hasAdopted: payload.hasAdopted, hasAdopted: payload.hasAdopted,
directReferralCount: payload.directReferralAdoptedCount, directReferralCount: payload.directReferralAdoptedCount,
unlockedLevelDepth: payload.unlockedLevelDepth, unlockedLevelDepth: payload.unlockedLevelDepth,
unlockedBonusTiers: payload.unlockedBonusTiers,
}, },
}); });
@ -496,6 +498,7 @@ export class CdcSyncService implements OnModuleInit {
hasAdopted: true, // 有算力计算说明已认种 hasAdopted: true, // 有算力计算说明已认种
directReferralCount: 0, directReferralCount: 0,
unlockedLevelDepth: 0, unlockedLevelDepth: 0,
unlockedBonusTiers: 0,
}, },
update: { update: {
// 增量更新个人算力 // 增量更新个人算力

View File

@ -175,35 +175,120 @@ export default function UserDetailPage() {
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent className="space-y-3"> <CardContent className="space-y-4">
<div className="flex justify-between"> {/* 个人算力 */}
<span className="text-sm text-muted-foreground"></span> <div className="flex justify-between items-center">
<span className="font-mono">{formatDecimal(user?.contributions?.personal, 4)}</span> <span className="text-sm text-muted-foreground"> (70%)</span>
<span className="font-mono font-medium">{formatDecimal(user?.contributions?.personal, 4)}</span>
</div> </div>
<div className="flex justify-between">
<span className="text-sm text-muted-foreground"></span> {/* 层级算力 */}
<span className="font-mono">{formatDecimal(user?.contributions?.systemOperation, 4)}</span> <div className="space-y-2">
<div className="flex justify-between items-center">
<span className="text-sm text-muted-foreground"> (7.5%)</span>
<span className="font-mono font-medium">{formatDecimal(user?.contributions?.teamLevel, 4)}</span>
</div>
<div className="pl-3 space-y-1 text-xs">
<div className="flex items-center gap-2">
{(user?.contributions?.unlockedLevelDepth ?? 0) >= 5 ? (
<span className="text-green-600"></span>
) : (
<span className="text-muted-foreground"></span>
)}
<span className={`${(user?.contributions?.unlockedLevelDepth ?? 0) >= 5 ? 'text-foreground' : 'text-muted-foreground'}`}>
L1-L5 {(user?.contributions?.unlockedLevelDepth ?? 0) >= 5 ? '已解锁' : '未解锁'}
</span>
<span className="text-muted-foreground">(1)</span>
</div>
<div className="flex items-center gap-2">
{(user?.contributions?.unlockedLevelDepth ?? 0) >= 10 ? (
<span className="text-green-600"></span>
) : (
<span className="text-muted-foreground"></span>
)}
<span className={`${(user?.contributions?.unlockedLevelDepth ?? 0) >= 10 ? 'text-foreground' : 'text-muted-foreground'}`}>
L6-L10 {(user?.contributions?.unlockedLevelDepth ?? 0) >= 10 ? '已解锁' : '未解锁'}
</span>
<span className="text-muted-foreground">(3)</span>
</div>
<div className="flex items-center gap-2">
{(user?.contributions?.unlockedLevelDepth ?? 0) >= 15 ? (
<span className="text-green-600"></span>
) : (
<span className="text-muted-foreground"></span>
)}
<span className={`${(user?.contributions?.unlockedLevelDepth ?? 0) >= 15 ? 'text-foreground' : 'text-muted-foreground'}`}>
L11-L15 {(user?.contributions?.unlockedLevelDepth ?? 0) >= 15 ? '已解锁' : '未解锁'}
</span>
<span className="text-muted-foreground">(5)</span>
</div>
</div>
</div> </div>
<div className="flex justify-between">
<span className="text-sm text-muted-foreground"></span> {/* 团队奖励 */}
<span className="font-mono">{formatDecimal(user?.contributions?.systemProvince, 4)}</span> <div className="space-y-2">
</div> <div className="flex justify-between items-center">
<div className="flex justify-between"> <span className="text-sm text-muted-foreground"> (7.5%)</span>
<span className="text-sm text-muted-foreground"></span> <span className="font-mono font-medium">{formatDecimal(user?.contributions?.teamBonus, 4)}</span>
<span className="font-mono">{formatDecimal(user?.contributions?.systemCity, 4)}</span> </div>
</div> <div className="pl-3 space-y-1 text-xs">
<div className="flex justify-between"> <div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground"></span> {(user?.contributions?.unlockedBonusTiers ?? 0) >= 1 ? (
<span className="font-mono">{formatDecimal(user?.contributions?.teamLevel, 4)}</span> <span className="text-green-600"></span>
</div> ) : (
<div className="flex justify-between"> <span className="text-muted-foreground"></span>
<span className="text-sm text-muted-foreground"></span> )}
<span className="font-mono">{formatDecimal(user?.contributions?.teamBonus, 4)}</span> <span className={`${(user?.contributions?.unlockedBonusTiers ?? 0) >= 1 ? 'text-foreground' : 'text-muted-foreground'}`}>
1 2.5% {(user?.contributions?.unlockedBonusTiers ?? 0) >= 1 ? '已解锁' : '未解锁'}
</span>
<span className="text-muted-foreground">()</span>
</div>
<div className="flex items-center gap-2">
{(user?.contributions?.unlockedBonusTiers ?? 0) >= 2 ? (
<span className="text-green-600"></span>
) : (
<span className="text-muted-foreground"></span>
)}
<span className={`${(user?.contributions?.unlockedBonusTiers ?? 0) >= 2 ? 'text-foreground' : 'text-muted-foreground'}`}>
2 2.5% {(user?.contributions?.unlockedBonusTiers ?? 0) >= 2 ? '已解锁' : '未解锁'}
</span>
<span className="text-muted-foreground">(2)</span>
</div>
<div className="flex items-center gap-2">
{(user?.contributions?.unlockedBonusTiers ?? 0) >= 3 ? (
<span className="text-green-600"></span>
) : (
<span className="text-muted-foreground"></span>
)}
<span className={`${(user?.contributions?.unlockedBonusTiers ?? 0) >= 3 ? 'text-foreground' : 'text-muted-foreground'}`}>
3 2.5% {(user?.contributions?.unlockedBonusTiers ?? 0) >= 3 ? '已解锁' : '未解锁'}
</span>
<span className="text-muted-foreground">(4)</span>
</div>
</div>
</div> </div>
{/* 有效算力 */}
<div className="flex justify-between pt-3 border-t font-medium"> <div className="flex justify-between pt-3 border-t font-medium">
<span> ()</span> <span> ()</span>
<span className="font-mono text-primary text-lg">{formatDecimal(user?.effectiveContribution, 4)}</span> <span className="font-mono text-primary text-lg">{formatDecimal(user?.effectiveContribution, 4)}</span>
</div> </div>
{/* 解锁状态汇总 */}
<div className="pt-2 border-t text-xs text-muted-foreground">
<div className="flex justify-between">
<span></span>
<span className="font-mono">{user?.contributions?.directReferralCount ?? 0} </span>
</div>
<div className="flex justify-between">
<span></span>
<span className="font-mono">{user?.contributions?.unlockedLevelDepth ?? 0} / 15 </span>
</div>
<div className="flex justify-between">
<span></span>
<span className="font-mono">{user?.contributions?.unlockedBonusTiers ?? 0} / 3 </span>
</div>
</div>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>

View File

@ -54,15 +54,17 @@ function transformUserDetail(backendUser: any): UserDetail {
// 认种数据 // 认种数据
personalAdoptions: backendUser.adoption?.personalAdoptionCount || 0, personalAdoptions: backendUser.adoption?.personalAdoptionCount || 0,
teamAdoptions: backendUser.adoption?.teamAdoptions || 0, teamAdoptions: backendUser.adoption?.teamAdoptions || 0,
// 算力明细 // 算力明细及解锁状态
contributions: { contributions: {
personal: backendUser.contribution?.personalContribution || '0', personal: backendUser.contribution?.personalContribution || '0',
systemOperation: '0',
systemProvince: '0',
systemCity: '0',
teamLevel: backendUser.contribution?.teamLevelContribution || '0', teamLevel: backendUser.contribution?.teamLevelContribution || '0',
teamBonus: backendUser.contribution?.teamBonusContribution || '0', teamBonus: backendUser.contribution?.teamBonusContribution || '0',
total: backendUser.contribution?.totalContribution || '0', total: backendUser.contribution?.totalContribution || '0',
// 解锁状态
hasAdopted: backendUser.contribution?.hasAdopted || false,
directReferralCount: backendUser.contribution?.directReferralCount || 0,
unlockedLevelDepth: backendUser.contribution?.unlockedLevelDepth || 0,
unlockedBonusTiers: backendUser.contribution?.unlockedBonusTiers || 0,
}, },
kycStatus: backendUser.kycStatus, kycStatus: backendUser.kycStatus,
registeredAt: backendUser.createdAt, registeredAt: backendUser.createdAt,

View File

@ -53,12 +53,14 @@ export interface UserDetail extends UserOverview {
export interface ContributionBreakdown { export interface ContributionBreakdown {
personal: string; personal: string;
systemOperation: string;
systemProvince: string;
systemCity: string;
teamLevel: string; teamLevel: string;
teamBonus: string; teamBonus: string;
total: string; total: string;
// 解锁状态
hasAdopted: boolean;
directReferralCount: number;
unlockedLevelDepth: number; // 0, 5, 10, 15
unlockedBonusTiers: number; // 0, 1, 2, 3
} }
export interface ContributionRecord { export interface ContributionRecord {