feat(交易记录): 新增交易记录管理页面与API接口 feat(上传): 添加统一上传接口支持自动识别文件类型 feat(用户管理): 为用户模型添加备注字段并更新相关接口 feat(邮件): 实现SMTP邮件发送功能并添加测试脚本 feat(短信): 增强短信服务配置灵活性与日志记录 fix(发票): 修复发票列表时间筛选功能 fix(nginx): 调整上传大小限制与超时配置 docs: 添加多个功能模块的说明文档 docs(估值): 补充估值计算流程与API提交数据说明 chore: 更新依赖与Docker镜像版本
107 lines
4.4 KiB
Python
107 lines
4.4 KiB
Python
import json
|
|
from typing import Optional, Dict, Any
|
|
|
|
from app.settings import settings
|
|
from app.log import logger
|
|
|
|
|
|
class SMSClient:
|
|
def __init__(self) -> None:
|
|
"""初始化短信客户端
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
self.client = None
|
|
|
|
def _ensure_client(self) -> None:
|
|
"""确保客户端初始化
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
if self.client is not None:
|
|
return
|
|
from alibabacloud_tea_openapi import models as open_api_models # type: ignore
|
|
from alibabacloud_dysmsapi20170525.client import Client as DysmsClient # type: ignore
|
|
from alibabacloud_credentials.client import Client as CredentialClient # type: ignore
|
|
use_chain = bool(settings.ALIYUN_USE_DEFAULT_CREDENTIALS) or (not settings.ALIBABA_CLOUD_ACCESS_KEY_ID or not settings.ALIBABA_CLOUD_ACCESS_KEY_SECRET)
|
|
if not use_chain and (not settings.ALIBABA_CLOUD_ACCESS_KEY_ID or not settings.ALIBABA_CLOUD_ACCESS_KEY_SECRET):
|
|
raise RuntimeError("短信凭证未配置:请设置 ALIBABA_CLOUD_ACCESS_KEY_ID/ALIBABA_CLOUD_ACCESS_KEY_SECRET 或启用默认凭据链")
|
|
if not settings.ALIYUN_SMS_SIGN_NAME:
|
|
raise RuntimeError("短信签名未配置:请设置 ALIYUN_SMS_SIGN_NAME")
|
|
if str(settings.ALIYUN_SMS_SIGN_NAME).upper().startswith("SMS_"):
|
|
raise RuntimeError("短信签名配置错误:签名不应为模板码")
|
|
if settings.ALIYUN_SMS_TEMPLATE_CODE_VERIFY and settings.ALIYUN_SMS_TEMPLATE_CODE_REPORT and settings.ALIYUN_SMS_TEMPLATE_CODE_VERIFY == settings.ALIYUN_SMS_TEMPLATE_CODE_REPORT:
|
|
raise RuntimeError("短信模板配置错误:验证码模板与报告模板重复")
|
|
if use_chain:
|
|
credential = CredentialClient()
|
|
config = open_api_models.Config(credential=credential)
|
|
else:
|
|
config = open_api_models.Config(
|
|
access_key_id=settings.ALIBABA_CLOUD_ACCESS_KEY_ID,
|
|
access_key_secret=settings.ALIBABA_CLOUD_ACCESS_KEY_SECRET,
|
|
)
|
|
config.endpoint = settings.ALIYUN_SMS_ENDPOINT
|
|
self.client = DysmsClient(config)
|
|
|
|
def send_by_template(self, phone: str, template_code: str, template_param: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
"""按模板发送短信
|
|
|
|
Args:
|
|
phone: 接收短信手机号
|
|
template_code: 模板 Code
|
|
template_param: 模板变量字典
|
|
|
|
Returns:
|
|
返回体映射字典
|
|
"""
|
|
from alibabacloud_dysmsapi20170525 import models as sms_models # type: ignore
|
|
self._ensure_client()
|
|
req = sms_models.SendSmsRequest(
|
|
phone_numbers=phone,
|
|
sign_name=settings.ALIYUN_SMS_SIGN_NAME,
|
|
template_code=template_code,
|
|
template_param=json.dumps(template_param or {}),
|
|
)
|
|
|
|
logger.info("sms.send start phone={} sign={} template={}", phone, settings.ALIYUN_SMS_SIGN_NAME, template_code)
|
|
try:
|
|
resp = self.client.send_sms(req)
|
|
body = resp.body.to_map() if hasattr(resp, "body") else {}
|
|
logger.info("sms.send response code={} request_id={} phone={}", body.get("Code"), body.get("RequestId"), phone)
|
|
return body
|
|
except Exception as e:
|
|
logger.error("sms.provider_error err={}", repr(e))
|
|
return {"Code": "ERROR", "Message": str(e)}
|
|
|
|
def send_code(self, phone: str, code: str) -> Dict[str, Any]:
|
|
"""发送验证码短信
|
|
|
|
Args:
|
|
phone: 接收短信手机号
|
|
code: 验证码
|
|
|
|
Returns:
|
|
返回体映射字典
|
|
"""
|
|
key = settings.ALIYUN_SMS_TEMPLATE_PARAM_CODE_KEY or "code"
|
|
template = settings.ALIYUN_SMS_TEMPLATE_CODE_VERIFY or "SMS_498190229"
|
|
logger.info("sms.send_code using key={} template={} phone={}", key, template, phone)
|
|
return self.send_by_template(phone, template, {key: code})
|
|
|
|
def send_report(self, phone: str) -> Dict[str, Any]:
|
|
"""发送报告通知短信
|
|
|
|
Args:
|
|
phone: 接收短信手机号
|
|
|
|
Returns:
|
|
返回体映射字典
|
|
"""
|
|
template = settings.ALIYUN_SMS_TEMPLATE_CODE_REPORT or "SMS_498140213"
|
|
logger.info("sms.send_report using template={} phone={}", template, phone)
|
|
return self.send_by_template(phone, template, {})
|
|
|
|
|
|
sms_client = SMSClient() |