ee6c2e4710
本次(M1 记账): savings_record 升级为记账唯一真相表(新增 源平台原价/比价价/支付渠道/平台包名/源平台名/源链接/幂等键/device_id, 加 user_id+client_event_id 唯一约束); /api/v1/order/report 改为写 savings_record(source=compare), 省额=源平台原价 减 实付, 按 client_event_id 幂等; 统计与明细改为 有真实记录用真实、否则 demo 兜底; 修复 dishes 列 JSONB 改 with_variant 兼容 SQLite 测试库; 新增 tests/test_order_savings.py, 全量 pytest 54 passed 一并纳入(此前工作区已有、未提交, 非本次改动): order_record 归因订单 模型/repo/api/迁移, 及 main.py、models/__init__ 注册等改动 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
43 lines
2.0 KiB
Python
43 lines
2.0 KiB
Python
from datetime import datetime
|
|
|
|
from sqlalchemy import DateTime, ForeignKey, Integer, String, UniqueConstraint, func
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.db.base import Base
|
|
|
|
|
|
class OrderRecord(Base):
|
|
"""归因订单记录。
|
|
|
|
客户端判定「比价完 5 分钟内 + 用户点了结果链接 + 支付金额与比价价相差 ≤1 元」成立时,
|
|
带 JWT 上报一条,视为用户经由我们下了一笔单。本表只做记录(不发金币,避免刷单风险)。
|
|
|
|
幂等:(user_id, client_event_id) 唯一。client_event_id 由客户端为每次归因生成 UUID,
|
|
重试/重复上报不会产生多条。
|
|
"""
|
|
|
|
__tablename__ = "order_record"
|
|
__table_args__ = (
|
|
UniqueConstraint("user_id", "client_event_id", name="uq_order_user_event"),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
user_id: Mapped[int] = mapped_column(
|
|
Integer, ForeignKey("user.id"), index=True, nullable=False
|
|
)
|
|
client_event_id: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
# 平台展示名(如 美团 / 淘宝闪购),来自比价结果 comparison_results.platform_name
|
|
platform: Mapped[str] = mapped_column(String(32), nullable=False)
|
|
platform_package: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
|
# 支付渠道:wechat / alipay(哪条支付通道检测到的支付成功)
|
|
pay_channel: Mapped[str] = mapped_column(String(16), nullable=False)
|
|
# 我们给出的(用户点击的那个平台的)比价价,单位分
|
|
compared_price_cents: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
# 实际支付金额,单位分
|
|
paid_amount_cents: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
device_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
|
source: Mapped[str] = mapped_column(String(32), nullable=False, default="attribution")
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), nullable=False
|
|
)
|