DataFlowing / collector /core /database.py
stouchinglhh
collector: 同步采集工程 v1.2.1(全部 LFS 模式文件指针化,与 Space 现有文件合并)
d646dd7
Raw History Blame Contribute Delete
3.75 kB
"""
SQLite 数据库管理器 —— 线程安全的单例模式,管理录制历史记录表。
"""
import sqlite3
import os
import threading
from config.settings import DB_PATH, DATA_DIR
class Database:
"""简单的 SQLite 封装,含建表逻辑和线程本地连接。"""
_local = threading.local()
def __init__(self, db_path: str = DB_PATH):
self._db_path = db_path
@property
def conn(self) -> sqlite3.Connection:
"""获取当前线程的数据库连接(自动创建)。"""
if not hasattr(self._local, "conn") or self._local.conn is None:
os.makedirs(os.path.dirname(self._db_path), exist_ok=True)
c = sqlite3.connect(self._db_path)
c.row_factory = sqlite3.Row
c.execute("PRAGMA journal_mode=WAL")
c.execute("PRAGMA foreign_keys=ON")
self._local.conn = c
return self._local.conn
def close(self):
"""关闭当前线程的数据库连接。"""
if hasattr(self._local, "conn") and self._local.conn:
self._local.conn.close()
self._local.conn = None
# ── 建表 SQL ──────────────────────────────────────
SCHEMA_SQL = """
CREATE TABLE IF NOT EXISTS recording (
id TEXT PRIMARY KEY,
camera_index INTEGER NOT NULL,
camera_name TEXT NOT NULL,
file_path TEXT,
episode_index INTEGER DEFAULT 0, -- v1.1.0 池化:任务目录内的全局 episode 序号
file_size_mb REAL DEFAULT 0,
duration_sec REAL DEFAULT 0,
resolution_w INTEGER DEFAULT 0,
resolution_h INTEGER DEFAULT 0,
status TEXT NOT NULL DEFAULT 'completed', -- completed | uploaded | aborted | deleted | uploaded_deleted
started_at TEXT NOT NULL,
finished_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_recording_camera ON recording(camera_index);
CREATE INDEX IF NOT EXISTS idx_recording_date ON recording(started_at);
CREATE TABLE IF NOT EXISTS upload_task (
id TEXT PRIMARY KEY,
session_path TEXT NOT NULL,
session_name TEXT NOT NULL,
episode_index INTEGER DEFAULT 0, -- v1.1.0 池化:全局 episode 序号
status TEXT NOT NULL DEFAULT 'pending', -- pending | uploading | completed | failed | skipped
progress REAL DEFAULT 0.0,
retry_count INTEGER DEFAULT 0,
server_url TEXT NOT NULL,
server_session_id TEXT DEFAULT '',
error_message TEXT DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_upload_status ON upload_task(status);
CREATE INDEX IF NOT EXISTS idx_upload_session ON upload_task(session_path);
"""
def init_schema(self):
"""执行建表语句,并做既有库的列迁移(向后兼容)。"""
self.conn.executescript(self.SCHEMA_SQL)
# CREATE TABLE IF NOT EXISTS 不给既有表加列,需 ALTER 迁移
self._ensure_column("recording", "episode_index", "INTEGER DEFAULT 0")
self._ensure_column("upload_task", "episode_index", "INTEGER DEFAULT 0")
self.conn.commit()
def _ensure_column(self, table: str, column: str, decl: str):
"""表缺列时 ALTER TABLE ADD COLUMN(幂等)。"""
cols = {r[1] for r in self.conn.execute(f"PRAGMA table_info({table})")}
if column not in cols:
self.conn.execute(
f"ALTER TABLE {table} ADD COLUMN {column} {decl}")
# 模块级单例
db = Database()