125 lines
3.8 KiB
Python
125 lines
3.8 KiB
Python
"""店铺管理:绑定 Ozon Client-Id/Api-Key(加密落库)+ 连通性校验。"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timezone
|
|
from uuid import UUID
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from core.security import decrypt_secret, encrypt_secret
|
|
from db import get_db
|
|
from deps import get_current_user
|
|
from models import Shop
|
|
from models.enums import ShopStatus
|
|
from schemas.shop import ShopCreate, ShopListItem, ShopUpdate
|
|
from services.ozon_client import OzonClient, OzonAPIError
|
|
|
|
router = APIRouter(prefix="/api/shops", tags=["shops"])
|
|
|
|
|
|
def _mask(client_id: str) -> str:
|
|
return f"…{client_id[-4:]}" if len(client_id) > 4 else "…"
|
|
|
|
|
|
@router.get("", response_model=list[ShopListItem])
|
|
async def list_shops(
|
|
db: AsyncSession = Depends(get_db),
|
|
_user: dict = Depends(get_current_user),
|
|
):
|
|
rows = (await db.scalars(select(Shop).order_by(Shop.created_at))).all()
|
|
items = []
|
|
for s in rows:
|
|
item = ShopListItem.model_validate(s)
|
|
try:
|
|
item.client_id_masked = _mask(decrypt_secret(s.client_id_enc))
|
|
except Exception: # noqa: BLE001
|
|
item.client_id_masked = "…"
|
|
items.append(item)
|
|
return items
|
|
|
|
|
|
@router.post("", response_model=ShopListItem)
|
|
async def create_shop(
|
|
body: ShopCreate,
|
|
db: AsyncSession = Depends(get_db),
|
|
_user: dict = Depends(get_current_user),
|
|
):
|
|
shop = Shop(
|
|
name=body.name,
|
|
client_id_enc=encrypt_secret(body.client_id),
|
|
api_key_enc=encrypt_secret(body.api_key),
|
|
currency_code=body.currency_code or "RUB",
|
|
status=ShopStatus.active,
|
|
)
|
|
db.add(shop)
|
|
await db.commit()
|
|
await db.refresh(shop)
|
|
item = ShopListItem.model_validate(shop)
|
|
item.client_id_masked = _mask(body.client_id)
|
|
return item
|
|
|
|
|
|
@router.patch("/{shop_id}", response_model=ShopListItem)
|
|
async def update_shop(
|
|
shop_id: str,
|
|
body: ShopUpdate,
|
|
db: AsyncSession = Depends(get_db),
|
|
_user: dict = Depends(get_current_user),
|
|
):
|
|
shop = await db.get(Shop, UUID(shop_id))
|
|
if shop is None:
|
|
raise HTTPException(status_code=404, detail="店铺不存在")
|
|
if body.name is not None:
|
|
shop.name = body.name
|
|
if body.currency_code is not None:
|
|
shop.currency_code = body.currency_code
|
|
if body.client_id:
|
|
shop.client_id_enc = encrypt_secret(body.client_id)
|
|
if body.api_key:
|
|
shop.api_key_enc = encrypt_secret(body.api_key)
|
|
await db.commit()
|
|
await db.refresh(shop)
|
|
item = ShopListItem.model_validate(shop)
|
|
item.client_id_masked = _mask(decrypt_secret(shop.client_id_enc))
|
|
return item
|
|
|
|
|
|
@router.delete("/{shop_id}")
|
|
async def delete_shop(
|
|
shop_id: str,
|
|
db: AsyncSession = Depends(get_db),
|
|
_user: dict = Depends(get_current_user),
|
|
):
|
|
shop = await db.get(Shop, UUID(shop_id))
|
|
if shop is None:
|
|
raise HTTPException(status_code=404, detail="店铺不存在")
|
|
await db.delete(shop)
|
|
await db.commit()
|
|
return {"deleted": True}
|
|
|
|
|
|
@router.post("/{shop_id}/test")
|
|
async def test_shop(
|
|
shop_id: str,
|
|
db: AsyncSession = Depends(get_db),
|
|
_user: dict = Depends(get_current_user),
|
|
):
|
|
shop = await db.get(Shop, UUID(shop_id))
|
|
if shop is None:
|
|
raise HTTPException(status_code=404, detail="店铺不存在")
|
|
client = OzonClient(decrypt_secret(shop.client_id_enc), decrypt_secret(shop.api_key_enc))
|
|
try:
|
|
result = await client.test_credentials()
|
|
except OzonAPIError as exc:
|
|
shop.status = ShopStatus.invalid
|
|
await db.commit()
|
|
return {"ok": False, "error": exc.detail, "roles": []}
|
|
|
|
shop.status = ShopStatus.active
|
|
shop.last_checked_at = datetime.now(timezone.utc)
|
|
await db.commit()
|
|
roles = [r.get("name") for r in result.get("roles", [])]
|
|
return {"ok": True, "roles": roles}
|