Files
iddaai-be/prisma/schema.prisma
T
fahricansecer 9481ad7094
Deploy Iddaai Backend / build-and-deploy (push) Successful in 42s
changes
2026-05-20 10:10:28 +03:00

867 lines
36 KiB
Plaintext
Executable File

generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "linux-musl-arm64-openssl-3.0.x", "linux-musl-openssl-3.0.x"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// ─────────────────────────────────────────────────────────────
// Domain Models — Sports Data
// ─────────────────────────────────────────────────────────────
model Country {
id String @id
name String @unique
flagUrl String? @map("flag_url")
createdAt DateTime @default(now()) @map("created_at")
leagues League[]
@@map("countries")
}
model League {
id String @id
name String
countryId String? @map("country_id")
sport Sport
competitionSlug String? @map("competition_slug")
code String?
logoUrl String? @map("logo_url")
sortOrder Int? @map("sort_order")
createdAt DateTime @default(now()) @map("created_at")
country Country? @relation(fields: [countryId], references: [id])
liveMatches LiveMatch[]
matches Match[]
@@unique([name, countryId, sport])
@@index([sport])
@@index([countryId])
@@map("leagues")
}
model Team {
id String @id
name String
slug String?
sport Sport
logoUrl String? @map("logo_url")
createdAt DateTime @default(now()) @map("created_at")
awayMatchesLive LiveMatch[] @relation("AwayTeamLive")
homeMatchesLive LiveMatch[] @relation("HomeTeamLive")
playerEvents MatchPlayerEvents[]
playerParticipations MatchPlayerParticipation[]
basketballPlayerStats BasketballPlayerStats[]
footballTeamStats FootballTeamStats[]
basketballTeamStats BasketballTeamStats[]
awayMatches Match[] @relation("AwayTeam")
homeMatches Match[] @relation("HomeTeam")
eloRating TeamEloRating?
@@index([name])
@@index([sport])
@@map("teams")
}
model Player {
id String @id
name String
slug String? @unique
createdAt DateTime @default(now()) @map("created_at")
assistEvents MatchPlayerEvents[] @relation("AssistPlayer")
playerEvents MatchPlayerEvents[] @relation("EventPlayer")
substitutedOutEvents MatchPlayerEvents[] @relation("SubstitutedOut")
participations MatchPlayerParticipation[]
playerStats BasketballPlayerStats[]
@@index([name])
@@map("players")
}
model Match {
id String @id
leagueId String? @map("league_id")
homeTeamId String? @map("home_team_id")
awayTeamId String? @map("away_team_id")
sport Sport
matchName String? @map("match_name")
matchSlug String? @map("match_slug")
mstUtc BigInt @map("mst_utc")
status String?
state String?
scoreHome Int? @map("score_home")
scoreAway Int? @map("score_away")
htScoreHome Int? @map("ht_score_home")
htScoreAway Int? @map("ht_score_away")
winner String?
iddaaCode String? @map("iddaa_code")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
footballAiFeatures FootballAiFeature?
basketballAiFeatures BasketballAiFeature?
officials MatchOfficial[]
playerEvents MatchPlayerEvents[]
playerParticipations MatchPlayerParticipation[]
basketballPlayerStats BasketballPlayerStats[]
footballTeamStats FootballTeamStats[]
basketballTeamStats BasketballTeamStats[]
awayTeam Team? @relation("AwayTeam", fields: [awayTeamId], references: [id])
homeTeam Team? @relation("HomeTeam", fields: [homeTeamId], references: [id])
league League? @relation(fields: [leagueId], references: [id])
oddCategories OddCategory[]
prediction Prediction?
predictionOutcomes PredictionOutcome[]
couponItems UserCouponItem[]
@@index([awayTeamId])
@@index([homeTeamId])
@@index([iddaaCode])
@@index([leagueId])
@@index([mstUtc(sort: Desc)])
@@index([sport])
@@index([state])
@@index([status, mstUtc(sort: Desc)])
@@map("matches")
}
model LiveMatch {
id String @id
leagueId String? @map("league_id")
homeTeamId String? @map("home_team_id")
awayTeamId String? @map("away_team_id")
sport String?
matchName String? @map("match_name")
matchSlug String? @map("match_slug")
mstUtc BigInt? @map("mst_utc")
status String?
state String?
substate String?
scoreHome Int? @map("score_home")
scoreAway Int? @map("score_away")
htScoreHome Int? @map("ht_score_home")
htScoreAway Int? @map("ht_score_away")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
odds Json?
oddsUpdatedAt DateTime? @map("odds_updated_at")
refereeName String? @map("referee_name")
lineups Json?
sidelined Json?
awayTeam Team? @relation("AwayTeamLive", fields: [awayTeamId], references: [id])
homeTeam Team? @relation("HomeTeamLive", fields: [homeTeamId], references: [id])
league League? @relation(fields: [leagueId], references: [id])
@@index([mstUtc])
@@index([state])
@@map("live_matches")
}
// ─────────────────────────────────────────────────────────────
// Match Details — Stats, Events, Participation, Officials
// ─────────────────────────────────────────────────────────────
model FootballAiFeature {
matchId String @id @map("match_id")
homeElo Float @default(1500.0) @map("home_elo")
awayElo Float @default(1500.0) @map("away_elo")
homeHomeElo Float @default(1500.0) @map("home_home_elo")
awayAwayElo Float @default(1500.0) @map("away_away_elo")
homeFormElo Float @default(1500.0) @map("home_form_elo")
awayFormElo Float @default(1500.0) @map("away_form_elo")
eloDiff Float @default(0.0) @map("elo_diff")
homeFormScore Float @default(50.0) @map("home_form_score")
awayFormScore Float @default(50.0) @map("away_form_score")
homeGoalsAvg5 Float @default(0.0) @map("home_goals_avg_5")
awayGoalsAvg5 Float @default(0.0) @map("away_goals_avg_5")
homeConcededAvg5 Float @default(0.0) @map("home_conceded_avg_5")
awayConcededAvg5 Float @default(0.0) @map("away_conceded_avg_5")
homeCleanSheetRate Float @default(0.0) @map("home_clean_sheet_rate")
awayCleanSheetRate Float @default(0.0) @map("away_clean_sheet_rate")
homeScoringRate Float @default(0.0) @map("home_scoring_rate")
awayScoringRate Float @default(0.0) @map("away_scoring_rate")
homeWinStreak Int @default(0) @map("home_win_streak")
awayWinStreak Int @default(0) @map("away_win_streak")
impliedHome Float @default(0.33) @map("implied_home")
impliedDraw Float @default(0.33) @map("implied_draw")
impliedAway Float @default(0.33) @map("implied_away")
impliedOver25 Float @default(0.5) @map("implied_over25")
impliedBttsYes Float @default(0.5) @map("implied_btts_yes")
oddsOverround Float @default(0.0) @map("odds_overround")
homeAvgPossession Float @default(50.0) @map("home_avg_possession")
awayAvgPossession Float @default(50.0) @map("away_avg_possession")
homeAvgShotsOnTarget Float @default(0.0) @map("home_avg_shots_on_target")
awayAvgShotsOnTarget Float @default(0.0) @map("away_avg_shots_on_target")
homeShotConversion Float @default(0.0) @map("home_shot_conversion")
awayShotConversion Float @default(0.0) @map("away_shot_conversion")
homeAvgCorners Float @default(0.0) @map("home_avg_corners")
awayAvgCorners Float @default(0.0) @map("away_avg_corners")
h2hTotal Int @default(0) @map("h2h_total")
h2hHomeWinRate Float @default(0.0) @map("h2h_home_win_rate")
h2hAvgGoals Float @default(0.0) @map("h2h_avg_goals")
h2hOver25Rate Float @default(0.0) @map("h2h_over25_rate")
h2hBttsRate Float @default(0.0) @map("h2h_btts_rate")
refereeAvgCards Float @default(0.0) @map("referee_avg_cards")
refereeHomeBias Float @default(0.0) @map("referee_home_bias")
refereeAvgGoals Float @default(0.0) @map("referee_avg_goals")
leagueAvgGoals Float @default(0.0) @map("league_avg_goals")
leagueHomeWinPct Float @default(0.0) @map("league_home_win_pct")
leagueOver25Pct Float @default(0.0) @map("league_over25_pct")
missingPlayersImpact Float @default(0.0) @map("missing_players_impact")
oddsMovementHome Float? @map("odds_movement_home")
oddsMovementDraw Float? @map("odds_movement_draw")
oddsMovementAway Float? @map("odds_movement_away")
oddsMovementO25 Float? @map("odds_movement_o25")
oddsMovementBtts Float? @map("odds_movement_btts")
oddsSharpness Float? @map("odds_sharpness")
calculatorVer String @default("v2.0") @map("calculator_ver")
updatedAt DateTime @updatedAt @map("updated_at")
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
@@map("football_ai_features")
}
model BasketballAiFeature {
matchId String @id @map("match_id")
homeElo Float @default(1500.0) @map("home_elo")
awayElo Float @default(1500.0) @map("away_elo")
homeHomeElo Float @default(1500.0) @map("home_home_elo")
awayAwayElo Float @default(1500.0) @map("away_away_elo")
homeFormElo Float @default(1500.0) @map("home_form_elo")
awayFormElo Float @default(1500.0) @map("away_form_elo")
eloDiff Float @default(0.0) @map("elo_diff")
homeFormScore Float @default(50.0) @map("home_form_score")
awayFormScore Float @default(50.0) @map("away_form_score")
homePtsAvg5 Float @default(0.0) @map("home_pts_avg_5")
awayPtsAvg5 Float @default(0.0) @map("away_pts_avg_5")
homeConcededAvg5 Float @default(0.0) @map("home_conceded_avg_5")
awayConcededAvg5 Float @default(0.0) @map("away_conceded_avg_5")
homeWinStreak Int @default(0) @map("home_win_streak")
awayWinStreak Int @default(0) @map("away_win_streak")
impliedHome Float @default(0.5) @map("implied_home")
impliedAway Float @default(0.5) @map("implied_away")
impliedOverTotal Float @default(0.5) @map("implied_over_total")
impliedSpreadHome Float @default(0.5) @map("implied_spread_home")
oddsOverround Float @default(0.0) @map("odds_overround")
homeAvgPts Float @default(0.0) @map("home_avg_pts")
awayAvgPts Float @default(0.0) @map("away_avg_pts")
homeAvgRebounds Float @default(0.0) @map("home_avg_rebounds")
awayAvgRebounds Float @default(0.0) @map("away_avg_rebounds")
homeFgPct Float @default(0.0) @map("home_fg_pct")
awayFgPct Float @default(0.0) @map("away_fg_pct")
homeAvgThreePtMade Float @default(0.0) @map("home_avg_three_pt_made")
awayAvgThreePtMade Float @default(0.0) @map("away_avg_three_pt_made")
homeAvgTurnovers Float @default(0.0) @map("home_avg_turnovers")
awayAvgTurnovers Float @default(0.0) @map("away_avg_turnovers")
h2hTotal Int @default(0) @map("h2h_total")
h2hHomeWinRate Float @default(0.0) @map("h2h_home_win_rate")
h2hAvgPts Float @default(0.0) @map("h2h_avg_pts")
h2hAvgMargin Float @default(0.0) @map("h2h_avg_margin")
missingPlayersImpact Float @default(0.0) @map("missing_players_impact")
calculatorVer String @default("v2.0") @map("calculator_ver")
updatedAt DateTime @updatedAt @map("updated_at")
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
@@map("basketball_ai_features")
}
model TeamEloRating {
teamId String @id @map("team_id")
overallElo Float @default(1500.0) @map("overall_elo")
homeElo Float @default(1500.0) @map("home_elo")
awayElo Float @default(1500.0) @map("away_elo")
formElo Float @default(1500.0) @map("form_elo")
matchesPlayed Int @default(0) @map("matches_played")
recentForm String @default("") @map("recent_form")
updatedAt DateTime @updatedAt @map("updated_at")
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
@@map("team_elo_ratings")
}
model MatchPlayerEvents {
id Int @id @default(autoincrement())
matchId String @map("match_id")
playerId String @map("player_id")
teamId String @map("team_id")
eventType EventType @map("event_type")
eventSubtype String? @map("event_subtype")
timeMinute String @map("time_minute")
timeSeconds Int? @map("time_seconds")
periodId Int? @map("period_id")
assistPlayerId String? @map("assist_player_id")
scoreAfter String? @map("score_after")
playerOutId String? @map("player_out_id")
position MatchPosition?
createdAt DateTime @default(now()) @map("created_at")
assistPlayer Player? @relation("AssistPlayer", fields: [assistPlayerId], references: [id])
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
player Player @relation("EventPlayer", fields: [playerId], references: [id], onDelete: Cascade)
substitutedOut Player? @relation("SubstitutedOut", fields: [playerOutId], references: [id])
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
@@index([assistPlayerId])
@@index([eventType])
@@index([matchId])
@@index([playerId])
@@index([teamId])
@@map("match_player_events")
}
model MatchPlayerParticipation {
id Int @id @default(autoincrement())
matchId String @map("match_id")
playerId String @map("player_id")
teamId String @map("team_id")
position PlayerPosition?
shirtNumber Int? @map("shirt_number")
isStarting Boolean @default(true) @map("is_starting")
createdAt DateTime @default(now()) @map("created_at")
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
@@unique([matchId, playerId, teamId])
@@index([matchId])
@@index([playerId])
@@index([teamId])
@@map("match_player_participation")
}
model BasketballPlayerStats {
id Int @id @default(autoincrement())
matchId String @map("match_id")
playerId String @map("player_id")
teamId String @map("team_id")
minutes String?
points Int?
rebounds Int?
assists Int?
steals Int?
blocks Int?
turnovers Int?
fgMade Int? @map("fg_made")
fgAttempted Int? @map("fg_attempted")
threePtMade Int? @map("three_pt_made")
threePtAttempted Int? @map("three_pt_attempted")
ftMade Int? @map("ft_made")
ftAttempted Int? @map("ft_attempted")
fouls Int?
createdAt DateTime @default(now()) @map("created_at")
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
@@unique([matchId, playerId, teamId])
@@index([matchId])
@@map("basketball_player_stats")
}
model FootballTeamStats {
id Int @id @default(autoincrement())
matchId String @map("match_id")
teamId String @map("team_id")
possessionPercentage Float? @map("possession_percentage")
shotsOnTarget Int? @map("shots_on_target")
shotsOffTarget Int? @map("shots_off_target")
totalShots Int? @map("total_shots")
totalPasses Int? @map("total_passes")
corners Int?
fouls Int?
offsides Int?
createdAt DateTime @default(now()) @map("created_at")
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
@@unique([matchId, teamId])
@@index([matchId])
@@index([teamId])
@@map("football_team_stats")
}
model BasketballTeamStats {
id Int @id @default(autoincrement())
matchId String @map("match_id")
teamId String @map("team_id")
points Int?
rebounds Int?
assists Int?
fgMade Int? @map("fg_made")
fgAttempted Int? @map("fg_attempted")
threePtMade Int? @map("three_pt_made")
threePtAttempted Int? @map("three_pt_attempted")
ftMade Int? @map("ft_made")
ftAttempted Int? @map("ft_attempted")
steals Int?
blocks Int?
turnovers Int?
fouls Int?
q1Score Int? @map("q1_score")
q2Score Int? @map("q2_score")
q3Score Int? @map("q3_score")
q4Score Int? @map("q4_score")
otScore Int? @map("ot_score")
createdAt DateTime @default(now()) @map("created_at")
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
@@unique([matchId, teamId])
@@index([matchId])
@@index([teamId])
@@map("basketball_team_stats")
}
model MatchOfficial {
id Int @id @default(autoincrement())
matchId String @map("match_id")
name String
roleId Int @map("role_id")
createdAt DateTime @default(now()) @map("created_at")
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
role OfficialRole @relation(fields: [roleId], references: [id])
@@unique([matchId, name, roleId])
@@index([matchId])
@@index([roleId])
@@map("match_officials")
}
model OfficialRole {
id Int @id @default(autoincrement())
name String @unique
officials MatchOfficial[]
@@map("official_roles")
}
// ─────────────────────────────────────────────────────────────
// Odds & Predictions
// ─────────────────────────────────────────────────────────────
model OddCategory {
dbId Int @id @default(autoincrement()) @map("db_id")
matchId String @map("match_id")
categoryJsonId Int? @map("category_json_id")
name String?
sport Sport?
createdAt DateTime @default(now()) @map("created_at")
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
selections OddSelection[]
@@unique([matchId, name])
@@index([matchId])
@@index([sport])
@@map("odd_categories")
}
model OddSelection {
dbId Int @id @default(autoincrement()) @map("db_id")
categoryId Int @map("odd_category_db_id")
name String?
oddValue String? @map("odd_value")
openingValue String? @map("opening_value")
position String?
sov Float?
state String?
sport Sport?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @map("updated_at")
category OddCategory @relation(fields: [categoryId], references: [dbId], onDelete: Cascade)
history OddsHistory[]
@@unique([categoryId, name])
@@index([categoryId])
@@index([sport])
@@map("odd_selections")
}
model OddsHistory {
id BigInt @id @default(autoincrement())
selectionId Int @map("selection_id")
matchId String @map("match_id")
previousValue Float @map("previous_value")
newValue Float @map("new_value")
bookmaker String? @default("MACKOLIK")
changeTime DateTime @default(now()) @map("change_time")
selection OddSelection @relation(fields: [selectionId], references: [dbId], onDelete: Cascade)
@@index([matchId, changeTime])
@@index([selectionId])
@@map("odds_history")
}
model Prediction {
matchId String @id @map("match_id")
predictionJson Json @map("prediction_json")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
@@map("predictions")
}
model PredictionRun {
id BigInt @id @default(autoincrement())
matchId String @map("match_id")
engineVersion String @map("engine_version")
decisionTraceId String? @map("decision_trace_id")
generatedAt DateTime @default(now()) @map("generated_at")
oddsSnapshot Json? @map("odds_snapshot")
payloadSummary Json @map("payload_summary")
eventualOutcome String? @map("eventual_outcome")
unitProfit Float? @map("unit_profit")
@@index([matchId, generatedAt(sort: Desc)])
@@index([engineVersion, generatedAt(sort: Desc)])
@@map("prediction_runs")
}
model PredictionOutcome {
id BigInt @id @default(autoincrement())
matchId String @map("match_id")
market String
pick String
probability Float
confidence Float
odds Float?
playable Boolean @default(false)
engineVersion String @map("engine_version")
generatedAt DateTime @default(now()) @map("generated_at")
resolved Boolean @default(false)
hit Boolean?
resolvedAt DateTime? @map("resolved_at")
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
@@unique([matchId, market, engineVersion])
@@index([market, resolvedAt(sort: Desc)])
@@index([resolved, generatedAt])
@@index([playable, resolved])
@@map("prediction_outcomes")
}
model AiPredictionsLog {
id Int @id @default(autoincrement())
matchId String @map("match_id")
modelVersion String @map("model_version")
recommendedBets Json? @map("recommended_bets")
confidenceScore Float? @map("confidence_score")
createdAt DateTime @default(now()) @map("created_at")
isResolved Boolean @default(false) @map("is_resolved")
actualResult String? @map("actual_result")
isCorrect Boolean? @map("is_correct")
accuracyScore Float? @map("accuracy_score")
@@index([matchId])
@@index([createdAt(sort: Desc)])
@@map("ai_predictions_log")
}
// ─────────────────────────────────────────────────────────────
// User Domain — Auth, Coupons, Usage
// ─────────────────────────────────────────────────────────────
model User {
id String @id @default(uuid())
email String @unique
passwordHash String @map("password_hash")
firstName String? @map("first_name")
lastName String? @map("last_name")
role UserRole @default(user)
subscriptionStatus SubscriptionStatus @default(free) @map("subscription_status")
subscriptionExpiresAt DateTime? @map("subscription_expires_at")
encryptedApiKey String? @map("encrypted_api_key")
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")
analyses Analysis[]
refreshTokens RefreshToken[]
usageLimit UsageLimit?
subscription Subscription?
coupons UserCoupon[]
totoCoupons TotoCoupon[]
@@index([email])
@@index([subscriptionStatus, subscriptionExpiresAt])
@@map("users")
}
model Subscription {
id String @id @default(uuid())
userId String @unique @map("user_id")
paddleSubscriptionId String? @unique @map("paddle_subscription_id")
paddleCustomerId String? @map("paddle_customer_id")
plan SubscriptionStatus @default(free)
billingInterval BillingInterval? @map("billing_interval")
currentPeriodStart DateTime? @map("current_period_start")
currentPeriodEnd DateTime? @map("current_period_end")
cancelledAt DateTime? @map("cancelled_at")
cancelEffectiveDate DateTime? @map("cancel_effective_date")
paddlePriceId String? @map("paddle_price_id")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([paddleSubscriptionId])
@@index([paddleCustomerId])
@@map("subscriptions")
}
model RefreshToken {
id String @id @default(uuid())
token String @unique
userId String @map("user_id")
expiresAt DateTime @map("expires_at")
createdAt DateTime @default(now()) @map("created_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([token])
@@index([userId])
@@map("refresh_tokens")
}
model UsageLimit {
id Int @id @default(autoincrement())
userId String @unique @map("user_id")
analysisCount Int @default(0) @map("analysis_count")
couponCount Int @default(0) @map("coupon_count")
maxAnalyses Int @default(3) @map("max_analyses")
maxCoupons Int @default(1) @map("max_coupons")
lastResetDate DateTime @map("last_reset_date") @db.Date
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([lastResetDate])
@@map("usage_limits")
}
model Analysis {
id Int @id @default(autoincrement())
userId String @map("user_id")
matchIds String @map("match_ids")
analysisResultJson String @map("analysis_result_json")
isDeleted Boolean @default(false) @map("is_deleted")
createdAt DateTime @default(now()) @map("created_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([createdAt(sort: Desc)])
@@map("analyses")
}
model UserCoupon {
id String @id @default(uuid())
userId String @map("user_id")
strategy String
totalOdds Float @map("total_odds")
status String @default("PENDING")
isPublic Boolean @default(false)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
couponItems UserCouponItem[]
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([status])
@@map("user_coupons")
}
model UserCouponItem {
id Int @id @default(autoincrement())
couponId String @map("coupon_id")
matchId String @map("match_id")
selection String
oddAtTime Float @map("odd_at_time")
isCorrect Boolean? @map("is_correct")
coupon UserCoupon @relation(fields: [couponId], references: [id], onDelete: Cascade)
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
@@index([couponId])
@@map("user_coupon_items")
}
// ─────────────────────────────────────────────────────────────
// Spor Toto Domain
// ─────────────────────────────────────────────────────────────
model TotoBulletin {
id String @id @default(uuid())
gameCycleNo Int @unique @map("game_cycle_no")
programName String? @map("program_name")
season String?
status TotoBulletinStatus @default(UPCOMING)
payinBeginDate DateTime? @map("payin_begin_date")
payinEndDate DateTime? @map("payin_end_date")
poolTotal Float? @map("pool_total")
rolloverAmount Float? @map("rollover_amount")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
matches TotoBulletinMatch[]
result TotoResult?
coupons TotoCoupon[]
@@index([status])
@@map("toto_bulletins")
}
model TotoBulletinMatch {
id Int @id @default(autoincrement())
bulletinId String @map("bulletin_id")
matchOrder Int @map("match_order")
homeTeamName String @map("home_team_name")
awayTeamName String @map("away_team_name")
leagueName String? @map("league_name")
kickoffTime DateTime? @map("kickoff_time")
matchId String? @map("match_id")
result TotoMatchResult?
isCancelled Boolean @default(false) @map("is_cancelled")
drawResult TotoMatchResult? @map("draw_result")
bulletin TotoBulletin @relation(fields: [bulletinId], references: [id], onDelete: Cascade)
@@unique([bulletinId, matchOrder])
@@index([bulletinId])
@@index([matchId])
@@map("toto_bulletin_matches")
}
model TotoResult {
id String @id @default(uuid())
bulletinId String @unique @map("bulletin_id")
winners15 Int @default(0) @map("winners_15")
prize15 Float? @map("prize_15")
winners14 Int @default(0) @map("winners_14")
prize14 Float? @map("prize_14")
winners13 Int @default(0) @map("winners_13")
prize13 Float? @map("prize_13")
winners12 Int @default(0) @map("winners_12")
prize12 Float? @map("prize_12")
rolloverNext Float? @map("rollover_next")
poolDistributed Float? @map("pool_distributed")
createdAt DateTime @default(now()) @map("created_at")
bulletin TotoBulletin @relation(fields: [bulletinId], references: [id], onDelete: Cascade)
@@map("toto_results")
}
model TotoCoupon {
id String @id @default(uuid())
userId String @map("user_id")
bulletinId String @map("bulletin_id")
strategy String?
columnCount Int @map("column_count")
totalCost Float @map("total_cost")
status String @default("PENDING")
totalPrize Float? @map("total_prize")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
columns TotoColumn[]
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
bulletin TotoBulletin @relation(fields: [bulletinId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([bulletinId])
@@index([status])
@@map("toto_coupons")
}
model TotoColumn {
id Int @id @default(autoincrement())
couponId String @map("coupon_id")
predictions String @db.VarChar(15)
correctCount Int? @map("correct_count")
prizeAmount Float? @map("prize_amount")
coupon TotoCoupon @relation(fields: [couponId], references: [id], onDelete: Cascade)
@@index([couponId])
@@index([correctCount])
@@map("toto_columns")
}
// ─────────────────────────────────────────────────────────────
// System & i18n
// ─────────────────────────────────────────────────────────────
model AppSetting {
key String @id
value String?
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
@@map("app_settings")
}
model Translation {
id String @id @default(uuid())
key String
locale String
value String
namespace String @default("common")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@unique([key, locale, namespace])
@@index([key])
@@index([locale])
@@index([namespace])
@@map("translations")
}
// ─────────────────────────────────────────────────────────────
// Enums
// ─────────────────────────────────────────────────────────────
enum Sport {
football
basketball
}
enum UserRole {
user
superadmin
}
enum SubscriptionStatus {
free
plus
premium
past_due
cancelled
}
enum BillingInterval {
monthly
yearly
}
enum PlayerPosition {
goalkeeper
defender
midfielder
striker
}
enum EventType {
goal
card
substitute
}
enum MatchPosition {
home
away
}
enum TotoBulletinStatus {
UPCOMING
ACTIVE
COMPLETED
CANCELLED
}
enum TotoMatchResult {
HOME_WIN
DRAW
AWAY_WIN
}