feat(mail): 支持发件人名称展示与存储

- 为邮件消息新增 `fromName` 字段,并同步扩展数据库迁移、API 类型与前后端查询/入库逻辑。
- 解析入站邮件时提取并保存发件人名称,发信时将邮箱显示名写入 MIME `From` 头。
- 邮件列表、详情、通知与引用内容改为优先展示发件人名称,提升可读性。
This commit is contained in:
LanQin
2026-06-16 23:29:23 +08:00
parent 045132146b
commit 5821d9aa30
10 changed files with 135 additions and 37 deletions
+12 -1
View File
@@ -10,6 +10,7 @@ import (
"mime"
"mime/multipart"
"net"
netmail "net/mail"
"net/smtp"
"net/textproto"
"strings"
@@ -20,6 +21,7 @@ const smtpSessionTimeout = 45 * time.Second
type MIMEMessage struct {
From string
FromName string
To []string
CC []string
BCC []string
@@ -38,7 +40,7 @@ func BuildMIME(m MIMEMessage) ([]byte, error) {
fmt.Fprintf(&buf, "%s: %s\r\n", k, v)
}
}
writeHeader("From", m.From)
writeHeader("From", formatAddressHeader(m.FromName, m.From))
writeHeader("To", strings.Join(m.To, ", "))
writeHeader("Cc", strings.Join(m.CC, ", "))
writeHeader("Subject", mime.QEncoding.Encode("utf-8", m.Subject))
@@ -104,6 +106,15 @@ func BuildMIME(m MIMEMessage) ([]byte, error) {
return buf.Bytes(), nil
}
func formatAddressHeader(name, address string) string {
address = strings.TrimSpace(address)
name = strings.TrimSpace(name)
if address == "" || name == "" || strings.EqualFold(name, address) {
return address
}
return (&netmail.Address{Name: name, Address: address}).String()
}
func textprotoMIMEHeader(values map[string]string) textproto.MIMEHeader {
h := textproto.MIMEHeader{}
for k, v := range values {