将 Clover 从上层产品包旧仓库中独立出来,建立专属版本控制。 当前状态=纵切片端到端已打通(登录→选品→出文出图→审核→下载包), M1文案质量去套路化已验收。此提交作为后续按核销清单逐条修复的基线。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
86 lines
2.8 KiB
Python
86 lines
2.8 KiB
Python
"""
|
||
app/repositories/base_workspace_repo.py — 多租户基础 Repo
|
||
所有 workspace 相关 Repo 继承此类,强制过滤 workspace_id(基石C)。
|
||
禁止 SELECT *,明确字段(db.md规范)。
|
||
"""
|
||
|
||
import logging
|
||
from typing import Any, Generic, TypeVar
|
||
|
||
from sqlalchemy import select
|
||
from sqlalchemy.orm import Session
|
||
|
||
from app.core.response import raise_not_found
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
T = TypeVar("T")
|
||
|
||
|
||
class BaseWorkspaceRepo(Generic[T]):
|
||
"""
|
||
workspace 感知的基础 Repo。
|
||
子类设置 model_class,所有查询自动注入 workspace_id 过滤。
|
||
"""
|
||
|
||
model_class: type[T]
|
||
|
||
def __init__(self, db: Session, workspace_id: int):
|
||
self.db = db
|
||
self.workspace_id = workspace_id
|
||
|
||
def _base_query(self):
|
||
"""所有查询的基础:强制带 workspace_id 过滤(多租户红线)。"""
|
||
return self.db.query(self.model_class).filter(
|
||
self.model_class.workspace_id == self.workspace_id # type: ignore[attr-defined]
|
||
)
|
||
|
||
def get_by_id(self, record_id: int) -> T | None:
|
||
"""按 ID 查单条,强制带 workspace_id 防越权读。"""
|
||
return self._base_query().filter(
|
||
self.model_class.id == record_id # type: ignore[attr-defined]
|
||
).first()
|
||
|
||
def get_by_id_or_404(self, record_id: int) -> T:
|
||
obj = self.get_by_id(record_id)
|
||
if not obj:
|
||
raise_not_found(f"{self.model_class.__name__} {record_id} 不存在")
|
||
return obj # type: ignore[return-value]
|
||
|
||
def list_all(
|
||
self,
|
||
offset: int = 0,
|
||
limit: int = 20,
|
||
filters: list[Any] | None = None,
|
||
) -> tuple[list[T], int]:
|
||
"""分页列表,返回 (items, total)。"""
|
||
q = self._base_query()
|
||
if filters:
|
||
q = q.filter(*filters)
|
||
total = q.count()
|
||
items = q.offset(offset).limit(limit).all()
|
||
return items, total
|
||
|
||
def create(self, obj: T) -> T:
|
||
"""插入一条记录,自动设置 workspace_id。"""
|
||
obj.workspace_id = self.workspace_id # type: ignore[attr-defined]
|
||
self.db.add(obj)
|
||
self.db.flush()
|
||
self.db.refresh(obj)
|
||
logger.debug("Created %s id=%s ws=%s", self.model_class.__name__, obj.id, self.workspace_id) # type: ignore[attr-defined]
|
||
return obj
|
||
|
||
def delete(self, obj: T) -> None:
|
||
"""物理删除(软删各子类自己处理 archived 态)。"""
|
||
self.db.delete(obj)
|
||
self.db.flush()
|
||
|
||
def save(self) -> None:
|
||
"""提交事务,错误不静默(官网V1坑3)。"""
|
||
try:
|
||
self.db.commit()
|
||
except Exception:
|
||
self.db.rollback()
|
||
logger.error("DB commit failed in %s ws=%s", self.model_class.__name__, self.workspace_id)
|
||
raise
|