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>
74 lines
1.9 KiB
TOML
74 lines
1.9 KiB
TOML
[project]
|
|
name = "shaguabijia-app-server"
|
|
version = "0.1.0"
|
|
description = "Shaguabijia 正式 App 后端 (FastAPI + SQLAlchemy + JWT)"
|
|
requires-python = ">=3.10"
|
|
dependencies = [
|
|
# Web 框架 & ASGI
|
|
"fastapi>=0.115.0",
|
|
"uvicorn[standard]>=0.32.0",
|
|
|
|
# 数据校验
|
|
"pydantic>=2.9.0",
|
|
"pydantic-settings>=2.5.0",
|
|
"email-validator>=2.2.0",
|
|
|
|
# ORM & 迁移
|
|
"sqlalchemy>=2.0.35",
|
|
"alembic>=1.13.3",
|
|
|
|
# PostgreSQL 驱动 (psycopg3, SQLAlchemy 2.0 时代默认, 不要再装 psycopg2)
|
|
"psycopg[binary]>=3.1",
|
|
|
|
# JWT 签名 / 校验
|
|
"pyjwt[crypto]>=2.9.0",
|
|
|
|
# 极光一键登录:RSA 解密极光返回的加密手机号
|
|
"cryptography>=42.0.0",
|
|
|
|
# HTTP 客户端 (调极光 REST)
|
|
"httpx>=0.27.0",
|
|
|
|
# 阿里云号码认证(dypns)短信验证码 provider(SMS_PROVIDER=aliyun 时用;签名由 SDK 处理)
|
|
"alibabacloud_dypnsapi20170525>=2.0.0",
|
|
|
|
# multipart form (FastAPI 表单上传依赖)
|
|
"python-multipart>=0.0.9",
|
|
|
|
# admin 后台账号密码 hash(用户侧是手机号+验证码登录,不需要密码;admin 才用)
|
|
"bcrypt>=4.0.0",
|
|
|
|
# 邀请指纹归因:解析浏览器 UA 拿手机型号(Build.MODEL),跨端匹配用
|
|
"user-agents>=2.2.0",
|
|
|
|
# 离线经纬度→城市反查(美团 CPS 按城市过滤);会带入 numpy/scipy 依赖
|
|
"reverse_geocoder>=1.5.1",
|
|
]
|
|
|
|
[project.optional-dependencies]
|
|
dev = [
|
|
"pytest>=8.0.0",
|
|
"pytest-asyncio>=0.24.0",
|
|
"httpx>=0.27.0",
|
|
"ruff>=0.6.0",
|
|
]
|
|
|
|
[tool.setuptools.packages.find]
|
|
include = ["app*"]
|
|
|
|
# 随包分发的运行时数据文件(美团城市词典;被 app/utils/meituan_city.py 加载)
|
|
[tool.setuptools.package-data]
|
|
"app.utils" = ["data/*.txt"]
|
|
|
|
[tool.pytest.ini_options]
|
|
asyncio_mode = "auto"
|
|
testpaths = ["tests"]
|
|
|
|
[tool.ruff]
|
|
line-length = 100
|
|
target-version = "py311"
|
|
|
|
[tool.ruff.lint]
|
|
select = ["E", "F", "W", "I", "B", "UP"]
|
|
ignore = ["E501"]
|