package smtputil import ( "crypto/tls" "fmt" "net/smtp" "strings" "time" ) type SMTPConfig struct { Host string Port int Username string Password string From string UseTLS bool } type EmailMessage struct { To []string Subject string Body string HTML bool } type SMTPResult struct { Success bool `json:"success"` Error string `json:"error,omitempty"` } type SMTPClient struct { config SMTPConfig timeout time.Duration } func NewSMTPClient(config SMTPConfig, timeout time.Duration) *SMTPClient { if timeout <= 0 { timeout = 30 * time.Second } return &SMTPClient{ config: config, timeout: timeout, } } func (c *SMTPClient) Send(msg EmailMessage) *SMTPResult { result := &SMTPResult{Success: false} if len(msg.To) == 0 { result.Error = "收件人不能为空" return result } if c.config.Host == "" { result.Error = "SMTP服务器地址不能为空" return result } addr := fmt.Sprintf("%s:%d", c.config.Host, c.config.Port) var auth smtp.Auth if c.config.Username != "" && c.config.Password != "" { auth = smtp.PlainAuth("", c.config.Username, c.config.Password, c.config.Host) } from := c.config.From if from == "" { from = c.config.Username } contentType := "text/plain" if msg.HTML { contentType = "text/html" } headers := make(map[string]string) headers["From"] = from headers["To"] = strings.Join(msg.To, ", ") headers["Subject"] = msg.Subject headers["MIME-Version"] = "1.0" headers["Content-Type"] = fmt.Sprintf("%s; charset=UTF-8", contentType) headers["Date"] = time.Now().Format(time.RFC1123Z) var message strings.Builder for k, v := range headers { message.WriteString(fmt.Sprintf("%s: %s\r\n", k, v)) } message.WriteString("\r\n") message.WriteString(msg.Body) if c.config.UseTLS { tlsConfig := &tls.Config{ InsecureSkipVerify: false, ServerName: c.config.Host, } conn, err := tls.Dial("tcp", addr, tlsConfig) if err != nil { result.Error = fmt.Sprintf("TLS连接失败: %v", err) return result } defer conn.Close() client, err := smtp.NewClient(conn, c.config.Host) if err != nil { result.Error = fmt.Sprintf("创建SMTP客户端失败: %v", err) return result } defer client.Close() if auth != nil { if err := client.Auth(auth); err != nil { result.Error = fmt.Sprintf("SMTP认证失败: %v", err) return result } } if err := client.Mail(from); err != nil { result.Error = fmt.Sprintf("设置发件人失败: %v", err) return result } for _, to := range msg.To { if err := client.Rcpt(to); err != nil { result.Error = fmt.Sprintf("设置收件人失败: %v", err) return result } } w, err := client.Data() if err != nil { result.Error = fmt.Sprintf("准备邮件数据失败: %v", err) return result } _, err = w.Write([]byte(message.String())) if err != nil { result.Error = fmt.Sprintf("写入邮件内容失败: %v", err) return result } if err := w.Close(); err != nil { result.Error = fmt.Sprintf("关闭邮件写入失败: %v", err) return result } if err := client.Quit(); err != nil { result.Error = fmt.Sprintf("关闭SMTP连接失败: %v", err) return result } } else { err := smtp.SendMail(addr, auth, from, msg.To, []byte(message.String())) if err != nil { result.Error = fmt.Sprintf("发送邮件失败: %v", err) return result } } result.Success = true return result }