#!/usr/bin/env python # -*- coding: utf-8 -*- """ 邮件服务工具类,使用腾讯云SES服务发送邮件 """ import logging import random import string import json import time from typing import Dict, Any, Optional import requests from tencentcloud.common import credential from tencentcloud.common.profile.client_profile import ClientProfile from tencentcloud.common.profile.http_profile import HttpProfile from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException from tencentcloud.ses.v20201002 import ses_client, models from cryptoai.utils.config_loader import ConfigLoader # 配置日志 logger = logging.getLogger('email_service') # 验证码缓存,格式: {email: {'code': '123456', 'expire_time': timestamp}} verification_codes = {} class EmailService: """邮件服务类,用于发送验证码邮件""" def __init__(self): """初始化邮件服务""" # 加载腾讯云SES配置 config_loader = ConfigLoader() self.ses_config = config_loader.get_ses_config() # 初始化SES客户端 self.client = None self._init_client() def _init_client(self) -> None: """初始化腾讯云SES客户端""" try: # 创建认证对象 cred = credential.Credential( self.ses_config.get('secret_id'), self.ses_config.get('secret_key') ) # 创建HTTP配置 http_profile = HttpProfile() http_profile.endpoint = "ses.tencentcloudapi.com" # 创建客户端配置 client_profile = ClientProfile() client_profile.httpProfile = http_profile # 创建SES客户端 self.client = ses_client.SesClient(cred, self.ses_config.get('region'), client_profile) logger.info("成功初始化腾讯云SES客户端") except TencentCloudSDKException as e: logger.error(f"初始化腾讯云SES客户端失败: {e}") self.client = None def generate_verification_code(self, length: int = 6) -> str: """ 生成数字验证码 Args: length: 验证码长度,默认6位 Returns: 生成的验证码 """ return ''.join(random.choices(string.digits, k=length)) def save_verification_code(self, email: str, code: str, expire_minutes: int = 10) -> None: """ 保存验证码到缓存 Args: email: 邮箱地址 code: 验证码 expire_minutes: 过期时间(分钟),默认10分钟 """ expire_time = time.time() + expire_minutes * 60 verification_codes[email] = { 'code': code, 'expire_time': expire_time } def verify_code(self, email: str, code: str) -> bool: """ 验证邮箱验证码 Args: email: 邮箱地址 code: 验证码 Returns: 验证是否成功 """ # 检查验证码是否存在 if email not in verification_codes: return False # 获取验证码信息 code_info = verification_codes[email] # 检查验证码是否过期 if time.time() > code_info['expire_time']: # 删除过期验证码 del verification_codes[email] return False # 验证验证码 if code_info['code'] == code: # 验证成功后删除验证码 del verification_codes[email] return True return False def send_verification_email(self, email: str) -> Dict[str, Any]: """ 发送验证码邮件 Args: email: 收件人邮箱 Returns: 发送结果 """ if not self.client: try: self._init_client() except Exception as e: logger.error(f"重新初始化SES客户端失败: {e}") return { 'success': False, 'message': '邮件服务初始化失败' } try: # 生成验证码 code = self.generate_verification_code() # 保存验证码 self.save_verification_code(email, code) # 构建邮件内容 subject = "CryptoAI - 您的验证码" html_content = f"""

CryptoAI 验证码

您好,

您正在注册 CryptoAI 账号,请使用以下验证码完成注册:

{code}

验证码有效期为10分钟,请勿将验证码泄露给他人。

如果您没有进行此操作,请忽略此邮件。

""" # 创建发送邮件请求 req = models.SendEmailRequest() req.FromEmailAddress = self.ses_config.get('from_email') req.Destination = [email] req.Subject = subject req.Template = { "TemplateID": self.ses_config.get('template_id', 1), # 使用默认模板ID或配置的模板ID "TemplateData": json.dumps({ "code": code }) } # 如果没有配置模板ID,则使用HTML内容 if not self.ses_config.get('template_id'): req.Template = None req.Simple = { "Html": html_content } # 发送邮件 resp = self.client.SendEmail(req) logger.info(f"成功发送验证码邮件到 {email}") return { 'success': True, 'message': '验证码已发送', 'request_id': resp.RequestId } except TencentCloudSDKException as e: logger.error(f"发送验证码邮件失败: {e}") return { 'success': False, 'message': f'发送验证码失败: {e}' } except Exception as e: logger.error(f"发送验证码邮件出现未知错误: {e}") return { 'success': False, 'message': f'发送验证码失败: {str(e)}' } # 单例模式 _email_service_instance = None def get_email_service() -> EmailService: """ 获取邮件服务实例(单例模式) Returns: 邮件服务实例 """ global _email_service_instance # 如果已经初始化过,直接返回 if _email_service_instance is not None: return _email_service_instance # 创建实例 _email_service_instance = EmailService() return _email_service_instance