baseline: Clover 独立仓库首次基线提交
将 Clover 从上层产品包旧仓库中独立出来,建立专属版本控制。 当前状态=纵切片端到端已打通(登录→选品→出文出图→审核→下载包), M1文案质量去套路化已验收。此提交作为后续按核销清单逐条修复的基线。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
3
backend/app/repositories/__init__.py
Normal file
3
backend/app/repositories/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
app/repositories/__init__.py
|
||||
"""
|
||||
85
backend/app/repositories/base_workspace_repo.py
Normal file
85
backend/app/repositories/base_workspace_repo.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user