From cbe1d340d6b7314985038dfe56bd42e69f4df1b8 Mon Sep 17 00:00:00 2001 From: guke Date: Thu, 30 Jul 2026 15:39:31 +0800 Subject: [PATCH] =?UTF-8?q?feat(sms):=20=E5=8A=A0=20SendResult=20=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E7=B1=BB=E5=9E=8B(cooldown+provider+fallback)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- app/integrations/sms/base.py | 15 +++++++++++++++ tests/test_sms_base.py | 26 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/test_sms_base.py diff --git a/app/integrations/sms/base.py b/app/integrations/sms/base.py index 702910b..7458014 100644 --- a/app/integrations/sms/base.py +++ b/app/integrations/sms/base.py @@ -5,6 +5,8 @@ """ from __future__ import annotations +from dataclasses import dataclass + from app.core.config import settings @@ -18,6 +20,19 @@ class SmsError(Exception): self.status_code = status_code +@dataclass(frozen=True) +class SendResult: + """发码结果:距下次可发秒数 + 实际发码的 provider 名 + 是否走了备(fallback)。 + + 分派层 send_code 的返回类型(provider 各自的 send_code 仍返回 int cooldown, + 由分派层包装)。auth 层据此把渠道记入风控流水 details。 + """ + + cooldown_sec: int + provider: str + fallback: bool = False + + def mock_verify(code: str) -> bool: """mock 模式校验:放行任意 SMS_CODE_LENGTH 位数字(provider 无关,测试/开发便利,不真校验)。""" return len(code) == settings.SMS_CODE_LENGTH and code.isdigit() diff --git a/tests/test_sms_base.py b/tests/test_sms_base.py new file mode 100644 index 0000000..50ee530 --- /dev/null +++ b/tests/test_sms_base.py @@ -0,0 +1,26 @@ +"""SMS 共享基座类型 SendResult。""" +from __future__ import annotations + +import dataclasses + +import pytest + +from app.integrations.sms.base import SendResult + + +def test_send_result_fields(): + r = SendResult(cooldown_sec=60, provider="jiguang", fallback=True) + assert r.cooldown_sec == 60 + assert r.provider == "jiguang" + assert r.fallback is True + + +def test_send_result_fallback_defaults_false(): + r = SendResult(cooldown_sec=60, provider="jiguang") + assert r.fallback is False + + +def test_send_result_is_frozen(): + r = SendResult(cooldown_sec=60, provider="jiguang") + with pytest.raises(dataclasses.FrozenInstanceError): + r.provider = "chuanglan"