baseline: Clover 独立仓库首次基线提交

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
yangqianqian
2026-06-16 11:30:22 +08:00
commit 6a2632da70
253 changed files with 27467 additions and 0 deletions

View 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