7a2b7cb8ed
现有 sms.py(极光自管码)升级为 app/integrations/sms/ 包:
- base : SmsError + provider 无关的 mock_verify
- jiguang: 原极光自管码逻辑逐字迁入,行为零改动(git 识别为 sms.py 的 rename)
- aliyun : 新增阿里云 dypns 号码认证(Mode A:阿里云生成+下发+校验,核验免费)
- __init__: 按 SMS_PROVIDER 每次调用路由的分派器(默认 jiguang,可秒切回退)
关键决策:
- Mode A:发码 SendSmsVerifyCode(##code## 占位)、校验 CheckSmsVerifyCode(PASS/UNKNOWN);
本服务不再存码 → 消除极光路径「内存存码、多 worker 不共享」技术债。
- 防爆破与极光一致:aliyun 保留 per-phone 失败计数(SMS_MAX_VERIFY_ATTEMPTS),达上限本地作废,
避免两 provider 行为不同致排查困惑(此为 aliyun 路径唯一本地态)。
- 校验降级:阿里云接口异常 → verify_code 抛 SmsError(503),auth 两处 try/except 透出 503(非误报 400)。
- 官方 SDK alibabacloud_dypnsapi20170525;SDK 交互隔离在 _call_send/_call_check(惰性 import + 惰性建
client),单测 monkeypatch 不触真网络。
测试:test_sms_aliyun(17)+ test_sms_dispatch(3)全绿;test_auth 内部访问 retarget 到 jiguang.*。
配置:SMS_PROVIDER + ALIYUN_SMS_*(见 .env.example);文档 docs/integrations/sms.md + aliyun 接口参考。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
24 lines
938 B
Python
24 lines
938 B
Python
"""短信 provider 共享基座:业务异常 + provider 无关的 mock 校验。
|
|
|
|
各 provider(jiguang / aliyun)都 `from .base import SmsError`,api 层也从包入口拿到同一个
|
|
`SmsError` —— 保证无论用哪个 provider,异常类型与 HTTP 码映射语义一致。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from app.core.config import settings
|
|
|
|
|
|
class SmsError(Exception):
|
|
"""业务异常。`status_code` 决定 api 层翻成哪个 HTTP 码:
|
|
过频/每日超限 = 429(客户端等会再来),供应商不可用 = 503,手机号无效 = 400。
|
|
"""
|
|
|
|
def __init__(self, message: str, status_code: int = 429) -> None:
|
|
super().__init__(message)
|
|
self.status_code = status_code
|
|
|
|
|
|
def mock_verify(code: str) -> bool:
|
|
"""mock 模式校验:放行任意 SMS_CODE_LENGTH 位数字(provider 无关,测试/开发便利,不真校验)。"""
|
|
return len(code) == settings.SMS_CODE_LENGTH and code.isdigit()
|