feat: 接入数据大盘聚合与美团 CPS 对账

改了什么:新增新版大盘日期窗口聚合、广告收益拆分、比价耗时字段、美团 CPS 拉单入库与大盘展示,并同步反馈审核和邀请奖励下线口径。
验证:python -m pytest tests/test_admin_read.py tests/test_admin_write.py tests/test_compare_record.py tests/test_invite.py tests/test_feedback.py -q。
This commit is contained in:
lowmaster-chen
2026-06-28 09:56:13 +08:00
parent 900b64d4f9
commit e1bd0e3ef7
49 changed files with 2376 additions and 119 deletions
+21 -23
View File
@@ -1,4 +1,4 @@
"""好友邀请测试:邀请码、绑定、双方发金币、幂等、自邀/无效码屏蔽、指纹兜底归因。
"""好友邀请测试:邀请码、绑定、幂等、自邀/无效码屏蔽、指纹兜底归因。
用 sms mock 登录拿 token(同 test_welfare),再跑邀请闭环。
"""
@@ -10,8 +10,6 @@ from sqlalchemy import select
from app.core.rewards import (
INVITE_FP_WINDOW_DAYS,
INVITE_INVITEE_COINS,
INVITE_INVITER_COINS,
INVITE_NEW_USER_WINDOW_HOURS,
)
from app.db.session import SessionLocal
@@ -56,8 +54,8 @@ def test_invite_me_returns_stable_code(client) -> None:
assert _my_code(client, token) == body["invite_code"]
def test_bind_flow_both_get_coins(client) -> None:
"""B 用 A 的码绑定 → 双方各得 1 万金币;A 战绩 +1"""
def test_bind_flow_records_relation_without_invite_coins(client) -> None:
"""B 用 A 的码绑定 → 邀请关系生效;邀请金币已下线"""
a = _login(client, "13800002002")
b = _login(client, "13800002003")
a_code = _my_code(client, a)
@@ -69,20 +67,20 @@ def test_bind_flow_both_get_coins(client) -> None:
assert r.status_code == 200, r.text
res = r.json()
assert res["status"] == "success"
assert res["coins_awarded"] == INVITE_INVITEE_COINS
assert res["coins_awarded"] == 0
# 双方金币到账
assert _coin_balance(client, b) == INVITE_INVITEE_COINS
assert _coin_balance(client, a) == INVITE_INVITER_COINS
# 邀请金币已下线,双方余额不因邀请变化
assert _coin_balance(client, b) == 0
assert _coin_balance(client, a) == 0
# A 的战绩:已邀 1 人,累计获得 = 邀请人那份
# A 的战绩:已邀 1 人,邀请金币保持 0
r = client.get("/api/v1/invite/me", headers=_auth(a))
assert r.json()["invited_count"] == 1
assert r.json()["coins_earned"] == INVITE_INVITER_COINS
assert r.json()["coins_earned"] == 0
def test_bind_idempotent_no_double_reward(client) -> None:
"""同一被邀请人二次绑定 → already_bound,不重复发奖"""
def test_bind_idempotent_no_duplicate_relation(client) -> None:
"""同一被邀请人二次绑定 → already_bound,不重复绑定"""
a = _login(client, "13800002004")
b = _login(client, "13800002005")
a_code = _my_code(client, a)
@@ -99,14 +97,14 @@ def test_bind_idempotent_no_double_reward(client) -> None:
assert r2.json()["status"] == "already_bound"
assert r2.json()["coins_awarded"] == 0
# 余额没变(没二次发奖),C 也没拿到邀请奖励
# 余额没变,C 也没有邀请金币
assert _coin_balance(client, b) == bal_b
assert _coin_balance(client, a) == bal_a
assert _coin_balance(client, c) == 0
def test_self_invite_blocked(client) -> None:
"""填自己的邀请码 → self_invite,不发奖"""
"""填自己的邀请码 → self_invite,不产生金币"""
a = _login(client, "13800002007")
a_code = _my_code(client, a)
r = client.post("/api/v1/invite/bind", json={"invite_code": a_code}, headers=_auth(a))
@@ -116,7 +114,7 @@ def test_self_invite_blocked(client) -> None:
def test_invalid_code(client) -> None:
"""无效邀请码 → invalid_code,不发奖"""
"""无效邀请码 → invalid_code,不产生金币"""
b = _login(client, "13800002008")
r = client.post("/api/v1/invite/bind", json={"invite_code": "ZZZZZZ"}, headers=_auth(b))
assert r.status_code == 200, r.text
@@ -145,7 +143,7 @@ def test_invite_requires_auth(client) -> None:
def test_old_user_not_eligible(client) -> None:
"""新用户闸:被邀请人注册超过窗口 → not_eligible,双方都不发奖"""
"""新用户闸:被邀请人注册超过窗口 → not_eligible,双方都不产生金币"""
a = _login(client, "13800002030")
a_code = _my_code(client, a)
b = _login(client, "13800002031")
@@ -217,13 +215,13 @@ def test_bind_by_fingerprint_success(client) -> None:
)
assert r2.status_code == 200, r2.text
assert r2.json()["status"] == "success"
# 双方各发金币
assert _coin_balance(client, a) == INVITE_INVITER_COINS
assert _coin_balance(client, b) == INVITE_INVITEE_COINS
# 邀请金币已下线,双方余额不因邀请变化
assert _coin_balance(client, a) == 0
assert _coin_balance(client, b) == 0
def test_bind_by_fingerprint_not_found(client) -> None:
"""没有匹配的指纹记录 → fp_not_found,不发奖"""
"""没有匹配的指纹记录 → fp_not_found,不产生金币"""
b = _login(client, "13800002043")
r = client.post(
"/api/v1/invite/bind",
@@ -300,7 +298,7 @@ def test_bind_no_code_no_fingerprint(client) -> None:
# =====================================================================
def test_invitees_basic(client) -> None:
"""A 邀 B、C → 列表返 2 条、total=2、没设资料的名字=脱敏手机号、头像 null、金币"""
"""A 邀 B、C → 列表返 2 条、total=2、没设资料的名字=脱敏手机号、头像 null、金币为 0"""
a = _login(client, "13800002050")
a_code = _my_code(client, a)
for phone in ("13800002051", "13800002052"):
@@ -317,7 +315,7 @@ def test_invitees_basic(client) -> None:
names = {it["display_name"] for it in body["items"]}
assert names == {"138****2051", "138****2052"}
assert all(it["avatar_url"] is None for it in body["items"])
assert all(it["coins"] == INVITE_INVITER_COINS for it in body["items"])
assert all(it["coins"] == 0 for it in body["items"])
def test_invitees_order_desc(client) -> None: