From e7260be21965a23386b9805bad1dc41589495dc2 Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 21 Jan 2026 04:23:50 -0800 Subject: [PATCH] =?UTF-8?q?feat(contribution):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E8=B4=A6=E6=88=B7=E7=AE=97=E5=8A=9B=E6=9D=A5?= =?UTF-8?q?=E6=BA=90=E7=B1=BB=E5=9E=8B=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 sourceType 字段区分算力来源类型: - FIXED_RATE: 固定比例分配(OPERATION 12%、PROVINCE 1%、CITY 2%) - LEVEL_OVERFLOW: 层级溢出归总部(上线未解锁该级别) - LEVEL_NO_ANCESTOR: 无上线归总部(该级无上线) - BONUS_TIER_1/2/3: 团队奖励未解锁归总部 - 添加 levelDepth 字段记录层级深度(1-15级) - 更新前端表格显示来源类型列 Co-Authored-By: Claude Opus 4.5 --- .../migration.sql | 12 +++++ .../contribution-service/prisma/schema.prisma | 6 +++ .../contribution-calculation.service.ts | 12 +++++ ...ystem-contribution-record-created.event.ts | 19 ++++++++ .../repositories/system-account.repository.ts | 8 ++++ .../prisma/migrations/0001_init/migration.sql | 5 ++ .../mining-admin-service/prisma/schema.prisma | 6 +++ .../services/system-accounts.service.ts | 3 ++ .../infrastructure/kafka/cdc-sync.service.ts | 4 ++ .../api/system-accounts.api.ts | 13 +++++- .../components/contribution-records-table.tsx | 43 ++++++++++++++++-- 挖矿.xlsx | Bin 0 -> 15865 bytes 12 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 backend/services/contribution-service/prisma/migrations/0003_add_source_type_to_system_contribution_records/migration.sql create mode 100644 挖矿.xlsx diff --git a/backend/services/contribution-service/prisma/migrations/0003_add_source_type_to_system_contribution_records/migration.sql b/backend/services/contribution-service/prisma/migrations/0003_add_source_type_to_system_contribution_records/migration.sql new file mode 100644 index 00000000..dd787ec7 --- /dev/null +++ b/backend/services/contribution-service/prisma/migrations/0003_add_source_type_to_system_contribution_records/migration.sql @@ -0,0 +1,12 @@ +-- AlterTable: Add source_type and level_depth columns to system_contribution_records +-- source_type: FIXED_RATE(固定比例分配) / LEVEL_OVERFLOW(层级溢出) / LEVEL_NO_ANCESTOR(无上线) / BONUS_TIER_1/2/3(团队奖励未解锁) +-- level_depth: 层级深度(1-15),仅对 LEVEL_OVERFLOW 和 LEVEL_NO_ANCESTOR 类型有效 + +ALTER TABLE "system_contribution_records" ADD COLUMN "source_type" VARCHAR(30) NOT NULL DEFAULT 'FIXED_RATE'; +ALTER TABLE "system_contribution_records" ADD COLUMN "level_depth" INTEGER; + +-- Remove default after adding column (existing records are FIXED_RATE) +ALTER TABLE "system_contribution_records" ALTER COLUMN "source_type" DROP DEFAULT; + +-- CreateIndex +CREATE INDEX "system_contribution_records_source_type_idx" ON "system_contribution_records"("source_type"); diff --git a/backend/services/contribution-service/prisma/schema.prisma b/backend/services/contribution-service/prisma/schema.prisma index f63775b1..84810b53 100644 --- a/backend/services/contribution-service/prisma/schema.prisma +++ b/backend/services/contribution-service/prisma/schema.prisma @@ -327,6 +327,11 @@ model SystemContributionRecord { sourceAdoptionId BigInt @map("source_adoption_id") sourceAccountSequence String @map("source_account_sequence") @db.VarChar(20) + // 来源类型:FIXED_RATE(固定比例分配) / LEVEL_OVERFLOW(层级溢出) / LEVEL_NO_ANCESTOR(无上线) / BONUS_TIER_1/2/3(团队奖励未解锁) + sourceType String @map("source_type") @db.VarChar(30) + // 层级深度:对于 LEVEL_OVERFLOW 和 LEVEL_NO_ANCESTOR 类型,表示第几级(1-15) + levelDepth Int? @map("level_depth") + distributionRate Decimal @map("distribution_rate") @db.Decimal(10, 6) amount Decimal @map("amount") @db.Decimal(30, 10) @@ -342,6 +347,7 @@ model SystemContributionRecord { @@index([systemAccountId]) @@index([sourceAdoptionId]) @@index([deletedAt]) + @@index([sourceType]) @@map("system_contribution_records") } diff --git a/backend/services/contribution-service/src/application/services/contribution-calculation.service.ts b/backend/services/contribution-service/src/application/services/contribution-calculation.service.ts index 51b1e138..0f4ca3d6 100644 --- a/backend/services/contribution-service/src/application/services/contribution-calculation.service.ts +++ b/backend/services/contribution-service/src/application/services/contribution-calculation.service.ts @@ -293,11 +293,17 @@ export class ContributionCalculationService { // 为每笔未分配算力创建 HEADQUARTERS 明细记录 for (const unallocated of result.unallocatedContributions) { + // 确定来源类型和层级深度 + const sourceType = unallocated.type as string; // LEVEL_OVERFLOW / LEVEL_NO_ANCESTOR / BONUS_TIER_1/2/3 + const levelDepth = unallocated.levelDepth; + const savedRecord = await this.systemAccountRepository.saveContributionRecord({ accountType: 'HEADQUARTERS', regionCode: null, sourceAdoptionId, sourceAccountSequence, + sourceType, + levelDepth, distributionRate: 0, // 未分配算力没有固定比例 amount: unallocated.amount, effectiveDate, @@ -311,6 +317,8 @@ export class ContributionCalculationService { null, sourceAdoptionId, sourceAccountSequence, + sourceType as any, + levelDepth, 0, unallocated.amount.value.toString(), effectiveDate, @@ -382,6 +390,8 @@ export class ContributionCalculationService { regionCode: sys.regionCode, sourceAdoptionId, sourceAccountSequence, + sourceType: 'FIXED_RATE', // 固定比例分配 + levelDepth: null, distributionRate: sys.rate.value.toNumber(), amount: sys.amount, effectiveDate, @@ -415,6 +425,8 @@ export class ContributionCalculationService { sys.regionCode, // 传递区域代码 sourceAdoptionId, sourceAccountSequence, + 'FIXED_RATE', // 固定比例分配 + null, // 无层级深度 sys.rate.value.toNumber(), sys.amount.value.toString(), effectiveDate, diff --git a/backend/services/contribution-service/src/domain/events/system-contribution-record-created.event.ts b/backend/services/contribution-service/src/domain/events/system-contribution-record-created.event.ts index 6ca1b87b..57375760 100644 --- a/backend/services/contribution-service/src/domain/events/system-contribution-record-created.event.ts +++ b/backend/services/contribution-service/src/domain/events/system-contribution-record-created.event.ts @@ -1,3 +1,18 @@ +/** + * 来源类型枚举 + * - FIXED_RATE: 固定比例分配(OPERATION 12%、PROVINCE 1%、CITY 2%) + * - LEVEL_OVERFLOW: 层级溢出归总部(上线未解锁该级别) + * - LEVEL_NO_ANCESTOR: 无上线归总部(该级无上线) + * - BONUS_TIER_1/2/3: 团队奖励未解锁归总部 + */ +export type SystemContributionSourceType = + | 'FIXED_RATE' + | 'LEVEL_OVERFLOW' + | 'LEVEL_NO_ANCESTOR' + | 'BONUS_TIER_1' + | 'BONUS_TIER_2' + | 'BONUS_TIER_3'; + /** * 系统账户算力明细创建事件 * 用于将系统账户的每笔算力来源明细同步到 mining-admin-service @@ -12,6 +27,8 @@ export class SystemContributionRecordCreatedEvent { public readonly regionCode: string | null, // 区域代码(省/市代码,如 440000, 440100) public readonly sourceAdoptionId: bigint, // 来源认种ID public readonly sourceAccountSequence: string, // 认种人账号 + public readonly sourceType: SystemContributionSourceType, // 来源类型 + public readonly levelDepth: number | null, // 层级深度(1-15),仅对 LEVEL_OVERFLOW/LEVEL_NO_ANCESTOR 有效 public readonly distributionRate: number, // 分配比例 public readonly amount: string, // 算力金额 public readonly effectiveDate: Date, // 生效日期 @@ -27,6 +44,8 @@ export class SystemContributionRecordCreatedEvent { regionCode: this.regionCode, sourceAdoptionId: this.sourceAdoptionId.toString(), sourceAccountSequence: this.sourceAccountSequence, + sourceType: this.sourceType, + levelDepth: this.levelDepth, distributionRate: this.distributionRate, amount: this.amount, effectiveDate: this.effectiveDate.toISOString(), diff --git a/backend/services/contribution-service/src/infrastructure/persistence/repositories/system-account.repository.ts b/backend/services/contribution-service/src/infrastructure/persistence/repositories/system-account.repository.ts index 4a4f3d4c..579a69a2 100644 --- a/backend/services/contribution-service/src/infrastructure/persistence/repositories/system-account.repository.ts +++ b/backend/services/contribution-service/src/infrastructure/persistence/repositories/system-account.repository.ts @@ -21,6 +21,8 @@ export interface SystemContributionRecord { systemAccountId: bigint; sourceAdoptionId: bigint; sourceAccountSequence: string; + sourceType: string; // 来源类型:FIXED_RATE / LEVEL_OVERFLOW / LEVEL_NO_ANCESTOR / BONUS_TIER_1/2/3 + levelDepth: number | null; // 层级深度(1-15),仅对层级相关类型有效 distributionRate: number; amount: ContributionAmount; effectiveDate: Date; @@ -224,6 +226,8 @@ export class SystemAccountRepository { regionCode: string | null; sourceAdoptionId: bigint; sourceAccountSequence: string; + sourceType: string; // 来源类型 + levelDepth?: number | null; // 层级深度 distributionRate: number; amount: ContributionAmount; effectiveDate: Date; @@ -239,6 +243,8 @@ export class SystemAccountRepository { systemAccountId: systemAccount.id, sourceAdoptionId: record.sourceAdoptionId, sourceAccountSequence: record.sourceAccountSequence, + sourceType: record.sourceType, + levelDepth: record.levelDepth ?? null, distributionRate: record.distributionRate, amount: record.amount.value, effectiveDate: record.effectiveDate, @@ -303,6 +309,8 @@ export class SystemAccountRepository { systemAccountId: record.systemAccountId, sourceAdoptionId: record.sourceAdoptionId, sourceAccountSequence: record.sourceAccountSequence, + sourceType: record.sourceType, + levelDepth: record.levelDepth, distributionRate: record.distributionRate, amount: new ContributionAmount(record.amount), effectiveDate: record.effectiveDate, diff --git a/backend/services/mining-admin-service/prisma/migrations/0001_init/migration.sql b/backend/services/mining-admin-service/prisma/migrations/0001_init/migration.sql index bd6a5060..6ff48a88 100644 --- a/backend/services/mining-admin-service/prisma/migrations/0001_init/migration.sql +++ b/backend/services/mining-admin-service/prisma/migrations/0001_init/migration.sql @@ -876,6 +876,10 @@ CREATE TABLE "synced_system_contribution_records" ( "region_code" TEXT, "source_adoption_id" BIGINT NOT NULL, "source_account_sequence" TEXT NOT NULL, + -- 来源类型: FIXED_RATE(固定比例) / LEVEL_OVERFLOW(层级溢出) / LEVEL_NO_ANCESTOR(无上线) / BONUS_TIER_1/2/3(团队奖励未解锁) + "source_type" VARCHAR(30) NOT NULL, + -- 层级深度(1-15),仅对 LEVEL_OVERFLOW 和 LEVEL_NO_ANCESTOR 类型有效 + "level_depth" INTEGER, "distribution_rate" DECIMAL(10,6) NOT NULL, "amount" DECIMAL(30,10) NOT NULL, "effective_date" DATE NOT NULL, @@ -893,4 +897,5 @@ CREATE UNIQUE INDEX "synced_system_contribution_records_original_record_id_key" CREATE INDEX "synced_system_contribution_records_account_type_region_code_idx" ON "synced_system_contribution_records"("account_type", "region_code"); CREATE INDEX "synced_system_contribution_records_source_adoption_id_idx" ON "synced_system_contribution_records"("source_adoption_id"); CREATE INDEX "synced_system_contribution_records_source_account_sequence_idx" ON "synced_system_contribution_records"("source_account_sequence"); +CREATE INDEX "synced_system_contribution_records_source_type_idx" ON "synced_system_contribution_records"("source_type"); CREATE INDEX "synced_system_contribution_records_created_at_idx" ON "synced_system_contribution_records"("created_at" DESC); diff --git a/backend/services/mining-admin-service/prisma/schema.prisma b/backend/services/mining-admin-service/prisma/schema.prisma index 828a01cb..0083602d 100644 --- a/backend/services/mining-admin-service/prisma/schema.prisma +++ b/backend/services/mining-admin-service/prisma/schema.prisma @@ -452,6 +452,11 @@ model SyncedSystemContributionRecord { sourceAdoptionId BigInt @map("source_adoption_id") // 来源认种ID sourceAccountSequence String @map("source_account_sequence") // 认种人账号 + // 来源类型: FIXED_RATE(固定比例) / LEVEL_OVERFLOW(层级溢出) / LEVEL_NO_ANCESTOR(无上线) / BONUS_TIER_1/2/3(团队奖励未解锁) + sourceType String @map("source_type") @db.VarChar(30) + // 层级深度(1-15),仅对 LEVEL_OVERFLOW 和 LEVEL_NO_ANCESTOR 类型有效 + levelDepth Int? @map("level_depth") + // 分配参数 distributionRate Decimal @map("distribution_rate") @db.Decimal(10, 6) // 分配比例 amount Decimal @map("amount") @db.Decimal(30, 10) // 算力金额 @@ -468,6 +473,7 @@ model SyncedSystemContributionRecord { @@index([accountType, regionCode]) @@index([sourceAdoptionId]) @@index([sourceAccountSequence]) + @@index([sourceType]) @@index([createdAt(sort: Desc)]) @@map("synced_system_contribution_records") } diff --git a/backend/services/mining-admin-service/src/application/services/system-accounts.service.ts b/backend/services/mining-admin-service/src/application/services/system-accounts.service.ts index 6191c0ca..98923e77 100644 --- a/backend/services/mining-admin-service/src/application/services/system-accounts.service.ts +++ b/backend/services/mining-admin-service/src/application/services/system-accounts.service.ts @@ -444,6 +444,9 @@ export class SystemAccountsService { regionCode: record.regionCode, sourceAdoptionId: record.sourceAdoptionId.toString(), sourceAccountSequence: record.sourceAccountSequence, + // 来源类型 + sourceType: record.sourceType, + levelDepth: record.levelDepth, // 认种订单详情 adoptionTreeCount: adoption?.treeCount || 0, adoptionDate: adoption?.adoptionDate || null, diff --git a/backend/services/mining-admin-service/src/infrastructure/kafka/cdc-sync.service.ts b/backend/services/mining-admin-service/src/infrastructure/kafka/cdc-sync.service.ts index a1a56a09..501dc8e7 100644 --- a/backend/services/mining-admin-service/src/infrastructure/kafka/cdc-sync.service.ts +++ b/backend/services/mining-admin-service/src/infrastructure/kafka/cdc-sync.service.ts @@ -673,6 +673,8 @@ export class CdcSyncService implements OnModuleInit { regionCode, sourceAdoptionId: BigInt(payload.sourceAdoptionId), sourceAccountSequence: payload.sourceAccountSequence, + sourceType: payload.sourceType || 'FIXED_RATE', // 来源类型 + levelDepth: payload.levelDepth ?? null, // 层级深度 distributionRate: payload.distributionRate, amount: payload.amount, effectiveDate: new Date(payload.effectiveDate), @@ -685,6 +687,8 @@ export class CdcSyncService implements OnModuleInit { regionCode, sourceAdoptionId: BigInt(payload.sourceAdoptionId), sourceAccountSequence: payload.sourceAccountSequence, + sourceType: payload.sourceType || 'FIXED_RATE', + levelDepth: payload.levelDepth ?? null, distributionRate: payload.distributionRate, amount: payload.amount, effectiveDate: new Date(payload.effectiveDate), diff --git a/frontend/mining-admin-web/src/features/system-accounts/api/system-accounts.api.ts b/frontend/mining-admin-web/src/features/system-accounts/api/system-accounts.api.ts index 95a58313..525d08bf 100644 --- a/frontend/mining-admin-web/src/features/system-accounts/api/system-accounts.api.ts +++ b/frontend/mining-admin-web/src/features/system-accounts/api/system-accounts.api.ts @@ -43,14 +43,25 @@ export interface SystemTransactionsResponse { pageSize: number; } +// 来源类型枚举 +export type ContributionSourceType = + | 'FIXED_RATE' // 固定比例分配(OPERATION 12%、PROVINCE 1%、CITY 2%) + | 'LEVEL_OVERFLOW' // 层级溢出归总部(上线未解锁该级别) + | 'LEVEL_NO_ANCESTOR' // 无上线归总部(该级无上线) + | 'BONUS_TIER_1' // 团队奖励T1未解锁归总部 + | 'BONUS_TIER_2' // 团队奖励T2未解锁归总部 + | 'BONUS_TIER_3'; // 团队奖励T3未解锁归总部 + // 算力来源明细记录 export interface SystemContributionRecord { - id: string; originalRecordId: string; accountType: string; regionCode: string | null; sourceAdoptionId: string; sourceAccountSequence: string; + // 来源类型 + sourceType: ContributionSourceType; + levelDepth: number | null; // 层级深度(1-15),仅对 LEVEL_OVERFLOW 和 LEVEL_NO_ANCESTOR 有效 // 认种订单详情 adoptionTreeCount: number; adoptionDate: string | null; diff --git a/frontend/mining-admin-web/src/features/system-accounts/components/contribution-records-table.tsx b/frontend/mining-admin-web/src/features/system-accounts/components/contribution-records-table.tsx index ae43b7e7..3430b6a7 100644 --- a/frontend/mining-admin-web/src/features/system-accounts/components/contribution-records-table.tsx +++ b/frontend/mining-admin-web/src/features/system-accounts/components/contribution-records-table.tsx @@ -16,7 +16,35 @@ import { formatDecimal } from '@/lib/utils/format'; import { format } from 'date-fns'; import { zhCN } from 'date-fns/locale'; import { ChevronLeft, ChevronRight, Users, FileStack, TrendingUp } from 'lucide-react'; -import type { SystemContributionRecord, SystemContributionStats } from '../api/system-accounts.api'; +import type { SystemContributionRecord, SystemContributionStats, ContributionSourceType } from '../api/system-accounts.api'; + +// 来源类型显示名称映射 +const SOURCE_TYPE_LABELS: Record = { + FIXED_RATE: '固定比例', + LEVEL_OVERFLOW: '层级溢出', + LEVEL_NO_ANCESTOR: '无上线', + BONUS_TIER_1: '团队T1', + BONUS_TIER_2: '团队T2', + BONUS_TIER_3: '团队T3', +}; + +// 来源类型颜色映射 +const SOURCE_TYPE_COLORS: Record = { + FIXED_RATE: 'bg-green-100 text-green-800', + LEVEL_OVERFLOW: 'bg-orange-100 text-orange-800', + LEVEL_NO_ANCESTOR: 'bg-red-100 text-red-800', + BONUS_TIER_1: 'bg-purple-100 text-purple-800', + BONUS_TIER_2: 'bg-purple-100 text-purple-800', + BONUS_TIER_3: 'bg-purple-100 text-purple-800', +}; + +// 获取来源类型显示文本 +function getSourceTypeDisplay(sourceType: ContributionSourceType, levelDepth: number | null): string { + if (sourceType === 'LEVEL_OVERFLOW' || sourceType === 'LEVEL_NO_ANCESTOR') { + return `${SOURCE_TYPE_LABELS[sourceType]}(第${levelDepth}级)`; + } + return SOURCE_TYPE_LABELS[sourceType] || sourceType; +} interface ContributionRecordsTableProps { records: SystemContributionRecord[]; @@ -110,6 +138,7 @@ export function ContributionRecordsTable({ 认种订单 认种用户 + 来源类型 认种树数 分配比例 算力金额 @@ -121,7 +150,7 @@ export function ContributionRecordsTable({ {isLoading ? ( [...Array(5)].map((_, i) => ( - {[...Array(7)].map((_, j) => ( + {[...Array(8)].map((_, j) => ( @@ -131,7 +160,7 @@ export function ContributionRecordsTable({ ) : records.length === 0 ? ( 暂无算力来源记录 @@ -164,6 +193,14 @@ export function ContributionRecordsTable({ )} + + + {getSourceTypeDisplay(record.sourceType, record.levelDepth)} + + {record.adoptionTreeCount > 0 ? record.adoptionTreeCount : '-'} diff --git a/挖矿.xlsx b/挖矿.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..98cf3306d6b7f25e39aed81f89c23aa3e03c93b2 GIT binary patch literal 15865 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 M!a$U#bH3*OA2ywewg3PC literal 0 HcmV?d00001