generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ============================================ // 推荐关系表 (聚合根1) // 记录用户与推荐人的关系,推荐关系一旦建立终生不可修改 // ============================================ model ReferralRelationship { id BigInt @id @default(autoincrement()) @map("relationship_id") userId BigInt @unique @map("user_id") accountSequence String @unique @map("account_sequence") @db.VarChar(12) // 格式: D + YYMMDD + 5位序号 // 推荐人信息 referrerId BigInt? @map("referrer_id") // 直接推荐人 (null = 无推荐人/根节点) rootUserId BigInt? @map("root_user_id") // 顶级上级用户ID // 推荐码 myReferralCode String @unique @map("my_referral_code") @db.VarChar(20) usedReferralCode String? @map("used_referral_code") @db.VarChar(20) // 推荐链 (使用PostgreSQL数组类型,最多存储10层上级) ancestorPath BigInt[] @map("ancestor_path") // [父节点, 祖父节点, ...] 从根到父的路径 depth Int @default(0) @map("depth") // 层级深度 (0=根节点) // 直推统计 (快速查询用,冗余存储) directReferralCount Int @default(0) @map("direct_referral_count") activeDirectCount Int @default(0) @map("active_direct_count") // 已认种的直推人数 createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") // 自引用关系 (方便查询推荐人) referrer ReferralRelationship? @relation("ReferrerToReferral", fields: [referrerId], references: [userId]) directReferrals ReferralRelationship[] @relation("ReferrerToReferral") // 关联团队统计 teamStatistics TeamStatistics? @@map("referral_relationships") @@index([referrerId], name: "idx_referrer") @@index([accountSequence], name: "idx_account_sequence") @@index([myReferralCode], name: "idx_my_referral_code") @@index([usedReferralCode], name: "idx_used_referral_code") @@index([rootUserId], name: "idx_root_user") @@index([depth], name: "idx_depth") @@index([createdAt], name: "idx_referral_created") } // ============================================ // 团队统计表 (聚合根2) // 每个用户的团队认种统计数据,需要实时更新 // ============================================ model TeamStatistics { id BigInt @id @default(autoincrement()) @map("statistics_id") userId BigInt @unique @map("user_id") // === 注册统计 === directReferralCount Int @default(0) @map("direct_referral_count") // 直推注册数 totalTeamCount Int @default(0) @map("total_team_count") // 团队总注册数 // === 个人认种 === selfPlantingCount Int @default(0) @map("self_planting_count") // 自己认种数量 selfPlantingAmount Decimal @default(0) @map("self_planting_amount") @db.Decimal(20, 8) // === 团队认种 (包含自己和所有下级) === directPlantingCount Int @default(0) @map("direct_planting_count") // 直推认种数 totalTeamPlantingCount Int @default(0) @map("total_team_planting_count") // 团队总认种数 totalTeamPlantingAmount Decimal @default(0) @map("total_team_planting_amount") @db.Decimal(20, 8) // === 直推团队数据 (JSON存储每个直推的团队认种量) === // 格式: [{ userId: bigint, personalCount: int, teamCount: int, amount: decimal }, ...] directTeamPlantingData Json @default("[]") @map("direct_team_planting_data") // === 龙虎榜相关 === // 龙虎榜分值 = 团队总认种量 - 最大单个直推团队认种量 maxSingleTeamPlantingCount Int @default(0) @map("max_single_team_planting_count") effectivePlantingCountForRanking Int @default(0) @map("effective_planting_count_for_ranking") // === 本省本市统计 (用于省市授权考核) === ownProvinceTeamCount Int @default(0) @map("own_province_team_count") // 自有团队本省认种 ownCityTeamCount Int @default(0) @map("own_city_team_count") // 自有团队本市认种 provinceTeamPercentage Decimal @default(0) @map("province_team_percentage") @db.Decimal(5, 2) // 本省占比 cityTeamPercentage Decimal @default(0) @map("city_team_percentage") @db.Decimal(5, 2) // 本市占比 // === 省市分布 (JSON存储详细分布) === // 格式: { "provinceCode": { "cityCode": count, ... }, ... } provinceCityDistribution Json @default("{}") @map("province_city_distribution") // 时间戳 lastCalcAt DateTime? @map("last_calc_at") // 最后计算时间 createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") // 关联 referralRelationship ReferralRelationship @relation(fields: [userId], references: [userId]) @@map("team_statistics") @@index([effectivePlantingCountForRanking(sort: Desc)], name: "idx_leaderboard_score") @@index([totalTeamPlantingCount(sort: Desc)], name: "idx_team_planting") @@index([selfPlantingCount], name: "idx_self_planting") } // ============================================ // 直推用户列表 (冗余表,便于分页查询) // ============================================ model DirectReferral { id BigInt @id @default(autoincrement()) @map("direct_referral_id") referrerId BigInt @map("referrer_id") // 推荐人ID referralId BigInt @map("referral_id") // 被推荐人ID referralSequence String @map("referral_sequence") @db.VarChar(12) // 被推荐人序列号 (格式: D + YYMMDD + 5位序号) // 被推荐人信息快照 (冗余存储,避免跨服务查询) referralNickname String? @map("referral_nickname") @db.VarChar(100) referralAvatar String? @map("referral_avatar") @db.VarChar(255) // 该直推的认种统计 personalPlantingCount Int @default(0) @map("personal_planting_count") // 个人认种数 teamPlantingCount Int @default(0) @map("team_planting_count") // 团队认种数(含个人) // 是否已认种 (用于区分活跃/非活跃) hasPlanted Boolean @default(false) @map("has_planted") firstPlantedAt DateTime? @map("first_planted_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@unique([referrerId, referralId], name: "uk_referrer_referral") @@map("direct_referrals") @@index([referrerId], name: "idx_direct_referrer") @@index([referralId], name: "idx_direct_referral") @@index([hasPlanted], name: "idx_has_planted") @@index([teamPlantingCount(sort: Desc)], name: "idx_direct_team_planting") } // ============================================ // 团队省市分布表 (用于省市权益分配) // ============================================ model TeamProvinceCityDetail { id BigInt @id @default(autoincrement()) @map("detail_id") userId BigInt @map("user_id") provinceCode String @map("province_code") @db.VarChar(10) cityCode String @map("city_code") @db.VarChar(10) teamPlantingCount Int @default(0) @map("team_planting_count") // 该省/市团队认种数 updatedAt DateTime @updatedAt @map("updated_at") @@unique([userId, provinceCode, cityCode], name: "uk_user_province_city") @@map("team_province_city_details") @@index([userId], name: "idx_detail_user") @@index([provinceCode], name: "idx_detail_province") @@index([cityCode], name: "idx_detail_city") } // ============================================ // 已处理事件表 (幂等性检查) // 用于防止重复处理Kafka消息 // ============================================ model ProcessedEvent { id BigInt @id @default(autoincrement()) eventId String @unique @map("event_id") @db.VarChar(100) // 事件唯一标识 (outbox.aggregateId) eventType String @map("event_type") @db.VarChar(50) processedAt DateTime @default(now()) @map("processed_at") @@map("processed_events") @@index([eventId], name: "idx_processed_event_id") @@index([processedAt], name: "idx_processed_at") } // ============================================ // 推荐事件表 (行为表,append-only,用于审计和事件溯源) // ============================================ model ReferralEvent { id BigInt @id @default(autoincrement()) @map("event_id") eventType String @map("event_type") @db.VarChar(50) // 聚合根信息 aggregateId String @map("aggregate_id") @db.VarChar(100) aggregateType String @map("aggregate_type") @db.VarChar(50) // 事件数据 eventData Json @map("event_data") // 元数据 userId BigInt? @map("user_id") occurredAt DateTime @default(now()) @map("occurred_at") @db.Timestamp(6) version Int @default(1) @map("version") @@map("referral_events") @@index([aggregateType, aggregateId], name: "idx_event_aggregate") @@index([eventType], name: "idx_event_type") @@index([userId], name: "idx_event_user") @@index([occurredAt], name: "idx_event_occurred") }