rwadurian/backend/services/referral-service/prisma/schema.prisma

202 lines
9.1 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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