Files
ai_wht_wechat/backend/ali_sms_service.py
2026-01-06 19:36:42 +08:00

204 lines
7.5 KiB
Python
Raw Permalink 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.

"""
阿里云短信服务模块
用于发送手机验证码
"""
import json
import random
import sys
from typing import Dict, Any, Optional
from datetime import datetime, timedelta
from alibabacloud_dysmsapi20170525.client import Client as Dysmsapi20170525Client
from alibabacloud_credentials.client import Client as CredentialClient
from alibabacloud_credentials.models import Config as CredentialConfig
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_dysmsapi20170525 import models as dysmsapi_20170525_models
from alibabacloud_tea_util import models as util_models
class AliSmsService:
"""阿里云短信服务"""
def __init__(self, access_key_id: str, access_key_secret: str, sign_name: str, template_code: str):
"""
初始化阿里云短信服务
Args:
access_key_id: 阿里云AccessKey ID
access_key_secret: 阿里云AccessKey Secret
sign_name: 短信签名
template_code: 短信模板CODE
"""
self.sign_name = sign_name
self.template_code = template_code
# 创建阿里云短信客户端
credential_config = CredentialConfig(
type='access_key',
access_key_id=access_key_id,
access_key_secret=access_key_secret
)
credential = CredentialClient(credential_config)
config = open_api_models.Config(credential=credential)
config.endpoint = 'dysmsapi.aliyuncs.com'
self.client = Dysmsapi20170525Client(config)
# 验证码缓存简单内存存储生产环境应使用Redis
self._code_cache: Dict[str, Dict[str, Any]] = {}
# 验证码配置
self.code_length = 6 # 验证码长度
self.code_expire_minutes = 5 # 验证码过期时间(分钟)
def _generate_code(self) -> str:
"""生成随机验证码"""
return ''.join([str(random.randint(0, 9)) for _ in range(self.code_length)])
async def send_verification_code(self, phone: str) -> Dict[str, Any]:
"""
发送验证码到指定手机号
Args:
phone: 手机号
Returns:
Dict containing success status and error message if any
"""
try:
# 生成验证码
code = self._generate_code()
print(f"[短信服务] 正在发送验证码到 {phone},验证码: {code}", file=sys.stderr)
# 构建短信请求
send_sms_request = dysmsapi_20170525_models.SendSmsRequest(
phone_numbers=phone,
sign_name=self.sign_name,
template_code=self.template_code,
template_param=json.dumps({"code": code})
)
runtime = util_models.RuntimeOptions()
# 发送短信
try:
resp = self.client.send_sms_with_options(send_sms_request, runtime)
resp_dict = resp.to_map()
print(f"[短信服务] 阿里云响应: {json.dumps(resp_dict, default=str, indent=2, ensure_ascii=False)}", file=sys.stderr)
# 检查发送结果
if resp_dict.get('body', {}).get('Code') == 'OK':
# 缓存验证码
self._code_cache[phone] = {
'code': code,
'expire_time': datetime.now() + timedelta(minutes=self.code_expire_minutes),
'sent_at': datetime.now()
}
print(f"[短信服务] 验证码发送成功,手机号: {phone}", file=sys.stderr)
return {
"success": True,
"message": f"验证码已发送,{self.code_expire_minutes}分钟内有效",
"code": code # 开发环境返回验证码,生产环境应移除
}
else:
error_msg = resp_dict.get('body', {}).get('Message', '未知错误')
print(f"[短信服务] 发送失败: {error_msg}", file=sys.stderr)
return {
"success": False,
"error": f"短信发送失败: {error_msg}"
}
except Exception as e:
error_msg = str(e)
print(f"[短信服务] 发送异常: {error_msg}", file=sys.stderr)
# 如果有诊断地址,打印出来
if hasattr(e, 'data') and e.data:
recommend = e.data.get('Recommend')
if recommend:
print(f"[短信服务] 诊断地址: {recommend}", file=sys.stderr)
return {
"success": False,
"error": f"短信发送异常: {error_msg}"
}
except Exception as e:
print(f"[短信服务] 发送验证码失败: {str(e)}", file=sys.stderr)
return {
"success": False,
"error": str(e)
}
def verify_code(self, phone: str, code: str) -> Dict[str, Any]:
"""
验证手机号和验证码
Args:
phone: 手机号
code: 用户输入的验证码
Returns:
Dict containing verification result
"""
try:
# 检查验证码是否存在
if phone not in self._code_cache:
return {
"success": False,
"error": "验证码未发送或已过期,请重新获取"
}
cached_data = self._code_cache[phone]
# 检查是否过期
if datetime.now() > cached_data['expire_time']:
# 删除过期验证码
del self._code_cache[phone]
return {
"success": False,
"error": "验证码已过期,请重新获取"
}
# 验证码匹配
if code == cached_data['code']:
# 验证成功后删除验证码(一次性使用)
del self._code_cache[phone]
print(f"[短信服务] 验证码验证成功,手机号: {phone}", file=sys.stderr)
return {
"success": True,
"message": "验证码验证成功"
}
else:
return {
"success": False,
"error": "验证码错误,请重新输入"
}
except Exception as e:
print(f"[短信服务] 验证码验证失败: {str(e)}", file=sys.stderr)
return {
"success": False,
"error": str(e)
}
def cleanup_expired_codes(self):
"""清理过期的验证码"""
current_time = datetime.now()
expired_phones = [
phone for phone, data in self._code_cache.items()
if current_time > data['expire_time']
]
for phone in expired_phones:
del self._code_cache[phone]
if expired_phones:
print(f"[短信服务] 已清理 {len(expired_phones)} 个过期验证码", file=sys.stderr)