jiachenlong/backend/app/services/sms.py

143 lines
4.1 KiB
Python
Raw Normal View History

2026-03-23 11:08:52 +08:00
# 阿里云短信服务
import os
import random
import string
import time
from datetime import datetime, timedelta
from typing import Optional
2026-04-12 09:49:18 +08:00
# 阿里云短信配置 - 从 config.py 读取(不再硬编码默认值)
from app.core.config import settings
from app.core.logging_config import logger
2026-04-12 09:49:18 +08:00
SMS_CONFIG = settings.get_sms_config()
2026-03-23 11:08:52 +08:00
# 验证码缓存生产环境建议用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'
}
}
)
2026-03-23 11:08:52 +08:00
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
}
}
)
2026-03-23 11:08:52 +08:00
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
)
2026-03-23 11:08:52 +08:00
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