// 邮件发送工具 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(`
您好,
您正在进行邮箱验证操作,以下是您的验证码:
验证码有效期为5分钟,请在有效期内完成验证。
如果这不是您的操作,请忽略此邮件。
简记memo团队