Files
nba-fan-hub/db.py
T

208 lines
6.4 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"""
数据库层:SQLite 统一存储结构化数据。
Schema 采用"运动-联赛-球队-球员-比赛"分层设计,天然支持扩展:
- 新增 CBA → sports 表加一行 basketballleagues 表加 CBA(复用全部代码)
- 新增足球 → sports 表加 footballleague 可加 英超/西甲,球员字段可扩展
- 新增人物角色 → persons.role 自由扩展(coach/agent/commentator/host/gm...
"""
import sqlite3
import threading
from config import DB_PATH
_SCHEMA = """
CREATE TABLE IF NOT EXISTS sports (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT UNIQUE NOT NULL, -- 'basketball' / 'football' ...
name TEXT NOT NULL, -- '篮球' / '足球'
name_en TEXT,
created_at TEXT DEFAULT (datetime('now','localtime'))
);
CREATE TABLE IF NOT EXISTS leagues (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sport_id INTEGER NOT NULL REFERENCES sports(id),
code TEXT UNIQUE NOT NULL, -- 'NBA' / 'CBA' / 'EPL'
name TEXT NOT NULL, -- '美国职业篮球联赛'
name_en TEXT,
country TEXT,
season TEXT, -- '2025-26'
created_at TEXT DEFAULT (datetime('now','localtime'))
);
CREATE TABLE IF NOT EXISTS teams (
id INTEGER PRIMARY KEY AUTOINCREMENT,
league_id INTEGER NOT NULL REFERENCES leagues(id),
code TEXT, -- 'LAL'
name TEXT NOT NULL, -- '洛杉矶湖人'
name_en TEXT, -- 'Los Angeles Lakers'
city TEXT,
arena TEXT,
founded INTEGER,
champion_count INTEGER,
head_coach TEXT,
logo_url TEXT,
intro TEXT,
created_at TEXT DEFAULT (datetime('now','localtime'))
);
CREATE TABLE IF NOT EXISTS players (
id INTEGER PRIMARY KEY AUTOINCREMENT,
team_id INTEGER REFERENCES teams(id),
name TEXT NOT NULL,
name_en TEXT,
position TEXT, -- PG/SG/SF/PF/C
number INTEGER,
height_cm INTEGER,
weight_kg INTEGER,
birth_date TEXT,
country TEXT,
draft_year INTEGER,
draft_pick INTEGER, -- 0 = 落选
salary_m REAL, -- 年薪(万美元)
season_pts REAL, season_reb REAL, season_ast REAL,
season_stl REAL, season_blk REAL, season_min REAL, -- 本赛季场均
career_pts REAL, career_reb REAL, career_ast REAL, -- 生涯场均
career_games INTEGER,
career_steals INTEGER, career_blocks INTEGER, -- 生涯总数
awards TEXT, -- 主要荣誉
bio TEXT,
created_at TEXT DEFAULT (datetime('now','localtime'))
);
CREATE TABLE IF NOT EXISTS games (
id INTEGER PRIMARY KEY AUTOINCREMENT,
league_id INTEGER NOT NULL REFERENCES leagues(id),
season TEXT,
round_name TEXT, -- 常规赛/季后赛首轮/总决赛...
game_time TEXT, -- '2026-06-05 09:00'
status TEXT, -- scheduled / finished / live
home_team_id INTEGER REFERENCES teams(id),
away_team_id INTEGER REFERENCES teams(id),
home_score INTEGER,
away_score INTEGER,
venue TEXT,
broadcast TEXT,
created_at TEXT DEFAULT (datetime('now','localtime'))
);
CREATE TABLE IF NOT EXISTS game_player_stats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
game_id INTEGER NOT NULL REFERENCES games(id),
player_id INTEGER NOT NULL REFERENCES players(id),
team_id INTEGER NOT NULL REFERENCES teams(id),
points INTEGER DEFAULT 0, rebounds INTEGER DEFAULT 0,
assists INTEGER DEFAULT 0, steals INTEGER DEFAULT 0,
blocks INTEGER DEFAULT 0, minutes INTEGER DEFAULT 0
);
CREATE TABLE IF NOT EXISTS standings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
league_id INTEGER NOT NULL REFERENCES leagues(id),
season TEXT,
team_id INTEGER NOT NULL REFERENCES teams(id),
wins INTEGER DEFAULT 0, losses INTEGER DEFAULT 0,
win_pct REAL DEFAULT 0,
conference TEXT, -- 西部/东部
rank INTEGER
);
CREATE TABLE IF NOT EXISTS news (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sport_id INTEGER NOT NULL REFERENCES sports(id),
league_id INTEGER REFERENCES leagues(id),
team_id INTEGER REFERENCES teams(id),
title TEXT NOT NULL,
content TEXT NOT NULL,
author TEXT,
source TEXT,
publish_time TEXT,
tags TEXT,
kind TEXT DEFAULT 'news', -- news / wiki(百科词条)
created_at TEXT DEFAULT (datetime('now','localtime'))
);
CREATE TABLE IF NOT EXISTS persons (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sport_id INTEGER NOT NULL REFERENCES sports(id),
league_id INTEGER REFERENCES leagues(id),
team_id INTEGER REFERENCES teams(id),
name TEXT NOT NULL,
name_en TEXT,
role TEXT, -- coach/agent/commentator/host/gm/legend...
role_cn TEXT, -- 教练/经纪人/评论员/主持人/总经理...
title TEXT,
bio TEXT,
achievements TEXT,
created_at TEXT DEFAULT (datetime('now','localtime'))
);
CREATE INDEX IF NOT EXISTS idx_players_team ON players(team_id);
CREATE INDEX IF NOT EXISTS idx_players_name ON players(name);
CREATE INDEX IF NOT EXISTS idx_games_time ON games(game_time);
CREATE INDEX IF NOT EXISTS idx_games_team ON games(home_team_id);
CREATE INDEX IF NOT EXISTS idx_news_time ON news(publish_time);
CREATE INDEX IF NOT EXISTS idx_persons_role ON persons(role);
"""
_lock = threading.Lock()
def get_conn():
conn = sqlite3.connect(DB_PATH, timeout=30)
conn.row_factory = sqlite3.Row
conn.execute("PRAGMA journal_mode=WAL")
conn.execute("PRAGMA foreign_keys=ON")
return conn
def init_db():
with _lock:
conn = get_conn()
try:
conn.executescript(_SCHEMA)
conn.commit()
finally:
conn.close()
def query(sql, args=()):
conn = get_conn()
try:
cur = conn.execute(sql, args)
return [dict(r) for r in cur.fetchall()]
finally:
conn.close()
def query_one(sql, args=()):
rows = query(sql, args)
return rows[0] if rows else None
def execute(sql, args=()):
conn = get_conn()
try:
cur = conn.execute(sql, args)
conn.commit()
return cur.lastrowid
finally:
conn.close()
def executemany(sql, rows):
conn = get_conn()
try:
conn.executemany(sql, rows)
conn.commit()
finally:
conn.close()
def table_count(table):
return query_one(f"SELECT COUNT(*) AS c FROM {table}")["c"]
def fuzzy(expr): # SQL LIKE 模糊匹配(转义 % _
return expr.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_")