package service import ( "crypto/tls" "fmt" "mime" "net/smtp" "strconv" "strings" ) type EmailService struct{} func NewEmailService() *EmailService { return &EmailService{} } type EmailConfig struct { Host string Port int User string Password string FromName string FromEmail string UseSSL bool } func (s *EmailService) SendEmail(config EmailConfig, to, subject, body string) error { if config.Host == "" || config.User == "" || config.Password == "" { return fmt.Errorf("邮件配置不完整") } from := config.FromEmail if from == "" { from = config.User } addr := fmt.Sprintf("%s:%d", config.Host, config.Port) msg := s.buildMessage(config.FromName, from, to, subject, body) auth := smtp.PlainAuth("", config.User, config.Password, config.Host) if config.Port == 465 || config.UseSSL { return s.sendWithImplicitTLS(addr, auth, from, []string{to}, []byte(msg)) } return s.sendWithSTARTTLS(addr, auth, from, []string{to}, []byte(msg)) } func (s *EmailService) sendWithImplicitTLS(addr string, auth smtp.Auth, from string, to []string, msg []byte) error { host := strings.Split(addr, ":")[0] tlsConfig := &tls.Config{ InsecureSkipVerify: true, ServerName: host, } conn, err := tls.Dial("tcp", addr, tlsConfig) if err != nil { return fmt.Errorf("TLS连接失败: %v", err) } client, err := smtp.NewClient(conn, host) if err != nil { return fmt.Errorf("创建SMTP客户端失败: %v", err) } defer client.Close() if err = client.Auth(auth); err != nil { return fmt.Errorf("SMTP认证失败: %v", err) } if err = client.Mail(from); err != nil { return fmt.Errorf("设置发件人失败: %v", err) } for _, addr := range to { if err = client.Rcpt(addr); err != nil { return fmt.Errorf("设置收件人失败: %v", err) } } w, err := client.Data() if err != nil { return fmt.Errorf("准备邮件数据失败: %v", err) } _, err = w.Write(msg) if err != nil { return fmt.Errorf("写入邮件内容失败: %v", err) } err = w.Close() if err != nil { return fmt.Errorf("关闭邮件写入失败: %v", err) } return client.Quit() } func (s *EmailService) sendWithSTARTTLS(addr string, auth smtp.Auth, from string, to []string, msg []byte) error { host := strings.Split(addr, ":")[0] client, err := smtp.Dial(addr) if err != nil { return fmt.Errorf("连接SMTP服务器失败: %v", err) } defer client.Close() if err = client.Hello("localhost"); err != nil { return fmt.Errorf("HELO命令失败: %v", err) } tlsConfig := &tls.Config{ InsecureSkipVerify: true, ServerName: host, } if ok, _ := client.Extension("STARTTLS"); ok { if err = client.StartTLS(tlsConfig); err != nil { return fmt.Errorf("STARTTLS失败: %v", err) } } if err = client.Auth(auth); err != nil { return fmt.Errorf("SMTP认证失败: %v", err) } if err = client.Mail(from); err != nil { return fmt.Errorf("设置发件人失败: %v", err) } for _, addr := range to { if err = client.Rcpt(addr); err != nil { return fmt.Errorf("设置收件人失败: %v", err) } } w, err := client.Data() if err != nil { return fmt.Errorf("准备邮件数据失败: %v", err) } _, err = w.Write(msg) if err != nil { return fmt.Errorf("写入邮件内容失败: %v", err) } err = w.Close() if err != nil { return fmt.Errorf("关闭邮件写入失败: %v", err) } return client.Quit() } func (s *EmailService) buildMessage(fromName, from, to, subject, body string) string { msg := "" if fromName != "" { encodedName := mime.QEncoding.Encode("UTF-8", fromName) msg += fmt.Sprintf("From: %s <%s>\r\n", encodedName, from) } else { msg += fmt.Sprintf("From: %s\r\n", from) } msg += fmt.Sprintf("To: %s\r\n", to) msg += fmt.Sprintf("Subject: %s\r\n", mime.QEncoding.Encode("UTF-8", subject)) msg += "MIME-version: 1.0;\r\nContent-Type: text/html; charset=\"UTF-8\";\r\n\r\n" msg += body return msg } func (s *EmailService) SendTestEmail(config EmailConfig, to, siteName string) error { subject := fmt.Sprintf("测试邮件 - %s", siteName) body := fmt.Sprintf(`
这是一封测试邮件,如果您收到此邮件,说明您的SMTP配置正确。
此邮件由 %s 系统自动发送,请勿回复。