chore(prep): shared groundwork for batch-3 (admin/golden/cache)
第三批(T12 后台/T13 黄金集/T14 检索缓存)协调提交: - kb:KbAssetService(登记/审核,tryon_ready 只能随 approved 打开)+ Asset 模型与 repo - retrieval:list_gaps 缺口看板查询 + invalidate_cache() 钩子(T14 背后实现真缓存) - 配置:ADMIN_TOKEN(空=后台 404)+ 检索缓存三参数 - pytest golden marker(默认跳过,CI 强制跑)+ Makefile golden + CI 防线 5c - 测试隔离修复:test_auth_sms 加 autouse 清 sms:* 键夹具(频控计数跨运行 累积会毒化后续运行——随机 IP 空间仅 254 且部分用例固定 device_id)
This commit is contained in:
+62
-1
@@ -19,6 +19,8 @@ from packages.domain.ids import next_id
|
||||
from packages.kb import repo
|
||||
from packages.kb.schemas import (
|
||||
MAX_GRAPH_HOPS,
|
||||
AssetCreate,
|
||||
AssetReview,
|
||||
ColorOut,
|
||||
EntityCreate,
|
||||
EntityUpdate,
|
||||
@@ -30,7 +32,7 @@ from packages.kb.schemas import (
|
||||
)
|
||||
from packages.shared.errors import AppError, ErrorCode, NotFoundError
|
||||
from packages.shared.logging import get_logger
|
||||
from packages.shared.models import KbColor, KbEntity, KbRelation
|
||||
from packages.shared.models import KbAsset, KbColor, KbEntity, KbRelation
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -390,3 +392,62 @@ def _color_out(color: KbColor) -> ColorOut:
|
||||
hex=color.hex,
|
||||
name_cn=color.name_cn,
|
||||
)
|
||||
|
||||
|
||||
class KbAssetService:
|
||||
"""素材登记与审核(kb_asset)。与实体/图谱分开放:素材由运营流水线
|
||||
(T-P2-07/15)高频写入,审核是唯一能把 tryon_ready 打开的入口。"""
|
||||
|
||||
def __init__(self, session: AsyncSession) -> None:
|
||||
self._session = session
|
||||
|
||||
async def register_asset(self, payload: AssetCreate) -> KbAsset:
|
||||
entity = await repo.get_entity(self._session, int(payload.entity_id))
|
||||
if entity is None:
|
||||
raise AppError(ErrorCode.NOT_FOUND, message="实体不存在,先建实体再登记素材")
|
||||
asset = repo.add_asset(
|
||||
self._session,
|
||||
KbAsset(
|
||||
asset_id=next_id(),
|
||||
entity_id=entity.entity_id,
|
||||
asset_type=payload.asset_type,
|
||||
cos_key=payload.cos_key,
|
||||
spec_check=payload.spec_check,
|
||||
tryon_ready=False, # 登记时恒为 False,只有审核能打开
|
||||
review_status="pending",
|
||||
license=payload.license,
|
||||
),
|
||||
)
|
||||
await self._session.flush()
|
||||
return asset
|
||||
|
||||
async def review_asset(self, asset_id: int, payload: AssetReview) -> KbAsset:
|
||||
asset = await repo.get_asset(self._session, asset_id)
|
||||
if asset is None:
|
||||
raise AppError(ErrorCode.NOT_FOUND, message="素材不存在")
|
||||
# tryon_ready 只能随 approved 一起打开:审核是唯一放行开关
|
||||
if payload.tryon_ready and payload.review_status != "approved":
|
||||
raise AppError(
|
||||
ErrorCode.BAD_REQUEST,
|
||||
message="tryon_ready 只能在审核通过时开启",
|
||||
)
|
||||
asset.review_status = payload.review_status
|
||||
asset.tryon_ready = payload.tryon_ready
|
||||
await self._session.flush()
|
||||
return asset
|
||||
|
||||
async def list_assets(
|
||||
self,
|
||||
*,
|
||||
entity_id: int | None = None,
|
||||
tryon_ready: bool | None = None,
|
||||
limit: int = 50,
|
||||
offset: int = 0,
|
||||
) -> tuple[list[KbAsset], int]:
|
||||
return await repo.list_assets(
|
||||
self._session,
|
||||
entity_id=entity_id,
|
||||
tryon_ready=tryon_ready,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user