84 lines
2.9 KiB
Python
84 lines
2.9 KiB
Python
|
|
"""
|
|||
|
|
SMS 服务抽象层
|
|||
|
|
Mock 模式:验证码打印到后端日志
|
|||
|
|
后续可接入阿里云短信 / 腾讯云 SMS,只需替换 Provider 实现
|
|||
|
|
"""
|
|||
|
|
import logging
|
|||
|
|
from abc import ABC, abstractmethod
|
|||
|
|
from app.core.config import settings
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class SmsProvider(ABC):
|
|||
|
|
"""SMS 发送提供者基类"""
|
|||
|
|
|
|||
|
|
@abstractmethod
|
|||
|
|
async def send(self, phone: str, code: str) -> bool:
|
|||
|
|
"""发送验证码短信,成功返回 True"""
|
|||
|
|
...
|
|||
|
|
|
|||
|
|
|
|||
|
|
class MockSmsProvider(SmsProvider):
|
|||
|
|
"""Mock 提供者 —— 验证码打印到后端日志(开发/测试环境用)"""
|
|||
|
|
|
|||
|
|
async def send(self, phone: str, code: str) -> bool:
|
|||
|
|
logger.info("=" * 50)
|
|||
|
|
logger.info("📱 [MockSMS] 验证码: %s → %s", code, phone)
|
|||
|
|
logger.info("=" * 50)
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TencentSmsProvider(SmsProvider):
|
|||
|
|
"""腾讯云 SMS 提供者(待接入)"""
|
|||
|
|
|
|||
|
|
def __init__(self, secret_id: str, secret_key: str, app_id: str, sign_name: str, template_id: str):
|
|||
|
|
self.secret_id = secret_id
|
|||
|
|
self.secret_key = secret_key
|
|||
|
|
self.app_id = app_id
|
|||
|
|
self.sign_name = sign_name
|
|||
|
|
self.template_id = template_id
|
|||
|
|
|
|||
|
|
async def send(self, phone: str, code: str) -> bool:
|
|||
|
|
# TODO: 接入腾讯云 SMS SDK
|
|||
|
|
logger.warning("TencentSmsProvider 尚未实现,验证码: %s → %s", code, phone)
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
|
|||
|
|
class AliyunSmsProvider(SmsProvider):
|
|||
|
|
"""阿里云短信提供者(待接入)"""
|
|||
|
|
|
|||
|
|
def __init__(self, access_key_id: str, access_key_secret: str, sign_name: str, template_code: str):
|
|||
|
|
self.access_key_id = access_key_id
|
|||
|
|
self.access_key_secret = access_key_secret
|
|||
|
|
self.sign_name = sign_name
|
|||
|
|
self.template_code = template_code
|
|||
|
|
|
|||
|
|
async def send(self, phone: str, code: str) -> bool:
|
|||
|
|
# TODO: 接入阿里云短信 SDK
|
|||
|
|
logger.warning("AliyunSmsProvider 尚未实现,验证码: %s → %s", code, phone)
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
|
|||
|
|
def get_sms_provider() -> SmsProvider:
|
|||
|
|
"""根据配置返回 SMS 提供者实例"""
|
|||
|
|
provider_name = getattr(settings, "SMS_PROVIDER", "mock") or "mock"
|
|||
|
|
|
|||
|
|
if provider_name == "tencent":
|
|||
|
|
return TencentSmsProvider(
|
|||
|
|
secret_id=getattr(settings, "TENCENT_SMS_SECRET_ID", ""),
|
|||
|
|
secret_key=getattr(settings, "TENCENT_SMS_SECRET_KEY", ""),
|
|||
|
|
app_id=getattr(settings, "TENCENT_SMS_APP_ID", ""),
|
|||
|
|
sign_name=getattr(settings, "TENCENT_SMS_SIGN_NAME", ""),
|
|||
|
|
template_id=getattr(settings, "TENCENT_SMS_TEMPLATE_ID", ""),
|
|||
|
|
)
|
|||
|
|
elif provider_name == "aliyun":
|
|||
|
|
return AliyunSmsProvider(
|
|||
|
|
access_key_id=getattr(settings, "ALIYUN_SMS_ACCESS_KEY_ID", ""),
|
|||
|
|
access_key_secret=getattr(settings, "ALIYUN_SMS_ACCESS_KEY_SECRET", ""),
|
|||
|
|
sign_name=getattr(settings, "ALIYUN_SMS_SIGN_NAME", ""),
|
|||
|
|
template_code=getattr(settings, "ALIYUN_SMS_TEMPLATE_CODE", ""),
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
return MockSmsProvider()
|