upload project source code

This commit is contained in:
2026-04-30 18:49:43 +08:00
commit 9b394ba682
2277 changed files with 660945 additions and 0 deletions

View File

@@ -0,0 +1,190 @@
# -*- coding: utf-8 -*-
import json
import random
import string
from typing import Dict, Any
from alibabacloud_dysmsapi20170525.client import Client as DysmsapiClient
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_dysmsapi20170525 import models as dysmsapi_models
from alibabacloud_tea_util import models as util_models
from app.core.logger import log
from app.core.exceptions import CustomException
class SMSUtil:
"""阿里云短信服务工具类"""
def __init__(self):
"""初始化阿里云短信客户端"""
# 阿里云短信配置信息
self.access_key_id = "LTAI5t7ox6LSot4bTXQiU39R"
self.access_key_secret = "X4Z5K3ZSrZcXzcc5HgWZNmMUmTvK8N"
self.region_id = "cn-hangzhou"
self.sign_name = "山东实战派网络科技"
self.international_sign_name = "Shando Tech"
self.enable_international = True
self.domestic_endpoint = "dysmsapi.aliyuncs.com"
self.international_endpoint = "dysmsapi.aliyuncs.com"
# 短信模板配置
self.templates = {
# 国内短信模板
"register": "SMS_486390015", # 注册验证码模板
"resetpwd": "SMS_486445022", # 重置密码验证码模板
"changepwd": "SMS_486450016", # 修改密码验证码模板
"changemobile": "SMS_487250049", # 修改手机号验证码模板
"mobilelogin": "SMS_487410035", # 手机登录验证码模板
# 国际短信模板
"international_register": "SMS_INTL_486390015",
"international_resetpwd": "SMS_INTL_486445022",
"international_changepwd": "SMS_INTL_486450016",
"international_changemobile": "SMS_INTL_487250049",
"international_mobilelogin": "SMS_INTL_487410035"
}
def _create_client(self, is_international: bool = False) -> DysmsapiClient:
"""创建阿里云短信客户端"""
config = open_api_models.Config(
access_key_id=self.access_key_id,
access_key_secret=self.access_key_secret
)
# 设置访问的域名
if is_international:
config.endpoint = self.international_endpoint
else:
config.endpoint = self.domestic_endpoint
return DysmsapiClient(config)
def _is_international_mobile(self, mobile: str) -> bool:
"""判断是否为国际手机号"""
# 简单判断中国大陆手机号以1开头11位数字
if mobile.startswith('+'):
return True
if len(mobile) == 11 and mobile.startswith('1') and mobile.isdigit():
return False
return True
def generate_verification_code(self, length: int = 6) -> str:
"""生成验证码"""
return ''.join(random.choices(string.digits, k=length))
async def send_sms(
self,
mobile: str,
template_type: str,
template_params: Dict[str, Any] = None
) -> bool:
"""
发送短信
参数:
- mobile: 手机号
- template_type: 模板类型 (register, resetpwd, changepwd, changemobile, mobilelogin)
- template_params: 模板参数,如 {"code": "123456"}
返回:
- bool: 发送是否成功
"""
try:
# 判断是否为国际手机号
is_international = self._is_international_mobile(mobile)
# 获取对应的模板ID和签名
if is_international and self.enable_international:
template_id = self.templates.get(f"international_{template_type}")
sign_name = self.international_sign_name
else:
template_id = self.templates.get(template_type)
sign_name = self.sign_name
if not template_id:
raise CustomException(msg=f"未找到模板类型: {template_type}")
# 创建客户端
client = self._create_client(is_international)
# 构建请求
send_sms_request = dysmsapi_models.SendSmsRequest(
phone_numbers=mobile,
sign_name=sign_name,
template_code=template_id,
template_param=json.dumps(template_params) if template_params else None
)
# 发送短信
runtime = util_models.RuntimeOptions()
response = client.send_sms_with_options(send_sms_request, runtime)
# 检查响应
if response.body.code == "OK":
log.info(f"短信发送成功: {mobile}, 模板: {template_type}")
return True
else:
error_code = response.body.code
error_message = response.body.message
log.error(f"短信发送失败: {mobile}, 错误码: {error_code}, 错误信息: {error_message}")
# 根据错误码抛出不同的异常信息
if error_code == "isv.BUSINESS_LIMIT_CONTROL":
if "小时级流控" in error_message:
raise CustomException(msg="发送过于频繁同一手机号1小时内最多发送5条短信请稍后再试")
elif "天级流控" in error_message:
raise CustomException(msg="今日短信发送次数已达上限,请明天再试")
else:
raise CustomException(msg="短信发送频率超限,请稍后再试")
elif error_code == "isv.SMS_SIGNATURE_ILLEGAL":
raise CustomException(msg="短信签名配置错误,请联系管理员")
elif error_code == "isv.SMS_TEMPLATE_ILLEGAL":
raise CustomException(msg="短信模板配置错误,请联系管理员")
elif error_code == "isv.INVALID_PARAMETERS":
raise CustomException(msg="手机号格式错误")
elif error_code == "isv.MOBILE_NUMBER_ILLEGAL":
raise CustomException(msg="手机号格式不正确或为空号")
elif error_code == "isv.AMOUNT_NOT_ENOUGH":
raise CustomException(msg="短信余额不足,请联系管理员")
else:
raise CustomException(msg=f"短信发送失败: {error_message}")
return False
except CustomException:
# 重新抛出业务异常
raise
except Exception as e:
log.error(f"短信发送异常: {mobile}, 错误: {str(e)}")
raise CustomException(msg="短信服务异常,请稍后重试")
async def send_verification_code(
self,
mobile: str,
code_type: str = "register"
) -> tuple[bool, str]:
"""
发送验证码短信
参数:
- mobile: 手机号
- code_type: 验证码类型 (register, resetpwd, changepwd, changemobile, mobilelogin)
返回:
- tuple[bool, str]: (是否成功, 验证码)
"""
# 生成验证码
verification_code = self.generate_verification_code()
# 发送短信如果发生异常会直接抛出不会返回False
try:
success = await self.send_sms(
mobile=mobile,
template_type=code_type,
template_params={"code": verification_code}
)
return success, verification_code if success else ""
except CustomException:
# 重新抛出业务异常,让上层处理
raise