d9177cbb71
Co-authored-by: exinglang <exinglang@qq.com> Reviewed-on: #208 Co-authored-by: zuochenyong <zuochenyong@wonderable.ai> Co-committed-by: zuochenyong <zuochenyong@wonderable.ai>
80 lines
3.1 KiB
Python
80 lines
3.1 KiB
Python
"""测试账号:免验证码登录 + 每次重走新手引导 + 每日使用上限。
|
|
|
|
为打通 **release 包全流程**(无 SIM 卡、不走极光一键登录)而设的一个固定测试手机号。
|
|
对这个号(且仅这个号)放开三件事:
|
|
1. **免短信验证码**:`/sms/send` 不真发、`/sms/login` 跳过 `verify_code`,real 模式下同样生效
|
|
—— 测试时直接填任意验证码即可登入,不用等真短信。
|
|
2. **每次都走新手引导**:登录响应 `onboarding_completed` 恒为 false,不读/不依赖
|
|
onboarding_completion 表,所以反复登录都能体验引导。
|
|
3. **每日使用次数上限**:防被人猜到这个号后写脚本一直刷。当天登录数超过上限即拒绝(429),
|
|
次日自动归零。
|
|
|
|
手机号与上限都在 .env 配(`TEST_ACCOUNT_PHONES` / `TEST_ACCOUNT_DAILY_LIMIT`),随时可改。
|
|
兼容旧的单号配置 `TEST_ACCOUNT_PHONE`;两项都留空 = 整个功能关闭(生产默认态)。
|
|
|
|
计数存**进程内存**(单 worker 够用,与 sms.py 同款约定):重启清零、多 worker 不共享。作为
|
|
一个测试号的粗粒度防滥用闸够用;且因所有登录都落同一个 phone → 同一个 user,滥用面天然只
|
|
限于这一个账号。要严格持久化/跨进程再迁 DB 或 Redis(同 sms.py 的技术债)。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from datetime import datetime
|
|
from threading import Lock
|
|
|
|
from app.core.config import settings
|
|
|
|
logger = logging.getLogger("shagua.test_account")
|
|
|
|
# 进程内每日计数:{手机号: (date_str, 当日已登录次数)}。单 worker 有效,重启清零。
|
|
_lock = Lock()
|
|
_usage: dict[str, tuple[str, int]] = {}
|
|
|
|
|
|
def _today() -> str:
|
|
return datetime.now().strftime("%Y-%m-%d")
|
|
|
|
|
|
def is_enabled() -> bool:
|
|
"""功能总开关:至少配置了一个测试手机号才启用。"""
|
|
return bool(settings.test_account_phones)
|
|
|
|
|
|
def is_test_account(phone: str) -> bool:
|
|
"""该手机号是否在测试账号集合中。"""
|
|
return phone in settings.test_account_phones
|
|
|
|
|
|
def try_consume_quota(phone: str) -> bool:
|
|
"""测试账号登录时调:当日计数 +1。
|
|
|
|
Returns:
|
|
True —— 未超当日上限,本次放行(计数已 +1);
|
|
False —— 已达上限,本次拒绝(计数不变)。
|
|
|
|
线程安全;跨天自动归零。上限取自 settings.TEST_ACCOUNT_DAILY_LIMIT(可在 .env 改)。
|
|
"""
|
|
global _usage
|
|
limit = settings.TEST_ACCOUNT_DAILY_LIMIT
|
|
with _lock:
|
|
today = _today()
|
|
day, cnt = _usage.get(phone, ("", 0))
|
|
if day != today: # 跨天归零
|
|
cnt = 0
|
|
if cnt >= limit:
|
|
_usage[phone] = (today, cnt) # 已满,保持不变
|
|
logger.warning(
|
|
"测试账号 %s 今日登录数已达上限 %d,拒绝", phone, limit
|
|
)
|
|
return False
|
|
_usage[phone] = (today, cnt + 1)
|
|
logger.info("测试账号 %s 第 %d/%d 次登录", phone, cnt + 1, limit)
|
|
return True
|
|
|
|
|
|
def _reset_for_test() -> None:
|
|
"""仅供单测:清空进程内计数,隔离用例间状态。"""
|
|
global _usage
|
|
with _lock:
|
|
_usage = {}
|