Files
iddaai-be/mds/DATABASE_SCHEMA_FULL.md
fahricansecer 2f0b85a0c7
Deploy Iddaai Backend / build-and-deploy (push) Failing after 18s
first (part 2: other directories)
2026-04-16 15:11:25 +03:00

21 KiB
Raw Permalink Blame History

Suggest-Bet-BE — Veritabanı Şemas ve Proje Tam Referansı

Tarih: 2026-03-13 Google Gemini 3.1 Pro Deep Think ile çözüm üretimi için hazırlanmış kapsamlı referans dokümanıdır.


1. Proje Özeti

Suggest-Bet-BE, yapay zeka destekli bir spor bahis tahmin ve analiz platformu backend servisidir.

Katman Teknoloji Port
Backend API NestJS 11 (TypeScript) 3005
AI Engine Python FastAPI, XGBoost, LightGBM Ensemble 8000
Veritabanı PostgreSQL 16 + Prisma ORM 5432
Kuyruk/Cache BullMQ + Redis (opsiyonel) 6379
Auth JWT + Passport (Access 15dk + Refresh 7gün)
Scraping Axios + Cheerio (Mackolik.com)
AI Google Gemini API (yorum üretimi)

Temel Akış:

  1. Mackolik API'den canlı maç verisi çekilir (15dk cron)
  2. Kullanıcı maç seçer → AI Engine'e gönderilir
  3. V20+ model çoklu market tahmin paketi üretir (MS, OU, BTTS, DC, HT/FT)
  4. İsteğe bağlı akıllı kupon önerilir (5 strateji: SAFE, BALANCED, AGGRESSIVE, VALUE, MIRACLE)

2. Veritabanı Şeması (PostgreSQL — 27 Tablo, 6 Enum)

2.1 Enum Tanımları

-- Spor türleri
enum Sport { football, basketball }

-- Kullanıcı rolleri
enum UserRole { user, superadmin }

-- Abonelik durumu
enum SubscriptionStatus { free, active, expired }

-- Oyuncu pozisyonları (futbol)
enum PlayerPosition { goalkeeper, defender, midfielder, striker }

-- Maç olayı türleri
enum EventType { goal, card, substitute }

-- Maç pozisyonu (ev sahibi/deplasman)
enum MatchPosition { home, away }

2.2 Tablo Detayları


countries — Ülkeler (160 kayıt)

Kolon Tip Açıklama
id String PK Mackolik ülke ID
name String UNIQUE Ülke adı
flag_url String? Bayrak görseli URL
created_at DateTime

İlişkiler:leagues[]


leagues — Ligler (1,505 kayıt)

Kolon Tip Açıklama
id String PK Mackolik lig ID
name String Lig adı
country_id String? FK → countries Ülke
sport Sport football / basketball
competition_slug String? URL slug
code String? Kısa kod
logo_url String? Logo URL
created_at DateTime

Unique: (name, country_id, sport) İndeksler: sport, country_id İlişkiler:matches[], live_matches[]


teams — Takımlar (19,595 kayıt)

Kolon Tip Açıklama
id String PK Mackolik takım ID
name String Takım adı
slug String? URL slug
sport Sport football / basketball
logo_url String? Logo URL. CDN: https://file.mackolikfeeds.com/teams/{id}
created_at DateTime

İndeksler: sport, name İlişkiler: → home/away matches, participations, events, stats


players — Oyuncular (217,040 kayıt)

Kolon Tip Açıklama
id String PK Mackolik oyuncu ID
name String Oyuncu adı
slug String? UNIQUE URL slug
created_at DateTime

İndeksler: name İlişkiler: → participations, events (scorer, assist, out), stats


matches — Kalıcı Maç Kayıtları (236,859 kayıt, 100 MB)

