调整项目结构,factory只负责暴露方法,不实现业务细节

This commit is contained in:
2025-12-07 00:04:01 +08:00
parent b66f345281
commit 339920a940
23 changed files with 2165 additions and 1231 deletions

View File

@@ -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,
})
}