generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ============ Users & Auth ============ model User { id BigInt @id @default(autoincrement()) username String @unique @db.VarChar(64) userType String @map("user_type") @db.VarChar(20) status String @default("ACTIVE") @db.VarChar(20) parentId BigInt? @map("parent_id") agentLevel Int? @map("agent_level") locale String @default("en-US") @db.VarChar(10) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") deletedAt DateTime? @map("deleted_at") auth UserAuth? wallet Wallet? agentProfile AgentProfile? adminRole AdminUserRole? bets Bet[] preferences UserPreference? parent User? @relation("UserHierarchy", fields: [parentId], references: [id]) children User[] @relation("UserHierarchy") @@index([userType]) @@index([parentId]) @@map("users") } model UserAuth { id BigInt @id @default(autoincrement()) userId BigInt @unique @map("user_id") passwordHash String @map("password_hash") @db.VarChar(255) loginFailCount Int @default(0) @map("login_fail_count") lockedUntil DateTime? @map("locked_until") lastLoginAt DateTime? @map("last_login_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id]) @@map("user_auth") } model UserPreference { id BigInt @id @default(autoincrement()) userId BigInt @unique @map("user_id") locale String @default("en-US") @db.VarChar(10) phone String? @db.VarChar(32) email String? @db.VarChar(128) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id]) @@map("user_preferences") } model Role { id BigInt @id @default(autoincrement()) code String @unique @db.VarChar(64) name String @db.VarChar(128) description String? @db.VarChar(255) createdAt DateTime @default(now()) @map("created_at") permissions RolePermission[] adminUsers AdminUserRole[] @@map("roles") } model Permission { id BigInt @id @default(autoincrement()) code String @unique @db.VarChar(128) name String @db.VarChar(128) module String @db.VarChar(64) createdAt DateTime @default(now()) @map("created_at") roles RolePermission[] @@map("permissions") } model RolePermission { roleId BigInt @map("role_id") permissionId BigInt @map("permission_id") role Role @relation(fields: [roleId], references: [id]) permission Permission @relation(fields: [permissionId], references: [id]) @@id([roleId, permissionId]) @@map("role_permissions") } model AdminUserRole { userId BigInt @unique @map("user_id") roleId BigInt @map("role_id") createdAt DateTime @default(now()) @map("created_at") user User @relation(fields: [userId], references: [id]) role Role @relation(fields: [roleId], references: [id]) @@map("admin_user_roles") } // ============ Agent ============ model AgentProfile { id BigInt @id @default(autoincrement()) userId BigInt @unique @map("user_id") level Int parentAgentId BigInt? @map("parent_agent_id") creditLimit Decimal @default(0) @map("credit_limit") @db.Decimal(18, 4) usedCredit Decimal @default(0) @map("used_credit") @db.Decimal(18, 4) directPlayerLiability Decimal @default(0) @map("direct_player_liability") @db.Decimal(18, 4) childAgentExposure Decimal @default(0) @map("child_agent_exposure") @db.Decimal(18, 4) status String @default("ACTIVE") @db.VarChar(20) maxSingleDeposit Decimal? @map("max_single_deposit") @db.Decimal(18, 4) maxDailyDeposit Decimal? @map("max_daily_deposit") @db.Decimal(18, 4) cashbackRate Decimal @default(0) @map("cashback_rate") @db.Decimal(8, 4) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id]) @@index([parentAgentId]) @@map("agent_profiles") } model AgentClosure { ancestorId BigInt @map("ancestor_id") descendantId BigInt @map("descendant_id") depth Int @@id([ancestorId, descendantId]) @@index([descendantId]) @@map("agent_closure") } model AgentCreditTransaction { id BigInt @id @default(autoincrement()) agentId BigInt @map("agent_id") transactionType String @map("transaction_type") @db.VarChar(32) amount Decimal @db.Decimal(18, 4) creditBefore Decimal @map("credit_before") @db.Decimal(18, 4) creditAfter Decimal @map("credit_after") @db.Decimal(18, 4) referenceType String? @map("reference_type") @db.VarChar(32) referenceId String? @map("reference_id") @db.VarChar(64) operatorId BigInt? @map("operator_id") requestId String? @map("request_id") @db.VarChar(128) remark String? @db.VarChar(500) createdAt DateTime @default(now()) @map("created_at") @@unique([operatorId, requestId]) @@index([agentId]) @@map("agent_credit_transactions") } // ============ Wallet ============ model Wallet { id BigInt @id @default(autoincrement()) userId BigInt @unique @map("user_id") availableBalance Decimal @default(0) @map("available_balance") @db.Decimal(18, 4) frozenBalance Decimal @default(0) @map("frozen_balance") @db.Decimal(18, 4) currency String @default("USD") @db.VarChar(16) status String @default("ACTIVE") @db.VarChar(20) version Int @default(0) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id]) transactions WalletTransaction[] @@map("wallets") } model WalletTransaction { id BigInt @id @default(autoincrement()) transactionId String @unique @map("transaction_id") @db.VarChar(64) userId BigInt @map("user_id") walletId BigInt @map("wallet_id") transactionType String @map("transaction_type") @db.VarChar(32) amount Decimal @db.Decimal(18, 4) balanceBefore Decimal @map("balance_before") @db.Decimal(18, 4) balanceAfter Decimal @map("balance_after") @db.Decimal(18, 4) frozenBefore Decimal @map("frozen_before") @db.Decimal(18, 4) frozenAfter Decimal @map("frozen_after") @db.Decimal(18, 4) referenceType String? @map("reference_type") @db.VarChar(32) referenceId String? @map("reference_id") @db.VarChar(64) operatorId BigInt? @map("operator_id") remark String? @db.VarChar(500) createdAt DateTime @default(now()) @map("created_at") wallet Wallet @relation(fields: [walletId], references: [id]) @@index([userId]) @@index([walletId]) @@index([createdAt]) @@map("wallet_transactions") } // ============ Sports Data ============ model League { id BigInt @id @default(autoincrement()) sportType String @default("FOOTBALL") @map("sport_type") @db.VarChar(20) code String @unique @db.VarChar(64) displayOrder Int @default(0) @map("display_order") isActive Boolean @default(true) @map("is_active") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") deletedAt DateTime? @map("deleted_at") matches Match[] @@map("leagues") } model Team { id BigInt @id @default(autoincrement()) sportType String @default("FOOTBALL") @map("sport_type") @db.VarChar(20) code String @unique @db.VarChar(64) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") deletedAt DateTime? @map("deleted_at") homeMatches Match[] @relation("HomeTeam") awayMatches Match[] @relation("AwayTeam") @@map("teams") } model EntityTranslation { id BigInt @id @default(autoincrement()) entityType String @map("entity_type") @db.VarChar(32) entityId BigInt @map("entity_id") locale String @db.VarChar(10) fieldName String @map("field_name") @db.VarChar(32) value String @db.VarChar(500) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@unique([entityType, entityId, locale, fieldName]) @@index([entityType, entityId]) @@map("entity_translations") } model Match { id BigInt @id @default(autoincrement()) sportType String @default("FOOTBALL") @map("sport_type") @db.VarChar(20) leagueId BigInt @map("league_id") homeTeamId BigInt @map("home_team_id") awayTeamId BigInt @map("away_team_id") startTime DateTime @map("start_time") status String @default("DRAFT") @db.VarChar(32) isHot Boolean @default(false) @map("is_hot") displayOrder Int @default(0) @map("display_order") publishTime DateTime? @map("publish_time") closeTime DateTime? @map("close_time") isOutright Boolean @default(false) @map("is_outright") createdBy BigInt? @map("created_by") updatedBy BigInt? @map("updated_by") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") deletedAt DateTime? @map("deleted_at") league League @relation(fields: [leagueId], references: [id]) homeTeam Team @relation("HomeTeam", fields: [homeTeamId], references: [id]) awayTeam Team @relation("AwayTeam", fields: [awayTeamId], references: [id]) score MatchScore? markets Market[] settlements SettlementBatch[] @@index([status]) @@index([startTime]) @@index([leagueId]) @@map("matches") } model MatchScore { id BigInt @id @default(autoincrement()) matchId BigInt @unique @map("match_id") htHomeScore Int? @map("ht_home_score") htAwayScore Int? @map("ht_away_score") ftHomeScore Int? @map("ft_home_score") ftAwayScore Int? @map("ft_away_score") winnerTeamId BigInt? @map("winner_team_id") recordedBy BigInt? @map("recorded_by") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") match Match @relation(fields: [matchId], references: [id]) @@map("match_scores") } model Market { id BigInt @id @default(autoincrement()) matchId BigInt @map("match_id") marketType String @map("market_type") @db.VarChar(64) period String @db.VarChar(16) lineValue Decimal? @map("line_value") @db.Decimal(8, 2) status String @default("OPEN") @db.VarChar(20) allowSingle Boolean @default(true) @map("allow_single") allowParlay Boolean @default(true) @map("allow_parlay") sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") match Match @relation(fields: [matchId], references: [id]) selections MarketSelection[] @@index([matchId]) @@index([marketType]) @@map("markets") } model MarketSelection { id BigInt @id @default(autoincrement()) marketId BigInt @map("market_id") selectionCode String @map("selection_code") @db.VarChar(64) selectionName String @map("selection_name") @db.VarChar(255) odds Decimal @db.Decimal(18, 6) oddsVersion BigInt @default(1) @map("odds_version") status String @default("OPEN") @db.VarChar(20) sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") market Market @relation(fields: [marketId], references: [id]) oddsLogs OddsChangeLog[] @@index([marketId]) @@map("market_selections") } model OddsChangeLog { id BigInt @id @default(autoincrement()) selectionId BigInt @map("selection_id") oldOdds Decimal @map("old_odds") @db.Decimal(18, 6) newOdds Decimal @map("new_odds") @db.Decimal(18, 6) oddsVersion BigInt @map("odds_version") changedBy BigInt? @map("changed_by") createdAt DateTime @default(now()) @map("created_at") selection MarketSelection @relation(fields: [selectionId], references: [id]) @@index([selectionId]) @@map("odds_change_logs") } // ============ Bets ============ model Bet { id BigInt @id @default(autoincrement()) betNo String @unique @map("bet_no") @db.VarChar(64) userId BigInt @map("user_id") agentId BigInt? @map("agent_id") betType String @map("bet_type") @db.VarChar(20) stake Decimal @db.Decimal(18, 4) totalOdds Decimal? @map("total_odds") @db.Decimal(18, 6) potentialReturn Decimal? @map("potential_return") @db.Decimal(18, 4) actualReturn Decimal @default(0) @map("actual_return") @db.Decimal(18, 4) status String @default("PENDING") @db.VarChar(32) settlementStatus String? @map("settlement_status") @db.VarChar(32) currency String @default("USD") @db.VarChar(16) requestId String @map("request_id") @db.VarChar(128) placedAt DateTime @default(now()) @map("placed_at") settledAt DateTime? @map("settled_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id]) selections BetSelection[] @@unique([userId, requestId]) @@index([userId]) @@index([agentId]) @@index([status]) @@index([placedAt]) @@map("bets") } model BetSelection { id BigInt @id @default(autoincrement()) betId BigInt @map("bet_id") matchId BigInt? @map("match_id") marketId BigInt @map("market_id") selectionId BigInt @map("selection_id") marketType String @map("market_type") @db.VarChar(64) period String? @db.VarChar(16) selectionNameSnapshot String @map("selection_name_snapshot") @db.VarChar(255) handicapLine Decimal? @map("handicap_line") @db.Decimal(8, 2) totalLine Decimal? @map("total_line") @db.Decimal(8, 2) odds Decimal @db.Decimal(18, 6) oddsVersion BigInt @map("odds_version") resultStatus String? @map("result_status") @db.VarChar(32) effectiveOdds Decimal? @map("effective_odds") @db.Decimal(18, 6) sortOrder Int @default(0) @map("sort_order") createdAt DateTime @default(now()) @map("created_at") bet Bet @relation(fields: [betId], references: [id]) @@index([betId]) @@index([matchId]) @@map("bet_selections") } // ============ Settlement ============ model SettlementBatch { id BigInt @id @default(autoincrement()) matchId BigInt @map("match_id") batchNo String @unique @map("batch_no") @db.VarChar(64) htHomeScore Int? @map("ht_home_score") htAwayScore Int? @map("ht_away_score") ftHomeScore Int? @map("ft_home_score") ftAwayScore Int? @map("ft_away_score") status String @default("PREVIEW") @db.VarChar(20) totalBets Int @default(0) @map("total_bets") totalPayout Decimal @default(0) @map("total_payout") @db.Decimal(18, 4) totalRefund Decimal @default(0) @map("total_refund") @db.Decimal(18, 4) operatorId BigInt? @map("operator_id") confirmedAt DateTime? @map("confirmed_at") isResettle Boolean @default(false) @map("is_resettle") reason String? @db.VarChar(500) createdAt DateTime @default(now()) @map("created_at") match Match @relation(fields: [matchId], references: [id]) items SettlementItem[] @@index([matchId]) @@map("settlement_batches") } model SettlementItem { id BigInt @id @default(autoincrement()) batchId BigInt @map("batch_id") betId BigInt @map("bet_id") userId BigInt @map("user_id") result String @db.VarChar(32) payout Decimal @db.Decimal(18, 4) createdAt DateTime @default(now()) @map("created_at") batch SettlementBatch @relation(fields: [batchId], references: [id]) @@index([batchId]) @@index([betId]) @@map("settlement_items") } // ============ Cashback ============ model CashbackRule { id BigInt @id @default(autoincrement()) name String @db.VarChar(128) targetType String @map("target_type") @db.VarChar(32) targetId BigInt? @map("target_id") rate Decimal @db.Decimal(8, 4) marketType String? @map("market_type") @db.VarChar(64) isActive Boolean @default(true) @map("is_active") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@map("cashback_rules") } model CashbackBatch { id BigInt @id @default(autoincrement()) batchNo String @unique @map("batch_no") @db.VarChar(64) periodStart DateTime @map("period_start") periodEnd DateTime @map("period_end") status String @default("PREVIEW") @db.VarChar(20) totalAmount Decimal @default(0) @map("total_amount") @db.Decimal(18, 4) playerCount Int @default(0) @map("player_count") operatorId BigInt? @map("operator_id") confirmedAt DateTime? @map("confirmed_at") createdAt DateTime @default(now()) @map("created_at") items CashbackItem[] @@map("cashback_batches") } model CashbackItem { id BigInt @id @default(autoincrement()) batchId BigInt @map("batch_id") userId BigInt @map("user_id") effectiveStake Decimal @map("effective_stake") @db.Decimal(18, 4) rate Decimal @db.Decimal(8, 4) amount Decimal @db.Decimal(18, 4) createdAt DateTime @default(now()) @map("created_at") batch CashbackBatch @relation(fields: [batchId], references: [id]) @@index([batchId]) @@index([userId]) @@map("cashback_items") } // ============ Content & i18n ============ model Content { id BigInt @id @default(autoincrement()) contentType String @map("content_type") @db.VarChar(32) sortOrder Int @default(0) @map("sort_order") status String @default("DRAFT") @db.VarChar(20) linkType String? @map("link_type") @db.VarChar(32) linkTarget String? @map("link_target") @db.VarChar(500) startTime DateTime? @map("start_time") endTime DateTime? @map("end_time") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") translations ContentTranslation[] @@index([contentType, status]) @@map("contents") } model ContentTranslation { id BigInt @id @default(autoincrement()) contentId BigInt @map("content_id") locale String @db.VarChar(10) title String? @db.VarChar(255) body String? @db.Text imageUrl String? @map("image_url") @db.VarChar(500) content Content @relation(fields: [contentId], references: [id]) @@unique([contentId, locale]) @@map("content_translations") } model I18nMessage { id BigInt @id @default(autoincrement()) msgKey String @map("msg_key") @db.VarChar(128) locale String @db.VarChar(10) value String @db.Text createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@unique([msgKey, locale]) @@map("i18n_messages") } // ============ System Config & Audit ============ model SystemConfig { id BigInt @id @default(autoincrement()) configKey String @unique @map("config_key") @db.VarChar(128) configValue String @map("config_value") @db.Text description String? @db.VarChar(255) updatedAt DateTime @updatedAt @map("updated_at") @@map("system_configs") } model AuditLog { id BigInt @id @default(autoincrement()) operatorId BigInt? @map("operator_id") operatorType String @map("operator_type") @db.VarChar(20) action String @db.VarChar(128) module String @db.VarChar(64) targetType String? @map("target_type") @db.VarChar(32) targetId String? @map("target_id") @db.VarChar(64) beforeData String? @map("before_data") @db.Text afterData String? @map("after_data") @db.Text ipAddress String? @map("ip_address") @db.VarChar(45) userAgent String? @map("user_agent") @db.VarChar(500) createdAt DateTime @default(now()) @map("created_at") @@index([operatorId]) @@index([module]) @@index([createdAt]) @@map("audit_logs") }