dfa3d4f07e
新增第三个可切换短信 provider chuanglan,与极光/阿里云并列(SMS_PROVIDER 切换,默认仍 jiguang,未知值回退 jiguang)。创蓝 tpl/send v2 是纯发送网关:本服务生成码、创蓝只下发、 本地内存校验(与极光同 Mode B)。存码/冷却/一次性/防爆破/GC 从极光隔离复制(极光文件不动, 零回归风险),唯一新逻辑是 HMAC-SHA256 签名(password 不上行)+ httpx POST + 错误码映射。 复用现有 httpx + 标准库 hashlib/hmac,无新依赖。 - app/integrations/sms/chuanglan.py: 新 provider - app/integrations/sms/__init__.py: 分派器改 dict + chuanglan 路由 - app/core/config.py: SMS_PROVIDER 加 chuanglan + CHUANGLAN_SMS_* + chuanglan_sms_configured - .env.example / docs/integrations/sms.md / docs/integrations/chuanglan/tpl-send.md: 配置与接口调研文档 - docs/superpowers/specs/2026-07-26-chuanglan-sms-verify-design.md: 设计 spec - tests/test_sms_chuanglan.py(28 例)+ test_sms_dispatch.py 扩 chuanglan 路由 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
38 lines
1.7 KiB
Python
38 lines
1.7 KiB
Python
"""短信验证码服务 —— provider 分派入口。
|
|
|
|
对外只暴露 `send_code` / `verify_code` / `SmsError`,api 层无需关心用哪个 provider。
|
|
provider 由 `settings.SMS_PROVIDER` 选择(**每次调用读取**,支持运行时切换 + 灰度回退):
|
|
- `jiguang`(默认):自管码(本服务生成、内存存/校验,极光只发)。见 [jiguang.py](jiguang.py)。
|
|
- `aliyun`:阿里云号码认证(阿里云生成+下发+校验,Mode A)。见 [aliyun.py](aliyun.py)。
|
|
- `chuanglan`:创蓝云智(253)模板短信,自管码 Mode B(本服务生成、内存存/校验,创蓝只发)。见 [chuanglan.py](chuanglan.py)。
|
|
|
|
mock(`SMS_MOCK=true`)与各 provider 的行为差异都封在 provider 内部;本层只做路由。
|
|
拆包前本模块是单文件 `sms.py`;拆包后极光逻辑迁入 `jiguang` 子模块,行为零改动。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from app.core.config import settings
|
|
|
|
from . import aliyun, chuanglan, jiguang
|
|
from .base import SmsError
|
|
|
|
__all__ = ["SmsError", "send_code", "verify_code"]
|
|
|
|
# provider 名 -> 模块;未知/缺省值回退 jiguang(默认兜底,防误配把登录打挂)。
|
|
_PROVIDERS = {"aliyun": aliyun, "chuanglan": chuanglan}
|
|
|
|
|
|
def _provider():
|
|
"""按配置选 provider 模块(每次调用读 settings,支持运行时切换 / 测试注入)。"""
|
|
return _PROVIDERS.get(settings.SMS_PROVIDER, jiguang)
|
|
|
|
|
|
def send_code(phone: str) -> int:
|
|
"""发送验证码,返回距下次可发的冷却秒数;失败抛 SmsError。委托给当前 provider。"""
|
|
return _provider().send_code(phone)
|
|
|
|
|
|
def verify_code(phone: str, code: str) -> bool:
|
|
"""校验验证码,返回是否通过;provider 异常降级抛 SmsError。委托给当前 provider。"""
|
|
return _provider().verify_code(phone, code)
|