feat(wallet): 解绑微信前对待审核提现二次确认 (#46)
## 改动
解绑微信前若有**待审核(reviewing)**提现单,首次解绑被拦下返回二次确认,用户确认后带 `force=true` 才真解绑。
- `schemas`: `UnbindWechatRequest{force}` + `UnbindWechatResultOut{needs_confirm, message}`
- `repositories`: `has_reviewing_withdraw`(只看 reviewing)
- `api`: `unbind-wechat` 接受可选 body(兼容旧无 body 客户端);`bound` 返回真实绑定态
- `docs/api`: 同步协议
## 为什么只拦 reviewing
reviewing 单解绑后审核打款会回读空 openid → 自动退回现金(钱不丢、只为让用户知情);pending 转账已在途、解绑影响不到,不拦。
配套客户端见 shaguabijia-app-android 同名分支。
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: OuYingJun1024 <1034284404@qq.com>
Reviewed-on: #46
Co-authored-by: ouzhou <ouzhou@wonderable.ai>
Co-committed-by: ouzhou <ouzhou@wonderable.ai>
This commit was merged in pull request #46.
This commit is contained in:
@@ -70,8 +70,7 @@ def test_list_config(admin_client: TestClient, token: str) -> None:
|
||||
items = {i["key"]: i for i in r.json()}
|
||||
assert "signin_rewards" in items and "ad_daily_limit" in items
|
||||
assert items["signin_rewards"]["value"] == [
|
||||
200, 120, 150, 180, 200, 250, 500,
|
||||
200, 250, 300, 350, 400, 500, 3000,
|
||||
200, 200, 300, 200, 400, 400, 800,
|
||||
]
|
||||
assert items["signin_rewards"]["overridden"] is False
|
||||
|
||||
@@ -79,7 +78,7 @@ def test_list_config(admin_client: TestClient, token: str) -> None:
|
||||
def test_update_signin_takes_effect(admin_client: TestClient, token: str) -> None:
|
||||
r = admin_client.patch(
|
||||
"/admin/api/config/signin_rewards",
|
||||
json={"value": [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400]},
|
||||
json={"value": [100, 200, 300, 400, 500, 600, 700]},
|
||||
headers=_auth(token),
|
||||
)
|
||||
assert r.status_code == 200, r.text
|
||||
@@ -119,7 +118,7 @@ def test_update_ad_limit_takes_effect(admin_client: TestClient, token: str) -> N
|
||||
|
||||
|
||||
def test_config_validation(admin_client: TestClient, token: str) -> None:
|
||||
# 签到档位长度≠14
|
||||
# 签到档位长度≠7
|
||||
assert admin_client.patch(
|
||||
"/admin/api/config/signin_rewards", json={"value": [1, 2, 3]}, headers=_auth(token)
|
||||
).status_code == 400
|
||||
|
||||
+43
-13
@@ -145,37 +145,67 @@ def test_signin_boost_flow(client) -> None:
|
||||
|
||||
|
||||
def test_task_claim_flow(client) -> None:
|
||||
"""任务列表 → 领"打开消息提醒" → 余额到账 → 重复领 409。"""
|
||||
"""打开消息提醒=可重复任务:每次领取金额减半(750/375/188),claimed 恒 False,余额累加。"""
|
||||
token = _login(client, "13800001003")
|
||||
coin = TASK_REWARDS[TASK_ENABLE_NOTIFICATION]
|
||||
base = TASK_REWARDS[TASK_ENABLE_NOTIFICATION] # 750
|
||||
|
||||
# 1. 任务列表:未领取
|
||||
# 1. 任务列表:可重复任务 claimed 恒 False,coin=首次金额(750)
|
||||
r = client.get("/api/v1/tasks", headers=_auth(token))
|
||||
assert r.status_code == 200, r.text
|
||||
items = {it["task_key"]: it for it in r.json()["items"]}
|
||||
assert items[TASK_ENABLE_NOTIFICATION]["claimed"] is False
|
||||
assert items[TASK_ENABLE_NOTIFICATION]["coin"] == coin
|
||||
assert items[TASK_ENABLE_NOTIFICATION]["coin"] == base # 750
|
||||
|
||||
# 2. 领奖
|
||||
# 2. 第一次领 → 750,余额 750
|
||||
r = client.post(f"/api/v1/tasks/{TASK_ENABLE_NOTIFICATION}/claim", headers=_auth(token))
|
||||
assert r.status_code == 200, r.text
|
||||
assert r.json()["coin_awarded"] == coin
|
||||
assert r.json()["coin_balance"] == coin
|
||||
assert r.json()["coin_awarded"] == 750
|
||||
assert r.json()["coin_balance"] == 750
|
||||
|
||||
# 3. 重复领 → 409
|
||||
r = client.post(f"/api/v1/tasks/{TASK_ENABLE_NOTIFICATION}/claim", headers=_auth(token))
|
||||
assert r.status_code == 409
|
||||
|
||||
# 4. 列表变为已领取
|
||||
# 3. 列表:下一次金额减半=375,仍 claimed False(可重复)
|
||||
r = client.get("/api/v1/tasks", headers=_auth(token))
|
||||
items = {it["task_key"]: it for it in r.json()["items"]}
|
||||
assert items[TASK_ENABLE_NOTIFICATION]["claimed"] is True
|
||||
assert items[TASK_ENABLE_NOTIFICATION]["claimed"] is False
|
||||
assert items[TASK_ENABLE_NOTIFICATION]["coin"] == 375
|
||||
|
||||
# 4. 第二次领 → 375(余额 1125),第三次 → 188(四舍五入)
|
||||
r = client.post(f"/api/v1/tasks/{TASK_ENABLE_NOTIFICATION}/claim", headers=_auth(token))
|
||||
assert r.status_code == 200 and r.json()["coin_awarded"] == 375
|
||||
assert r.json()["coin_balance"] == 1125
|
||||
r = client.post(f"/api/v1/tasks/{TASK_ENABLE_NOTIFICATION}/claim", headers=_auth(token))
|
||||
assert r.status_code == 200 and r.json()["coin_awarded"] == 188
|
||||
|
||||
# 5. 未知任务 → 404
|
||||
r = client.post("/api/v1/tasks/no_such_task/claim", headers=_auth(token))
|
||||
assert r.status_code == 404
|
||||
|
||||
|
||||
def test_task_notification_claim_capped(client) -> None:
|
||||
"""可重复任务领满(减半到底)后封顶:不再可领 → 409,列表 claimed 置 True。"""
|
||||
from app.core.rewards import notification_max_claims
|
||||
|
||||
token = _login(client, "13800001009")
|
||||
base = TASK_REWARDS[TASK_ENABLE_NOTIFICATION]
|
||||
cap = notification_max_claims(base) # 750 → 11
|
||||
|
||||
# 领满 cap 次,每次都应 200(最后几次发 1 金币)
|
||||
last_coin = None
|
||||
for _ in range(cap):
|
||||
r = client.post(f"/api/v1/tasks/{TASK_ENABLE_NOTIFICATION}/claim", headers=_auth(token))
|
||||
assert r.status_code == 200, r.text
|
||||
last_coin = r.json()["coin_awarded"]
|
||||
assert last_coin == 1 # 末次是减半到底的 1
|
||||
|
||||
# 再领 → 409(封顶,不再发放)
|
||||
r = client.post(f"/api/v1/tasks/{TASK_ENABLE_NOTIFICATION}/claim", headers=_auth(token))
|
||||
assert r.status_code == 409, r.text
|
||||
|
||||
# 列表:领满后 claimed 置 True(客户端据此隐藏)
|
||||
r = client.get("/api/v1/tasks", headers=_auth(token))
|
||||
items = {it["task_key"]: it for it in r.json()["items"]}
|
||||
assert items[TASK_ENABLE_NOTIFICATION]["claimed"] is True
|
||||
|
||||
|
||||
def test_exchange_info(client) -> None:
|
||||
token = _login(client, "13800001004")
|
||||
r = client.get("/api/v1/wallet/exchange-info", headers=_auth(token))
|
||||
|
||||
Reference in New Issue
Block a user