feat(contribution-service, mining-service): 添加18级待解锁算力字段和挖矿收益分配表
contribution-service: - 添加15级层级待解锁字段 (level1-15Pending) - 添加3档加成待解锁字段 (bonusTier1-3Pending) - 添加解锁状态追踪字段 (hasAdopted, directReferralAdoptedCount等) - 重构ContributionAccountAggregate支持新字段结构 - 更新repository和query处理effectiveContribution mining-service: - 添加MiningRewardAllocation表追踪每日挖矿收益分配明细 - 添加DailyMiningRewardSummary表汇总账户每日收益 - 添加HeadquartersPendingReward表记录未解锁算力收益归总部明细 - 创建初始migration文件 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
4bb995f2c2
commit
6261679f5a
|
|
@ -0,0 +1,158 @@
|
|||
-- ============================================
|
||||
-- Migration: 添加待解锁算力字段和解锁事件记录
|
||||
--
|
||||
-- 业务背景:
|
||||
-- 1. 15级层级算力:每级0.5%,共7.5%
|
||||
-- - 自己认种解锁1-5级
|
||||
-- - 直推≥3人认种解锁6-10级
|
||||
-- - 直推≥5人认种解锁11-15级
|
||||
-- 2. 3档加成算力:每档2.5%,共7.5%
|
||||
-- - 自己认种解锁第1档
|
||||
-- - 直推≥2人认种解锁第2档
|
||||
-- - 直推≥4人认种解锁第3档
|
||||
-- ============================================
|
||||
|
||||
-- 1. 删除旧字段(如果存在)
|
||||
ALTER TABLE "contribution_accounts" DROP COLUMN IF EXISTS "team_level_contribution";
|
||||
ALTER TABLE "contribution_accounts" DROP COLUMN IF EXISTS "team_bonus_contribution";
|
||||
ALTER TABLE "contribution_accounts" DROP COLUMN IF EXISTS "total_contribution";
|
||||
|
||||
-- 2. 添加15级层级待解锁字段
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_1_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_2_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_3_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_4_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_5_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_6_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_7_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_8_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_9_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_10_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_11_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_12_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_13_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_14_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "level_15_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
|
||||
-- 3. 添加3档加成待解锁字段
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "bonus_tier_1_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "bonus_tier_2_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "bonus_tier_3_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
|
||||
-- 4. 添加汇总字段
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "total_level_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "total_bonus_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "total_pending" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "total_unlocked" DECIMAL(30,10) DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "effective_contribution" DECIMAL(30,10) DEFAULT 0;
|
||||
|
||||
-- 5. 添加解锁条件状态字段
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "has_adopted" BOOLEAN DEFAULT false;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "direct_referral_adopted_count" INTEGER DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "unlocked_level_depth" INTEGER DEFAULT 0;
|
||||
ALTER TABLE "contribution_accounts" ADD COLUMN IF NOT EXISTS "unlocked_bonus_tiers" INTEGER DEFAULT 0;
|
||||
|
||||
-- 6. 更新 contribution_records 表添加状态字段
|
||||
ALTER TABLE "contribution_records" ADD COLUMN IF NOT EXISTS "status" VARCHAR(20) DEFAULT 'PENDING';
|
||||
ALTER TABLE "contribution_records" ADD COLUMN IF NOT EXISTS "unlocked_at" TIMESTAMP(3);
|
||||
ALTER TABLE "contribution_records" ADD COLUMN IF NOT EXISTS "unlock_reason" VARCHAR(200);
|
||||
ALTER TABLE "contribution_records" ADD COLUMN IF NOT EXISTS "remark" VARCHAR(500);
|
||||
|
||||
-- 7. 创建索引(contribution_accounts)
|
||||
CREATE INDEX IF NOT EXISTS "contribution_accounts_has_adopted_idx" ON "contribution_accounts"("has_adopted");
|
||||
CREATE INDEX IF NOT EXISTS "contribution_accounts_direct_referral_adopted_count_idx" ON "contribution_accounts"("direct_referral_adopted_count");
|
||||
CREATE INDEX IF NOT EXISTS "contribution_accounts_effective_contribution_idx" ON "contribution_accounts"("effective_contribution" DESC);
|
||||
|
||||
-- 8. 创建索引(contribution_records)
|
||||
CREATE INDEX IF NOT EXISTS "contribution_records_status_idx" ON "contribution_records"("status");
|
||||
CREATE INDEX IF NOT EXISTS "contribution_records_account_status_idx" ON "contribution_records"("account_sequence", "status");
|
||||
|
||||
-- 9. 创建解锁事件记录表
|
||||
CREATE TABLE IF NOT EXISTS "unlock_events" (
|
||||
"id" BIGSERIAL NOT NULL,
|
||||
"account_sequence" VARCHAR(20) NOT NULL,
|
||||
|
||||
-- 触发信息
|
||||
"trigger_type" VARCHAR(30) NOT NULL,
|
||||
"trigger_adoption_id" BIGINT NOT NULL,
|
||||
"trigger_account_sequence" VARCHAR(20) NOT NULL,
|
||||
|
||||
-- 解锁内容
|
||||
"unlock_type" VARCHAR(30) NOT NULL,
|
||||
"unlock_condition" VARCHAR(100) NOT NULL,
|
||||
|
||||
-- 解锁前后状态
|
||||
"before_direct_referral_count" INTEGER NOT NULL,
|
||||
"after_direct_referral_count" INTEGER NOT NULL,
|
||||
"before_unlocked_level_depth" INTEGER NOT NULL,
|
||||
"after_unlocked_level_depth" INTEGER NOT NULL,
|
||||
"before_unlocked_bonus_tiers" INTEGER NOT NULL,
|
||||
"after_unlocked_bonus_tiers" INTEGER NOT NULL,
|
||||
|
||||
-- 解锁金额
|
||||
"unlocked_amount" DECIMAL(30,10) NOT NULL,
|
||||
"unlocked_record_count" INTEGER NOT NULL,
|
||||
|
||||
-- 备注
|
||||
"remark" VARCHAR(500),
|
||||
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "unlock_events_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- 10. 创建解锁事件索引
|
||||
CREATE INDEX IF NOT EXISTS "unlock_events_account_sequence_idx" ON "unlock_events"("account_sequence");
|
||||
CREATE INDEX IF NOT EXISTS "unlock_events_trigger_account_sequence_idx" ON "unlock_events"("trigger_account_sequence");
|
||||
CREATE INDEX IF NOT EXISTS "unlock_events_trigger_adoption_id_idx" ON "unlock_events"("trigger_adoption_id");
|
||||
CREATE INDEX IF NOT EXISTS "unlock_events_unlock_type_idx" ON "unlock_events"("unlock_type");
|
||||
CREATE INDEX IF NOT EXISTS "unlock_events_created_at_idx" ON "unlock_events"("created_at" DESC);
|
||||
|
||||
-- 11. 更新 unallocated_contributions 表
|
||||
ALTER TABLE "unallocated_contributions" ADD COLUMN IF NOT EXISTS "bonus_tier" INTEGER;
|
||||
ALTER TABLE "unallocated_contributions" ADD COLUMN IF NOT EXISTS "status" VARCHAR(20) DEFAULT 'PENDING';
|
||||
ALTER TABLE "unallocated_contributions" ADD COLUMN IF NOT EXISTS "allocated_to_account_sequence" VARCHAR(20);
|
||||
|
||||
-- 删除旧的列(如果存在)
|
||||
ALTER TABLE "unallocated_contributions" DROP COLUMN IF EXISTS "allocated_to_headquarters";
|
||||
|
||||
-- 12. 更新索引
|
||||
DROP INDEX IF EXISTS "unallocated_contributions_allocated_to_headquarters_idx";
|
||||
CREATE INDEX IF NOT EXISTS "unallocated_contributions_status_idx" ON "unallocated_contributions"("status");
|
||||
CREATE INDEX IF NOT EXISTS "unallocated_contributions_would_be_account_sequence_idx" ON "unallocated_contributions"("would_be_account_sequence");
|
||||
|
||||
-- 13. 添加注释
|
||||
COMMENT ON TABLE "unlock_events" IS '解锁事件记录表 - 记录每次算力解锁的触发原因和结果';
|
||||
COMMENT ON COLUMN "unlock_events"."trigger_type" IS '触发类型: SELF_ADOPT(自己认种) / REFERRAL_ADOPT(直推认种)';
|
||||
COMMENT ON COLUMN "unlock_events"."unlock_type" IS '解锁类型: LEVEL_1_5/LEVEL_6_10/LEVEL_11_15/BONUS_TIER_1/BONUS_TIER_2/BONUS_TIER_3';
|
||||
COMMENT ON COLUMN "unlock_events"."unlock_condition" IS '解锁条件描述,如"自己认种"、"直推认种人数达到3人"';
|
||||
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_1_pending" IS '第1级待解锁算力 (0.5%)';
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_2_pending" IS '第2级待解锁算力 (0.5%)';
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_3_pending" IS '第3级待解锁算力 (0.5%)';
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_4_pending" IS '第4级待解锁算力 (0.5%)';
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_5_pending" IS '第5级待解锁算力 (0.5%)';
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_6_pending" IS '第6级待解锁算力 (0.5%) - 直推≥3人认种解锁';
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_7_pending" IS '第7级待解锁算力 (0.5%) - 直推≥3人认种解锁';
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_8_pending" IS '第8级待解锁算力 (0.5%) - 直推≥3人认种解锁';
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_9_pending" IS '第9级待解锁算力 (0.5%) - 直推≥3人认种解锁';
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_10_pending" IS '第10级待解锁算力 (0.5%) - 直推≥3人认种解锁';
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_11_pending" IS '第11级待解锁算力 (0.5%) - 直推≥5人认种解锁';
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_12_pending" IS '第12级待解锁算力 (0.5%) - 直推≥5人认种解锁';
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_13_pending" IS '第13级待解锁算力 (0.5%) - 直推≥5人认种解锁';
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_14_pending" IS '第14级待解锁算力 (0.5%) - 直推≥5人认种解锁';
|
||||
COMMENT ON COLUMN "contribution_accounts"."level_15_pending" IS '第15级待解锁算力 (0.5%) - 直推≥5人认种解锁';
|
||||
|
||||
COMMENT ON COLUMN "contribution_accounts"."bonus_tier_1_pending" IS '第1档加成待解锁算力 (2.5%) - 自己认种解锁';
|
||||
COMMENT ON COLUMN "contribution_accounts"."bonus_tier_2_pending" IS '第2档加成待解锁算力 (2.5%) - 直推≥2人认种解锁';
|
||||
COMMENT ON COLUMN "contribution_accounts"."bonus_tier_3_pending" IS '第3档加成待解锁算力 (2.5%) - 直推≥4人认种解锁';
|
||||
|
||||
COMMENT ON COLUMN "contribution_accounts"."unlocked_level_depth" IS '已解锁层级深度: 0=未解锁, 5=1-5级, 10=1-10级, 15=全部';
|
||||
COMMENT ON COLUMN "contribution_accounts"."unlocked_bonus_tiers" IS '已解锁加成档位数: 0/1/2/3';
|
||||
COMMENT ON COLUMN "contribution_accounts"."effective_contribution" IS '有效算力 = 个人算力 + 已解锁算力';
|
||||
|
||||
COMMENT ON COLUMN "contribution_records"."status" IS '状态: PENDING(待解锁)/UNLOCKED(已解锁)/EFFECTIVE(已生效)';
|
||||
COMMENT ON COLUMN "contribution_records"."source_type" IS '来源类型: PERSONAL/LEVEL_1~15/BONUS_TIER_1~3';
|
||||
|
||||
-- 注意:挖矿收益分配相关的表(mining_reward_allocations, daily_mining_reward_summaries, headquarters_pending_rewards)
|
||||
-- 已移至 mining-service,由 mining-service 负责创建和管理
|
||||
|
|
@ -13,11 +13,11 @@ datasource db {
|
|||
|
||||
// 同步的用户数据
|
||||
model SyncedUser {
|
||||
id BigInt @id @default(autoincrement())
|
||||
accountSequence String @unique @map("account_sequence") @db.VarChar(20)
|
||||
originalUserId BigInt @map("original_user_id")
|
||||
phone String? @db.VarChar(20)
|
||||
status String? @db.VarChar(20)
|
||||
id BigInt @id @default(autoincrement())
|
||||
accountSequence String @unique @map("account_sequence") @db.VarChar(20)
|
||||
originalUserId BigInt @map("original_user_id")
|
||||
phone String? @db.VarChar(20)
|
||||
status String? @db.VarChar(20)
|
||||
|
||||
// CDC 同步元数据
|
||||
sourceSequenceNum BigInt @map("source_sequence_num")
|
||||
|
|
@ -29,19 +29,19 @@ model SyncedUser {
|
|||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@map("synced_users")
|
||||
@@index([originalUserId])
|
||||
@@index([contributionCalculated])
|
||||
@@map("synced_users")
|
||||
}
|
||||
|
||||
// 同步的认种数据
|
||||
model SyncedAdoption {
|
||||
id BigInt @id @default(autoincrement())
|
||||
originalAdoptionId BigInt @unique @map("original_adoption_id")
|
||||
accountSequence String @map("account_sequence") @db.VarChar(20)
|
||||
treeCount Int @map("tree_count")
|
||||
adoptionDate DateTime @map("adoption_date") @db.Date
|
||||
status String? @db.VarChar(20)
|
||||
id BigInt @id @default(autoincrement())
|
||||
originalAdoptionId BigInt @unique @map("original_adoption_id")
|
||||
accountSequence String @map("account_sequence") @db.VarChar(20)
|
||||
treeCount Int @map("tree_count")
|
||||
adoptionDate DateTime @map("adoption_date") @db.Date
|
||||
status String? @db.VarChar(20)
|
||||
|
||||
// 贡献值计算参数(从认种时的配置)
|
||||
contributionPerTree Decimal @map("contribution_per_tree") @db.Decimal(20, 10)
|
||||
|
|
@ -56,17 +56,17 @@ model SyncedAdoption {
|
|||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@map("synced_adoptions")
|
||||
@@index([accountSequence])
|
||||
@@index([adoptionDate])
|
||||
@@index([contributionDistributed])
|
||||
@@map("synced_adoptions")
|
||||
}
|
||||
|
||||
// 同步的推荐关系数据
|
||||
model SyncedReferral {
|
||||
id BigInt @id @default(autoincrement())
|
||||
accountSequence String @unique @map("account_sequence") @db.VarChar(20)
|
||||
referrerAccountSequence String? @map("referrer_account_sequence") @db.VarChar(20)
|
||||
id BigInt @id @default(autoincrement())
|
||||
accountSequence String @unique @map("account_sequence") @db.VarChar(20)
|
||||
referrerAccountSequence String? @map("referrer_account_sequence") @db.VarChar(20)
|
||||
|
||||
// 预计算的层级路径(便于快速查询上下级)
|
||||
ancestorPath String? @map("ancestor_path") @db.Text
|
||||
|
|
@ -78,8 +78,8 @@ model SyncedReferral {
|
|||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@map("synced_referrals")
|
||||
@@index([referrerAccountSequence])
|
||||
@@map("synced_referrals")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
|
|
@ -87,23 +87,64 @@ model SyncedReferral {
|
|||
// ============================================
|
||||
|
||||
// 算力账户表(汇总)
|
||||
// 设计说明:
|
||||
// - 个人算力:自己认种,立即生效
|
||||
// - 层级算力:下级1-15层认种的分成,每层0.5%,共7.5%
|
||||
// - 加成算力:团队加成,3档各2.5%,共7.5%
|
||||
// 解锁规则:
|
||||
// - 自己认种:解锁层级1-5 + 加成第1档
|
||||
// - 直推≥2人认种:解锁加成第2档
|
||||
// - 直推≥3人认种:解锁层级6-10
|
||||
// - 直推≥4人认种:解锁加成第3档
|
||||
// - 直推≥5人认种:解锁层级11-15
|
||||
model ContributionAccount {
|
||||
id BigInt @id @default(autoincrement())
|
||||
accountSequence String @unique @map("account_sequence") @db.VarChar(20)
|
||||
|
||||
// 算力汇总
|
||||
personalContribution Decimal @default(0) @map("personal_contribution") @db.Decimal(30, 10)
|
||||
teamLevelContribution Decimal @default(0) @map("team_level_contribution") @db.Decimal(30, 10)
|
||||
teamBonusContribution Decimal @default(0) @map("team_bonus_contribution") @db.Decimal(30, 10)
|
||||
totalContribution Decimal @default(0) @map("total_contribution") @db.Decimal(30, 10)
|
||||
effectiveContribution Decimal @default(0) @map("effective_contribution") @db.Decimal(30, 10)
|
||||
// ========== 个人算力(立即生效)==========
|
||||
personalContribution Decimal @default(0) @map("personal_contribution") @db.Decimal(30, 10)
|
||||
|
||||
// 用户条件(决定能获得多少团队算力)
|
||||
hasAdopted Boolean @default(false) @map("has_adopted")
|
||||
directReferralAdoptedCount Int @default(0) @map("direct_referral_adopted_count")
|
||||
// ========== 15级层级算力(待解锁/已解锁)==========
|
||||
// 每级0.5%,分三档解锁
|
||||
// 第1档(1-5级):自己认种解锁
|
||||
level1Pending Decimal @default(0) @map("level_1_pending") @db.Decimal(30, 10)
|
||||
level2Pending Decimal @default(0) @map("level_2_pending") @db.Decimal(30, 10)
|
||||
level3Pending Decimal @default(0) @map("level_3_pending") @db.Decimal(30, 10)
|
||||
level4Pending Decimal @default(0) @map("level_4_pending") @db.Decimal(30, 10)
|
||||
level5Pending Decimal @default(0) @map("level_5_pending") @db.Decimal(30, 10)
|
||||
// 第2档(6-10级):直推≥3人认种解锁
|
||||
level6Pending Decimal @default(0) @map("level_6_pending") @db.Decimal(30, 10)
|
||||
level7Pending Decimal @default(0) @map("level_7_pending") @db.Decimal(30, 10)
|
||||
level8Pending Decimal @default(0) @map("level_8_pending") @db.Decimal(30, 10)
|
||||
level9Pending Decimal @default(0) @map("level_9_pending") @db.Decimal(30, 10)
|
||||
level10Pending Decimal @default(0) @map("level_10_pending") @db.Decimal(30, 10)
|
||||
// 第3档(11-15级):直推≥5人认种解锁
|
||||
level11Pending Decimal @default(0) @map("level_11_pending") @db.Decimal(30, 10)
|
||||
level12Pending Decimal @default(0) @map("level_12_pending") @db.Decimal(30, 10)
|
||||
level13Pending Decimal @default(0) @map("level_13_pending") @db.Decimal(30, 10)
|
||||
level14Pending Decimal @default(0) @map("level_14_pending") @db.Decimal(30, 10)
|
||||
level15Pending Decimal @default(0) @map("level_15_pending") @db.Decimal(30, 10)
|
||||
|
||||
// 解锁状态
|
||||
// ========== 3档加成算力(待解锁)==========
|
||||
// 每档2.5%
|
||||
bonusTier1Pending Decimal @default(0) @map("bonus_tier_1_pending") @db.Decimal(30, 10) // 自己认种解锁
|
||||
bonusTier2Pending Decimal @default(0) @map("bonus_tier_2_pending") @db.Decimal(30, 10) // 直推≥2人认种解锁
|
||||
bonusTier3Pending Decimal @default(0) @map("bonus_tier_3_pending") @db.Decimal(30, 10) // 直推≥4人认种解锁
|
||||
|
||||
// ========== 汇总字段 ==========
|
||||
totalLevelPending Decimal @default(0) @map("total_level_pending") @db.Decimal(30, 10) // sum(level1-15 pending)
|
||||
totalBonusPending Decimal @default(0) @map("total_bonus_pending") @db.Decimal(30, 10) // sum(tier1-3 pending)
|
||||
totalPending Decimal @default(0) @map("total_pending") @db.Decimal(30, 10) // 所有待解锁总和
|
||||
totalUnlocked Decimal @default(0) @map("total_unlocked") @db.Decimal(30, 10) // 所有已解锁总和
|
||||
effectiveContribution Decimal @default(0) @map("effective_contribution") @db.Decimal(30, 10) // personal + unlocked
|
||||
|
||||
// ========== 解锁条件状态 ==========
|
||||
hasAdopted Boolean @default(false) @map("has_adopted")
|
||||
directReferralAdoptedCount Int @default(0) @map("direct_referral_adopted_count")
|
||||
|
||||
// 解锁深度:0=未解锁, 5=第1档, 10=第2档, 15=全部解锁
|
||||
unlockedLevelDepth Int @default(0) @map("unlocked_level_depth")
|
||||
// 解锁加成档位:0=未解锁, 1/2/3=对应档位
|
||||
unlockedBonusTiers Int @default(0) @map("unlocked_bonus_tiers")
|
||||
|
||||
// 乐观锁
|
||||
|
|
@ -112,80 +153,139 @@ model ContributionAccount {
|
|||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@map("contribution_accounts")
|
||||
@@index([totalContribution(sort: Desc)])
|
||||
@@index([effectiveContribution(sort: Desc)])
|
||||
@@index([hasAdopted])
|
||||
@@index([directReferralAdoptedCount])
|
||||
@@map("contribution_accounts")
|
||||
}
|
||||
|
||||
// 算力明细表(分类账)
|
||||
// 每笔算力变动都记录在此表,用于审计和追溯
|
||||
// 状态流转:PENDING -> UNLOCKED -> EXPIRED (或直接 EFFECTIVE -> EXPIRED)
|
||||
model ContributionRecord {
|
||||
id BigInt @id @default(autoincrement())
|
||||
accountSequence String @map("account_sequence") @db.VarChar(20)
|
||||
|
||||
// 来源信息(可追溯)
|
||||
sourceType String @map("source_type") @db.VarChar(30) // PERSONAL / TEAM_LEVEL / TEAM_BONUS
|
||||
sourceAdoptionId BigInt @map("source_adoption_id")
|
||||
sourceAccountSequence String @map("source_account_sequence") @db.VarChar(20)
|
||||
// ========== 来源信息(可追溯)==========
|
||||
sourceType String @map("source_type") @db.VarChar(30) // PERSONAL / LEVEL_1~15 / BONUS_TIER_1~3
|
||||
sourceAdoptionId BigInt @map("source_adoption_id") // 来源认种ID
|
||||
sourceAccountSequence String @map("source_account_sequence") @db.VarChar(20) // 认种人账号
|
||||
|
||||
// 计算参数(审计用)
|
||||
treeCount Int @map("tree_count")
|
||||
baseContribution Decimal @map("base_contribution") @db.Decimal(20, 10)
|
||||
distributionRate Decimal @map("distribution_rate") @db.Decimal(10, 6)
|
||||
levelDepth Int? @map("level_depth")
|
||||
bonusTier Int? @map("bonus_tier")
|
||||
// ========== 计算参数(审计用)==========
|
||||
treeCount Int @map("tree_count") // 认种棵数
|
||||
baseContribution Decimal @map("base_contribution") @db.Decimal(20, 10) // 基础贡献值/棵
|
||||
distributionRate Decimal @map("distribution_rate") @db.Decimal(10, 6) // 分配比例 (0.005=0.5%, 0.025=2.5%)
|
||||
levelDepth Int? @map("level_depth") // 层级深度 (1-15)
|
||||
bonusTier Int? @map("bonus_tier") // 加成档位 (1-3)
|
||||
|
||||
// 结果
|
||||
amount Decimal @map("amount") @db.Decimal(30, 10)
|
||||
// ========== 金额 ==========
|
||||
amount Decimal @map("amount") @db.Decimal(30, 10) // 算力金额
|
||||
|
||||
// 有效期
|
||||
effectiveDate DateTime @map("effective_date") @db.Date
|
||||
expireDate DateTime @map("expire_date") @db.Date
|
||||
isExpired Boolean @default(false) @map("is_expired")
|
||||
// ========== 解锁状态 ==========
|
||||
status String @default("PENDING") @map("status") @db.VarChar(20) // PENDING/UNLOCKED/EFFECTIVE
|
||||
unlockedAt DateTime? @map("unlocked_at") // 解锁时间
|
||||
unlockReason String? @map("unlock_reason") @db.VarChar(200) // 解锁原因描述
|
||||
|
||||
// ========== 有效期 ==========
|
||||
effectiveDate DateTime @map("effective_date") @db.Date // 生效日期
|
||||
expireDate DateTime @map("expire_date") @db.Date // 过期日期
|
||||
isExpired Boolean @default(false) @map("is_expired")
|
||||
expiredAt DateTime? @map("expired_at")
|
||||
|
||||
// ========== 备注 ==========
|
||||
remark String? @map("remark") @db.VarChar(500) // 备注说明
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@map("contribution_records")
|
||||
@@index([accountSequence, status])
|
||||
@@index([accountSequence, createdAt(sort: Desc)])
|
||||
@@index([sourceAdoptionId])
|
||||
@@index([sourceAccountSequence])
|
||||
@@index([sourceType])
|
||||
@@index([status])
|
||||
@@index([expireDate])
|
||||
@@index([isExpired])
|
||||
@@map("contribution_records")
|
||||
}
|
||||
|
||||
// 解锁事件记录表
|
||||
// 记录每次解锁的触发原因,便于审计和追溯
|
||||
model UnlockEvent {
|
||||
id BigInt @id @default(autoincrement())
|
||||
accountSequence String @map("account_sequence") @db.VarChar(20) // 被解锁的账户
|
||||
|
||||
// ========== 触发信息 ==========
|
||||
triggerType String @map("trigger_type") @db.VarChar(30) // SELF_ADOPT / REFERRAL_ADOPT
|
||||
triggerAdoptionId BigInt @map("trigger_adoption_id") // 触发认种的ID
|
||||
triggerAccountSequence String @map("trigger_account_sequence") @db.VarChar(20) // 触发认种的账户
|
||||
|
||||
// ========== 解锁内容 ==========
|
||||
unlockType String @map("unlock_type") @db.VarChar(30) // LEVEL_1_5 / LEVEL_6_10 / LEVEL_11_15 / BONUS_TIER_1 / BONUS_TIER_2 / BONUS_TIER_3
|
||||
unlockCondition String @map("unlock_condition") @db.VarChar(100) // 解锁条件描述,如 "自己认种" / "直推认种人数达到3人"
|
||||
|
||||
// ========== 解锁前后状态 ==========
|
||||
beforeDirectReferralCount Int @map("before_direct_referral_count") // 解锁前直推认种人数
|
||||
afterDirectReferralCount Int @map("after_direct_referral_count") // 解锁后直推认种人数
|
||||
beforeUnlockedLevelDepth Int @map("before_unlocked_level_depth") // 解锁前层级深度
|
||||
afterUnlockedLevelDepth Int @map("after_unlocked_level_depth") // 解锁后层级深度
|
||||
beforeUnlockedBonusTiers Int @map("before_unlocked_bonus_tiers") // 解锁前加成档位
|
||||
afterUnlockedBonusTiers Int @map("after_unlocked_bonus_tiers") // 解锁后加成档位
|
||||
|
||||
// ========== 解锁金额 ==========
|
||||
unlockedAmount Decimal @map("unlocked_amount") @db.Decimal(30, 10) // 本次解锁的算力金额
|
||||
unlockedRecordCount Int @map("unlocked_record_count") // 本次解锁的明细记录数
|
||||
|
||||
// ========== 备注 ==========
|
||||
remark String? @map("remark") @db.VarChar(500)
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@index([accountSequence])
|
||||
@@index([triggerAccountSequence])
|
||||
@@index([triggerAdoptionId])
|
||||
@@index([unlockType])
|
||||
@@index([createdAt(sort: Desc)])
|
||||
@@map("unlock_events")
|
||||
}
|
||||
|
||||
// 未分配算力记录(归总部)
|
||||
// 当上级未解锁对应层级/加成时,算力暂存于此
|
||||
model UnallocatedContribution {
|
||||
id BigInt @id @default(autoincrement())
|
||||
sourceAdoptionId BigInt @map("source_adoption_id")
|
||||
sourceAccountSequence String @map("source_account_sequence") @db.VarChar(20)
|
||||
id BigInt @id @default(autoincrement())
|
||||
sourceAdoptionId BigInt @map("source_adoption_id") // 来源认种ID
|
||||
sourceAccountSequence String @map("source_account_sequence") @db.VarChar(20) // 认种人账号
|
||||
|
||||
unallocType String @map("unalloc_type") @db.VarChar(30) // LEVEL_OVERFLOW / BONUS_TIER_1/2/3
|
||||
wouldBeAccountSequence String? @map("would_be_account_sequence") @db.VarChar(20)
|
||||
levelDepth Int? @map("level_depth")
|
||||
// ========== 未分配类型 ==========
|
||||
unallocType String @map("unalloc_type") @db.VarChar(30) // LEVEL_6~15_PENDING / BONUS_TIER_2_PENDING / BONUS_TIER_3_PENDING
|
||||
wouldBeAccountSequence String? @map("would_be_account_sequence") @db.VarChar(20) // 应该归属的账户
|
||||
levelDepth Int? @map("level_depth") // 层级深度
|
||||
bonusTier Int? @map("bonus_tier") // 加成档位
|
||||
|
||||
amount Decimal @map("amount") @db.Decimal(30, 10)
|
||||
reason String? @db.VarChar(200)
|
||||
amount Decimal @map("amount") @db.Decimal(30, 10)
|
||||
reason String? @db.VarChar(200) // 未分配原因
|
||||
|
||||
// 归总部后的处理
|
||||
allocatedToHeadquarters Boolean @default(false) @map("allocated_to_headquarters")
|
||||
allocatedAt DateTime? @map("allocated_at")
|
||||
// ========== 分配状态 ==========
|
||||
status String @default("PENDING") @map("status") @db.VarChar(20) // PENDING / ALLOCATED_TO_USER / ALLOCATED_TO_HQ
|
||||
allocatedAt DateTime? @map("allocated_at")
|
||||
allocatedToAccountSequence String? @map("allocated_to_account_sequence") @db.VarChar(20) // 最终分配给的账户
|
||||
|
||||
// ========== 有效期 ==========
|
||||
effectiveDate DateTime @map("effective_date") @db.Date
|
||||
expireDate DateTime @map("expire_date") @db.Date
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@map("unallocated_contributions")
|
||||
@@index([sourceAdoptionId])
|
||||
@@index([wouldBeAccountSequence])
|
||||
@@index([unallocType])
|
||||
@@index([allocatedToHeadquarters])
|
||||
@@index([status])
|
||||
@@map("unallocated_contributions")
|
||||
}
|
||||
|
||||
// 系统账户(运营/省/市/总部)
|
||||
model SystemAccount {
|
||||
id BigInt @id @default(autoincrement())
|
||||
accountType String @unique @map("account_type") @db.VarChar(20) // OPERATION / PROVINCE / CITY / HEADQUARTERS
|
||||
accountType String @unique @map("account_type") @db.VarChar(20) // OPERATION / PROVINCE / CITY / HEADQUARTERS
|
||||
name String @db.VarChar(100)
|
||||
|
||||
contributionBalance Decimal @default(0) @map("contribution_balance") @db.Decimal(30, 10)
|
||||
|
|
@ -219,9 +319,9 @@ model SystemContributionRecord {
|
|||
|
||||
systemAccount SystemAccount @relation(fields: [systemAccountId], references: [id])
|
||||
|
||||
@@map("system_contribution_records")
|
||||
@@index([systemAccountId])
|
||||
@@index([sourceAdoptionId])
|
||||
@@map("system_contribution_records")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
|
|
@ -230,20 +330,20 @@ model SystemContributionRecord {
|
|||
|
||||
// 每日算力快照(用于挖矿分配计算)
|
||||
model DailyContributionSnapshot {
|
||||
id BigInt @id @default(autoincrement())
|
||||
snapshotDate DateTime @map("snapshot_date") @db.Date
|
||||
accountSequence String @map("account_sequence") @db.VarChar(20)
|
||||
id BigInt @id @default(autoincrement())
|
||||
snapshotDate DateTime @map("snapshot_date") @db.Date
|
||||
accountSequence String @map("account_sequence") @db.VarChar(20)
|
||||
|
||||
effectiveContribution Decimal @map("effective_contribution") @db.Decimal(30, 10)
|
||||
networkTotalContribution Decimal @map("network_total_contribution") @db.Decimal(30, 10)
|
||||
contributionRatio Decimal @map("contribution_ratio") @db.Decimal(30, 18)
|
||||
effectiveContribution Decimal @map("effective_contribution") @db.Decimal(30, 10)
|
||||
networkTotalContribution Decimal @map("network_total_contribution") @db.Decimal(30, 10)
|
||||
contributionRatio Decimal @map("contribution_ratio") @db.Decimal(30, 18)
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@unique([snapshotDate, accountSequence])
|
||||
@@map("daily_contribution_snapshots")
|
||||
@@index([snapshotDate])
|
||||
@@index([accountSequence])
|
||||
@@map("daily_contribution_snapshots")
|
||||
}
|
||||
|
||||
// 用户团队统计(缓存,定期更新)
|
||||
|
|
@ -269,15 +369,15 @@ model UserTeamStats {
|
|||
level14Trees Int @default(0) @map("level_14_trees")
|
||||
level15Trees Int @default(0) @map("level_15_trees")
|
||||
|
||||
totalTeamTrees Int @default(0) @map("total_team_trees")
|
||||
directAdoptedReferrals Int @default(0) @map("direct_adopted_referrals")
|
||||
totalTeamTrees Int @default(0) @map("total_team_trees")
|
||||
directAdoptedReferrals Int @default(0) @map("direct_adopted_referrals")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@unique([accountSequence, statsDate])
|
||||
@@map("user_team_stats")
|
||||
@@index([accountSequence])
|
||||
@@index([statsDate])
|
||||
@@map("user_team_stats")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
|
|
@ -286,10 +386,10 @@ model UserTeamStats {
|
|||
|
||||
// CDC 同步进度表
|
||||
model CdcSyncProgress {
|
||||
id BigInt @id @default(autoincrement())
|
||||
sourceTopic String @unique @map("source_topic") @db.VarChar(100)
|
||||
lastSequenceNum BigInt @default(0) @map("last_sequence_num")
|
||||
lastSyncedAt DateTime? @map("last_synced_at")
|
||||
id BigInt @id @default(autoincrement())
|
||||
sourceTopic String @unique @map("source_topic") @db.VarChar(100)
|
||||
lastSequenceNum BigInt @default(0) @map("last_sequence_num")
|
||||
lastSyncedAt DateTime? @map("last_synced_at")
|
||||
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
|
|
@ -298,16 +398,16 @@ model CdcSyncProgress {
|
|||
|
||||
// 已处理事件表(幂等性)
|
||||
model ProcessedEvent {
|
||||
id BigInt @id @default(autoincrement())
|
||||
eventId String @unique @map("event_id") @db.VarChar(100)
|
||||
eventType String @map("event_type") @db.VarChar(50)
|
||||
sourceService String? @map("source_service") @db.VarChar(50)
|
||||
id BigInt @id @default(autoincrement())
|
||||
eventId String @unique @map("event_id") @db.VarChar(100)
|
||||
eventType String @map("event_type") @db.VarChar(50)
|
||||
sourceService String? @map("source_service") @db.VarChar(50)
|
||||
|
||||
processedAt DateTime @default(now()) @map("processed_at")
|
||||
|
||||
@@map("processed_events")
|
||||
@@index([eventType])
|
||||
@@index([processedAt])
|
||||
@@map("processed_events")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
|
|
@ -318,17 +418,17 @@ model ProcessedEvent {
|
|||
model ContributionConfig {
|
||||
id BigInt @id @default(autoincrement())
|
||||
|
||||
baseContribution Decimal @default(22617) @map("base_contribution") @db.Decimal(20, 10)
|
||||
incrementPercentage Decimal @default(0.003) @map("increment_percentage") @db.Decimal(10, 6)
|
||||
unitSize Int @default(100) @map("unit_size")
|
||||
startTreeNumber Int @default(1000) @map("start_tree_number")
|
||||
baseContribution Decimal @default(22617) @map("base_contribution") @db.Decimal(20, 10)
|
||||
incrementPercentage Decimal @default(0.003) @map("increment_percentage") @db.Decimal(10, 6)
|
||||
unitSize Int @default(100) @map("unit_size")
|
||||
startTreeNumber Int @default(1000) @map("start_tree_number")
|
||||
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@map("contribution_configs")
|
||||
@@index([isActive])
|
||||
@@map("contribution_configs")
|
||||
}
|
||||
|
||||
// 分配比例配置
|
||||
|
|
@ -343,8 +443,8 @@ model DistributionRateConfig {
|
|||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@map("distribution_rate_configs")
|
||||
@@index([isActive])
|
||||
@@map("distribution_rate_configs")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
|
|
@ -371,9 +471,9 @@ model OutboxEvent {
|
|||
publishedAt DateTime? @map("published_at")
|
||||
nextRetryAt DateTime? @map("next_retry_at")
|
||||
|
||||
@@map("outbox_events")
|
||||
@@index([status, createdAt])
|
||||
@@index([status, nextRetryAt])
|
||||
@@index([aggregateType, aggregateId])
|
||||
@@index([topic])
|
||||
@@map("outbox_events")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,14 +7,17 @@ export class ContributionRankingItemResponse {
|
|||
@ApiProperty({ description: '账户序号' })
|
||||
accountSequence: string;
|
||||
|
||||
@ApiProperty({ description: '总算力' })
|
||||
totalContribution: string;
|
||||
@ApiProperty({ description: '有效算力(个人 + 已解锁)' })
|
||||
effectiveContribution: string;
|
||||
|
||||
@ApiProperty({ description: '个人算力' })
|
||||
personalContribution: string;
|
||||
|
||||
@ApiProperty({ description: '团队算力' })
|
||||
teamContribution: string;
|
||||
@ApiProperty({ description: '待解锁算力总和' })
|
||||
totalPending: string;
|
||||
|
||||
@ApiProperty({ description: '已解锁算力总和' })
|
||||
totalUnlocked: string;
|
||||
}
|
||||
|
||||
export class ContributionRankingResponse {
|
||||
|
|
@ -26,8 +29,8 @@ export class UserRankResponse {
|
|||
@ApiProperty({ description: '排名', nullable: true })
|
||||
rank: number | null;
|
||||
|
||||
@ApiProperty({ description: '总算力' })
|
||||
totalContribution: string;
|
||||
@ApiProperty({ description: '有效算力' })
|
||||
effectiveContribution: string;
|
||||
|
||||
@ApiProperty({ description: '百分位', nullable: true })
|
||||
percentile: number | null;
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@ import { RedisService } from '../../infrastructure/redis/redis.service';
|
|||
export interface ContributionRankingDto {
|
||||
rank: number;
|
||||
accountSequence: string;
|
||||
totalContribution: string;
|
||||
effectiveContribution: string;
|
||||
personalContribution: string;
|
||||
teamContribution: string;
|
||||
totalPending: string;
|
||||
totalUnlocked: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
|
|
@ -36,9 +37,10 @@ export class GetContributionRankingQuery {
|
|||
const ranking: ContributionRankingDto[] = topContributors.map((account, index) => ({
|
||||
rank: index + 1,
|
||||
accountSequence: account.accountSequence,
|
||||
totalContribution: account.totalContribution.value.toString(),
|
||||
effectiveContribution: account.effectiveContribution.value.toString(),
|
||||
personalContribution: account.personalContribution.value.toString(),
|
||||
teamContribution: account.teamLevelContribution.add(account.teamBonusContribution).value.toString(),
|
||||
totalPending: account.totalPending.value.toString(),
|
||||
totalUnlocked: account.totalUnlocked.value.toString(),
|
||||
}));
|
||||
|
||||
// 缓存结果
|
||||
|
|
@ -52,7 +54,7 @@ export class GetContributionRankingQuery {
|
|||
*/
|
||||
async getUserRank(accountSequence: string): Promise<{
|
||||
rank: number | null;
|
||||
totalContribution: string;
|
||||
effectiveContribution: string;
|
||||
percentile: number | null;
|
||||
} | null> {
|
||||
const account = await this.accountRepository.findByAccountSequence(accountSequence);
|
||||
|
|
@ -67,7 +69,7 @@ export class GetContributionRankingQuery {
|
|||
|
||||
return {
|
||||
rank: rank !== null ? rank + 1 : null,
|
||||
totalContribution: account.totalContribution.value.toString(),
|
||||
effectiveContribution: account.effectiveContribution.value.toString(),
|
||||
percentile: rank !== null && totalAccounts > 0 ? ((totalAccounts - rank) / totalAccounts) * 100 : null,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,26 +6,68 @@ import { ContributionAmount } from '../value-objects/contribution-amount.vo';
|
|||
*/
|
||||
export enum ContributionSourceType {
|
||||
PERSONAL = 'PERSONAL', // 来自自己认种
|
||||
TEAM_LEVEL = 'TEAM_LEVEL', // 来自团队层级
|
||||
TEAM_BONUS = 'TEAM_BONUS', // 来自团队额外奖励
|
||||
TEAM_LEVEL = 'TEAM_LEVEL', // 来自团队层级 (1-15级)
|
||||
TEAM_BONUS = 'TEAM_BONUS', // 来自团队加成奖励 (3档)
|
||||
}
|
||||
|
||||
/**
|
||||
* 算力账户聚合根
|
||||
* 管理用户的贡献值/算力
|
||||
*
|
||||
* 设计说明:
|
||||
* - 个人算力:自己认种,立即生效
|
||||
* - 层级算力:下级1-15层认种的分成,每层0.5%,共7.5%
|
||||
* - 加成算力:团队加成,3档各2.5%,共7.5%
|
||||
*
|
||||
* 解锁规则:
|
||||
* - 自己认种:解锁层级1-5 + 加成第1档
|
||||
* - 直推≥2人认种:解锁加成第2档
|
||||
* - 直推≥3人认种:解锁层级6-10
|
||||
* - 直推≥4人认种:解锁加成第3档
|
||||
* - 直推≥5人认种:解锁层级11-15
|
||||
*/
|
||||
export class ContributionAccountAggregate {
|
||||
private _id: bigint | null;
|
||||
private _accountSequence: string;
|
||||
|
||||
// ========== 个人算力(立即生效)==========
|
||||
private _personalContribution: ContributionAmount;
|
||||
private _teamLevelContribution: ContributionAmount;
|
||||
private _teamBonusContribution: ContributionAmount;
|
||||
private _totalContribution: ContributionAmount;
|
||||
|
||||
// ========== 15级层级待解锁算力 ==========
|
||||
private _level1Pending: ContributionAmount;
|
||||
private _level2Pending: ContributionAmount;
|
||||
private _level3Pending: ContributionAmount;
|
||||
private _level4Pending: ContributionAmount;
|
||||
private _level5Pending: ContributionAmount;
|
||||
private _level6Pending: ContributionAmount;
|
||||
private _level7Pending: ContributionAmount;
|
||||
private _level8Pending: ContributionAmount;
|
||||
private _level9Pending: ContributionAmount;
|
||||
private _level10Pending: ContributionAmount;
|
||||
private _level11Pending: ContributionAmount;
|
||||
private _level12Pending: ContributionAmount;
|
||||
private _level13Pending: ContributionAmount;
|
||||
private _level14Pending: ContributionAmount;
|
||||
private _level15Pending: ContributionAmount;
|
||||
|
||||
// ========== 3档加成待解锁算力 ==========
|
||||
private _bonusTier1Pending: ContributionAmount;
|
||||
private _bonusTier2Pending: ContributionAmount;
|
||||
private _bonusTier3Pending: ContributionAmount;
|
||||
|
||||
// ========== 汇总字段 ==========
|
||||
private _totalLevelPending: ContributionAmount;
|
||||
private _totalBonusPending: ContributionAmount;
|
||||
private _totalPending: ContributionAmount;
|
||||
private _totalUnlocked: ContributionAmount;
|
||||
private _effectiveContribution: ContributionAmount;
|
||||
|
||||
// ========== 解锁条件状态 ==========
|
||||
private _hasAdopted: boolean;
|
||||
private _directReferralAdoptedCount: number;
|
||||
private _unlockedLevelDepth: number;
|
||||
private _unlockedBonusTiers: number;
|
||||
private _unlockedLevelDepth: number; // 0, 5, 10, 15
|
||||
private _unlockedBonusTiers: number; // 0, 1, 2, 3
|
||||
|
||||
private _version: number;
|
||||
private _createdAt: Date;
|
||||
private _updatedAt: Date;
|
||||
|
|
@ -34,9 +76,28 @@ export class ContributionAccountAggregate {
|
|||
id?: bigint | null;
|
||||
accountSequence: string;
|
||||
personalContribution?: ContributionAmount;
|
||||
teamLevelContribution?: ContributionAmount;
|
||||
teamBonusContribution?: ContributionAmount;
|
||||
totalContribution?: ContributionAmount;
|
||||
level1Pending?: ContributionAmount;
|
||||
level2Pending?: ContributionAmount;
|
||||
level3Pending?: ContributionAmount;
|
||||
level4Pending?: ContributionAmount;
|
||||
level5Pending?: ContributionAmount;
|
||||
level6Pending?: ContributionAmount;
|
||||
level7Pending?: ContributionAmount;
|
||||
level8Pending?: ContributionAmount;
|
||||
level9Pending?: ContributionAmount;
|
||||
level10Pending?: ContributionAmount;
|
||||
level11Pending?: ContributionAmount;
|
||||
level12Pending?: ContributionAmount;
|
||||
level13Pending?: ContributionAmount;
|
||||
level14Pending?: ContributionAmount;
|
||||
level15Pending?: ContributionAmount;
|
||||
bonusTier1Pending?: ContributionAmount;
|
||||
bonusTier2Pending?: ContributionAmount;
|
||||
bonusTier3Pending?: ContributionAmount;
|
||||
totalLevelPending?: ContributionAmount;
|
||||
totalBonusPending?: ContributionAmount;
|
||||
totalPending?: ContributionAmount;
|
||||
totalUnlocked?: ContributionAmount;
|
||||
effectiveContribution?: ContributionAmount;
|
||||
hasAdopted?: boolean;
|
||||
directReferralAdoptedCount?: number;
|
||||
|
|
@ -48,11 +109,38 @@ export class ContributionAccountAggregate {
|
|||
}) {
|
||||
this._id = props.id ?? null;
|
||||
this._accountSequence = props.accountSequence;
|
||||
|
||||
this._personalContribution = props.personalContribution ?? ContributionAmount.zero();
|
||||
this._teamLevelContribution = props.teamLevelContribution ?? ContributionAmount.zero();
|
||||
this._teamBonusContribution = props.teamBonusContribution ?? ContributionAmount.zero();
|
||||
this._totalContribution = props.totalContribution ?? ContributionAmount.zero();
|
||||
|
||||
// 15级待解锁
|
||||
this._level1Pending = props.level1Pending ?? ContributionAmount.zero();
|
||||
this._level2Pending = props.level2Pending ?? ContributionAmount.zero();
|
||||
this._level3Pending = props.level3Pending ?? ContributionAmount.zero();
|
||||
this._level4Pending = props.level4Pending ?? ContributionAmount.zero();
|
||||
this._level5Pending = props.level5Pending ?? ContributionAmount.zero();
|
||||
this._level6Pending = props.level6Pending ?? ContributionAmount.zero();
|
||||
this._level7Pending = props.level7Pending ?? ContributionAmount.zero();
|
||||
this._level8Pending = props.level8Pending ?? ContributionAmount.zero();
|
||||
this._level9Pending = props.level9Pending ?? ContributionAmount.zero();
|
||||
this._level10Pending = props.level10Pending ?? ContributionAmount.zero();
|
||||
this._level11Pending = props.level11Pending ?? ContributionAmount.zero();
|
||||
this._level12Pending = props.level12Pending ?? ContributionAmount.zero();
|
||||
this._level13Pending = props.level13Pending ?? ContributionAmount.zero();
|
||||
this._level14Pending = props.level14Pending ?? ContributionAmount.zero();
|
||||
this._level15Pending = props.level15Pending ?? ContributionAmount.zero();
|
||||
|
||||
// 3档加成待解锁
|
||||
this._bonusTier1Pending = props.bonusTier1Pending ?? ContributionAmount.zero();
|
||||
this._bonusTier2Pending = props.bonusTier2Pending ?? ContributionAmount.zero();
|
||||
this._bonusTier3Pending = props.bonusTier3Pending ?? ContributionAmount.zero();
|
||||
|
||||
// 汇总字段
|
||||
this._totalLevelPending = props.totalLevelPending ?? ContributionAmount.zero();
|
||||
this._totalBonusPending = props.totalBonusPending ?? ContributionAmount.zero();
|
||||
this._totalPending = props.totalPending ?? ContributionAmount.zero();
|
||||
this._totalUnlocked = props.totalUnlocked ?? ContributionAmount.zero();
|
||||
this._effectiveContribution = props.effectiveContribution ?? ContributionAmount.zero();
|
||||
|
||||
this._hasAdopted = props.hasAdopted ?? false;
|
||||
this._directReferralAdoptedCount = props.directReferralAdoptedCount ?? 0;
|
||||
this._unlockedLevelDepth = props.unlockedLevelDepth ?? 0;
|
||||
|
|
@ -66,10 +154,36 @@ export class ContributionAccountAggregate {
|
|||
get id(): bigint | null { return this._id; }
|
||||
get accountSequence(): string { return this._accountSequence; }
|
||||
get personalContribution(): ContributionAmount { return this._personalContribution; }
|
||||
get teamLevelContribution(): ContributionAmount { return this._teamLevelContribution; }
|
||||
get teamBonusContribution(): ContributionAmount { return this._teamBonusContribution; }
|
||||
get totalContribution(): ContributionAmount { return this._totalContribution; }
|
||||
|
||||
// 15级待解锁
|
||||
get level1Pending(): ContributionAmount { return this._level1Pending; }
|
||||
get level2Pending(): ContributionAmount { return this._level2Pending; }
|
||||
get level3Pending(): ContributionAmount { return this._level3Pending; }
|
||||
get level4Pending(): ContributionAmount { return this._level4Pending; }
|
||||
get level5Pending(): ContributionAmount { return this._level5Pending; }
|
||||
get level6Pending(): ContributionAmount { return this._level6Pending; }
|
||||
get level7Pending(): ContributionAmount { return this._level7Pending; }
|
||||
get level8Pending(): ContributionAmount { return this._level8Pending; }
|
||||
get level9Pending(): ContributionAmount { return this._level9Pending; }
|
||||
get level10Pending(): ContributionAmount { return this._level10Pending; }
|
||||
get level11Pending(): ContributionAmount { return this._level11Pending; }
|
||||
get level12Pending(): ContributionAmount { return this._level12Pending; }
|
||||
get level13Pending(): ContributionAmount { return this._level13Pending; }
|
||||
get level14Pending(): ContributionAmount { return this._level14Pending; }
|
||||
get level15Pending(): ContributionAmount { return this._level15Pending; }
|
||||
|
||||
// 3档加成待解锁
|
||||
get bonusTier1Pending(): ContributionAmount { return this._bonusTier1Pending; }
|
||||
get bonusTier2Pending(): ContributionAmount { return this._bonusTier2Pending; }
|
||||
get bonusTier3Pending(): ContributionAmount { return this._bonusTier3Pending; }
|
||||
|
||||
// 汇总
|
||||
get totalLevelPending(): ContributionAmount { return this._totalLevelPending; }
|
||||
get totalBonusPending(): ContributionAmount { return this._totalBonusPending; }
|
||||
get totalPending(): ContributionAmount { return this._totalPending; }
|
||||
get totalUnlocked(): ContributionAmount { return this._totalUnlocked; }
|
||||
get effectiveContribution(): ContributionAmount { return this._effectiveContribution; }
|
||||
|
||||
get hasAdopted(): boolean { return this._hasAdopted; }
|
||||
get directReferralAdoptedCount(): number { return this._directReferralAdoptedCount; }
|
||||
get unlockedLevelDepth(): number { return this._unlockedLevelDepth; }
|
||||
|
|
@ -79,35 +193,98 @@ export class ContributionAccountAggregate {
|
|||
get updatedAt(): Date { return this._updatedAt; }
|
||||
|
||||
/**
|
||||
* 添加个人贡献值
|
||||
* 添加个人贡献值(立即生效)
|
||||
*/
|
||||
addPersonalContribution(amount: ContributionAmount): void {
|
||||
this._personalContribution = this._personalContribution.add(amount);
|
||||
this.recalculateTotal();
|
||||
this._effectiveContribution = this._effectiveContribution.add(amount);
|
||||
this._updatedAt = new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加团队层级贡献值
|
||||
* 添加层级待解锁贡献值
|
||||
* @param levelDepth 层级深度 1-15
|
||||
* @param amount 贡献值金额
|
||||
*/
|
||||
addTeamLevelContribution(amount: ContributionAmount): void {
|
||||
this._teamLevelContribution = this._teamLevelContribution.add(amount);
|
||||
this.recalculateTotal();
|
||||
addLevelPendingContribution(levelDepth: number, amount: ContributionAmount): void {
|
||||
if (levelDepth < 1 || levelDepth > 15) {
|
||||
throw new Error(`Invalid level depth: ${levelDepth}, must be 1-15`);
|
||||
}
|
||||
|
||||
const levelPendingMap: Record<number, ContributionAmount> = {
|
||||
1: this._level1Pending,
|
||||
2: this._level2Pending,
|
||||
3: this._level3Pending,
|
||||
4: this._level4Pending,
|
||||
5: this._level5Pending,
|
||||
6: this._level6Pending,
|
||||
7: this._level7Pending,
|
||||
8: this._level8Pending,
|
||||
9: this._level9Pending,
|
||||
10: this._level10Pending,
|
||||
11: this._level11Pending,
|
||||
12: this._level12Pending,
|
||||
13: this._level13Pending,
|
||||
14: this._level14Pending,
|
||||
15: this._level15Pending,
|
||||
};
|
||||
|
||||
// 更新对应层级的待解锁金额
|
||||
switch (levelDepth) {
|
||||
case 1: this._level1Pending = this._level1Pending.add(amount); break;
|
||||
case 2: this._level2Pending = this._level2Pending.add(amount); break;
|
||||
case 3: this._level3Pending = this._level3Pending.add(amount); break;
|
||||
case 4: this._level4Pending = this._level4Pending.add(amount); break;
|
||||
case 5: this._level5Pending = this._level5Pending.add(amount); break;
|
||||
case 6: this._level6Pending = this._level6Pending.add(amount); break;
|
||||
case 7: this._level7Pending = this._level7Pending.add(amount); break;
|
||||
case 8: this._level8Pending = this._level8Pending.add(amount); break;
|
||||
case 9: this._level9Pending = this._level9Pending.add(amount); break;
|
||||
case 10: this._level10Pending = this._level10Pending.add(amount); break;
|
||||
case 11: this._level11Pending = this._level11Pending.add(amount); break;
|
||||
case 12: this._level12Pending = this._level12Pending.add(amount); break;
|
||||
case 13: this._level13Pending = this._level13Pending.add(amount); break;
|
||||
case 14: this._level14Pending = this._level14Pending.add(amount); break;
|
||||
case 15: this._level15Pending = this._level15Pending.add(amount); break;
|
||||
}
|
||||
|
||||
this._totalLevelPending = this._totalLevelPending.add(amount);
|
||||
this._totalPending = this._totalPending.add(amount);
|
||||
this._updatedAt = new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加团队额外奖励贡献值
|
||||
* 添加加成待解锁贡献值
|
||||
* @param tier 加成档位 1-3
|
||||
* @param amount 贡献值金额
|
||||
*/
|
||||
addTeamBonusContribution(amount: ContributionAmount): void {
|
||||
this._teamBonusContribution = this._teamBonusContribution.add(amount);
|
||||
this.recalculateTotal();
|
||||
addBonusTierPendingContribution(tier: number, amount: ContributionAmount): void {
|
||||
if (tier < 1 || tier > 3) {
|
||||
throw new Error(`Invalid bonus tier: ${tier}, must be 1-3`);
|
||||
}
|
||||
|
||||
switch (tier) {
|
||||
case 1: this._bonusTier1Pending = this._bonusTier1Pending.add(amount); break;
|
||||
case 2: this._bonusTier2Pending = this._bonusTier2Pending.add(amount); break;
|
||||
case 3: this._bonusTier3Pending = this._bonusTier3Pending.add(amount); break;
|
||||
}
|
||||
|
||||
this._totalBonusPending = this._totalBonusPending.add(amount);
|
||||
this._totalPending = this._totalPending.add(amount);
|
||||
this._updatedAt = new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记用户已认种
|
||||
* 解锁层级1-5和加成第1档
|
||||
*/
|
||||
markAsAdopted(): void {
|
||||
if (this._hasAdopted) return; // 已经认种过
|
||||
|
||||
this._hasAdopted = true;
|
||||
this.updateUnlockStatus();
|
||||
this._unlockedLevelDepth = 5; // 解锁1-5级
|
||||
this._unlockedBonusTiers = 1; // 解锁第1档
|
||||
this._updatedAt = new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -126,51 +303,129 @@ export class ContributionAccountAggregate {
|
|||
this.updateUnlockStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新有效贡献值(扣除过期的)
|
||||
*/
|
||||
updateEffectiveContribution(effective: ContributionAmount): void {
|
||||
this._effectiveContribution = effective;
|
||||
this._updatedAt = new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新计算总贡献值
|
||||
*/
|
||||
private recalculateTotal(): void {
|
||||
this._totalContribution = this._personalContribution
|
||||
.add(this._teamLevelContribution)
|
||||
.add(this._teamBonusContribution);
|
||||
this._effectiveContribution = this._totalContribution; // 初始时有效=总量
|
||||
this._updatedAt = new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新解锁状态
|
||||
* 根据直推认种用户数和是否认种过
|
||||
* 根据直推认种用户数
|
||||
*/
|
||||
private updateUnlockStatus(): void {
|
||||
// 层级解锁规则
|
||||
if (this._directReferralAdoptedCount >= 5) {
|
||||
this._unlockedLevelDepth = 15;
|
||||
} else if (this._directReferralAdoptedCount >= 3) {
|
||||
this._unlockedLevelDepth = 10;
|
||||
} else if (this._directReferralAdoptedCount >= 1) {
|
||||
this._unlockedLevelDepth = 5;
|
||||
} else {
|
||||
this._unlockedLevelDepth = 0;
|
||||
// 层级解锁规则(只有在已认种的情况下才会解锁)
|
||||
if (this._hasAdopted) {
|
||||
if (this._directReferralAdoptedCount >= 5) {
|
||||
this._unlockedLevelDepth = 15; // 解锁11-15级
|
||||
} else if (this._directReferralAdoptedCount >= 3) {
|
||||
this._unlockedLevelDepth = 10; // 解锁6-10级
|
||||
} else {
|
||||
this._unlockedLevelDepth = 5; // 保持1-5级
|
||||
}
|
||||
}
|
||||
|
||||
// 额外奖励解锁规则
|
||||
// 加成解锁规则
|
||||
let bonusTiers = 0;
|
||||
if (this._hasAdopted) bonusTiers++;
|
||||
if (this._directReferralAdoptedCount >= 2) bonusTiers++;
|
||||
if (this._directReferralAdoptedCount >= 4) bonusTiers++;
|
||||
if (this._hasAdopted) bonusTiers = 1; // 自己认种解锁第1档
|
||||
if (this._directReferralAdoptedCount >= 2) bonusTiers = 2; // 直推≥2解锁第2档
|
||||
if (this._directReferralAdoptedCount >= 4) bonusTiers = 3; // 直推≥4解锁第3档
|
||||
this._unlockedBonusTiers = bonusTiers;
|
||||
|
||||
this._updatedAt = new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解锁待解锁的贡献值
|
||||
* 根据当前解锁深度,将对应的 pending 转移到 unlocked
|
||||
* @returns 本次解锁的金额
|
||||
*/
|
||||
unlockPendingContributions(): ContributionAmount {
|
||||
let unlockAmount = ContributionAmount.zero();
|
||||
|
||||
// 解锁层级算力
|
||||
if (this._unlockedLevelDepth >= 5) {
|
||||
unlockAmount = unlockAmount.add(this._level1Pending);
|
||||
unlockAmount = unlockAmount.add(this._level2Pending);
|
||||
unlockAmount = unlockAmount.add(this._level3Pending);
|
||||
unlockAmount = unlockAmount.add(this._level4Pending);
|
||||
unlockAmount = unlockAmount.add(this._level5Pending);
|
||||
this._level1Pending = ContributionAmount.zero();
|
||||
this._level2Pending = ContributionAmount.zero();
|
||||
this._level3Pending = ContributionAmount.zero();
|
||||
this._level4Pending = ContributionAmount.zero();
|
||||
this._level5Pending = ContributionAmount.zero();
|
||||
}
|
||||
if (this._unlockedLevelDepth >= 10) {
|
||||
unlockAmount = unlockAmount.add(this._level6Pending);
|
||||
unlockAmount = unlockAmount.add(this._level7Pending);
|
||||
unlockAmount = unlockAmount.add(this._level8Pending);
|
||||
unlockAmount = unlockAmount.add(this._level9Pending);
|
||||
unlockAmount = unlockAmount.add(this._level10Pending);
|
||||
this._level6Pending = ContributionAmount.zero();
|
||||
this._level7Pending = ContributionAmount.zero();
|
||||
this._level8Pending = ContributionAmount.zero();
|
||||
this._level9Pending = ContributionAmount.zero();
|
||||
this._level10Pending = ContributionAmount.zero();
|
||||
}
|
||||
if (this._unlockedLevelDepth >= 15) {
|
||||
unlockAmount = unlockAmount.add(this._level11Pending);
|
||||
unlockAmount = unlockAmount.add(this._level12Pending);
|
||||
unlockAmount = unlockAmount.add(this._level13Pending);
|
||||
unlockAmount = unlockAmount.add(this._level14Pending);
|
||||
unlockAmount = unlockAmount.add(this._level15Pending);
|
||||
this._level11Pending = ContributionAmount.zero();
|
||||
this._level12Pending = ContributionAmount.zero();
|
||||
this._level13Pending = ContributionAmount.zero();
|
||||
this._level14Pending = ContributionAmount.zero();
|
||||
this._level15Pending = ContributionAmount.zero();
|
||||
}
|
||||
|
||||
// 解锁加成算力
|
||||
if (this._unlockedBonusTiers >= 1) {
|
||||
unlockAmount = unlockAmount.add(this._bonusTier1Pending);
|
||||
this._bonusTier1Pending = ContributionAmount.zero();
|
||||
}
|
||||
if (this._unlockedBonusTiers >= 2) {
|
||||
unlockAmount = unlockAmount.add(this._bonusTier2Pending);
|
||||
this._bonusTier2Pending = ContributionAmount.zero();
|
||||
}
|
||||
if (this._unlockedBonusTiers >= 3) {
|
||||
unlockAmount = unlockAmount.add(this._bonusTier3Pending);
|
||||
this._bonusTier3Pending = ContributionAmount.zero();
|
||||
}
|
||||
|
||||
// 更新汇总
|
||||
this._totalUnlocked = this._totalUnlocked.add(unlockAmount);
|
||||
this._totalPending = this._totalPending.subtract(unlockAmount);
|
||||
this._effectiveContribution = this._effectiveContribution.add(unlockAmount);
|
||||
this.recalculatePendingTotals();
|
||||
|
||||
this._updatedAt = new Date();
|
||||
return unlockAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新计算待解锁汇总
|
||||
*/
|
||||
private recalculatePendingTotals(): void {
|
||||
this._totalLevelPending = this._level1Pending
|
||||
.add(this._level2Pending)
|
||||
.add(this._level3Pending)
|
||||
.add(this._level4Pending)
|
||||
.add(this._level5Pending)
|
||||
.add(this._level6Pending)
|
||||
.add(this._level7Pending)
|
||||
.add(this._level8Pending)
|
||||
.add(this._level9Pending)
|
||||
.add(this._level10Pending)
|
||||
.add(this._level11Pending)
|
||||
.add(this._level12Pending)
|
||||
.add(this._level13Pending)
|
||||
.add(this._level14Pending)
|
||||
.add(this._level15Pending);
|
||||
|
||||
this._totalBonusPending = this._bonusTier1Pending
|
||||
.add(this._bonusTier2Pending)
|
||||
.add(this._bonusTier3Pending);
|
||||
|
||||
this._totalPending = this._totalLevelPending.add(this._totalBonusPending);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加版本号(乐观锁)
|
||||
*/
|
||||
|
|
@ -193,9 +448,28 @@ export class ContributionAccountAggregate {
|
|||
id: bigint;
|
||||
accountSequence: string;
|
||||
personalContribution: Decimal;
|
||||
teamLevelContribution: Decimal;
|
||||
teamBonusContribution: Decimal;
|
||||
totalContribution: Decimal;
|
||||
level1Pending: Decimal;
|
||||
level2Pending: Decimal;
|
||||
level3Pending: Decimal;
|
||||
level4Pending: Decimal;
|
||||
level5Pending: Decimal;
|
||||
level6Pending: Decimal;
|
||||
level7Pending: Decimal;
|
||||
level8Pending: Decimal;
|
||||
level9Pending: Decimal;
|
||||
level10Pending: Decimal;
|
||||
level11Pending: Decimal;
|
||||
level12Pending: Decimal;
|
||||
level13Pending: Decimal;
|
||||
level14Pending: Decimal;
|
||||
level15Pending: Decimal;
|
||||
bonusTier1Pending: Decimal;
|
||||
bonusTier2Pending: Decimal;
|
||||
bonusTier3Pending: Decimal;
|
||||
totalLevelPending: Decimal;
|
||||
totalBonusPending: Decimal;
|
||||
totalPending: Decimal;
|
||||
totalUnlocked: Decimal;
|
||||
effectiveContribution: Decimal;
|
||||
hasAdopted: boolean;
|
||||
directReferralAdoptedCount: number;
|
||||
|
|
@ -209,9 +483,28 @@ export class ContributionAccountAggregate {
|
|||
id: data.id,
|
||||
accountSequence: data.accountSequence,
|
||||
personalContribution: new ContributionAmount(data.personalContribution),
|
||||
teamLevelContribution: new ContributionAmount(data.teamLevelContribution),
|
||||
teamBonusContribution: new ContributionAmount(data.teamBonusContribution),
|
||||
totalContribution: new ContributionAmount(data.totalContribution),
|
||||
level1Pending: new ContributionAmount(data.level1Pending),
|
||||
level2Pending: new ContributionAmount(data.level2Pending),
|
||||
level3Pending: new ContributionAmount(data.level3Pending),
|
||||
level4Pending: new ContributionAmount(data.level4Pending),
|
||||
level5Pending: new ContributionAmount(data.level5Pending),
|
||||
level6Pending: new ContributionAmount(data.level6Pending),
|
||||
level7Pending: new ContributionAmount(data.level7Pending),
|
||||
level8Pending: new ContributionAmount(data.level8Pending),
|
||||
level9Pending: new ContributionAmount(data.level9Pending),
|
||||
level10Pending: new ContributionAmount(data.level10Pending),
|
||||
level11Pending: new ContributionAmount(data.level11Pending),
|
||||
level12Pending: new ContributionAmount(data.level12Pending),
|
||||
level13Pending: new ContributionAmount(data.level13Pending),
|
||||
level14Pending: new ContributionAmount(data.level14Pending),
|
||||
level15Pending: new ContributionAmount(data.level15Pending),
|
||||
bonusTier1Pending: new ContributionAmount(data.bonusTier1Pending),
|
||||
bonusTier2Pending: new ContributionAmount(data.bonusTier2Pending),
|
||||
bonusTier3Pending: new ContributionAmount(data.bonusTier3Pending),
|
||||
totalLevelPending: new ContributionAmount(data.totalLevelPending),
|
||||
totalBonusPending: new ContributionAmount(data.totalBonusPending),
|
||||
totalPending: new ContributionAmount(data.totalPending),
|
||||
totalUnlocked: new ContributionAmount(data.totalUnlocked),
|
||||
effectiveContribution: new ContributionAmount(data.effectiveContribution),
|
||||
hasAdopted: data.hasAdopted,
|
||||
directReferralAdoptedCount: data.directReferralAdoptedCount,
|
||||
|
|
@ -229,9 +522,28 @@ export class ContributionAccountAggregate {
|
|||
toPersistence(): {
|
||||
accountSequence: string;
|
||||
personalContribution: Decimal;
|
||||
teamLevelContribution: Decimal;
|
||||
teamBonusContribution: Decimal;
|
||||
totalContribution: Decimal;
|
||||
level1Pending: Decimal;
|
||||
level2Pending: Decimal;
|
||||
level3Pending: Decimal;
|
||||
level4Pending: Decimal;
|
||||
level5Pending: Decimal;
|
||||
level6Pending: Decimal;
|
||||
level7Pending: Decimal;
|
||||
level8Pending: Decimal;
|
||||
level9Pending: Decimal;
|
||||
level10Pending: Decimal;
|
||||
level11Pending: Decimal;
|
||||
level12Pending: Decimal;
|
||||
level13Pending: Decimal;
|
||||
level14Pending: Decimal;
|
||||
level15Pending: Decimal;
|
||||
bonusTier1Pending: Decimal;
|
||||
bonusTier2Pending: Decimal;
|
||||
bonusTier3Pending: Decimal;
|
||||
totalLevelPending: Decimal;
|
||||
totalBonusPending: Decimal;
|
||||
totalPending: Decimal;
|
||||
totalUnlocked: Decimal;
|
||||
effectiveContribution: Decimal;
|
||||
hasAdopted: boolean;
|
||||
directReferralAdoptedCount: number;
|
||||
|
|
@ -242,9 +554,28 @@ export class ContributionAccountAggregate {
|
|||
return {
|
||||
accountSequence: this._accountSequence,
|
||||
personalContribution: this._personalContribution.value,
|
||||
teamLevelContribution: this._teamLevelContribution.value,
|
||||
teamBonusContribution: this._teamBonusContribution.value,
|
||||
totalContribution: this._totalContribution.value,
|
||||
level1Pending: this._level1Pending.value,
|
||||
level2Pending: this._level2Pending.value,
|
||||
level3Pending: this._level3Pending.value,
|
||||
level4Pending: this._level4Pending.value,
|
||||
level5Pending: this._level5Pending.value,
|
||||
level6Pending: this._level6Pending.value,
|
||||
level7Pending: this._level7Pending.value,
|
||||
level8Pending: this._level8Pending.value,
|
||||
level9Pending: this._level9Pending.value,
|
||||
level10Pending: this._level10Pending.value,
|
||||
level11Pending: this._level11Pending.value,
|
||||
level12Pending: this._level12Pending.value,
|
||||
level13Pending: this._level13Pending.value,
|
||||
level14Pending: this._level14Pending.value,
|
||||
level15Pending: this._level15Pending.value,
|
||||
bonusTier1Pending: this._bonusTier1Pending.value,
|
||||
bonusTier2Pending: this._bonusTier2Pending.value,
|
||||
bonusTier3Pending: this._bonusTier3Pending.value,
|
||||
totalLevelPending: this._totalLevelPending.value,
|
||||
totalBonusPending: this._totalBonusPending.value,
|
||||
totalPending: this._totalPending.value,
|
||||
totalUnlocked: this._totalUnlocked.value,
|
||||
effectiveContribution: this._effectiveContribution.value,
|
||||
hasAdopted: this._hasAdopted,
|
||||
directReferralAdoptedCount: this._directReferralAdoptedCount,
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export interface IContributionAccountRepository {
|
|||
findMany(options: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
orderBy?: 'totalContribution' | 'effectiveContribution';
|
||||
orderBy?: 'effectiveContribution' | 'personalContribution';
|
||||
order?: 'asc' | 'desc';
|
||||
}): Promise<{
|
||||
items: ContributionAccountAggregate[];
|
||||
|
|
|
|||
|
|
@ -80,22 +80,27 @@ export class ContributionAccountRepository implements IContributionAccountReposi
|
|||
sourceType: ContributionSourceType,
|
||||
amount: ContributionAmount,
|
||||
): Promise<void> {
|
||||
const fieldMap: Record<ContributionSourceType, string> = {
|
||||
[ContributionSourceType.PERSONAL]: 'personalContribution',
|
||||
[ContributionSourceType.TEAM_LEVEL]: 'teamLevelContribution',
|
||||
[ContributionSourceType.TEAM_BONUS]: 'teamBonusContribution',
|
||||
};
|
||||
|
||||
const field = fieldMap[sourceType];
|
||||
|
||||
await this.client.contributionAccount.update({
|
||||
where: { accountSequence },
|
||||
data: {
|
||||
[field]: { increment: amount.value },
|
||||
totalContribution: { increment: amount.value },
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
});
|
||||
// 个人算力直接增加到 personalContribution 和 effectiveContribution
|
||||
// 层级/加成算力需要根据解锁状态分配到对应的 pending 字段
|
||||
if (sourceType === ContributionSourceType.PERSONAL) {
|
||||
await this.client.contributionAccount.update({
|
||||
where: { accountSequence },
|
||||
data: {
|
||||
personalContribution: { increment: amount.value },
|
||||
effectiveContribution: { increment: amount.value },
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// 层级和加成算力暂时累加到 effectiveContribution(待后续细化分配逻辑)
|
||||
await this.client.contributionAccount.update({
|
||||
where: { accountSequence },
|
||||
data: {
|
||||
effectiveContribution: { increment: amount.value },
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async findAllWithPagination(page: number, pageSize: number): Promise<{
|
||||
|
|
@ -106,7 +111,7 @@ export class ContributionAccountRepository implements IContributionAccountReposi
|
|||
this.client.contributionAccount.findMany({
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
orderBy: { totalContribution: 'desc' },
|
||||
orderBy: { effectiveContribution: 'desc' },
|
||||
}),
|
||||
this.client.contributionAccount.count(),
|
||||
]);
|
||||
|
|
@ -120,7 +125,7 @@ export class ContributionAccountRepository implements IContributionAccountReposi
|
|||
async findMany(options: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
orderBy?: 'totalContribution' | 'effectiveContribution';
|
||||
orderBy?: 'effectiveContribution' | 'personalContribution';
|
||||
order?: 'asc' | 'desc';
|
||||
}): Promise<{
|
||||
items: ContributionAccountAggregate[];
|
||||
|
|
@ -128,7 +133,7 @@ export class ContributionAccountRepository implements IContributionAccountReposi
|
|||
}> {
|
||||
const page = options.page ?? 1;
|
||||
const limit = options.limit ?? 50;
|
||||
const orderBy = options.orderBy ?? 'totalContribution';
|
||||
const orderBy = options.orderBy ?? 'effectiveContribution';
|
||||
const order = options.order ?? 'desc';
|
||||
|
||||
const [records, total] = await Promise.all([
|
||||
|
|
@ -165,8 +170,8 @@ export class ContributionAccountRepository implements IContributionAccountReposi
|
|||
|
||||
async findTopContributors(limit: number): Promise<ContributionAccountAggregate[]> {
|
||||
const records = await this.client.contributionAccount.findMany({
|
||||
where: { totalContribution: { gt: 0 } },
|
||||
orderBy: { totalContribution: 'desc' },
|
||||
where: { effectiveContribution: { gt: 0 } },
|
||||
orderBy: { effectiveContribution: 'desc' },
|
||||
take: limit,
|
||||
});
|
||||
|
||||
|
|
@ -175,10 +180,10 @@ export class ContributionAccountRepository implements IContributionAccountReposi
|
|||
|
||||
async getTotalContribution(): Promise<ContributionAmount> {
|
||||
const result = await this.client.contributionAccount.aggregate({
|
||||
_sum: { totalContribution: true },
|
||||
_sum: { effectiveContribution: true },
|
||||
});
|
||||
|
||||
return new ContributionAmount(result._sum.totalContribution || 0);
|
||||
return new ContributionAmount(result._sum.effectiveContribution || 0);
|
||||
}
|
||||
|
||||
async countAccounts(): Promise<number> {
|
||||
|
|
@ -187,7 +192,7 @@ export class ContributionAccountRepository implements IContributionAccountReposi
|
|||
|
||||
async countAccountsWithContribution(): Promise<number> {
|
||||
return this.client.contributionAccount.count({
|
||||
where: { totalContribution: { gt: 0 } },
|
||||
where: { effectiveContribution: { gt: 0 } },
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -196,10 +201,32 @@ export class ContributionAccountRepository implements IContributionAccountReposi
|
|||
id: record.id,
|
||||
accountSequence: record.accountSequence,
|
||||
personalContribution: record.personalContribution,
|
||||
teamLevelContribution: record.teamLevelContribution,
|
||||
teamBonusContribution: record.teamBonusContribution,
|
||||
totalContribution: record.totalContribution,
|
||||
// 18 个待解锁字段
|
||||
level1Pending: record.level1Pending,
|
||||
level2Pending: record.level2Pending,
|
||||
level3Pending: record.level3Pending,
|
||||
level4Pending: record.level4Pending,
|
||||
level5Pending: record.level5Pending,
|
||||
level6Pending: record.level6Pending,
|
||||
level7Pending: record.level7Pending,
|
||||
level8Pending: record.level8Pending,
|
||||
level9Pending: record.level9Pending,
|
||||
level10Pending: record.level10Pending,
|
||||
level11Pending: record.level11Pending,
|
||||
level12Pending: record.level12Pending,
|
||||
level13Pending: record.level13Pending,
|
||||
level14Pending: record.level14Pending,
|
||||
level15Pending: record.level15Pending,
|
||||
bonusTier1Pending: record.bonusTier1Pending,
|
||||
bonusTier2Pending: record.bonusTier2Pending,
|
||||
bonusTier3Pending: record.bonusTier3Pending,
|
||||
// 汇总字段
|
||||
totalLevelPending: record.totalLevelPending,
|
||||
totalBonusPending: record.totalBonusPending,
|
||||
totalPending: record.totalPending,
|
||||
totalUnlocked: record.totalUnlocked,
|
||||
effectiveContribution: record.effectiveContribution,
|
||||
// 解锁状态
|
||||
hasAdopted: record.hasAdopted,
|
||||
directReferralAdoptedCount: record.directReferralAdoptedCount,
|
||||
unlockedLevelDepth: record.unlockedLevelDepth,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,417 @@
|
|||
-- CreateEnum
|
||||
CREATE TYPE "PoolAccountType" AS ENUM ('SHARE_POOL', 'BLACK_HOLE_POOL', 'CIRCULATION_POOL');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "PoolTransactionType" AS ENUM ('MINING_DISTRIBUTE', 'FEE_COLLECT', 'INITIAL_INJECT', 'BURN', 'USER_TRANSFER_IN', 'USER_TRANSFER_OUT', 'TRADE_BUY', 'TRADE_SELL', 'POOL_TRANSFER', 'ADJUSTMENT');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "OutboxStatus" AS ENUM ('PENDING', 'PUBLISHED', 'FAILED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "mining_configs" (
|
||||
"id" TEXT NOT NULL,
|
||||
"totalShares" DECIMAL(30,8) NOT NULL,
|
||||
"distributionPool" DECIMAL(30,8) NOT NULL,
|
||||
"remainingDistribution" DECIMAL(30,8) NOT NULL,
|
||||
"halvingPeriodYears" INTEGER NOT NULL DEFAULT 2,
|
||||
"currentEra" INTEGER NOT NULL DEFAULT 1,
|
||||
"eraStartDate" TIMESTAMP(3) NOT NULL,
|
||||
"minuteDistribution" DECIMAL(30,18) NOT NULL,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT false,
|
||||
"activatedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "mining_configs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "mining_eras" (
|
||||
"id" TEXT NOT NULL,
|
||||
"eraNumber" INTEGER NOT NULL,
|
||||
"startDate" TIMESTAMP(3) NOT NULL,
|
||||
"endDate" TIMESTAMP(3),
|
||||
"initialDistribution" DECIMAL(30,8) NOT NULL,
|
||||
"totalDistributed" DECIMAL(30,8) NOT NULL DEFAULT 0,
|
||||
"minuteDistribution" DECIMAL(30,18) NOT NULL,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "mining_eras_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "mining_accounts" (
|
||||
"id" TEXT NOT NULL,
|
||||
"accountSequence" TEXT NOT NULL,
|
||||
"totalMined" DECIMAL(30,8) NOT NULL DEFAULT 0,
|
||||
"availableBalance" DECIMAL(30,8) NOT NULL DEFAULT 0,
|
||||
"frozenBalance" DECIMAL(30,8) NOT NULL DEFAULT 0,
|
||||
"totalContribution" DECIMAL(30,8) NOT NULL DEFAULT 0,
|
||||
"lastSyncedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "mining_accounts_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "mining_records" (
|
||||
"id" TEXT NOT NULL,
|
||||
"accountSequence" TEXT NOT NULL,
|
||||
"miningMinute" TIMESTAMP(3) NOT NULL,
|
||||
"contributionRatio" DECIMAL(30,18) NOT NULL,
|
||||
"totalContribution" DECIMAL(30,8) NOT NULL,
|
||||
"minuteDistribution" DECIMAL(30,18) NOT NULL,
|
||||
"minedAmount" DECIMAL(30,18) NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "mining_records_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "mining_transactions" (
|
||||
"id" TEXT NOT NULL,
|
||||
"accountSequence" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"amount" DECIMAL(30,8) NOT NULL,
|
||||
"balanceBefore" DECIMAL(30,8) NOT NULL,
|
||||
"balanceAfter" DECIMAL(30,8) NOT NULL,
|
||||
"referenceId" TEXT,
|
||||
"referenceType" TEXT,
|
||||
"counterparty_type" TEXT,
|
||||
"counterparty_account_seq" TEXT,
|
||||
"counterparty_user_id" TEXT,
|
||||
"memo" TEXT,
|
||||
"description" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "mining_transactions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "mining_reward_allocations" (
|
||||
"id" BIGSERIAL NOT NULL,
|
||||
"mining_date" DATE NOT NULL,
|
||||
"contribution_record_id" BIGINT NOT NULL,
|
||||
"source_adoption_id" BIGINT NOT NULL,
|
||||
"source_account_sequence" VARCHAR(20) NOT NULL,
|
||||
"owner_account_sequence" VARCHAR(20) NOT NULL,
|
||||
"contribution_type" VARCHAR(30) NOT NULL,
|
||||
"contribution_amount" DECIMAL(30,10) NOT NULL,
|
||||
"network_total_contribution" DECIMAL(30,10) NOT NULL,
|
||||
"contribution_ratio" DECIMAL(30,18) NOT NULL,
|
||||
"daily_mining_pool" DECIMAL(30,10) NOT NULL,
|
||||
"reward_amount" DECIMAL(30,10) NOT NULL,
|
||||
"allocation_status" VARCHAR(20) NOT NULL,
|
||||
"is_unlocked" BOOLEAN NOT NULL,
|
||||
"allocated_to_account_sequence" VARCHAR(20),
|
||||
"allocated_to_system_account" VARCHAR(20),
|
||||
"unlocked_reason" VARCHAR(200),
|
||||
"owner_has_adopted" BOOLEAN NOT NULL,
|
||||
"owner_direct_referral_count" INTEGER NOT NULL,
|
||||
"owner_unlocked_level_depth" INTEGER NOT NULL,
|
||||
"owner_unlocked_bonus_tiers" INTEGER NOT NULL,
|
||||
"remark" VARCHAR(500),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "mining_reward_allocations_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "daily_mining_reward_summaries" (
|
||||
"id" BIGSERIAL NOT NULL,
|
||||
"mining_date" DATE NOT NULL,
|
||||
"account_sequence" VARCHAR(20) NOT NULL,
|
||||
"unlocked_reward" DECIMAL(30,10) NOT NULL DEFAULT 0,
|
||||
"pending_reward_to_hq" DECIMAL(30,10) NOT NULL DEFAULT 0,
|
||||
"personal_reward" DECIMAL(30,10) NOT NULL DEFAULT 0,
|
||||
"level_reward" DECIMAL(30,10) NOT NULL DEFAULT 0,
|
||||
"bonus_reward" DECIMAL(30,10) NOT NULL DEFAULT 0,
|
||||
"pending_level_to_hq" DECIMAL(30,10) NOT NULL DEFAULT 0,
|
||||
"pending_bonus_to_hq" DECIMAL(30,10) NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "daily_mining_reward_summaries_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "headquarters_pending_rewards" (
|
||||
"id" BIGSERIAL NOT NULL,
|
||||
"mining_date" DATE NOT NULL,
|
||||
"would_be_account_sequence" VARCHAR(20) NOT NULL,
|
||||
"source_adoption_id" BIGINT NOT NULL,
|
||||
"source_account_sequence" VARCHAR(20) NOT NULL,
|
||||
"contribution_record_id" BIGINT NOT NULL,
|
||||
"contribution_type" VARCHAR(30) NOT NULL,
|
||||
"contribution_amount" DECIMAL(30,10) NOT NULL,
|
||||
"reward_amount" DECIMAL(30,10) NOT NULL,
|
||||
"reason" VARCHAR(200) NOT NULL,
|
||||
"owner_has_adopted" BOOLEAN NOT NULL,
|
||||
"owner_direct_referral_count" INTEGER NOT NULL,
|
||||
"required_condition" VARCHAR(100) NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "headquarters_pending_rewards_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "minute_mining_stats" (
|
||||
"id" TEXT NOT NULL,
|
||||
"minute" TIMESTAMP(3) NOT NULL,
|
||||
"totalContribution" DECIMAL(30,8) NOT NULL,
|
||||
"totalDistributed" DECIMAL(30,18) NOT NULL,
|
||||
"participantCount" INTEGER NOT NULL,
|
||||
"burnAmount" DECIMAL(30,8) NOT NULL DEFAULT 0,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "minute_mining_stats_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "daily_mining_stats" (
|
||||
"id" TEXT NOT NULL,
|
||||
"date" DATE NOT NULL,
|
||||
"totalContribution" DECIMAL(30,8) NOT NULL,
|
||||
"totalDistributed" DECIMAL(30,8) NOT NULL,
|
||||
"totalBurned" DECIMAL(30,8) NOT NULL,
|
||||
"participantCount" INTEGER NOT NULL,
|
||||
"avgContributionRate" DECIMAL(10,8) NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "daily_mining_stats_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "black_holes" (
|
||||
"id" TEXT NOT NULL,
|
||||
"totalBurned" DECIMAL(30,8) NOT NULL DEFAULT 0,
|
||||
"targetBurn" DECIMAL(30,8) NOT NULL,
|
||||
"remainingBurn" DECIMAL(30,8) NOT NULL,
|
||||
"lastBurnMinute" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "black_holes_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "burn_records" (
|
||||
"id" TEXT NOT NULL,
|
||||
"blackHoleId" TEXT NOT NULL,
|
||||
"burnMinute" TIMESTAMP(3) NOT NULL,
|
||||
"burnAmount" DECIMAL(30,18) NOT NULL,
|
||||
"remainingTarget" DECIMAL(30,8) NOT NULL,
|
||||
"source_type" TEXT,
|
||||
"source_account_seq" TEXT,
|
||||
"source_user_id" TEXT,
|
||||
"memo" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "burn_records_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "price_snapshots" (
|
||||
"id" TEXT NOT NULL,
|
||||
"snapshotTime" TIMESTAMP(3) NOT NULL,
|
||||
"price" DECIMAL(30,18) NOT NULL,
|
||||
"sharePool" DECIMAL(30,8) NOT NULL,
|
||||
"blackHoleAmount" DECIMAL(30,8) NOT NULL,
|
||||
"circulationPool" DECIMAL(30,8) NOT NULL,
|
||||
"effectiveDenominator" DECIMAL(30,8) NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "price_snapshots_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "pool_accounts" (
|
||||
"id" TEXT NOT NULL,
|
||||
"pool_type" "PoolAccountType" NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"balance" DECIMAL(30,8) NOT NULL DEFAULT 0,
|
||||
"totalInflow" DECIMAL(30,8) NOT NULL DEFAULT 0,
|
||||
"totalOutflow" DECIMAL(30,8) NOT NULL DEFAULT 0,
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"description" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "pool_accounts_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "pool_transactions" (
|
||||
"id" TEXT NOT NULL,
|
||||
"pool_account_id" TEXT NOT NULL,
|
||||
"pool_type" "PoolAccountType" NOT NULL,
|
||||
"transaction_type" "PoolTransactionType" NOT NULL,
|
||||
"amount" DECIMAL(30,8) NOT NULL,
|
||||
"balance_before" DECIMAL(30,8) NOT NULL,
|
||||
"balance_after" DECIMAL(30,8) NOT NULL,
|
||||
"counterparty_type" TEXT,
|
||||
"counterparty_account_seq" TEXT,
|
||||
"counterparty_user_id" TEXT,
|
||||
"counterparty_pool_type" "PoolAccountType",
|
||||
"reference_id" TEXT,
|
||||
"reference_type" TEXT,
|
||||
"tx_hash" TEXT,
|
||||
"memo" TEXT,
|
||||
"metadata" JSONB,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "pool_transactions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "outbox_events" (
|
||||
"id" TEXT NOT NULL,
|
||||
"aggregate_type" TEXT NOT NULL,
|
||||
"aggregate_id" TEXT NOT NULL,
|
||||
"event_type" TEXT NOT NULL,
|
||||
"payload" JSONB NOT NULL,
|
||||
"topic" TEXT NOT NULL DEFAULT 'mining.events',
|
||||
"key" TEXT,
|
||||
"status" "OutboxStatus" NOT NULL DEFAULT 'PENDING',
|
||||
"retry_count" INTEGER NOT NULL DEFAULT 0,
|
||||
"max_retries" INTEGER NOT NULL DEFAULT 5,
|
||||
"last_error" TEXT,
|
||||
"published_at" TIMESTAMP(3),
|
||||
"next_retry_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "outbox_events_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "mining_eras_eraNumber_key" ON "mining_eras"("eraNumber");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "mining_accounts_accountSequence_key" ON "mining_accounts"("accountSequence");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "mining_accounts_totalContribution_idx" ON "mining_accounts"("totalContribution" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "mining_records_miningMinute_idx" ON "mining_records"("miningMinute");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "mining_records_accountSequence_miningMinute_key" ON "mining_records"("accountSequence", "miningMinute");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "mining_transactions_accountSequence_createdAt_idx" ON "mining_transactions"("accountSequence", "createdAt" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "mining_transactions_type_idx" ON "mining_transactions"("type");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "mining_transactions_counterparty_account_seq_idx" ON "mining_transactions"("counterparty_account_seq");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "mining_transactions_counterparty_user_id_idx" ON "mining_transactions"("counterparty_user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "mining_reward_allocations_mining_date_idx" ON "mining_reward_allocations"("mining_date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "mining_reward_allocations_owner_account_sequence_mining_dat_idx" ON "mining_reward_allocations"("owner_account_sequence", "mining_date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "mining_reward_allocations_source_account_sequence_idx" ON "mining_reward_allocations"("source_account_sequence");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "mining_reward_allocations_source_adoption_id_idx" ON "mining_reward_allocations"("source_adoption_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "mining_reward_allocations_allocation_status_idx" ON "mining_reward_allocations"("allocation_status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "mining_reward_allocations_contribution_record_id_idx" ON "mining_reward_allocations"("contribution_record_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "daily_mining_reward_summaries_mining_date_idx" ON "daily_mining_reward_summaries"("mining_date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "daily_mining_reward_summaries_account_sequence_idx" ON "daily_mining_reward_summaries"("account_sequence");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "daily_mining_reward_summaries_mining_date_account_sequence_key" ON "daily_mining_reward_summaries"("mining_date", "account_sequence");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "headquarters_pending_rewards_mining_date_idx" ON "headquarters_pending_rewards"("mining_date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "headquarters_pending_rewards_would_be_account_sequence_idx" ON "headquarters_pending_rewards"("would_be_account_sequence");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "headquarters_pending_rewards_source_adoption_id_idx" ON "headquarters_pending_rewards"("source_adoption_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "minute_mining_stats_minute_key" ON "minute_mining_stats"("minute");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "minute_mining_stats_minute_idx" ON "minute_mining_stats"("minute" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "daily_mining_stats_date_key" ON "daily_mining_stats"("date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "burn_records_burnMinute_idx" ON "burn_records"("burnMinute");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "burn_records_source_account_seq_idx" ON "burn_records"("source_account_seq");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "burn_records_blackHoleId_burnMinute_key" ON "burn_records"("blackHoleId", "burnMinute");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "price_snapshots_snapshotTime_key" ON "price_snapshots"("snapshotTime");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "price_snapshots_snapshotTime_idx" ON "price_snapshots"("snapshotTime" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "pool_accounts_pool_type_key" ON "pool_accounts"("pool_type");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pool_accounts_pool_type_idx" ON "pool_accounts"("pool_type");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pool_transactions_pool_account_id_created_at_idx" ON "pool_transactions"("pool_account_id", "created_at" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pool_transactions_pool_type_transaction_type_idx" ON "pool_transactions"("pool_type", "transaction_type");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pool_transactions_counterparty_account_seq_idx" ON "pool_transactions"("counterparty_account_seq");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pool_transactions_counterparty_user_id_idx" ON "pool_transactions"("counterparty_user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pool_transactions_reference_id_idx" ON "pool_transactions"("reference_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "pool_transactions_created_at_idx" ON "pool_transactions"("created_at" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "outbox_events_status_idx" ON "outbox_events"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "outbox_events_next_retry_at_idx" ON "outbox_events"("next_retry_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "outbox_events_created_at_idx" ON "outbox_events"("created_at");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "mining_records" ADD CONSTRAINT "mining_records_accountSequence_fkey" FOREIGN KEY ("accountSequence") REFERENCES "mining_accounts"("accountSequence") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "mining_transactions" ADD CONSTRAINT "mining_transactions_accountSequence_fkey" FOREIGN KEY ("accountSequence") REFERENCES "mining_accounts"("accountSequence") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "burn_records" ADD CONSTRAINT "burn_records_blackHoleId_fkey" FOREIGN KEY ("blackHoleId") REFERENCES "black_holes"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "pool_transactions" ADD CONSTRAINT "pool_transactions_pool_account_id_fkey" FOREIGN KEY ("pool_account_id") REFERENCES "pool_accounts"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
||||
|
|
@ -111,6 +111,134 @@ model MiningTransaction {
|
|||
@@map("mining_transactions")
|
||||
}
|
||||
|
||||
// ==================== 挖矿收益分配明细 ====================
|
||||
// 特别关注:待解锁算力产生的收益分配到总部的情况
|
||||
|
||||
// 每日挖矿收益分配明细
|
||||
// 记录每天挖矿时,算力产生的收益分配给谁
|
||||
model MiningRewardAllocation {
|
||||
id BigInt @id @default(autoincrement())
|
||||
|
||||
// ========== 挖矿日期 ==========
|
||||
miningDate DateTime @map("mining_date") @db.Date
|
||||
|
||||
// ========== 算力来源(可追溯到认种)==========
|
||||
contributionRecordId BigInt @map("contribution_record_id") // 关联 contribution-service 的算力明细记录ID
|
||||
sourceAdoptionId BigInt @map("source_adoption_id") // 来源认种ID
|
||||
sourceAccountSequence String @map("source_account_sequence") @db.VarChar(20) // 认种人账号
|
||||
|
||||
// ========== 算力归属账户 ==========
|
||||
ownerAccountSequence String @map("owner_account_sequence") @db.VarChar(20) // 算力归属账户(收到分成的上级)
|
||||
|
||||
// ========== 算力类型 ==========
|
||||
contributionType String @map("contribution_type") @db.VarChar(30) // PERSONAL / LEVEL_1~15 / BONUS_TIER_1~3
|
||||
|
||||
// ========== 算力金额与挖矿收益 ==========
|
||||
contributionAmount Decimal @map("contribution_amount") @db.Decimal(30, 10) // 参与挖矿的算力
|
||||
networkTotalContribution Decimal @map("network_total_contribution") @db.Decimal(30, 10) // 当日全网算力
|
||||
contributionRatio Decimal @map("contribution_ratio") @db.Decimal(30, 18) // 算力占比
|
||||
dailyMiningPool Decimal @map("daily_mining_pool") @db.Decimal(30, 10) // 当日挖矿池总量
|
||||
rewardAmount Decimal @map("reward_amount") @db.Decimal(30, 10) // 应得挖矿收益
|
||||
|
||||
// ========== 分配状态(核心:是否因未解锁而归总部)==========
|
||||
allocationStatus String @map("allocation_status") @db.VarChar(20) // TO_USER / TO_HEADQUARTERS
|
||||
isUnlocked Boolean @map("is_unlocked") // 算力是否已解锁
|
||||
|
||||
// ========== 实际分配去向 ==========
|
||||
allocatedToAccountSequence String? @map("allocated_to_account_sequence") @db.VarChar(20) // 实际分配给的账户
|
||||
allocatedToSystemAccount String? @map("allocated_to_system_account") @db.VarChar(20) // 或分配给系统账户(HEADQUARTERS)
|
||||
|
||||
// ========== 未解锁原因说明 ==========
|
||||
unlockedReason String? @map("unlocked_reason") @db.VarChar(200) // 如果归总部,记录原因
|
||||
|
||||
// ========== 资质条件快照(便于追溯)==========
|
||||
ownerHasAdopted Boolean @map("owner_has_adopted") // 归属账户当时是否已认种
|
||||
ownerDirectReferralCount Int @map("owner_direct_referral_count") // 归属账户当时直推认种人数
|
||||
ownerUnlockedLevelDepth Int @map("owner_unlocked_level_depth") // 归属账户当时解锁层级深度
|
||||
ownerUnlockedBonusTiers Int @map("owner_unlocked_bonus_tiers") // 归属账户当时解锁加成档位
|
||||
|
||||
// ========== 备注 ==========
|
||||
remark String? @map("remark") @db.VarChar(500)
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@index([miningDate])
|
||||
@@index([ownerAccountSequence, miningDate])
|
||||
@@index([sourceAccountSequence])
|
||||
@@index([sourceAdoptionId])
|
||||
@@index([allocationStatus])
|
||||
@@index([contributionRecordId])
|
||||
@@map("mining_reward_allocations")
|
||||
}
|
||||
|
||||
// 每日挖矿汇总(按账户)
|
||||
// 汇总每个账户每天的挖矿收益
|
||||
model DailyMiningRewardSummary {
|
||||
id BigInt @id @default(autoincrement())
|
||||
|
||||
miningDate DateTime @map("mining_date") @db.Date
|
||||
accountSequence String @map("account_sequence") @db.VarChar(20)
|
||||
|
||||
// ========== 收益汇总 ==========
|
||||
// 来自已解锁算力的收益(归用户)
|
||||
unlockedReward Decimal @default(0) @map("unlocked_reward") @db.Decimal(30, 10)
|
||||
// 来自待解锁算力的收益(本应归用户,但因未解锁归了总部)
|
||||
pendingRewardToHq Decimal @default(0) @map("pending_reward_to_hq") @db.Decimal(30, 10)
|
||||
|
||||
// ========== 明细统计 ==========
|
||||
// 已解锁的各类算力收益
|
||||
personalReward Decimal @default(0) @map("personal_reward") @db.Decimal(30, 10) // 个人算力收益
|
||||
levelReward Decimal @default(0) @map("level_reward") @db.Decimal(30, 10) // 层级算力收益(已解锁部分)
|
||||
bonusReward Decimal @default(0) @map("bonus_reward") @db.Decimal(30, 10) // 加成算力收益(已解锁部分)
|
||||
|
||||
// 待解锁转总部的明细
|
||||
pendingLevelToHq Decimal @default(0) @map("pending_level_to_hq") @db.Decimal(30, 10) // 待解锁层级收益归总部
|
||||
pendingBonusToHq Decimal @default(0) @map("pending_bonus_to_hq") @db.Decimal(30, 10) // 待解锁加成收益归总部
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@unique([miningDate, accountSequence])
|
||||
@@index([miningDate])
|
||||
@@index([accountSequence])
|
||||
@@map("daily_mining_reward_summaries")
|
||||
}
|
||||
|
||||
// 总部待解锁收益明细账
|
||||
// 记录总部收到的来自各用户待解锁算力的收益
|
||||
model HeadquartersPendingReward {
|
||||
id BigInt @id @default(autoincrement())
|
||||
|
||||
miningDate DateTime @map("mining_date") @db.Date
|
||||
|
||||
// ========== 应归属账户(如果解锁了会归他)==========
|
||||
wouldBeAccountSequence String @map("would_be_account_sequence") @db.VarChar(20)
|
||||
|
||||
// ========== 算力来源 ==========
|
||||
sourceAdoptionId BigInt @map("source_adoption_id")
|
||||
sourceAccountSequence String @map("source_account_sequence") @db.VarChar(20)
|
||||
contributionRecordId BigInt @map("contribution_record_id")
|
||||
|
||||
// ========== 算力类型与金额 ==========
|
||||
contributionType String @map("contribution_type") @db.VarChar(30) // LEVEL_1~15 / BONUS_TIER_1~3
|
||||
contributionAmount Decimal @map("contribution_amount") @db.Decimal(30, 10)
|
||||
rewardAmount Decimal @map("reward_amount") @db.Decimal(30, 10)
|
||||
|
||||
// ========== 未解锁原因 ==========
|
||||
reason String @map("reason") @db.VarChar(200) // 如 "账户未认种,层级1-5未解锁" / "直推认种人数不足3人,层级6-10未解锁"
|
||||
|
||||
// ========== 资质条件快照 ==========
|
||||
ownerHasAdopted Boolean @map("owner_has_adopted")
|
||||
ownerDirectReferralCount Int @map("owner_direct_referral_count")
|
||||
requiredCondition String @map("required_condition") @db.VarChar(100) // 需要满足的条件
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@index([miningDate])
|
||||
@@index([wouldBeAccountSequence])
|
||||
@@index([sourceAdoptionId])
|
||||
@@map("headquarters_pending_rewards")
|
||||
}
|
||||
|
||||
// ==================== 挖矿统计 ====================
|
||||
|
||||
// 每分钟挖矿统计
|
||||
|
|
|
|||
Loading…
Reference in New Issue