From 07cf80680506ba9e12d2ae1472cc3d13f8c9b312 Mon Sep 17 00:00:00 2001 From: guke Date: Thu, 23 Jul 2026 16:48:55 +0800 Subject: [PATCH] =?UTF-8?q?fix(withdraw):=20=E5=85=8D=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?=E6=8E=88=E6=9D=83=E5=B7=B2=E5=BC=80=E5=90=AF=E5=88=A4=E5=AE=9A?= =?UTF-8?q?=E5=8A=A0=20authorization=5Fid=20=E9=9D=9E=E7=A9=BA,=E4=B8=8E?= =?UTF-8?q?=E6=89=93=E6=AC=BE=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- app/api/v1/wallet.py | 5 ++-- tests/test_withdraw.py | 59 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/app/api/v1/wallet.py b/app/api/v1/wallet.py index 454ff7c..7b0dac0 100644 --- a/app/api/v1/wallet.py +++ b/app/api/v1/wallet.py @@ -192,7 +192,7 @@ def withdraw_info( wechat_bound=bool(u and u.wechat_openid), wechat_nickname=u.wechat_nickname if u else None, wechat_avatar_url=u.wechat_avatar_url if u else None, - transfer_auth_enabled=bool(auth and auth.state == "active"), + transfer_auth_enabled=bool(auth and auth.state == "active" and auth.authorization_id), tiers=[WithdrawTierOut(**t) for t in crud_wallet.withdraw_tier_states(db, user.id, source)], ) @@ -335,7 +335,8 @@ def open_transfer_auth(user: CurrentUser, db: DbSession) -> TransferAuthResultOu def transfer_auth_status(user: CurrentUser, db: DbSession) -> TransferAuthStatusOut: auth = crud_wallet.sync_transfer_auth(db, user.id) state = auth.state if auth else "none" - return TransferAuthStatusOut(state=state, enabled=(state == "active")) + enabled = bool(auth and auth.state == "active" and auth.authorization_id) + return TransferAuthStatusOut(state=state, enabled=enabled) @router.post( diff --git a/tests/test_withdraw.py b/tests/test_withdraw.py index 55a978a..509fb0a 100644 --- a/tests/test_withdraw.py +++ b/tests/test_withdraw.py @@ -9,7 +9,7 @@ from sqlalchemy import select from app.db.session import SessionLocal from app.models.user import User -from app.models.wallet import CoinAccount, WithdrawOrder +from app.models.wallet import CoinAccount, WithdrawOrder, WechatTransferAuthorization from app.repositories import wallet as crud_wallet @@ -426,3 +426,60 @@ def test_withdraw_reject_refunds(client, monkeypatch) -> None: r = client.get("/api/v1/wallet/withdraw/status", params={"out_bill_no": bill}, headers=_auth(token)) assert r.json()["status"] == "rejected" assert r.json()["fail_reason"] == "测试拒绝" + + +# ===== §fix 免确认授权 enabled 判定收严:active+authorization_id 非空才算已授权 ===== + +def _seed_transfer_auth(phone: str, state: str, authorization_id: str | None) -> None: + """直接在库里写/改该用户的免确认授权记录(绕过微信,用于判定测试)。""" + db = SessionLocal() + try: + user = db.execute(select(User).where(User.phone == phone)).scalar_one() + auth = db.get(WechatTransferAuthorization, user.id) + if auth is None: + auth = WechatTransferAuthorization( + user_id=user.id, openid="openid_test_abc", + out_authorization_no=f"oan_{user.id}", + ) + db.add(auth) + auth.state = state + auth.authorization_id = authorization_id + db.commit() + finally: + db.close() + + +def test_withdraw_info_auth_enabled_requires_authorization_id(client, monkeypatch) -> None: + _patch_userinfo(monkeypatch) + token = _login(client, "13800002051") + # 触发建号 + 绑定微信(withdraw-info 读 openid) + client.get("/api/v1/wallet/withdraw-info", headers=_auth(token)) + client.post("/api/v1/wallet/bind-wechat", json={"code": "c1"}, headers=_auth(token)) + + # active 但无 authorization_id → 视为未授权 + _seed_transfer_auth("13800002051", "active", None) + r = client.get("/api/v1/wallet/withdraw-info", headers=_auth(token)) + assert r.status_code == 200, r.text + assert r.json()["transfer_auth_enabled"] is False + + # active 且有 authorization_id → 已授权 + _seed_transfer_auth("13800002051", "active", "wx_auth_123") + r = client.get("/api/v1/wallet/withdraw-info", headers=_auth(token)) + assert r.json()["transfer_auth_enabled"] is True + + +def test_transfer_auth_status_requires_authorization_id(client, monkeypatch) -> None: + _patch_userinfo(monkeypatch) + token = _login(client, "13800002052") + client.get("/api/v1/wallet/withdraw-info", headers=_auth(token)) + client.post("/api/v1/wallet/bind-wechat", json={"code": "c2"}, headers=_auth(token)) + + _seed_transfer_auth("13800002052", "active", None) + r = client.get("/api/v1/wallet/transfer-auth/status", headers=_auth(token)) + assert r.status_code == 200, r.text + assert r.json()["state"] == "active" + assert r.json()["enabled"] is False + + _seed_transfer_auth("13800002052", "active", "wx_auth_456") + r = client.get("/api/v1/wallet/transfer-auth/status", headers=_auth(token)) + assert r.json()["enabled"] is True