"""Ozon 类目/属性字典代理(服务端持店铺凭证调用 Ozon,前端不直连)。""" from __future__ import annotations from uuid import UUID from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from core.security import decrypt_secret from db import get_db from deps import get_current_user from models import Shop from services.ozon_client import OzonClient, OzonAPIError router = APIRouter(prefix="/api/categories", tags=["categories"]) class ShopRef(BaseModel): shop_id: str lang: str = "ZH_HANS" # 中文类目 async def _client(shop_id: str, db: AsyncSession) -> OzonClient: shop = await db.get(Shop, UUID(shop_id)) if shop is None: raise HTTPException(status_code=404, detail="店铺不存在") return OzonClient(decrypt_secret(shop.client_id_enc), decrypt_secret(shop.api_key_enc)) def _unwrap(result: dict) -> dict: return result.get("result", result) @router.post("/tree") async def category_tree( body: ShopRef, db: AsyncSession = Depends(get_db), _user: dict = Depends(get_current_user), ): client = await _client(body.shop_id, db) try: result = await client.post("/v1/description-category/tree", {"language": body.lang}) return _unwrap(result) except OzonAPIError as exc: raise HTTPException(status_code=502, detail=exc.detail) class AttributeQuery(BaseModel): shop_id: str type_id: int lang: str = "ZH_HANS" @router.post("/{category_id}/attributes") async def category_attributes( category_id: int, body: AttributeQuery, db: AsyncSession = Depends(get_db), _user: dict = Depends(get_current_user), ): client = await _client(body.shop_id, db) try: result = await client.post( "/v1/description-category/attribute", { "description_category_id": category_id, "type_id": body.type_id, "language": body.lang, }, ) return _unwrap(result) except OzonAPIError as exc: raise HTTPException(status_code=502, detail=exc.detail) class ValueQuery(BaseModel): shop_id: str category_id: int type_id: int q: str | None = None limit: int = 100 last_value_id: int | None = None lang: str = "ZH_HANS" @router.post("/attribute/{attribute_id}/values") async def attribute_values( attribute_id: int, body: ValueQuery, db: AsyncSession = Depends(get_db), _user: dict = Depends(get_current_user), ): client = await _client(body.shop_id, db) try: if body.q and len(body.q) >= 2: result = await client.post( "/v1/description-category/attribute/values/search", { "attribute_id": attribute_id, "description_category_id": body.category_id, "type_id": body.type_id, "limit": body.limit, "value": body.q, }, ) else: result = await client.post( "/v1/description-category/attribute/values", { "attribute_id": attribute_id, "description_category_id": body.category_id, "type_id": body.type_id, "limit": body.limit, "last_value_id": body.last_value_id or 0, "language": body.lang, }, ) return result # values 返回 {result, has_next} except OzonAPIError as exc: raise HTTPException(status_code=502, detail=exc.detail)