122 lines
3.6 KiB
Python
122 lines
3.6 KiB
Python
"""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)
|