| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- from __future__ import annotations
- from datetime import datetime
- from typing import List, Optional
- from fastapi import APIRouter, HTTPException
- from fastapi.responses import Response
- from pydantic import BaseModel, Field
- from app.db import get_pool
- router = APIRouter(tags=["discounts"])
- async def _bump_domain_version(conn, domain: str) -> None:
- """Upsert domain_version: insert with version=1 for new domains, increment for existing."""
- await conn.execute(
- """
- INSERT INTO domain_version (domain, version, updated_at)
- VALUES ($1, 1, NOW())
- ON CONFLICT (domain) DO UPDATE
- SET version = domain_version.version + 1,
- updated_at = NOW()
- """,
- domain,
- )
- class DiscountIn(BaseModel):
- domain: str
- discount: float = Field(..., gt=0, le=1, description="折扣系数,如 0.8 表示八折")
- note: Optional[str] = None
- class DiscountOut(BaseModel):
- id: int
- domain: str
- discount: float
- note: Optional[str]
- created_at: datetime
- updated_at: datetime
- @router.get("/discounts", response_model=List[DiscountOut])
- async def list_discounts() -> List[DiscountOut]:
- pool = get_pool()
- rows = await pool.fetch("SELECT * FROM discounts ORDER BY updated_at DESC")
- return [DiscountOut(**dict(r)) for r in rows]
- @router.post("/discounts", response_model=DiscountOut, status_code=201)
- async def create_discount(body: DiscountIn) -> DiscountOut:
- pool = get_pool()
- async with pool.acquire() as conn:
- async with conn.transaction():
- row = await conn.fetchrow(
- """
- INSERT INTO discounts (domain, discount, note)
- VALUES ($1, $2, $3)
- ON CONFLICT (domain) DO UPDATE
- SET discount = EXCLUDED.discount,
- note = EXCLUDED.note,
- updated_at = NOW()
- RETURNING *
- """,
- body.domain, body.discount, body.note,
- )
- await _bump_domain_version(conn, body.domain)
- return DiscountOut(**dict(row))
- @router.put("/discounts/{discount_id}", response_model=DiscountOut)
- async def update_discount(discount_id: int, body: DiscountIn) -> DiscountOut:
- pool = get_pool()
- async with pool.acquire() as conn:
- async with conn.transaction():
- row = await conn.fetchrow(
- """
- UPDATE discounts SET domain=$1, discount=$2, note=$3, updated_at=NOW()
- WHERE id=$4 RETURNING *
- """,
- body.domain, body.discount, body.note, discount_id,
- )
- if not row:
- raise HTTPException(status_code=404, detail="不存在")
- await _bump_domain_version(conn, body.domain)
- return DiscountOut(**dict(row))
- @router.delete("/discounts/{discount_id}", status_code=204, response_model=None)
- async def delete_discount(discount_id: int) -> Response:
- pool = get_pool()
- async with pool.acquire() as conn:
- async with conn.transaction():
- # 先查出 domain,再删除,再 bump 版本
- existing = await conn.fetchrow("SELECT domain FROM discounts WHERE id=$1", discount_id)
- if not existing:
- raise HTTPException(status_code=404, detail="不存在")
- result = await conn.execute("DELETE FROM discounts WHERE id=$1", discount_id)
- if result == "DELETE 0":
- raise HTTPException(status_code=404, detail="不存在")
- await conn.execute("DELETE FROM domain_version WHERE domain=$1", existing["domain"])
- return Response(status_code=204)
|