Files
beige/backend/app/repositories/base_workspace_repo.py
yangqianqian 6a2632da70 baseline: Clover 独立仓库首次基线提交
将 Clover 从上层产品包旧仓库中独立出来,建立专属版本控制。
当前状态=纵切片端到端已打通(登录→选品→出文出图→审核→下载包),
M1文案质量去套路化已验收。此提交作为后续按核销清单逐条修复的基线。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 11:30:22 +08:00

86 lines
2.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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