"""admin CPS 分发与对账 schemas。金额统一「分」(cents),前端 yuan() 展示。 平台:meituan(actId+sid 转链对账) / taobao(淘口令,只统计点击) / jd(链接 + 订单 API 对账)。 对账类字段对淘宝为 None → 前端显示 "-"(暂未对账)。 """ from __future__ import annotations from datetime import datetime from pydantic import BaseModel, ConfigDict, Field # ───────────── 群 ───────────── class CpsGroupOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int sid: str | None = None # 仅美团群有 name: str platforms: list[str] = Field(default_factory=list) member_count: int | None = None status: str remark: str | None = None created_at: datetime class CpsGroupCreate(BaseModel): name: str = Field(min_length=1, max_length=128) # 该群发哪些平台,至少选一个 platforms: list[str] = Field(min_length=1) # 仅当含 meituan 时需要(留空自动生成);纯淘宝/京东不填,后端置 None。 sid: str | None = Field(default=None, max_length=64, pattern=r"^[A-Za-z0-9]+$") member_count: int | None = Field(default=None, ge=0) remark: str | None = Field(default=None, max_length=256) class CpsGroupUpdate(BaseModel): name: str | None = Field(default=None, min_length=1, max_length=128) platforms: list[str] | None = None member_count: int | None = Field(default=None, ge=0) status: str | None = Field(default=None, pattern=r"^(active|archived)$") remark: str | None = Field(default=None, max_length=256) # ───────────── 活动 ───────────── class CpsActivityOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int platform: str name: str act_id: str | None = None product_view_sign: str | None = None payload: str | None = None # 淘宝淘口令 / 京东链接 image_url: str | None = None # 淘宝落地页图(绝对 URL) status: str remark: str | None = None created_at: datetime class CpsActivityCreate(BaseModel): name: str = Field(min_length=1, max_length=128) platform: str = Field(pattern=r"^(meituan|taobao|jd)$") # 美团:act_id 或 product_view_sign 二选一 act_id: str | None = Field(default=None, max_length=64) product_view_sign: str | None = Field(default=None, max_length=128) # 淘宝:整段淘口令文本 / 京东:推广链接 payload: str | None = Field(default=None, max_length=4096) # 淘宝落地页图(绝对/相对 URL;淘宝必填,由 router 校验) image_url: str | None = Field(default=None, max_length=512) remark: str | None = Field(default=None, max_length=256) class CpsActivityUpdate(BaseModel): """编辑活动(PATCH):全字段可选,只更新传入的非 None 项;最终平台对应字段由 router 校验。""" name: str | None = Field(default=None, min_length=1, max_length=128) platform: str | None = Field(default=None, pattern=r"^(meituan|taobao|jd)$") act_id: str | None = Field(default=None, max_length=64) product_view_sign: str | None = Field(default=None, max_length=128) payload: str | None = Field(default=None, max_length=4096) image_url: str | None = Field(default=None, max_length=512) remark: str | None = Field(default=None, max_length=256) status: str | None = Field(default=None, pattern=r"^(active|archived)$") # ───────────── 生成链接(批量) ───────────── class CpsReferralLinksRequest(BaseModel): group_id: int activity_ids: list[int] = Field(min_length=1) class CpsReferralLinkItem(BaseModel): activity_id: int activity_name: str platform: str redirect_url: str # 我们的落地页 /c/{code}(发群用) code: str class CpsReferralLinksOut(BaseModel): group_name: str results: list[CpsReferralLinkItem] # ───────────── 对账拉单 ───────────── class CpsReconcileResult(BaseModel): fetched: int inserted: int updated: int pages: int # ───────────── 订单明细 ───────────── class CpsOrderOut(BaseModel): model_config = ConfigDict(from_attributes=True) id: int platform: str = "meituan" order_id: str external_order_id: str | None = None external_row_id: str | None = None sid: str | None = None act_id: str | None = None pay_price_cents: int | None = None commission_cents: int | None = None estimated_commission_cents: int | None = None actual_commission_cents: int | None = None commission_rate: str | None = None mt_status: str | None = None jd_valid_code: str | None = None invalid_reason: str | None = None product_name: str | None = None settle_month: str | None = None pay_time: datetime | None = None # ───────────── 统计 ───────────── class CpsGroupStat(BaseModel): group_id: int | None = None sid: str | None = None name: str platforms: list[str] = Field(default_factory=list) member_count: int | None = None click_pv: int = 0 click_uv: int = 0 copy_pv: int = 0 # 淘宝"复制口令"次数 copy_uv: int = 0 # 对账类:淘宝/京东无法对账 → None(前端显示 "-");美团群为数值 order_count: int | None = None settled_count: int | None = None canceled_count: int | None = None gmv_cents: int | None = None est_commission_cents: int | None = None settled_commission_cents: int | None = None class CpsStatsOut(BaseModel): groups: list[CpsGroupStat] total_order_count: int total_est_commission_cents: int total_settled_commission_cents: int