Kolon Tip Açıklama
id String PK Mackolik maç ID
league_id String? FK → leagues Lig
home_team_id String? FK → teams Ev sahibi
away_team_id String? FK → teams Deplasman
sport Sport football / basketball
match_name String? "Galatasaray vs Fenerbahçe"
match_slug String? URL slug
mst_utc BigInt Maç zamanı (Unix ms)
status String? Durum
state String? postGame, preGame, live, etc.
score_home Int? Ev sahibi skor
score_away Int? Deplasman skor
ht_score_home Int? İlk yarı ev sahibi skor
ht_score_away Int? İlk yarı deplasman skor
winner String? Kazanan
iddaa_code String? İddaa maç kodu
created_at DateTime
updated_at DateTime

İndeksler: mst_utc DESC, sport, state, league_id, home_team_id, away_team_id, iddaa_code İlişkiler: → oddCategories, teamStats, playerParticipations, playerEvents, playerStats, officials, prediction, aiFeatures, couponItems


live_matches — Canlı/Yaklaşan Maçlar (82 kayıt, döngüsel)

Kolon Tip Açıklama
id String PK Mackolik maç ID
league_id String? FK → leagues Lig
home_team_id String? FK → teams Ev sahibi
away_team_id String? FK → teams Deplasman
sport String? football / basketball
match_name String? Maç adı
match_slug String? URL slug
mst_utc BigInt? Maç zamanı (Unix ms)
status String? Durum
state String? pre, live, post
substate String? Alt durum
score_home Int? Ev sahibi skor
score_away Int? Deplasman skor
updated_at DateTime Son güncelleme
odds Json? Tüm bahis oranları (JSON blob)
odds_updated_at DateTime? Oran güncelleme zamanı
referee_name String? Hakem adı
lineups Json? Kadro (JSON blob, home/away dizileri)
sidelined Json? Sakatlar/Cezalılar (JSON blob)

İndeksler: mst_utc, state Not: Maç bitince live_matchesmatches tablosuna migrate edilir (30dk cron).


odd_categories — Bahis Kategorileri (3,161,172 kayıt, 689 MB)

Kolon Tip Açıklama
db_id Int PK autoincrement
match_id String FK → matches Maç
category_json_id Int? Mackolik kategori ID
name String? "Maç Sonucu", "2,5 Alt/Üst", "Karşılıklı Gol" vb.
created_at DateTime

Unique: (match_id, name) İndeksler: match_id


odd_selections — Bahis Seçimleri ve Oranları (8,511,132 kayıt, 1 GB)

Kolon Tip Açıklama
db_id Int PK autoincrement
odd_category_db_id Int FK → odd_categories Kategori
name String? "1", "X", "2", "Alt", "Üst", "1-X", vb.
odd_value String? Oran değeri ("1.85", "3.04")
position String? Sıralama pozisyonu
sov Float?
state String?
created_at DateTime
updated_at DateTime

Unique: (odd_category_db_id, name) İndeksler: odd_category_db_id


odds_history — Oran Değişim Geçmişi (0 kayıt, henüz aktif değil)

Kolon Tip Açıklama
id BigInt PK autoincrement
selection_id Int FK → odd_selections Seçim
match_id String Maç ID
previous_value Float Önceki oran
new_value Float Yeni oran
bookmaker String? default "MACKOLIK"
change_time DateTime Değişim zamanı

İndeksler: (match_id, change_time), selection_id


match_team_stats — Takım İstatistikleri (310,991 kayıt, 91 MB)

Kolon Tip Açıklama
id Int PK autoincrement
match_id String FK → matches Maç
team_id String FK → teams Takım
Futbol Alanları:
possession_percentage Float? Topla oynama %
shots_on_target Int? İsabetli şut
shots_off_target Int? İsabetsiz şut
total_shots Int? Toplam şut
total_passes Int? Toplam pas
corners Int? Korner
fouls Int? Faul
offsides Int? Ofsayt
Basketbol Alanları:
points Int? Toplam sayı
rebounds Int? Ribaund
assists Int? Asist
fg_made / fg_attempted Int? Field goal
three_pt_made / three_pt_attempted Int? 3 sayı
ft_made / ft_attempted Int? Serbest atış
steals Int? Top çalma
blocks Int? Blok
turnovers Int? Top kaybı
q1_score ... q4_score, ot_score Int? Periyot skorları

