package mailgun import ( "context" "errors" "fmt" "time" "code.yun.ink/pkg/mailx/interfaces" "github.com/mailgun/mailgun-go/v4" ) const ( MaxRetries = 3 MaxRecipients = 1000 // Mailgun limit ) type MailGun struct { interfaces.DefaultEmail // params *interfaces.EmialConfigDataMailgun mg *mailgun.MailgunImpl // logger loggerx.LoggerInterface } func NewMailGun() *MailGun { mailgun := &MailGun{} mailgun.Options = interfaces.DefaultOptions() mailgun.EmailType = interfaces.EmailTypeMailgun return mailgun } func (l *MailGun) SetOption(ctx context.Context, opt ...interfaces.Option) (interfaces.EmailInterface, error) { for _, o := range opt { o(&l.Options) } if l.Options.Mailgun == nil { return nil, fmt.Errorf("Mailgun configuration is required") } // 验证配置 if err := l.validateConfig(); err != nil { return nil, fmt.Errorf("invalid Mailgun config: %w", err) } // 安全日志输出 l.Options.Logger.Infof(ctx, "Mailgun configured - Domain:%s Sender:%s", l.Options.Mailgun.Domain, l.Options.Mailgun.Sender) l.mg = mailgun.NewMailgun(l.Options.Mailgun.Domain, l.Options.Mailgun.ApiKey) return l, nil } func (l *MailGun) Send(ctx context.Context, params interfaces.Message) error { if l.mg == nil { return fmt.Errorf("Mailgun client not initialized") } // 验证消息 if err := l.validateMessage(params); err != nil { return fmt.Errorf("invalid message: %w", err) } // 重试机制 var lastErr error for i := 0; i < MaxRetries; i++ { if i > 0 { l.Options.Logger.Infof(ctx, "Retrying Mailgun email send, attempt %d/%d", i+1, MaxRetries) time.Sleep(time.Duration(i) * time.Second) } lastErr = l.sendEmail(ctx, params) if lastErr == nil { l.Options.Logger.Infof(ctx, "Mailgun email sent successfully to %v", params.To) return nil } l.Options.Logger.Errorf(ctx, "Mailgun send attempt %d failed: %v", i+1, lastErr) } return fmt.Errorf("failed to send Mailgun email after %d attempts: %w", MaxRetries, lastErr) } // 验证配置 func (l *MailGun) validateConfig() error { if l.Options.Mailgun.Domain == "" { return errors.New("Domain is required") } if l.Options.Mailgun.ApiKey == "" { return errors.New("ApiKey is required") } if l.Options.Mailgun.Sender == "" { return errors.New("Sender is required") } return nil } // 验证消息 func (l *MailGun) validateMessage(params interfaces.Message) error { if len(params.To) == 0 { return errors.New("at least one recipient is required") } if len(params.To) > MaxRecipients { return fmt.Errorf("too many recipients: %d (max: %d)", len(params.To), MaxRecipients) } if params.Subject == "" { return errors.New("subject is required") } if params.Body == "" { return errors.New("body is required") } return nil } // 发送邮件核心逻辑 func (l *MailGun) sendEmail(ctx context.Context, params interfaces.Message) error { // 创建邮件消息 message := l.mg.NewMessage(l.Options.Mailgun.Sender, params.Subject, "", params.To...) // 设置HTML内容 message.SetHtml(params.Body) // 设置Cc收件人 for _, cc := range params.Cc { message.AddCC(cc) } // 设置Bcc收件人 for _, bcc := range params.Bcc { message.AddBCC(bcc) } // 设置回复地址 if params.ReplyTo != "" { message.SetReplyTo(params.ReplyTo) } // 设置发件人名称 if params.Form != "" { // } // 添加附件 for _, attachment := range params.Attachment { message.AddAttachment(attachment.Content) } // 发送邮件 resp, id, err := l.mg.Send(ctx, message) if err != nil { return fmt.Errorf("Mailgun API call failed: %w", err) } // 记录响应信息 l.Options.Logger.Infof(ctx, "Mailgun email sent - ID:%s Response:%s", id, resp) return nil }