""" 短信发送服务 """ import hashlib import time import requests from typing import List, Optional from loguru import logger from config import settings class SmsService: """短信发送服务""" def __init__(self): self.api_url = settings.sms_api_url self.username = settings.sms_username self.password = settings.sms_password self.session = requests.Session() self.session.headers.update({ "Accept": "application/json", "Content-Type": "application/json;charset=utf-8" }) def _generate_sign(self, timestamp: int) -> str: """ 生成签名 计算规则:MD5(userName+timestamp+MD5(password)) Args: timestamp: 时间戳(毫秒) Returns: 签名字符串 """ # 计算密码的MD5 password_md5 = hashlib.md5(self.password.encode('utf-8')).hexdigest() # 组合字符串:userName+timestamp+MD5(password) combined_str = f"{self.username}{timestamp}{password_md5}" # 计算最终签名 sign = hashlib.md5(combined_str.encode('utf-8')).hexdigest() return sign def send_sms(self, phone_list: List[str], content: str) -> bool: """ 发送短信 Args: phone_list: 手机号码列表 content: 短信内容 Returns: 发送成功返回True,失败返回False """ if not settings.sms_enabled: logger.info("短信发送功能已禁用") return True if not phone_list: logger.warning("手机号码列表为空") return False try: # 生成时间戳(毫秒) timestamp = int(time.time() * 1000) # 生成签名 sign = self._generate_sign(timestamp) # 构建请求参数 payload = { "userName": self.username, "content": content, "phoneList": phone_list, "timestamp": timestamp, "sign": sign } logger.info(f"发送短信: phones={phone_list}, content_length={len(content)}") # 发送请求 response = self.session.post(self.api_url, json=payload, timeout=30) response.raise_for_status() result = response.json() if result.get("code") == 0: msg_id = result.get("msgId") sms_count = result.get("smsCount") logger.info(f"短信发送成功: msgId={msg_id}, smsCount={sms_count}, phones={phone_list}") return True else: logger.error( f"短信发送失败: code={result.get('code')}, message={result.get('message')}, phones={phone_list}" ) return False except requests.exceptions.RequestException as e: logger.error(f"短信发送网络错误: phones={phone_list}, error={str(e)}") return False except Exception as e: logger.error(f"短信发送异常: phones={phone_list}, error={str(e)}") return False def send_notification(self, content: str) -> bool: """ 发送通知短信到配置的手机号码列表 Args: content: 短信内容 Returns: 发送成功返回True,失败返回False """ return self.send_sms(settings.sms_phone_numbers, content) def test_connection(self) -> bool: """ 测试短信服务连接(发送测试短信) Returns: 连接成功返回True,失败返回False """ if not settings.sms_enabled: logger.info("短信发送功能已禁用") return True # 发送测试短信到第一个号码 if settings.sms_phone_numbers: test_phone = [settings.sms_phone_numbers[0]] test_content = "【测试】短信服务连接测试" return self.send_sms(test_phone, test_content) else: logger.warning("没有配置短信接收号码") return False # 全局短信服务实例 sms_service = SmsService()