"""SQLAlchemy ORM 模型:账号 / 设置 / 登录记录 / 运行日志 / 运行状态 / 快照。""" from sqlalchemy import Float, Index, Integer, String from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column from sqlalchemy.types import JSON class Base(DeclarativeBase): pass class Setting(Base): """系统配置(name -> JSON),目前只有 llm。""" __tablename__ = "settings" name: Mapped[str] = mapped_column(String, primary_key=True) value: Mapped[dict] = mapped_column(JSON, nullable=False) updated: Mapped[float] = mapped_column(Float, nullable=False) class Kv(Base): """通用缓存(任意 key -> JSON),例如 StudyClient 试出来的接口前缀。""" __tablename__ = "kv" key: Mapped[str] = mapped_column(String, primary_key=True) value: Mapped[dict] = mapped_column(JSON) updated: Mapped[float] = mapped_column(Float, nullable=False) class Account(Base): """账号 / 密码 / 会话 cookie / 课程缓存 / 勾选 / 开关。""" __tablename__ = "accounts" id: Mapped[str] = mapped_column(String, primary_key=True) username: Mapped[str] = mapped_column(String, default="") platform: Mapped[str] = mapped_column(String, default="") password: Mapped[str] = mapped_column(String, default="") note: Mapped[str] = mapped_column(String, default="") user: Mapped[str] = mapped_column(String, default="") cookies: Mapped[list] = mapped_column(JSON, default=list) courses: Mapped[list] = mapped_column(JSON, default=list) selected: Mapped[list] = mapped_column(JSON, default=list) status: Mapped[str] = mapped_column(String, default="idle") last_error: Mapped[str] = mapped_column(String, default="") last_error_at: Mapped[float] = mapped_column(Float, default=0) opt_quiz: Mapped[int] = mapped_column(Integer, default=0) opt_feedback: Mapped[int] = mapped_column(Integer, default=1) login_at: Mapped[float] = mapped_column(Float, default=0) created: Mapped[float] = mapped_column(Float, default=0) updated: Mapped[float] = mapped_column(Float, default=0) class LoginLog(Base): """每次登录尝试(平台原话)。""" __tablename__ = "login_log" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) ts: Mapped[str] = mapped_column(String) username: Mapped[str] = mapped_column(String) ok: Mapped[int] = mapped_column(Integer) code: Mapped[str] = mapped_column(String) elapsed: Mapped[float] = mapped_column(Float) stage: Mapped[str] = mapped_column(String) error: Mapped[str] = mapped_column(String) class RunLog(Base): """每个账号(key)的运行日志。""" __tablename__ = "run_log" __table_args__ = (Index("idx_run_log_key", "key", "id"),) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) key: Mapped[str] = mapped_column(String, nullable=False) ts: Mapped[float] = mapped_column(Float) line: Mapped[str] = mapped_column(String) class RunState(Base): """运行状态快照,进程重启后 /api/runs 还能看到上次跑到哪。""" __tablename__ = "run_state" key: Mapped[str] = mapped_column(String, primary_key=True) state: Mapped[dict] = mapped_column(JSON) updated: Mapped[float] = mapped_column(Float) class Snapshot(Base): """课程页原始 HTML(站点改版时定位选择器用)。""" __tablename__ = "snapshots" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) plan_id: Mapped[str] = mapped_column(String) project_id: Mapped[str] = mapped_column(String) url: Mapped[str] = mapped_column(String) html: Mapped[str] = mapped_column(String) created: Mapped[float] = mapped_column(Float)