yj
2025-07-28 69945b730fd3f6b6138ce50e49fc3392fcd74d71
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
"""
短信发送服务
"""
 
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()