jiachenlong/backend/app/services/sms.py

143 lines
4.1 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.

# 阿里云短信服务
import os
import random
import string
import time
from datetime import datetime, timedelta
from typing import Optional
# 阿里云短信配置 - 从 config.py 读取(不再硬编码默认值)
from app.core.config import settings
from app.core.logging_config import logger
SMS_CONFIG = settings.get_sms_config()
# 验证码缓存生产环境建议用Redis
# 格式: { phone: { code: "123456", expire: 1234567890 } }
VERIFICATION_CODES = {}
def generate_code(length: int = 6) -> str:
"""生成6位数字验证码"""
return ''.join(random.choices(string.digits, k=length))
def send_verification_code(phone: str) -> dict:
"""发送短信验证码"""
from alibabacloud_dysmsapi20170525 import models
from alibabacloud_dysmsapi20170525.client import Client
from alibabacloud_tea_openapi import models as open_models
try:
# 生成验证码
code = generate_code(6)
# 配置客户端
config = open_models.Config(
access_key_id=SMS_CONFIG["access_key_id"],
access_key_secret=SMS_CONFIG["access_key_secret"],
)
config.endpoint = "dysmsapi.aliyuncs.com"
config.region_id = "cn-hangzhou"
client = Client(config)
# 构造请求
request = models.SendSmsRequest(
phone_numbers=phone,
sign_name=SMS_CONFIG["sign_name"],
template_code=SMS_CONFIG["template_code"],
template_param=f'{{"code":"{code}"}}'
)
# 发送
response = client.send_sms(request)
# 检查结果
if response.body.code == "OK":
# 保存验证码
VERIFICATION_CODES[phone] = {
"code": code,
"expire": int(time.time()) + 300 # 5分钟有效
}
# 记录短信发送成功日志(不记录验证码)
logger.info(
"短信验证码发送成功",
extra={
'data': {
'phone': phone[:3] + '****' + phone[7:],
'provider': 'aliyun'
}
}
)
return {
"success": True,
"message": "验证码已发送",
"expire": 300
}
else:
# 记录短信发送失败日志
logger.warning(
"短信验证码发送失败",
extra={
'data': {
'phone': phone[:3] + '****' + phone[7:],
'provider': 'aliyun',
'error_code': response.body.code,
'error_message': response.body.message
}
}
)
return {
"success": False,
"message": f"发送失败: {response.body.message}"
}
except Exception as e:
# 记录短信发送异常日志
logger.error(
"短信验证码发送异常",
extra={
'data': {
'phone': phone[:3] + '****' + phone[7:] if len(phone) >= 11 else phone,
'provider': 'aliyun',
'error': str(e)
}
},
exc_info=True
)
return {
"success": False,
"message": f"发送失败: {str(e)}"
}
def verify_code(phone: str, code: str) -> bool:
"""验证验证码"""
if phone not in VERIFICATION_CODES:
return False
stored = VERIFICATION_CODES[phone]
# 检查是否过期
if int(time.time()) > stored["expire"]:
del VERIFICATION_CODES[phone]
return False
# 验证码匹配
if stored["code"] == code:
# 验证成功,删除验证码
del VERIFICATION_CODES[phone]
return True
return False
def check_code_exists(phone: str) -> bool:
"""检查是否已发送过验证码"""
return phone in VERIFICATION_CODES