Files
DjangoAdmin_Flask_EleVue/application/services/sms_svc.py
T
DjangoAdmin DevandClaude 15f2a5e097 chore: 基线提交(重构前代码快照)
- git 初始化 + .gitignore 补全(根 .env / logs / instance / __pycache__ 等)
- 修复 utils/.gitignore 误将整个 utils/ 源码层忽略的问题,补入全部工具源码(含 ip2region.xdb)
- 含后端全部代码:application / extensions / utils / config / migrations
- 含前端 ui/(后续整体迁走)
- 不含根 .env(含密钥,本地保留)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-07 12:12:06 +08:00

194 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
短信发送服务模块
=============================================================
封装阿里云短信 SDK 提供短信发送能力,供业务模块和定时任务调用。
使用方式:
from application.services import sms_svc
# 模板短信(推荐)
sms_svc.send_sms(
phone='13800138000',
template_code='SMS_123456',
template_params={'code': '123456'}
)
# 指定签名(覆盖默认签名)
sms_svc.send_sms(
phone='13800138000',
template_code='SMS_123456',
template_params={'code': '123456'},
sign_name='我的应用'
)
# 群发(多号码)
sms_svc.send_sms(
phone=['13800138000', '13900139000'],
template_code='SMS_123456',
template_params={'name': '张三'}
)
"""
import json
import logging
from typing import Dict, List, Optional, Union
from config import (
SMS_ALIYUN_ACCESS_KEY_ID,
SMS_ALIYUN_ACCESS_KEY_SECRET,
SMS_ALIYUN_CONNECT_TIMEOUT,
SMS_ALIYUN_ENDPOINT,
SMS_ALIYUN_READ_TIMEOUT,
SMS_ALIYUN_SIGN_NAME,
SMS_PROVIDER,
)
logger = logging.getLogger(__name__)
# ============================================================
# 阿里云短信客户端(延迟初始化)
# ============================================================
_client = None
def _get_client():
"""
获取阿里云短信客户端(单例,延迟初始化)
Returns:
dysmsapi.Client: 阿里云短信客户端实例
Raises:
ImportError: 阿里云 SDK 未安装
Exception: 客户端创建失败
"""
global _client
if _client is not None:
return _client
try:
from alibabacloud_dysmsapi20170525.client import Client
from alibabacloud_tea_openapi.models import Config
except ImportError:
raise ImportError(
"阿里云短信 SDK 未安装,请执行: "
"pip install alibabacloud-dysmsapi20170525 alibabacloud-tea-openapi"
)
config = Config(
access_key_id=SMS_ALIYUN_ACCESS_KEY_ID,
access_key_secret=SMS_ALIYUN_ACCESS_KEY_SECRET,
endpoint=SMS_ALIYUN_ENDPOINT,
connect_timeout=SMS_ALIYUN_CONNECT_TIMEOUT,
read_timeout=SMS_ALIYUN_READ_TIMEOUT,
)
_client = Client(config)
return _client
# ============================================================
# 短信发送
# ============================================================
def send_sms(
phone: Union[str, List[str]],
template_code: str,
template_params: Optional[Dict] = None,
sign_name: Optional[str] = None,
) -> bool:
"""
发送短信(阿里云模板短信)
Args:
phone: 手机号,字符串或列表(群发时传入多个号码)
template_code: 短信模板CODE(在阿里云控制台获取,如 SMS_123456)
template_params: 模板变量,如 {'code': '123456'} 对应模板中的 ${code}
sign_name: 短信签名(可选,默认使用配置中的 SMS_ALIYUN_SIGN_NAME)
Returns:
bool: 全部发送成功返回 True,任一失败返回 False
"""
if not phone:
logger.warning("发送短信失败:手机号不能为空")
return False
if not template_code:
logger.warning("发送短信失败:模板CODE不能为空")
return False
if not sign_name:
sign_name = SMS_ALIYUN_SIGN_NAME
if not sign_name:
logger.warning("发送短信失败:短信签名未配置")
return False
# 统一转为列表
phones = [phone] if isinstance(phone, str) else phone
# 检查 AccessKey 配置
if not SMS_ALIYUN_ACCESS_KEY_ID or not SMS_ALIYUN_ACCESS_KEY_SECRET:
logger.warning("发送短信失败:阿里云 AccessKey 未配置")
return False
# 模板变量序列化
params_str = json.dumps(template_params, ensure_ascii=False) if template_params else ''
# 逐条发送(阿里云短信 API 不支持批量同发)
all_success = True
for p in phones:
if not _send_one(p, sign_name, template_code, params_str):
all_success = False
return all_success
def _send_one(phone: str, sign_name: str, template_code: str, template_params: str) -> bool:
"""
发送单条短信
Args:
phone: 手机号
sign_name: 短信签名
template_code: 模板CODE
template_params: 模板变量 JSON 字符串
Returns:
bool: 发送成功返回 True
"""
try:
from alibabacloud_dysmsapi20170525 import models as dysms_models
client = _get_client()
request = dysms_models.SendSmsRequest(
phone_numbers=phone,
sign_name=sign_name,
template_code=template_code,
template_param=template_params,
)
response = client.send_sms(request)
body = response.body
if body.code == 'OK':
logger.info(f"短信发送成功: phone={phone}, template={template_code}, biz_id={body.biz_id}")
return True
else:
logger.warning(
f"短信发送失败: phone={phone}, template={template_code}, "
f"code={body.code}, message={body.message}"
)
return False
except ImportError as e:
logger.error(f"阿里云短信 SDK 未安装: {str(e)}")
return False
except Exception as e:
logger.error(f"短信发送异常: phone={phone}, template={template_code}, error={str(e)}")
return False