调整项目结构,factory只负责暴露方法,不实现业务细节
This commit is contained in:
179
email/email.go
179
email/email.go
@@ -17,37 +17,43 @@ type Email struct {
|
||||
}
|
||||
|
||||
// NewEmail 创建邮件发送器
|
||||
func NewEmail(cfg *config.EmailConfig) (*Email, error) {
|
||||
if cfg == nil {
|
||||
func NewEmail(cfg *config.Config) *Email {
|
||||
if cfg == nil || cfg.Email == nil {
|
||||
return &Email{config: nil}
|
||||
}
|
||||
return &Email{config: cfg.Email}
|
||||
}
|
||||
|
||||
// getEmailConfig 获取邮件配置(内部方法)
|
||||
func (e *Email) getEmailConfig() (*config.EmailConfig, error) {
|
||||
if e.config == nil {
|
||||
return nil, fmt.Errorf("email config is nil")
|
||||
}
|
||||
|
||||
if cfg.Host == "" {
|
||||
if e.config.Host == "" {
|
||||
return nil, fmt.Errorf("email host is required")
|
||||
}
|
||||
|
||||
if cfg.Username == "" {
|
||||
if e.config.Username == "" {
|
||||
return nil, fmt.Errorf("email username is required")
|
||||
}
|
||||
|
||||
if cfg.Password == "" {
|
||||
if e.config.Password == "" {
|
||||
return nil, fmt.Errorf("email password is required")
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if cfg.Port == 0 {
|
||||
cfg.Port = 587
|
||||
if e.config.Port == 0 {
|
||||
e.config.Port = 587
|
||||
}
|
||||
if cfg.From == "" {
|
||||
cfg.From = cfg.Username
|
||||
if e.config.From == "" {
|
||||
e.config.From = e.config.Username
|
||||
}
|
||||
if cfg.Timeout == 0 {
|
||||
cfg.Timeout = 30
|
||||
if e.config.Timeout == 0 {
|
||||
e.config.Timeout = 30
|
||||
}
|
||||
|
||||
return &Email{
|
||||
config: cfg,
|
||||
}, nil
|
||||
return e.config, nil
|
||||
}
|
||||
|
||||
// Message 邮件消息
|
||||
@@ -69,66 +75,91 @@ type Message struct {
|
||||
|
||||
// HTMLBody HTML正文(可选,如果设置了会优先使用)
|
||||
HTMLBody string
|
||||
|
||||
// Attachments 附件列表(可选)
|
||||
Attachments []Attachment
|
||||
}
|
||||
|
||||
// Attachment 附件
|
||||
type Attachment struct {
|
||||
// Filename 文件名
|
||||
Filename string
|
||||
// SendEmail 发送邮件
|
||||
// to: 收件人列表
|
||||
// subject: 邮件主题
|
||||
// body: 邮件正文(纯文本)
|
||||
// htmlBody: HTML正文(可选,如果设置了会优先使用)
|
||||
func (e *Email) SendEmail(to []string, subject, body string, htmlBody ...string) error {
|
||||
cfg, err := e.getEmailConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Content 文件内容
|
||||
Content []byte
|
||||
msg := &Message{
|
||||
To: to,
|
||||
Subject: subject,
|
||||
Body: body,
|
||||
}
|
||||
|
||||
// ContentType 文件类型(如:application/pdf)
|
||||
ContentType string
|
||||
if len(htmlBody) > 0 && htmlBody[0] != "" {
|
||||
msg.HTMLBody = htmlBody[0]
|
||||
}
|
||||
|
||||
return e.send(msg, cfg)
|
||||
}
|
||||
|
||||
// SendRaw 发送原始邮件内容
|
||||
// recipients: 收件人列表(To、Cc、Bcc的合并列表)
|
||||
// body: 完整的邮件内容(MIME格式),由外部构建
|
||||
func (e *Email) SendRaw(recipients []string, body []byte) error {
|
||||
if len(recipients) == 0 {
|
||||
// send 发送邮件(内部方法)
|
||||
func (e *Email) send(msg *Message, cfg *config.EmailConfig) error {
|
||||
if msg == nil {
|
||||
return fmt.Errorf("message is nil")
|
||||
}
|
||||
|
||||
if len(msg.To) == 0 {
|
||||
return fmt.Errorf("recipients are required")
|
||||
}
|
||||
|
||||
if len(body) == 0 {
|
||||
return fmt.Errorf("email body is required")
|
||||
if msg.Subject == "" {
|
||||
return fmt.Errorf("subject is required")
|
||||
}
|
||||
|
||||
if msg.Body == "" && msg.HTMLBody == "" {
|
||||
return fmt.Errorf("body or HTMLBody is required")
|
||||
}
|
||||
|
||||
// 构建邮件内容
|
||||
emailBody, err := e.buildEmailBody(msg, cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build email body: %w", err)
|
||||
}
|
||||
|
||||
// 合并收件人列表
|
||||
recipients := append(msg.To, msg.Cc...)
|
||||
recipients = append(recipients, msg.Bcc...)
|
||||
|
||||
// 连接SMTP服务器
|
||||
addr := fmt.Sprintf("%s:%d", e.config.Host, e.config.Port)
|
||||
auth := smtp.PlainAuth("", e.config.Username, e.config.Password, e.config.Host)
|
||||
addr := net.JoinHostPort(cfg.Host, fmt.Sprintf("%d", cfg.Port))
|
||||
auth := smtp.PlainAuth("", cfg.Username, cfg.Password, cfg.Host)
|
||||
|
||||
// 创建连接
|
||||
conn, err := net.DialTimeout("tcp", addr, time.Duration(e.config.Timeout)*time.Second)
|
||||
conn, err := net.DialTimeout("tcp", addr, time.Duration(cfg.Timeout)*time.Second)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to SMTP server: %w", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// 创建SMTP客户端
|
||||
client, err := smtp.NewClient(conn, e.config.Host)
|
||||
client, err := smtp.NewClient(conn, cfg.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create SMTP client: %w", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// TLS/SSL处理
|
||||
if e.config.UseSSL {
|
||||
if cfg.UseSSL {
|
||||
// SSL模式(端口通常是465)
|
||||
tlsConfig := &tls.Config{
|
||||
ServerName: e.config.Host,
|
||||
ServerName: cfg.Host,
|
||||
}
|
||||
if err := client.StartTLS(tlsConfig); err != nil {
|
||||
return fmt.Errorf("failed to start TLS: %w", err)
|
||||
}
|
||||
} else if e.config.UseTLS {
|
||||
} else if cfg.UseTLS {
|
||||
// TLS模式(STARTTLS,端口通常是587)
|
||||
tlsConfig := &tls.Config{
|
||||
ServerName: e.config.Host,
|
||||
ServerName: cfg.Host,
|
||||
}
|
||||
if err := client.StartTLS(tlsConfig); err != nil {
|
||||
return fmt.Errorf("failed to start TLS: %w", err)
|
||||
@@ -141,7 +172,7 @@ func (e *Email) SendRaw(recipients []string, body []byte) error {
|
||||
}
|
||||
|
||||
// 设置发件人
|
||||
if err := client.Mail(e.config.From); err != nil {
|
||||
if err := client.Mail(cfg.From); err != nil {
|
||||
return fmt.Errorf("failed to set sender: %w", err)
|
||||
}
|
||||
|
||||
@@ -158,7 +189,7 @@ func (e *Email) SendRaw(recipients []string, body []byte) error {
|
||||
return fmt.Errorf("failed to get data writer: %w", err)
|
||||
}
|
||||
|
||||
_, err = writer.Write(body)
|
||||
_, err = writer.Write(emailBody)
|
||||
if err != nil {
|
||||
writer.Close()
|
||||
return fmt.Errorf("failed to write email body: %w", err)
|
||||
@@ -177,47 +208,14 @@ func (e *Email) SendRaw(recipients []string, body []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send 发送邮件(使用Message结构,内部会构建邮件内容)
|
||||
// 注意:如果需要完全控制邮件内容,请使用SendRaw方法
|
||||
func (e *Email) Send(msg *Message) error {
|
||||
if msg == nil {
|
||||
return fmt.Errorf("message is nil")
|
||||
}
|
||||
|
||||
if len(msg.To) == 0 {
|
||||
return fmt.Errorf("recipients are required")
|
||||
}
|
||||
|
||||
if msg.Subject == "" {
|
||||
return fmt.Errorf("subject is required")
|
||||
}
|
||||
|
||||
if msg.Body == "" && msg.HTMLBody == "" {
|
||||
return fmt.Errorf("body or HTMLBody is required")
|
||||
}
|
||||
|
||||
// 构建邮件内容
|
||||
emailBody, err := e.buildEmailBody(msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build email body: %w", err)
|
||||
}
|
||||
|
||||
// 合并收件人列表
|
||||
recipients := append(msg.To, msg.Cc...)
|
||||
recipients = append(recipients, msg.Bcc...)
|
||||
|
||||
// 使用SendRaw发送
|
||||
return e.SendRaw(recipients, emailBody)
|
||||
}
|
||||
|
||||
// buildEmailBody 构建邮件内容
|
||||
func (e *Email) buildEmailBody(msg *Message) ([]byte, error) {
|
||||
func (e *Email) buildEmailBody(msg *Message, cfg *config.EmailConfig) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
// 邮件头
|
||||
from := e.config.From
|
||||
if e.config.FromName != "" {
|
||||
from = fmt.Sprintf("%s <%s>", e.config.FromName, e.config.From)
|
||||
from := cfg.From
|
||||
if cfg.FromName != "" {
|
||||
from = fmt.Sprintf("%s <%s>", cfg.FromName, cfg.From)
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("From: %s\r\n", from))
|
||||
|
||||
@@ -281,26 +279,3 @@ func joinEmails(emails []string) string {
|
||||
return result
|
||||
}
|
||||
|
||||
// SendSimple 发送简单邮件(便捷方法)
|
||||
// to: 收件人
|
||||
// subject: 主题
|
||||
// body: 正文
|
||||
func (e *Email) SendSimple(to []string, subject, body string) error {
|
||||
return e.Send(&Message{
|
||||
To: to,
|
||||
Subject: subject,
|
||||
Body: body,
|
||||
})
|
||||
}
|
||||
|
||||
// SendHTML 发送HTML邮件(便捷方法)
|
||||
// to: 收件人
|
||||
// subject: 主题
|
||||
// htmlBody: HTML正文
|
||||
func (e *Email) SendHTML(to []string, subject, htmlBody string) error {
|
||||
return e.Send(&Message{
|
||||
To: to,
|
||||
Subject: subject,
|
||||
HTMLBody: htmlBody,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user