62 lines
2.4 KiB
Python
62 lines
2.4 KiB
Python
"""邮件发送工具(基于标准库 smtplib,465 端口 SSL)。
|
||||
|
|
|
|||
|
|
配置来自 app.config.setting.settings(SMTP_HOST/PORT/USER/PASSWORD/FROM/FROM_NAME/TLS)。
|
|||
|
|
"""
|
|||
|
|
from email.header import Header
|
|||
|
|
from email.mime.text import MIMEText
|
|||
|
|
from smtplib import SMTP_SSL, SMTPException
|
|||
|
|
|
|||
|
|
from app.config.setting import settings
|
|||
|
|
from app.core.logger import logger
|
|||
|
|
|
|||
|
|
|
|||
|
|
def send_email(to_addr: str, subject: str, html_body: str) -> None:
|
|||
|
|
"""发送一封 HTML 邮件(同步,内部阻塞;服务层用线程池调用)。
|
|||
|
|
|
|||
|
|
参数:
|
|||
|
|
- to_addr (str): 收件人邮箱
|
|||
|
|
- subject (str): 邮件主题
|
|||
|
|
- html_body (str): HTML 正文
|
|||
|
|
|
|||
|
|
异常:
|
|||
|
|
- SMTPException: 发送失败时抛出,由调用方捕获并处理
|
|||
|
|
"""
|
|||
|
|
if not settings.SMTP_HOST or not settings.SMTP_USER:
|
|||
|
|
raise RuntimeError("SMTP 未配置:请在 .env 中设置 SMTP_HOST/SMTP_USER/SMTP_PASSWORD")
|
|||
|
|
|
|||
|
|
from_addr = settings.SMTP_FROM or settings.SMTP_USER
|
|||
|
|
display = settings.SMTP_FROM_NAME or from_addr
|
|||
|
|
# 显示名单独编码,邮箱地址保持 ASCII
|
|||
|
|
from_header = f"{Header(display, 'utf-8').encode()} <{from_addr}>"
|
|||
|
|
|
|||
|
|
msg = MIMEText(html_body, "html", "utf-8")
|
|||
|
|
msg["From"] = from_header
|
|||
|
|
msg["To"] = to_addr
|
|||
|
|
msg["Subject"] = Header(subject, "utf-8").encode()
|
|||
|
|
|
|||
|
|
with SMTP_SSL(settings.SMTP_HOST, settings.SMTP_PORT, timeout=10) as server:
|
|||
|
|
server.login(settings.SMTP_USER, settings.SMTP_PASSWORD)
|
|||
|
|
server.sendmail(from_addr, [to_addr], msg.as_string())
|
|||
|
|
|
|||
|
|
|
|||
|
|
def send_reset_code_email(to_addr: str, code: str, expire_seconds: int) -> None:
|
|||
|
|
"""发送找回密码验证码邮件。"""
|
|||
|
|
try:
|
|||
|
|
send_email(
|
|||
|
|
to_addr=to_addr,
|
|||
|
|
subject="【dpb】找回密码验证码",
|
|||
|
|
html_body=(
|
|||
|
|
f"<p>您好,</p>"
|
|||
|
|
f"<p>您正在申请找回密码,验证码为:</p>"
|
|||
|
|
f'<p style="font-size:24px;font-weight:bold;color:#1677ff;letter-spacing:4px;">{code}</p>'
|
|||
|
|
f"<p>验证码 {expire_seconds // 60} 分钟内有效,请勿泄露给他人。</p>"
|
|||
|
|
f"<p>若非本人操作,请忽略本邮件。</p>"
|
|||
|
|
),
|
|||
|
|
)
|
|||
|
|
except SMTPException as e:
|
|||
|
|
logger.error(f"发送验证码邮件失败 -> {to_addr}: {e!s}")
|
|||
|
|
raise
|
|||
|
|
except Exception as e: # noqa: BLE001
|
|||
|
|
logger.error(f"发送验证码邮件异常 -> {to_addr}: {e!s}")
|
|||
|
|
raise
|