完善日程提醒功能

This commit is contained in:
2026-06-18 00:55:14 +08:00
parent 6ee1f26124
commit bd30b68766
16 changed files with 1621 additions and 297 deletions
+94
View File
@@ -117,6 +117,100 @@ func SendTestEmailSMTP(cfg SMTPConfig, to string) error {
return client.Quit()
}
// SendHTMLEmailSMTP 发送一封 HTML 格式邮件
func SendHTMLEmailSMTP(cfg SMTPConfig, to string, subject string, htmlBody string) error {
to = strings.TrimSpace(to)
if to == "" {
return fmt.Errorf("收件人不能为空")
}
if cfg.Host == "" || cfg.FromAddress == "" {
return fmt.Errorf("SMTP 主机或发件人不能为空")
}
if cfg.Port == 0 {
cfg.Port = 465
}
timeout := cfg.Timeout
if timeout == 0 {
timeout = 30
}
d := net.Dialer{Timeout: time.Duration(timeout) * time.Second}
addr := net.JoinHostPort(cfg.Host, strconv.FormatUint(uint64(cfg.Port), 10))
enc := strings.ToLower(strings.TrimSpace(cfg.Encryption))
if enc == "" {
enc = "ssl"
}
var client *smtp.Client
var err error
switch enc {
case "ssl":
conn, derr := tls.DialWithDialer(&d, "tcp", addr, &tls.Config{ServerName: cfg.Host, MinVersion: tls.VersionTLS12})
if derr != nil {
return fmt.Errorf("连接 SMTP 失败: %w", derr)
}
defer conn.Close()
client, err = smtp.NewClient(conn, cfg.Host)
if err != nil {
return fmt.Errorf("SMTP 握手失败: %w", err)
}
case "tls":
conn, derr := d.Dial("tcp", addr)
if derr != nil {
return fmt.Errorf("连接 SMTP 失败: %w", derr)
}
defer conn.Close()
client, err = smtp.NewClient(conn, cfg.Host)
if err != nil {
return fmt.Errorf("SMTP 握手失败: %w", err)
}
if ok, _ := client.Extension("STARTTLS"); ok {
if err = client.StartTLS(&tls.Config{ServerName: cfg.Host, MinVersion: tls.VersionTLS12}); err != nil {
_ = client.Close()
return fmt.Errorf("STARTTLS 失败: %w", err)
}
}
case "none":
conn, derr := d.Dial("tcp", addr)
if derr != nil {
return fmt.Errorf("连接 SMTP 失败: %w", derr)
}
defer conn.Close()
client, err = smtp.NewClient(conn, cfg.Host)
if err != nil {
return fmt.Errorf("SMTP 握手失败: %w", err)
}
default:
return fmt.Errorf("不支持的加密方式: %s", cfg.Encryption)
}
defer func() { _ = client.Close() }()
auth := smtp.PlainAuth("", cfg.FromAddress, cfg.Password, cfg.Host)
if err = client.Auth(auth); err != nil {
return fmt.Errorf("SMTP 认证失败: %w", err)
}
if err = client.Mail(cfg.FromAddress); err != nil {
return fmt.Errorf("MAIL FROM 失败: %w", err)
}
if err = client.Rcpt(to); err != nil {
return fmt.Errorf("RCPT TO 失败: %w", err)
}
wc, err := client.Data()
if err != nil {
return fmt.Errorf("DATA 失败: %w", err)
}
fromName := strings.TrimSpace(cfg.FromName)
headers := fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\nMIME-Version: 1.0\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Transfer-Encoding: 8bit\r\n\r\n",
formatFromHeader(fromName, cfg.FromAddress), to, subject)
if _, err = wc.Write([]byte(headers + htmlBody)); err != nil {
return fmt.Errorf("写入邮件内容失败: %w", err)
}
if err = wc.Close(); err != nil {
return fmt.Errorf("结束 DATA 失败: %w", err)
}
return client.Quit()
}
func formatFromHeader(name, addr string) string {
name = strings.TrimSpace(name)
if name == "" {