feat: 故障推送 / 公需课自动重学 / 插件解耦,发版 v0.0.8

体检修复:启动日志不再打印 AUTH_TOKEN、密钥可清空、推送失败不占整段冷却、目录名与文案统一成 plugins/。
This commit is contained in:
study-tool
2026-11-01 17:47:22 +08:00
parent 491fd08c1e
commit aa48145557
114 changed files with 7779 additions and 3302 deletions
+102
View File
@@ -0,0 +1,102 @@
"""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)