python
复制代码
"""
邮箱工具模块
提供:
1. EmailClient - 邮箱客户端类(封装SMTP连接池)
2. 各种邮件模板生成函数
"""
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from flask import current_app
class EmailClient:
"""邮箱客户端(SMTP连接管理)"""
@staticmethod
def _get_config():
"""获取邮件配置"""
return {
'server': current_app.config.get('MAIL_SERVER', 'smtp.qq.com'),
'port': current_app.config.get('MAIL_PORT', 587),
'use_tls': current_app.config.get('MAIL_USE_TLS', True),
'use_ssl': current_app.config.get('MAIL_USE_SSL', False),
'username': current_app.config.get('MAIL_USERNAME'),
'password': current_app.config.get('MAIL_PASSWORD'),
'default_sender': current_app.config.get('MAIL_DEFAULT_SENDER'),
}
@classmethod
def send(cls, to_email, subject, body_html, body_text=None):
"""
发送邮件
Args:
to_email: 收件人邮箱
subject: 邮件主题
body_html: HTML格式邮件正文
body_text: 纯文本格式邮件正文(可选)
Returns:
bool: 发送是否成功
"""
try:
config = cls._get_config()
if not config['username'] or not config['password']:
current_app.logger.error("邮件配置不完整:缺少MAIL_USERNAME或MAIL_PASSWORD")
return False
msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = config['default_sender'] or config['username']
msg['To'] = to_email
if body_text:
msg.attach(MIMEText(body_text, 'plain', 'utf-8'))
msg.attach(MIMEText(body_html, 'html', 'utf-8'))
if config['use_ssl']:
server = smtplib.SMTP_SSL(config['server'], config['port'])
else:
server = smtplib.SMTP(config['server'], config['port'])
if config['use_tls']:
server.starttls()
server.login(config['username'], config['password'])
server.sendmail(msg['From'], [to_email], msg.as_string())
server.quit()
current_app.logger.info(f"邮件发送成功: {to_email}")
return True
except Exception as e:
current_app.logger.error(f"邮件发送失败: {to_email}, 错误: {str(e)}")
return False
@classmethod
def send_verification_email(cls, user, verification_url, token):
"""
发送注册验证邮件
Args:
user: 用户对象(需包含username和email属性)
verification_url: 验证链接
token: 验证令牌
Returns:
bool: 发送是否成功
"""
subject = '房屋租赁系统 - 邮箱验证'
html_content = f"""
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }}
.header {{ background-color: #5b6bf8; color: white; padding: 20px; text-align: center; border-radius: 8px; }}
.content {{ padding: 20px; background-color: #f9f9f9; border-radius: 8px; margin-top: 20px; }}
.button {{ display: inline-block; background-color: #5b6bf8; color: white; padding: 12px 30px; text-decoration: none; border-radius: 6px; margin: 20px 0; }}
.token-box {{ background-color: #fff; border: 2px dashed #5b6bf8; border-radius: 8px; padding: 20px; margin: 20px 0; text-align: center; }}
.token-label {{ color: #666; font-size: 14px; margin-bottom: 10px; }}
.token-code {{ font-size: 28px; font-weight: bold; color: #5b6bf8; letter-spacing: 3px; word-break: break-all; background-color: #f0f4ff; padding: 12px; border-radius: 6px; margin: 10px 0; }}
.copy-hint {{ color: #999; font-size: 12px; margin-top: 10px; }}
.footer {{ text-align: center; margin-top: 20px; color: #666; font-size: 12px; }}
</style>
</head>
<body>
<div class="header">
<h1>欢迎加入房屋租赁系统</h1>
</div>
<div class="content">
<h2>尊敬的 {user.username},您好!</h2>
<p>感谢您注册房屋租赁系统。为了完成注册,请点击下方按钮验证您的邮箱:</p>
<div style="text-align: center;">
<a href="{verification_url}" class="button">验证邮箱</a>
</div>
<div class="token-box">
<p class="token-label">您的验证令牌(可直接复制):</p>
<div class="token-code">{token}</div>
<p class="copy-hint">请复制上方令牌,在验证页面输入完成验证</p>
</div>
<p>如果按钮无法点击,请复制以下链接到浏览器地址栏:</p>
<p style="word-break: break-all; color: #666; font-size: 12px;">{verification_url}</p>
<p>本验证链接有效期为 24 小时,过期后请重新申请。</p>
</div>
<div class="footer">
<p>此邮件由系统自动发送,请勿直接回复。</p>
</div>
</body>
</html>
"""
return cls.send(user.email, subject, html_content)
@classmethod
def send_verification_code(cls, to_email, code, purpose='忘记密码'):
"""
发送验证码邮件
Args:
to_email: 收件人邮箱
code: 验证码
purpose: 验证码用途(默认:忘记密码)
Returns:
bool: 发送是否成功
"""
subject = f'房屋租赁系统 - {purpose}验证码'
html_content = f"""
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }}
.header {{ background-color: #5b6bf8; color: white; padding: 20px; text-align: center; border-radius: 8px; }}
.content {{ padding: 20px; background-color: #f9f9f9; border-radius: 8px; margin-top: 20px; }}
.token-box {{ background-color: #fff; border: 2px dashed #5b6bf8; border-radius: 8px; padding: 20px; margin: 20px 0; text-align: center; }}
.token-label {{ color: #666; font-size: 14px; margin-bottom: 10px; }}
.token-code {{ font-size: 36px; font-weight: bold; color: #5b6bf8; letter-spacing: 5px; background-color: #f0f4ff; padding: 15px; border-radius: 6px; margin: 10px 0; }}
.copy-hint {{ color: #999; font-size: 12px; margin-top: 10px; }}
.footer {{ text-align: center; margin-top: 20px; color: #666; font-size: 12px; }}
</style>
</head>
<body>
<div class="header">
<h1>{purpose} - 验证码</h1>
</div>
<div class="content">
<h2>您好!</h2>
<p>您正在进行{purpose}操作,请使用以下验证码完成操作:</p>
<div class="token-box">
<p class="token-label">您的验证码:</p>
<div class="token-code">{code}</div>
<p class="copy-hint">请在5分钟内使用此验证码</p>
</div>
<p>如果您没有进行此操作,请忽略此邮件。</p>
</div>
<div class="footer">
<p>此邮件由系统自动发送,请勿直接回复。</p>
</div>
</body>
</html>
"""
return cls.send(to_email, subject, html_content)