eb5f0dc460
- 新增 POST /api/v1/ad/ecpm-report:激励视频展示后客户端上报本次 eCPM,落 ad_ecpm_record 做内部收益统计(model/repository/schema/alembic 迁移 + 接口文档) - 看广告冷却策略抽到 app/core/ad_cooldown.py 纯函数;ad_reward.today_status 只取数据,换策略只改这一处 - savings.dishes 改 JSON().with_variant(JSONB,"postgresql"):SQLite 无 visit_JSONB 会让 create_all 编译崩,修复后测试套件恢复 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
47 lines
2.2 KiB
Python
47 lines
2.2 KiB
Python
"""省钱记录表。
|
|
|
|
是 profile「累计帮你省了」和「省钱战绩」的唯一数据源:每完成一次比价/下单,
|
|
省了多少记一行。本期比价还没接后端,数据由 demo seeder 幂等灌入(source='demo'),
|
|
聚合接口照常做真实 SUM/分组;等比价真接入后,改成上报写入、停掉 seeder 即可。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import JSON, DateTime, ForeignKey, Integer, String, func
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.db.base import Base
|
|
|
|
|
|
class SavingsRecord(Base):
|
|
__tablename__ = "savings_record"
|
|
|
|
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
|
|
)
|
|
# 订单金额 / 省下金额,单位:分
|
|
order_amount_cents: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
saved_amount_cents: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
platform: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
|
title: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
|
# 店铺名(订单明细卡标题,如「窑鸡王(王府井店)」)
|
|
shop_name: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
|
# 菜品名列表。PG 上用 JSONB(可建 GIN 索引),SQLite(本地/测试)退化为通用 JSON——
|
|
# 否则 SQLite 无 visit_JSONB,Base.metadata.create_all 编译报错(测试套件 setup 全崩)。
|
|
# 前 2 道直接展示,其余收进「还有 N 道菜」展开。
|
|
dishes: Mapped[list[str]] = mapped_column(
|
|
JSON().with_variant(JSONB(), "postgresql"), nullable=False, default=list
|
|
)
|
|
# 来源:demo(演示) / compare(真实比价上报)
|
|
source: Mapped[str] = mapped_column(String(16), nullable=False, default="compare")
|
|
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), index=True, nullable=False
|
|
)
|
|
|
|
def __repr__(self) -> str: # pragma: no cover
|
|
return f"<SavingsRecord id={self.id} user_id={self.user_id} saved={self.saved_amount_cents}>"
|