Unique: (match_id, team_id)


match_player_participation — Oyuncu Kadro Katılımları (3,342,839 kayıt, 1 GB)

Kolon Tip Açıklama
id Int PK autoincrement
match_id String FK → matches Maç
player_id String FK → players Oyuncu
team_id String FK → teams Takım
position PlayerPosition? goalkeeper, defender, midfielder, striker
shirt_number Int? Forma numarası
is_starting Boolean default true İlk 11'de mi?
created_at DateTime

Unique: (match_id, player_id, team_id)


match_player_events — Maç Olayları (1,453,227 kayıt, 356 MB)

Kolon Tip Açıklama
id Int PK autoincrement
match_id String FK → matches Maç
player_id String FK → players Olay yapan oyuncu
team_id String FK → teams Takım
event_type EventType goal, card, substitute
event_subtype String? "yellow", "red", "penalty", vb.
time_minute String Dakika ("45+2")
time_seconds Int? Saniye
period_id Int? Periyot
assist_player_id String? FK → players Asist yapan oyuncu
score_after String? Olay sonrası skor ("1-0")
player_out_id String? FK → players Çıkan oyuncu (değişiklik için)
position MatchPosition? home / away

İndeksler: match_id, player_id, team_id, event_type, assist_player_id Dağılım: substitute: 787K, card: 409K, goal: 257K


match_player_stats — Oyuncu İstatistikleri (344,688 kayıt, basketbol odaklı)

Kolon Tip Açıklama
id Int PK autoincrement
match_id String FK → matches Maç
player_id String FK → players Oyuncu
team_id String FK → teams Takım
minutes String? Oynanan süre
points, rebounds, assists Int? Temel istatistikler
steals, blocks, turnovers, fouls Int? Detay istatistikler
fg_made/attempted, three_pt_made/attempted, ft_made/attempted Int? Şut istatistikleri

Unique: (match_id, player_id, team_id)


match_officials — Hakem Bilgileri (340,824 kayıt)

Kolon Tip Açıklama
id Int PK autoincrement
match_id String FK → matches Maç
name String Hakem adı
role_id Int FK → official_roles Hakem rolü

Unique: (match_id, name, role_id)


official_roles — Hakem Rolleri (5 kayıt)

id Rol
1 Orta Hakem
2 Yardımcı Hakem
3 4. Hakem
4 VAR
5 AVAR

match_ai_features — AI Feature Cache (279 kayıt)

Kolon Tip Açıklama
match_id String PK FK → matches Maç
home_elo Float default 1500 Ev sahibi ELO
away_elo Float default 1500 Deplasman ELO
home_form_score Float default 50 Ev sahibi form skoru
away_form_score Float default 50 Deplasman form skoru
missing_players_impact Float default 0 Eksik oyuncu etkisi
calculator_ver String default "v1.0" Hesaplama versiyonu
updated_at DateTime

predictions — AI Tahmin Cache (3 kayıt)

Kolon Tip Açıklama
match_id String PK FK → matches Maç
prediction_json Json Tam tahmin paketi (SingleMatchPredictionPackage)
created_at DateTime
updated_at DateTime TTL: 6 saat

ai_predictions_log — AI Tahmin Loglama (0 kayıt)

Kolon Tip Açıklama
id Int PK autoincrement
match_id String Maç ID
model_version String "v20plus.X"
recommended_bets Json? Önerilen bahisler
confidence_score Float? Güven skoru
is_resolved Boolean default false Sonuçlandı mı?
actual_result String? Gerçek sonuç
is_correct Boolean? Doğru mu?
accuracy_score Float? Doğruluk puanı

users — Kullanıcılar (1 kayıt)

Kolon Tip Açıklama
id UUID PK
email String UNIQUE
password_hash String bcrypt (12 rounds)
first_name String?
last_name String?
role UserRole default user user / superadmin
subscription_status SubscriptionStatus default free free / active / expired
subscription_expires_at DateTime?
encrypted_api_key String?
is_active Boolean default true
deleted_at DateTime? Soft delete

