// ============================================ // [2026-02-17] 预种计划独立 Prisma Schema // ============================================ // // 本 schema 仅包含预种计划在 contribution-service 中的追踪表。 // 与主 schema (prisma/schema.prisma) 完全隔离,拥有独立的: // - Prisma Client(生成到 src/pre-planting/infrastructure/prisma/generated/) // - Migration 目录(prisma/pre-planting/migrations/) // // 预种的算力分配结果仍然写入主 schema 的 contribution_accounts、 // contribution_records 等表(通过现有 Repository),以便挖矿系统读取。 // 本 schema 仅负责预种 CDC 同步追踪、冻结状态等预种专属数据。 generator client { provider = "prisma-client-js" output = "../../src/pre-planting/infrastructure/prisma/generated" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ============================================ // 预种 CDC 同步追踪表 // ============================================ /// 预种订单同步记录(从 planting-service CDC 同步) /// 用于追踪每笔预种订单的算力分配状态 model PrePlantingSyncedOrder { id BigInt @id @default(autoincrement()) originalOrderId BigInt @unique @map("original_order_id") orderNo String @map("order_no") @db.VarChar(50) userId BigInt @map("user_id") accountSequence String @map("account_sequence") @db.VarChar(20) portionCount Int @map("portion_count") pricePerPortion Decimal @map("price_per_portion") @db.Decimal(20, 8) totalAmount Decimal @map("total_amount") @db.Decimal(20, 8) provinceCode String @map("province_code") @db.VarChar(10) cityCode String @map("city_code") @db.VarChar(10) status String @map("status") @db.VarChar(20) // CREATED, PAID, MERGED mergedToMergeId BigInt? @map("merged_to_merge_id") paidAt DateTime? @map("paid_at") createdAt DateTime @map("created_at") // 算力追踪 contributionPerPortion Decimal @map("contribution_per_portion") @db.Decimal(20, 10) contributionDistributed Boolean @default(false) @map("contribution_distributed") contributionDistributedAt DateTime? @map("contribution_distributed_at") // CDC 同步元数据 sourceTopic String @map("source_topic") @db.VarChar(200) sourceOffset BigInt @map("source_offset") syncedAt DateTime @default(now()) @map("synced_at") @@index([accountSequence]) @@index([status]) @@index([contributionDistributed]) @@map("pre_planting_synced_orders") } /// 预种持仓同步记录(从 planting-service CDC 同步) /// 用于追踪用户预种总量,判断冻结条件 model PrePlantingSyncedPosition { id BigInt @id @default(autoincrement()) userId BigInt @unique @map("user_id") accountSequence String @unique @map("account_sequence") @db.VarChar(20) totalPortions Int @default(0) @map("total_portions") mergedPortions Int @default(0) @map("merged_portions") totalTreesMerged Int @default(0) @map("total_trees_merged") firstPurchaseAt DateTime? @map("first_purchase_at") // CDC 同步元数据 sourceTopic String @map("source_topic") @db.VarChar(200) sourceOffset BigInt @map("source_offset") syncedAt DateTime @default(now()) @map("synced_at") @@map("pre_planting_synced_positions") } // ============================================ // 预种冻结状态表 // ============================================ /// 预种算力冻结状态(每用户一条) /// /// 冻结规则: /// - firstPurchaseAt + 1 年后仍未满 5 份 → 所有预种算力冻结(暂停分配) /// - 后续累积满 5 份 → 解冻,恢复分配 /// - 解冻后的失效期 = 解冻日起算 + 2 年 /// - 未被冻结过的正常到期 = 首次产生挖矿收益日 + 2 年 model PrePlantingFreezeState { id BigInt @id @default(autoincrement()) accountSequence String @unique @map("account_sequence") @db.VarChar(20) totalPortions Int @default(0) @map("total_portions") totalTreesMerged Int @default(0) @map("total_trees_merged") firstPurchaseAt DateTime? @map("first_purchase_at") // 冻结状态 isFrozen Boolean @default(false) @map("is_frozen") frozenAt DateTime? @map("frozen_at") unfrozenAt DateTime? @map("unfrozen_at") // 解冻后的过期日期(解冻日 + 2 年) postUnfreezeExpireDate DateTime? @map("post_unfreeze_expire_date") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@index([isFrozen]) @@index([firstPurchaseAt]) @@map("pre_planting_freeze_states") } // ============================================ // 预种 CDC 幂等性追踪表 // ============================================ /// 已处理的预种 CDC 事件(幂等性保证) /// 使用 (sourceTopic, offset) 作为复合唯一键 model PrePlantingProcessedCdcEvent { id BigInt @id @default(autoincrement()) sourceTopic String @map("source_topic") @db.VarChar(200) offset BigInt @map("offset") tableName String @map("table_name") @db.VarChar(100) operation String @map("operation") @db.VarChar(10) processedAt DateTime @default(now()) @map("processed_at") @@unique([sourceTopic, offset]) @@index([processedAt]) @@map("pre_planting_processed_cdc_events") }