feat(contribution): 添加系统账户算力来源类型字段

- 添加 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 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-21 04:23:50 -08:00
parent e89c3166bf
commit e7260be219
12 changed files with 127 additions and 4 deletions

View File

@ -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");

View File

@ -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")
}

View File

@ -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,

View File

@ -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(),

View File

@ -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,

View File

@ -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);

View File

@ -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")
}

View File

@ -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,

View File

@ -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),

View File

@ -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;

View File

@ -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<ContributionSourceType, string> = {
FIXED_RATE: '固定比例',
LEVEL_OVERFLOW: '层级溢出',
LEVEL_NO_ANCESTOR: '无上线',
BONUS_TIER_1: '团队T1',
BONUS_TIER_2: '团队T2',
BONUS_TIER_3: '团队T3',
};
// 来源类型颜色映射
const SOURCE_TYPE_COLORS: Record<ContributionSourceType, string> = {
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({
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead className="text-right"></TableHead>
<TableHead className="text-right"></TableHead>
@ -121,7 +150,7 @@ export function ContributionRecordsTable({
{isLoading ? (
[...Array(5)].map((_, i) => (
<TableRow key={i}>
{[...Array(7)].map((_, j) => (
{[...Array(8)].map((_, j) => (
<TableCell key={j}>
<Skeleton className="h-4 w-full" />
</TableCell>
@ -131,7 +160,7 @@ export function ContributionRecordsTable({
) : records.length === 0 ? (
<TableRow>
<TableCell
colSpan={7}
colSpan={8}
className="text-center text-muted-foreground py-8"
>
@ -164,6 +193,14 @@ export function ContributionRecordsTable({
)}
</div>
</TableCell>
<TableCell>
<Badge
variant="secondary"
className={`text-xs ${SOURCE_TYPE_COLORS[record.sourceType] || 'bg-gray-100 text-gray-800'}`}
>
{getSourceTypeDisplay(record.sourceType, record.levelDepth)}
</Badge>
</TableCell>
<TableCell className="text-right">
<span className="font-mono text-sm">
{record.adoptionTreeCount > 0 ? record.adoptionTreeCount : '-'}

BIN
挖矿.xlsx Normal file

Binary file not shown.