Limitler: Free: 10 analiz + 3 kupon/gün, Active: 50 analiz + 10 kupon/gün


user_coupons — Kullanıcı Kuponları (0 kayıt)

Kolon Tip Açıklama
id UUID PK
user_id String FK → users Kullanıcı
strategy String SAFE, BALANCED, AGGRESSIVE, VALUE, MIRACLE
total_odds Float Toplam oran
status String default "PENDING" PENDING, WON, LOST
is_public Boolean default false Herkes görebilir mi?

user_coupon_items — Kupon Bahisleri (0 kayıt)

Kolon Tip Açıklama
id Int PK autoincrement
coupon_id String FK → user_coupons Kupon
match_id String FK → matches Maç
selection String "MS 1", "2.5 Üst", "KG Var" vb.
odd_at_time Float Kayıt anındaki oran
is_correct Boolean? Sonuç

usage_limits, analyses, refresh_tokens, app_settings, translations

Bu tablolar standart destek tablolarıdır (kullanım limiti, analiz geçmişi, JWT refresh token, key-value ayarlar, çeviri verileri).


3. İlişki Diyagramı (ER)

Country 1──N League 1──N Match N──1 Team (home/away)
                              │
                              ├──N OddCategory 1──N OddSelection 1──N OddsHistory
                              ├──N MatchTeamStats N──1 Team
                              ├──N MatchPlayerParticipation N──1 Player, N──1 Team
                              ├──N MatchPlayerEvents N──1 Player (scorer, assist, out), N──1 Team
                              ├──N MatchPlayerStats N──1 Player, N──1 Team
                              ├──N MatchOfficial N──1 OfficialRole
                              ├──1 MatchAiFeature
                              └──1 Prediction

User 1──N Analysis
User 1──N UserCoupon 1──N UserCouponItem N──1 Match
User 1──1 UsageLimit
User 1──N RefreshToken

League 1──N LiveMatch N──1 Team (home/away)

4. Canlı Veritabanı İstatistikleri (2026-03-12)

Tablo Kayıt Boyut
odd_selections 8,511,132 1,070 MB
match_player_participation 3,342,839 1,077 MB
odd_categories 3,161,172 689 MB
match_player_events 1,453,227 356 MB
match_player_stats 344,688 120 MB
match_officials 340,824 75 MB
match_team_stats 310,991 91 MB
matches 236,859 100 MB
players 217,040 64 MB
teams 19,595 5.2 MB
leagues 1,505 760 KB
Toplam DB 3,658 MB

Spor Dağılımı

Spor Maç Lig Ort. Ev Skor Ort. Dep. Skor
Futbol 189,291 1,094 1.55 1.27
Basketbol 47,568 304 84.36 81.57

5. AI Engine V20+ Tahmin Çıktısı (SingleMatchPredictionPackage)

{
  "model_version": "v20plus.X",
  "match_info": { "match_id", "match_name", "home_team", "away_team", "league", "match_date_ms" },
  "data_quality": { "label": "HIGH|MEDIUM|LOW", "score": 0-1, "flags": [], "home_lineup_count", "away_lineup_count" },
  "risk": { "level": "LOW|MEDIUM|HIGH|EXTREME", "score", "is_surprise_risk", "surprise_type", "warnings" },
  "engine_breakdown": { "team", "player", "odds", "referee" },
  "main_pick": { "market", "pick", "probability", "confidence", "odds", "raw_confidence", "calibrated_confidence", "min_required_confidence", "edge", "play_score", "playable", "bet_grade": "A|B|C|PASS", "stake_units", "decision_reasons" },
  "value_pick": { same as main_pick, odds >= 1.60 },
  "bet_advice": { "playable", "suggested_stake_units", "reason" },
  "bet_summary": [{ "market", "pick", "raw_confidence", "calibrated_confidence", "bet_grade", "playable", "stake_units", "play_score", "reasons" }],
  "supporting_picks": [pick objects],
  "aggressive_pick": { "market", "pick", "probability", "confidence", "odds" },
  "scenario_top5": [{ "score", "prob" }],
  "score_prediction": { "ft", "ht", "xg_home", "xg_away", "xg_total" },
  "market_board": { "MS": {pick, confidence, probs}, "DC", "OU15", "OU25", "OU35", "BTTS", "HT", "HTFT": {probs: {"1/1": n, ...}} },
  "reasoning_factors": ["..."]
}

