diff --git a/app/api/v1/meituan.py b/app/api/v1/meituan.py index 935755e..9d7510e 100644 --- a/app/api/v1/meituan.py +++ b/app/api/v1/meituan.py @@ -7,10 +7,14 @@ from __future__ import annotations import logging from concurrent.futures import ThreadPoolExecutor, as_completed -from fastapi import APIRouter, HTTPException +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy import select +from sqlalchemy.orm import Session from app.core.config import settings +from app.db.session import get_db from app.integrations.meituan import MeituanCpsError, get_referral_link, query_coupon +from app.models.meituan_coupon import MeituanCoupon from app.schemas.meituan import ( CouponCard, CouponListRequest, @@ -19,6 +23,7 @@ from app.schemas.meituan import ( FeedResponse, ReferralLinkRequest, ReferralLinkResponse, + TopSalesRequest, ) logger = logging.getLogger("shagua.meituan") @@ -162,3 +167,37 @@ def referral_link(req: ReferralLinkRequest) -> ReferralLinkResponse: link_map = raw.get("referralLinkMap") or {} link = raw.get("data") or link_map.get("1") or link_map.get("3") or next(iter(link_map.values()), "") return ReferralLinkResponse(link=link, link_map=link_map) + + +@router.post("/top-sales", response_model=CouponListResponse, + summary="销量最高(从离线库 meituan_coupon 按销量降序 + 跨源去重,不实时打美团)") +def top_sales(req: TopSalesRequest, db: Session = Depends(get_db)) -> CouponListResponse: + # 只取有销量档的(美团很多券没销量,排不了);按销量降序、同销量再按佣金降序 + stmt = select(MeituanCoupon).where(MeituanCoupon.sale_volume_num.isnot(None)) + if req.platform is not None: + stmt = stmt.where(MeituanCoupon.platform == req.platform) + stmt = stmt.order_by( + MeituanCoupon.sale_volume_num.desc(), + MeituanCoupon.commission_percent.desc(), + ) + rows = db.execute(stmt).scalars().all() + + # 跨源去重(dedup_key = 品牌|名|价):按销量降序遍历,每个 dedup_key 只留第一条(=销量最高那条)。 + # 用 raw(整条原始返回)重建 CouponCard,字段与实时接口完全一致,前端无需改渲染。 + seen: set[str] = set() + cards: list[CouponCard] = [] + for row in rows: + if row.dedup_key in seen: + continue + seen.add(row.dedup_key) + try: + card = CouponCard.from_raw(row.raw or {}) + except Exception: # noqa: BLE001 + continue + if card.product_view_sign: + cards.append(card) + + start = (req.page - 1) * req.page_size + page_items = cards[start:start + req.page_size] + has_next = start + req.page_size < len(cards) + return CouponListResponse(items=page_items, has_next=has_next, search_id=None) diff --git a/app/schemas/meituan.py b/app/schemas/meituan.py index db4d8f2..67cf36c 100644 --- a/app/schemas/meituan.py +++ b/app/schemas/meituan.py @@ -148,6 +148,13 @@ class FeedResponse(BaseModel): page: int = 1 +class TopSalesRequest(BaseModel): + """销量最高 tab:从离线库 meituan_coupon 按销量降序取(不实时打美团)。""" + page: int = Field(1, ge=1) + page_size: int = Field(20, ge=1, le=50) + platform: int | None = Field(None, description="可选: 1只外卖 / 2只到店; 不填=全部(全城销量)") + + # ───────────────── 换链 请求 / 响应 ───────────────── class ReferralLinkRequest(BaseModel):