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ış:
- Mackolik API'den canlı maç verisi çekilir (15dk cron)
- Kullanıcı maç seçer → AI Engine'e gönderilir
- V20+ model çoklu market tahmin paketi üretir (MS, OU, BTTS, DC, HT/FT)
- İ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ı
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_matches → matches 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)
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
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ı
Not: Global Exception Filter tüm hataları HTTP 200 olarak döner, gerçek status body içindedir.