6. API Endpointleri (50 Toplam)

Auth (4) — Public

  • POST /api/auth/register — Kayıt ol
  • POST /api/auth/login — Giriş yap
  • POST /api/auth/refresh — Token yenile
  • POST /api/auth/logout — Çıkış yap

Matches (4) — Public

  • GET /api/matches — Maç listesi (paginated, matches tablosundan)
  • POST /api/matches/query — Gelişmiş maç sorgusu (sport, league, status, date, team filtresi, live_matches tablosundan)
  • GET /api/matches/leagues/active — Aktif ligler (cached 1dk)
  • GET /api/matches/:id — Maç detayı (kadro, stat, oran, olaylar)

Leagues (8) — Public

  • GET /api/leagues — Tüm ligler
  • GET /api/leagues/:id — Lig detay
  • GET /api/leagues/countries — Ülke listesi
  • GET /api/leagues/countries/:id — Ülke detay + ligleri
  • GET /api/leagues/teams/search — Takım arama
  • GET /api/leagues/teams/:id — Takım detay
  • GET /api/leagues/teams/:id/matches — Takım son maçları
  • GET /api/leagues/teams/h2h — Head-to-head

Coupon (6) — Mixed

  • POST /api/coupon/analyze-match — Tekil maç analizi (Public)
  • POST /api/coupon/daily-banko — Günün bankosu (Public)
  • POST /api/coupon/suggest — Akıllı kupon öner (Public)
  • POST /api/coupon/create — Kupon kaydet (Auth)
  • GET /api/coupon/my-stats — Kullanıcı istatistikleri (Auth)
  • GET /api/coupon/history — Kupon geçmişi (Auth)

Predictions (7) — Requires Redis

  • GET /api/predictions/health — AI Engine health
  • GET /api/predictions/upcoming — Yaklaşan tahminler
  • GET /api/predictions/value-bets — EV+ fırsatları
  • GET /api/predictions/history — Tahmin geçmişi
  • GET /api/predictions/:matchId — Tekil tahmin (cached 6 saat)
  • POST /api/predictions/generate — Tahmin üret
  • POST /api/predictions/smart-coupon — Smart Coupon

Admin (11) — Superadmin

  • Kullanıcı CRUD, rol/abonelik güncelleme, ayarlar, analytics

Analysis (2) — Auth

  • POST /api/analysis/analyze-matches — Çoklu maç analizi
  • GET /api/analysis/history — Analiz geçmişi

Users (5) — Auth

  • CRUD + restore

Health (3) — Public

  • GET /api/health — Readiness
  • GET /api/health/live — Liveness
  • GET /api/health/detail — Detaylı sağlık

7. Cron/Zamanlanmış Görevler

Görev Cron Açıklama
fetchLiveMatches() */15 * * * * Mackolik API'den futbol maçlarını çek → live_matches
fetchOddsForPreMatches() */15 * * * * Başlamamış maçların oranlarını çek
fetchBasketballMatches() Manuel Basketbol maçlarını çek
updateLiveScores() */15 * * * * Canlı maç skorlarını güncelle
finalizeFinishedMatches() */30 * * * * Bitmiş maçları live_matches → matches'e migrate et
resetUsageLimits() 0 3 * * * Günlük kullanım limitlerini sıfırla
cleanupOldData() 0 4 * * * 30 günlük AI logları sil
checkSubscriptions() 0 0 * * * Süresi dolmuş abonelikleri expired yap

8. Standart API Response Formatı

{
  "success": true,
  "status": 200,
  "message": "Success",
  "data": { ... },
  "errors": []
}

Not: Global Exception Filter tüm hataları HTTP 200 olarak döner, gerçek status body içindedir.