202 lines
9.1 KiB
Plaintext
202 lines
9.1 KiB
Plaintext
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")
|
||
}
|