refactor: migrate user model queries

This commit is contained in:
jxxghp
2026-08-23 02:06:14 +08:00
parent d0ebdd60c0
commit de85ae3ef9
5 changed files with 48 additions and 22 deletions
+33 -7
View File
@@ -4,7 +4,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Mapped, Session, mapped_column
from app.db.base import Base, get_id_column
from app.db.decorators import db_query, async_db_query
from app.db.decorators import async_db_query, run_legacy_sync_query
class User(Base):
@@ -35,9 +35,24 @@ class User(Base):
settings: Mapped[Optional[Any]] = mapped_column(JSON, default=dict)
@classmethod
@db_query
def get_by_name(cls, db: Session, name: str):
return db.execute(select(cls).where(cls.name == name)).scalars().first()
def get_by_name(
cls,
db: Session | str | None = None,
name: str | None = None,
):
"""按用户名查询用户,兼容显式会话和旧插件无会话调用。"""
if name is None and isinstance(db, str):
name, db = db, None
if name is None:
raise TypeError("name is required")
def query(session: Session):
"""在给定会话中执行用户名查询。"""
return session.execute(select(cls).where(cls.name == name)).scalars().first()
if isinstance(db, Session):
return query(db)
return run_legacy_sync_query(query)
@classmethod
@async_db_query
@@ -48,9 +63,20 @@ class User(Base):
return result.scalars().first()
@classmethod
@db_query
def get_by_id(cls, db: Session, user_id: int):
return db.execute(select(cls).where(cls.id == user_id)).scalars().first()
def get_by_id(cls, db: Session | int | None = None, user_id: int | None = None):
"""按用户 ID 查询用户,兼容显式会话和旧插件无会话调用。"""
if user_id is None and isinstance(db, int):
user_id, db = db, None
if user_id is None:
raise TypeError("user_id is required")
def query(session: Session):
"""在给定会话中执行用户 ID 查询。"""
return session.execute(select(cls).where(cls.id == user_id)).scalars().first()
if isinstance(db, Session):
return query(db)
return run_legacy_sync_query(query)
@classmethod
@async_db_query
@@ -72,7 +72,7 @@ MoviePilot V3 当前不是“目录混乱、必须推倒重来”的状态。第
| 专用 EventData model | 53 | Event Contract Registry 已为全部事件登记 typed payload/fallback 原因 |
| 直接读取 `settings` 的文件 | 107 | 仍按模块族迁移,动态协议和安全端口暂保留 |
| `SystemConfigOper()` | 1 个 | 仅组合根创建 `SystemConfigService` 时保留 |
| Model 上的 DB 查询装饰器 | 119 | `db_update`/`async_db_update` 为 0;查询 ABI 继续按 canonical 用例迁移 |
| Model 上的 DB 查询装饰器 | 117 | `db_update`/`async_db_update` 为 0;查询 ABI 继续按 canonical 用例迁移 |
| 路由端点 | 335 | 11 个已装饰端点超过 80 行,最大 400 行 |
| Chain 方法超过 150 行 | 18 | 最大 `TransferChain.do_transfer()` 885 行 |
| Application 方法超过 150 行 | 8 | 最大 296 行 |
@@ -1031,6 +1031,8 @@ MFA/Passkey 专项测试与架构门禁通过,密钥类配置仍保留在安
Pylint 与架构基线通过,配置债务由 112 个文件降至 109 个文件。
缓存 Redis 连接池、内存限制和文件缓存工厂随后改用 runtime 配置端口,保留旧模块级 Settings 覆盖入口;
缓存专项 41 项测试与 Pylint 通过,配置债务由 109 个文件降至 107 个文件。
用户模型的 `get_by_name``get_by_id` 同步查询改为显式 Session 执行,并以一次性短会话保留旧插件
无 Session ABI;用户查询与兼容专项 75 项测试、Pylint 及架构基线通过,查询装饰器由 119 个降至 117 个。
同日修正适配器配置下沉边界:OCR、CookieCloud、DoH、Rust 和资源签名等低层实现不再直接依赖
`app.application`,由 `app.runtime.settings` 端口承接组合根注入;未启动装配时仍回退旧 Settings ABI
+2 -12
View File
@@ -3,10 +3,10 @@
"by_kind": {
"async_db_query": 49,
"async_db_update": 0,
"db_query": 70,
"db_query": 68,
"db_update": 0
},
"count": 119,
"count": 117,
"methods": [
{
"decorator": "async_db_query",
@@ -548,16 +548,6 @@
"file": "app/db/models/user.py",
"method": "User.async_get_by_name"
},
{
"decorator": "db_query",
"file": "app/db/models/user.py",
"method": "User.get_by_id"
},
{
"decorator": "db_query",
"file": "app/db/models/user.py",
"method": "User.get_by_name"
},
{
"decorator": "db_query",
"file": "app/db/models/userconfig.py",
+2 -2
View File
@@ -126,8 +126,8 @@ def test_transaction_debt_baseline_is_a_model_and_oper_ratchet() -> None:
baseline = json.loads(baseline_path.read_text(encoding="utf-8"))
assert baseline["schema_version"] == 1
assert baseline["model_decorators"]["count"] == 119
assert sum(baseline["model_decorators"]["by_kind"].values()) == 119
assert baseline["model_decorators"]["count"] == 117
assert sum(baseline["model_decorators"]["by_kind"].values()) == 117
assert baseline["model_decorators"]["by_kind"]["db_update"] == 0
assert baseline["model_decorators"]["by_kind"]["async_db_update"] == 0
assert baseline["model_transaction_calls"] == {"count": 0, "calls": []}
+8
View File
@@ -133,6 +133,14 @@ def test_user_lookup_returns_none_when_absent(db):
assert User.get_by_id(db.session, -1) is None
def test_user_sync_queries_preserve_legacy_no_session_abi(db):
"""旧插件省略 Session 时仍可按用户名和用户 ID 查询。"""
created = db.add(User(name="mp-legacy-query-user", hashed_password="secret"))
assert User.get_by_name("mp-legacy-query-user").id == created.id
assert User.get_by_id(created.id).name == "mp-legacy-query-user"
def test_user_delete_by_name_and_by_id_remove_only_the_target(db):
"""
按名、按 ID 删除都只能删掉目标用户。