Files
iddaai-be/prisma/schema.prisma
T
fahricansecer 21e05148c8
Deploy Iddaai Backend / build-and-deploy (push) Failing after 3m56s
feat: league tier system + retrained V25 models (48 quality leagues)
- Add LeagueTier DB model and Prisma schema
- Add league-tiers service (CRUD, sync, retrain trigger)
- Add league-tiers controller with admin API endpoints
- Add /v1/admin/retrain endpoint in AI engine (extract→train→reload pipeline)
- Retrain V25 Pro with 48 quality leagues (MS accuracy: 26.9%→51.4%)
- Update qualified_leagues.json (443→48 leagues)
- Include V25 model files in repo for Docker deployment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-20 21:57:15 +03:00

893 lines
37 KiB
Plaintext
Executable File
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"
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[]
leagueTier LeagueTier?
@@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")
}
// ─────────────────────────────────────────────────────────────
// League Tier System — Controls which leagues are used for
// model training, live data fetching, and predictions.
// Managed from admin panel. When a league is added/removed,
// qualified_leagues.json is auto-regenerated and model
// retraining is triggered.
// ─────────────────────────────────────────────────────────────
model LeagueTier {
id Int @id @default(autoincrement())
leagueId String @map("league_id")
tier Int @default(1) // 1=Elmas, 2=Altın, 3=Gümüş
isActive Boolean @default(true) @map("is_active")
addedBy String? @map("added_by") // admin user id
notes String? // e.g. "Margin 0.138, 828 maç"
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
league League @relation(fields: [leagueId], references: [id], onDelete: Cascade)
@@unique([leagueId])
@@index([tier, isActive])
@@map("league_tiers")
}
// ─────────────────────────────────────────────────────────────
// 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
}