- 新增「简记memo」一体化小程序产品原型设计文档 - 新增简记memo完整版UI视觉设计规范和界面细节 - 添加IDEA项目配置文件.gitignore - 创建404页面HTML文件,包含响应式布局和错误提示 - 添加关于页面HTML文件,展示品牌介绍和团队信息 - 实现AES加解密工具函数,支持请求体加密 - 添加用户协议页面基础框架
184 lines
6.5 KiB
Go
184 lines
6.5 KiB
Go
// 邮件发送工具
|
|
package utils
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"go.uber.org/zap"
|
|
"net/smtp"
|
|
"simple-memo/global"
|
|
"time"
|
|
)
|
|
|
|
// SendEmail 发送邮件 (阿里企业邮箱标准实现)
|
|
func SendEmail(to, subject, body string) error {
|
|
from := global.VP.GetString("email.from")
|
|
smtpHost := global.VP.GetString("email.smtp_host")
|
|
smtpPort := global.VP.GetInt("email.smtp_port")
|
|
smtpUser := global.VP.GetString("email.smtp_user")
|
|
smtpPassword := global.VP.GetString("email.smtp_password")
|
|
|
|
if smtpUser == "" || smtpPassword == "" {
|
|
return fmt.Errorf("邮件配置未完善")
|
|
}
|
|
|
|
auth := smtp.PlainAuth("", smtpUser, smtpPassword, smtpHost)
|
|
|
|
msg := []byte(fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\nMIME-Version: 1.0\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n%s",
|
|
from, to, subject, body))
|
|
|
|
addr := fmt.Sprintf("%s:%d", smtpHost, smtpPort)
|
|
|
|
var client *smtp.Client
|
|
var err error
|
|
|
|
switch smtpPort {
|
|
case 465:
|
|
tlsConfig := &tls.Config{
|
|
ServerName: smtpHost,
|
|
InsecureSkipVerify: true,
|
|
}
|
|
|
|
conn, err := tls.Dial("tcp", addr, tlsConfig)
|
|
if err != nil {
|
|
global.Logger.Error("TLS连接失败", zap.String("host", smtpHost), zap.Int("port", smtpPort), zap.Error(err))
|
|
return fmt.Errorf("TLS连接失败: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
client, err = smtp.NewClient(conn, smtpHost)
|
|
if err != nil {
|
|
global.Logger.Error("创建SMTP客户端失败", zap.Error(err))
|
|
return fmt.Errorf("创建SMTP客户端失败: %v", err)
|
|
}
|
|
defer client.Quit()
|
|
|
|
if err = client.Auth(auth); err != nil {
|
|
global.Logger.Error("SMTP认证失败", zap.Error(err))
|
|
return fmt.Errorf("SMTP认证失败: %v", err)
|
|
}
|
|
|
|
if err = client.Mail(smtpUser); err != nil {
|
|
global.Logger.Error("设置发件人失败", zap.Error(err))
|
|
return fmt.Errorf("设置发件人失败: %v", err)
|
|
}
|
|
|
|
if err = client.Rcpt(to); err != nil {
|
|
global.Logger.Error("设置收件人失败", zap.String("to", to), zap.Error(err))
|
|
return fmt.Errorf("设置收件人失败: %v", err)
|
|
}
|
|
|
|
w, err := client.Data()
|
|
if err != nil {
|
|
global.Logger.Error("获取数据写入器失败", zap.Error(err))
|
|
return fmt.Errorf("获取数据写入器失败: %v", err)
|
|
}
|
|
|
|
_, err = w.Write(msg)
|
|
if err != nil {
|
|
w.Close()
|
|
global.Logger.Error("写入邮件内容失败", zap.Error(err))
|
|
return fmt.Errorf("写入邮件内容失败: %v", err)
|
|
}
|
|
|
|
err = w.Close()
|
|
if err != nil {
|
|
global.Logger.Error("关闭写入器失败", zap.Error(err))
|
|
return fmt.Errorf("关闭写入器失败: %v", err)
|
|
}
|
|
|
|
global.Logger.Info("邮件发送成功", zap.String("to", to), zap.String("subject", subject))
|
|
return nil
|
|
|
|
case 587:
|
|
client, err = smtp.Dial(addr)
|
|
if err != nil {
|
|
global.Logger.Error("连接SMTP服务器失败", zap.String("addr", addr), zap.Error(err))
|
|
return fmt.Errorf("连接SMTP服务器失败: %v", err)
|
|
}
|
|
defer client.Close()
|
|
|
|
if err = client.StartTLS(&tls.Config{ServerName: smtpHost}); err != nil {
|
|
global.Logger.Error("启动TLS失败", zap.Error(err))
|
|
return fmt.Errorf("启动TLS失败: %v", err)
|
|
}
|
|
|
|
if err = client.Auth(auth); err != nil {
|
|
global.Logger.Error("SMTP认证失败", zap.Error(err))
|
|
return fmt.Errorf("SMTP认证失败: %v", err)
|
|
}
|
|
|
|
if err = client.Mail(smtpUser); err != nil {
|
|
global.Logger.Error("设置发件人失败", zap.Error(err))
|
|
return fmt.Errorf("设置发件人失败: %v", err)
|
|
}
|
|
|
|
if err = client.Rcpt(to); err != nil {
|
|
global.Logger.Error("设置收件人失败", zap.String("to", to), zap.Error(err))
|
|
return fmt.Errorf("设置收件人失败: %v", err)
|
|
}
|
|
|
|
w, err := client.Data()
|
|
if err != nil {
|
|
global.Logger.Error("获取数据写入器失败", zap.Error(err))
|
|
return fmt.Errorf("获取数据写入器失败: %v", err)
|
|
}
|
|
|
|
_, err = w.Write(msg)
|
|
if err != nil {
|
|
w.Close()
|
|
global.Logger.Error("写入邮件内容失败", zap.Error(err))
|
|
return fmt.Errorf("写入邮件内容失败: %v", err)
|
|
}
|
|
|
|
err = w.Close()
|
|
if err != nil {
|
|
global.Logger.Error("关闭写入器失败", zap.Error(err))
|
|
return fmt.Errorf("关闭写入器失败: %v", err)
|
|
}
|
|
|
|
global.Logger.Info("邮件发送成功", zap.String("to", to), zap.String("subject", subject))
|
|
return nil
|
|
|
|
default:
|
|
err = smtp.SendMail(addr, auth, smtpUser, []string{to}, msg)
|
|
if err != nil {
|
|
global.Logger.Error("SMTP发送失败", zap.String("addr", addr), zap.Error(err))
|
|
return fmt.Errorf("SMTP发送失败: %v", err)
|
|
}
|
|
global.Logger.Info("邮件发送成功", zap.String("to", to), zap.String("subject", subject))
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// SendEmailCode 发送邮箱验证码
|
|
func SendEmailCode(to, code string) error {
|
|
subject := "简记memo - 邮箱验证"
|
|
|
|
body := fmt.Sprintf(`<!DOCTYPE html><html><head><meta charset="UTF-8"><title>邮箱验证</title></head><body style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;max-width:500px;margin:0 auto;padding:20px;"><div style="background:#ffffff;border-radius:8px;padding:24px;"><div style="font-size:18px;font-weight:600;color:#333;margin-bottom:16px;">简记memo</div><p style="color:#555;font-size:14px;line-height:1.6;margin-bottom:16px;">您好,</p><p style="color:#555;font-size:14px;line-height:1.6;margin-bottom:20px;">您正在进行邮箱验证操作,以下是您的验证码:</p><div style="background:#f5f5f5;border-radius:4px;padding:16px 24px;text-align:center;margin-bottom:20px;"><span style="font-size:28px;font-weight:600;color:#333;letter-spacing:4px;">%s</span></div><p style="color:#888;font-size:12px;line-height:1.6;margin-bottom:8px;">验证码有效期为5分钟,请在有效期内完成验证。</p><p style="color:#888;font-size:12px;line-height:1.6;">如果这不是您的操作,请忽略此邮件。</p><div style="margin-top:24px;padding-top:16px;border-top:1px solid #eee;"><p style="color:#aaa;font-size:11px;">简记memo团队</p></div></div></body></html>`, code)
|
|
|
|
// 使用重试机制发送邮件
|
|
var lastErr error
|
|
for attempt := 1; attempt <= 3; attempt++ {
|
|
err := SendEmail(to, subject, body)
|
|
if err == nil {
|
|
global.Logger.Info("邮件发送成功", zap.String("to", to), zap.String("subject", subject))
|
|
return nil
|
|
}
|
|
|
|
lastErr = err
|
|
global.Logger.Warn("邮件发送失败,准备重试",
|
|
zap.String("to", to),
|
|
zap.Int("attempt", attempt),
|
|
zap.Int("maxRetries", 3),
|
|
zap.Error(err))
|
|
|
|
// 等待一段时间后重试(指数退避)
|
|
if attempt < 3 {
|
|
time.Sleep(time.Duration(attempt) * time.Second)
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("邮件发送失败,已重试3次: %v", lastErr)
|
|
}
|