feat(wallet): coin_transaction 增 trace_id 列 + grant_coins 透传
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,16 @@ SIGNIN_REWARDS: tuple[int, ...] = (
|
||||
SIGNIN_CYCLE_LEN: int = len(SIGNIN_REWARDS)
|
||||
|
||||
|
||||
# ===== 信息流广告「按会话聚合」的 biz_type =====
|
||||
# 比价 / 领券等候期看的信息流广告,每条各写一条 coin_transaction;这两类在「金币变动记录」
|
||||
# 里按 trace_id 聚合成一条展示(见 repositories/wallet.list_coin_transactions)。其余类型
|
||||
# (reward_video / guide_video / 通用 feed_ad_reward / signin / task_* 等)不聚合。
|
||||
FEED_AD_SESSION_BIZ_TYPES: tuple[str, str] = (
|
||||
"feed_ad_reward_comparison",
|
||||
"feed_ad_reward_coupon",
|
||||
)
|
||||
|
||||
|
||||
def signin_reward(cycle_day: int) -> int:
|
||||
"""cycle_day 取值 1..SIGNIN_CYCLE_LEN。"""
|
||||
return SIGNIN_REWARDS[cycle_day - 1]
|
||||
|
||||
@@ -84,6 +84,9 @@ class CoinTransaction(Base):
|
||||
# 关联业务 id(签到日期、任务 key 等),可空
|
||||
ref_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
remark: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||
# 会话键:仅比价/领券信息流发奖(feed_ad_reward_comparison/coupon)时写入本场 trace_id,
|
||||
# 金币记录列表据此把一次比价/领券的多条广告金币聚合成一条。其余类型 = NULL。
|
||||
trace_id: Mapped[str | None] = mapped_column(String(64), index=True, nullable=True)
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), index=True, nullable=False
|
||||
|
||||
@@ -170,6 +170,7 @@ def grant_coins(
|
||||
biz_type: str,
|
||||
ref_id: str | None = None,
|
||||
remark: str | None = None,
|
||||
trace_id: str | None = None,
|
||||
) -> tuple[CoinAccount, CoinTransaction]:
|
||||
"""金币变动入口(正数入账 / 负数出账)。更新余额 + 写流水,不 commit。
|
||||
|
||||
@@ -189,6 +190,7 @@ def grant_coins(
|
||||
biz_type=biz_type,
|
||||
ref_id=ref_id,
|
||||
remark=remark,
|
||||
trace_id=trace_id,
|
||||
created_at=datetime.now(rewards.CN_TZ).replace(tzinfo=None), # 存北京 wall-clock(客户端原样切片显示)
|
||||
)
|
||||
db.add(txn)
|
||||
|
||||
@@ -384,3 +384,22 @@ def test_endpoints_require_auth(client) -> None:
|
||||
assert client.get("/api/v1/savings/summary").status_code == 401
|
||||
assert client.get("/api/v1/savings/battle").status_code == 401
|
||||
assert client.get("/api/v1/savings/records").status_code == 401
|
||||
|
||||
|
||||
def test_grant_coins_persists_trace_id(client) -> None:
|
||||
"""grant_coins 传 trace_id 落库;不传则为 None。"""
|
||||
phone = "13800002001"
|
||||
_login(client, phone)
|
||||
with SessionLocal() as db:
|
||||
user = get_user_by_phone(db, phone)
|
||||
assert user is not None
|
||||
_, txn1 = crud_wallet.grant_coins(
|
||||
db, user.id, 5, biz_type="feed_ad_reward_comparison",
|
||||
ref_id="evt1", remark="比价奖励", trace_id="trace-A",
|
||||
)
|
||||
_, txn2 = crud_wallet.grant_coins(
|
||||
db, user.id, 30, biz_type="signin", remark="每日签到奖励",
|
||||
)
|
||||
db.commit()
|
||||
assert txn1.trace_id == "trace-A"
|
||||
assert txn2.trace_id is None
|
||||
|
||||
Reference in New Issue
Block a user