调整SetOptions的方式

This commit is contained in:
Yun
2025-11-21 15:54:49 +08:00
parent 04bdd98d6f
commit eeaa518fc3
17 changed files with 90 additions and 86 deletions
+8 -6
View File
@@ -34,18 +34,18 @@ func NewAliyun() *Aliyun {
return aliyun return aliyun
} }
func (l *Aliyun) SetOption(ctx context.Context, opt ...interfaces.Option) (interfaces.EmailInterface, error) { func (l *Aliyun) SetOption(ctx context.Context, opt ...interfaces.Option) error {
for _, o := range opt { for _, o := range opt {
o(&l.Options) o(&l.Options)
} }
if l.Options.Aliyun == nil { if l.Options.Aliyun == nil {
return nil, fmt.Errorf("Aliyun configuration is required") return fmt.Errorf("Aliyun configuration is required")
} }
// 验证配置 // 验证配置
if err := l.validateConfig(); err != nil { if err := l.validateConfig(); err != nil {
return nil, fmt.Errorf("invalid Aliyun config: %w", err) return fmt.Errorf("invalid Aliyun config: %w", err)
} }
// 安全日志输出 // 安全日志输出
@@ -64,15 +64,17 @@ func (l *Aliyun) SetOption(ctx context.Context, opt ...interfaces.Option) (inter
client, err := dm20151123.NewClient(config) client, err := dm20151123.NewClient(config)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create Aliyun client: %w", err) return fmt.Errorf("failed to create Aliyun client: %w", err)
} }
l.IsSet = true
l.client = client l.client = client
return l, nil return nil
} }
func (l *Aliyun) Send(ctx context.Context, params interfaces.Message) error { func (l *Aliyun) Send(ctx context.Context, params interfaces.Message) error {
if l.client == nil { if !l.IsSet {
return fmt.Errorf("Aliyun client not initialized") return fmt.Errorf("Aliyun client not initialized")
} }
+2 -2
View File
@@ -10,10 +10,10 @@ import (
) )
func TestSend(t *testing.T) { func TestSend(t *testing.T) {
aliyun := aliyun.NewAliyun() ali := aliyun.NewAliyun()
ctx := context.Background() ctx := context.Background()
ali, err := aliyun.SetOption(ctx, interfaces.SetAliyun(&interfaces.EmialConfigDataAliyun{ err := ali.SetOption(ctx, interfaces.SetAliyun(&interfaces.EmialConfigDataAliyun{
AccessId: "LTAI5tEQ8L8fmDir8udD3CFr", AccessId: "LTAI5tEQ8L8fmDir8udD3CFr",
AccessKey: "llg9M1U56s2SW5PuerlKPvTB1xYhn0", AccessKey: "llg9M1U56s2SW5PuerlKPvTB1xYhn0",
Endpoint: "dm.aliyuncs.com", Endpoint: "dm.aliyuncs.com",
+13 -11
View File
@@ -14,9 +14,9 @@ import (
) )
const ( const (
MaxRetries = 3 MaxRetries = 3
DefaultRegion = "us-east-1" DefaultRegion = "us-east-1"
MaxRecipients = 50 // AWS SES limit MaxRecipients = 50 // AWS SES limit
) )
type Aws struct { type Aws struct {
@@ -31,18 +31,18 @@ func NewAws() *Aws {
return aws return aws
} }
func (l *Aws) SetOption(ctx context.Context, opt ...interfaces.Option) (interfaces.EmailInterface, error) { func (l *Aws) SetOption(ctx context.Context, opt ...interfaces.Option) error {
for _, o := range opt { for _, o := range opt {
o(&l.Options) o(&l.Options)
} }
if l.Options.Aws == nil { if l.Options.Aws == nil {
return nil, fmt.Errorf("AWS configuration is required") return fmt.Errorf("AWS configuration is required")
} }
// 验证配置 // 验证配置
if err := l.validateConfig(); err != nil { if err := l.validateConfig(); err != nil {
return nil, fmt.Errorf("invalid AWS config: %w", err) return fmt.Errorf("invalid AWS config: %w", err)
} }
if l.Options.Aws.Region == "" { if l.Options.Aws.Region == "" {
@@ -51,18 +51,20 @@ func (l *Aws) SetOption(ctx context.Context, opt ...interfaces.Option) (interfac
// 初始化SES客户端 // 初始化SES客户端
if err := l.initSESClient(); err != nil { if err := l.initSESClient(); err != nil {
return nil, fmt.Errorf("failed to initialize SES client: %w", err) return fmt.Errorf("failed to initialize SES client: %w", err)
} }
// 安全日志输出 // 安全日志输出
l.Options.Logger.Infof(ctx, "AWS SES configured - Region:%s Sender:%s", l.Options.Logger.Infof(ctx, "AWS SES configured - Region:%s Sender:%s",
l.Options.Aws.Region, l.Options.Aws.Sender) l.Options.Aws.Region, l.Options.Aws.Sender)
return l, nil l.IsSet = true
return nil
} }
func (l *Aws) Send(ctx context.Context, params interfaces.Message) error { func (l *Aws) Send(ctx context.Context, params interfaces.Message) error {
if l.sesClient == nil { if !l.IsSet {
return fmt.Errorf("AWS SES client not initialized") return fmt.Errorf("AWS SES client not initialized")
} }
@@ -204,4 +206,4 @@ func (l *Aws) sendEmail(ctx context.Context, params interfaces.Message) error {
} }
return nil return nil
} }
+3 -3
View File
@@ -25,7 +25,7 @@ func NewAwsOld() *AwsOld {
return aws return aws
} }
func (l *AwsOld) SetOption(ctx context.Context, opt ...interfaces.Option) (interfaces.EmailInterface, error) { func (l *AwsOld) SetOption(ctx context.Context, opt ...interfaces.Option) error {
for _, o := range opt { for _, o := range opt {
o(&l.Options) o(&l.Options)
@@ -33,14 +33,14 @@ func (l *AwsOld) SetOption(ctx context.Context, opt ...interfaces.Option) (inter
l.Options.Logger.Infof(ctx, "Aws:%+v", l.Options.Aws) l.Options.Logger.Infof(ctx, "Aws:%+v", l.Options.Aws)
if l.Options.Aws == nil { if l.Options.Aws == nil {
return nil, errors.New("not aws") return errors.New("not aws")
} }
if l.Options.Aws.Region == "" { if l.Options.Aws.Region == "" {
l.Options.Aws.Region = "ap-northeast-1" l.Options.Aws.Region = "ap-northeast-1"
} }
return l, nil return nil
} }
func (l *AwsOld) Send(ctx context.Context, params interfaces.Message) error { func (l *AwsOld) Send(ctx context.Context, params interfaces.Message) error {
+2 -2
View File
@@ -21,11 +21,11 @@ func TestSend(t *testing.T) {
// #发件人 // #发件人
// Source: "chenlihan@dreaminglife.cn" // Source: "chenlihan@dreaminglife.cn"
a := aws.NewAws() ini := aws.NewAws()
ctx := context.Background() ctx := context.Background()
ini, err := a.SetOption(ctx, interfaces.SetAws(&interfaces.EmailConfigDataAws{ err := ini.SetOption(ctx, interfaces.SetAws(&interfaces.EmailConfigDataAws{
AccessId: "AKIAU6GD3MNRHKR4RZG5", AccessId: "AKIAU6GD3MNRHKR4RZG5",
AccessSecret: "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", AccessSecret: "GSdGuFbZlcpVHMODlqeIKr07R/BdTBGeurq0s+4l",
Region: "ap-northeast-1", Region: "ap-northeast-1",
+4 -4
View File
@@ -18,7 +18,7 @@ func main() {
panic(err) panic(err)
} }
// 使用em进行后续操作 // 使用em进行后续操作
em,err = em.SetOption(ctx, interfaces.SetSmtp(&interfaces.EmailConfigDataSmtp{ err = em.SetOption(ctx, interfaces.SetSmtp(&interfaces.EmailConfigDataSmtp{
Username: "support@email.blueoceanpay.com", Username: "support@email.blueoceanpay.com",
Password: "SupporT2017", Password: "SupporT2017",
ReplyTo: "", ReplyTo: "",
@@ -30,10 +30,10 @@ func main() {
} }
err = em.Send(ctx, interfaces.Message{ err = em.Send(ctx, interfaces.Message{
Form: "yun@blueoceanpay.com", Form: "yun@blueoceanpay.com",
To: []string{"995116474@qq.com"}, To: []string{"995116474@qq.com"},
Subject: "Test Email", Subject: "Test Email",
Body: "Hello, this is a test email.", Body: "Hello, this is a test email.",
}) })
if err != nil { if err != nil {
panic(err) panic(err)
+4 -4
View File
@@ -19,7 +19,7 @@ func TestSMTPEnhanced(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// 测试配置验证 // 测试配置验证
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.example.com", Host: "smtp.example.com",
Port: "587", Port: "587",
@@ -53,7 +53,7 @@ func TestAliyunEnhanced(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// 测试配置验证 // 测试配置验证
_, err := aliyunClient.SetOption(ctx, func(opt *interfaces.Options) { err := aliyunClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Aliyun = &interfaces.EmialConfigDataAliyun{ opt.Aliyun = &interfaces.EmialConfigDataAliyun{
AccessId: "test-access-id", AccessId: "test-access-id",
AccessKey: "test-access-key", AccessKey: "test-access-key",
@@ -74,7 +74,7 @@ func TestAWSEnhanced(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// 测试配置验证 // 测试配置验证
_, err := awsClient.SetOption(ctx, func(opt *interfaces.Options) { err := awsClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Aws = &interfaces.EmailConfigDataAws{ opt.Aws = &interfaces.EmailConfigDataAws{
AccessId: "test-access-id", AccessId: "test-access-id",
AccessSecret: "test-access-secret", AccessSecret: "test-access-secret",
@@ -95,7 +95,7 @@ func TestMailgunEnhanced(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// 测试配置验证 // 测试配置验证
_, err := mailgunClient.SetOption(ctx, func(opt *interfaces.Options) { err := mailgunClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Mailgun = &interfaces.EmialConfigDataMailgun{ opt.Mailgun = &interfaces.EmialConfigDataMailgun{
ApiKey: "test-api-key", ApiKey: "test-api-key",
Domain: "example.com", Domain: "example.com",
+5 -5
View File
@@ -15,7 +15,7 @@ func ExampleHTMLEmail() {
smtpClient := smtp.NewSmtp() smtpClient := smtp.NewSmtp()
// 配置SMTP // 配置SMTP
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.gmail.com", Host: "smtp.gmail.com",
Port: "587", Port: "587",
@@ -110,7 +110,7 @@ func ExampleTextEmail() {
ctx := context.Background() ctx := context.Background()
smtpClient := smtp.NewSmtp() smtpClient := smtp.NewSmtp()
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.gmail.com", Host: "smtp.gmail.com",
Port: "587", Port: "587",
@@ -159,7 +159,7 @@ func ExampleAutoDetectContentType() {
ctx := context.Background() ctx := context.Background()
smtpClient := smtp.NewSmtp() smtpClient := smtp.NewSmtp()
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.gmail.com", Host: "smtp.gmail.com",
Port: "587", Port: "587",
@@ -205,7 +205,7 @@ func ExampleByteDataAttachment() {
ctx := context.Background() ctx := context.Background()
smtpClient := smtp.NewSmtp() smtpClient := smtp.NewSmtp()
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.gmail.com", Host: "smtp.gmail.com",
Port: "587", Port: "587",
@@ -250,7 +250,7 @@ func ExampleComplexHTMLEmail() {
ctx := context.Background() ctx := context.Background()
smtpClient := smtp.NewSmtp() smtpClient := smtp.NewSmtp()
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.gmail.com", Host: "smtp.gmail.com",
Port: "587", Port: "587",
+7 -7
View File
@@ -5,10 +5,8 @@ import (
"errors" "errors"
) )
type EmailInterface interface { type EmailInterface interface {
SetOption(ctx context.Context, opt ...Option) (EmailInterface, error) // 初始化 SetOption(ctx context.Context, opt ...Option) error // 初始化
GetEmailType() EmailType GetEmailType() EmailType
// Send 发送邮件 // Send 发送邮件
Send(ctx context.Context, params Message) error Send(ctx context.Context, params Message) error
@@ -24,6 +22,7 @@ type EmailFactoryInterface interface {
type DefaultEmail struct { type DefaultEmail struct {
Options Options // 配置信息 Options Options // 配置信息
EmailType EmailType EmailType EmailType
IsSet bool
} }
func NewDefaultEmail() *DefaultEmail { func NewDefaultEmail() *DefaultEmail {
@@ -33,15 +32,16 @@ func NewDefaultEmail() *DefaultEmail {
} }
} }
func (l *DefaultEmail) SetOption(ctx context.Context, opt ...Option) (EmailInterface, error) { func (l *DefaultEmail) SetOption(ctx context.Context, opt ...Option) error {
// 深复制l并且返回新的 // 深复制l并且返回新的
newL := *l
for _, o := range opt { for _, o := range opt {
o(&newL.Options) o(&l.Options)
} }
return &newL, nil l.IsSet = true
return nil
} }
func (l *DefaultEmail) GetEmailType() EmailType { func (l *DefaultEmail) GetEmailType() EmailType {
+7 -6
View File
@@ -29,31 +29,32 @@ func NewMailGun() *MailGun {
return mailgun return mailgun
} }
func (l *MailGun) SetOption(ctx context.Context, opt ...interfaces.Option) (interfaces.EmailInterface, error) { func (l *MailGun) SetOption(ctx context.Context, opt ...interfaces.Option) error {
for _, o := range opt { for _, o := range opt {
o(&l.Options) o(&l.Options)
} }
if l.Options.Mailgun == nil { if l.Options.Mailgun == nil {
return nil, fmt.Errorf("Mailgun configuration is required") return fmt.Errorf("Mailgun configuration is required")
} }
// 验证配置 // 验证配置
if err := l.validateConfig(); err != nil { if err := l.validateConfig(); err != nil {
return nil, fmt.Errorf("invalid Mailgun config: %w", err) return fmt.Errorf("invalid Mailgun config: %w", err)
} }
// 安全日志输出 // 安全日志输出
l.Options.Logger.Infof(ctx, "Mailgun configured - Domain:%s Sender:%s", l.Options.Logger.Infof(ctx, "Mailgun configured - Domain:%s Sender:%s",
l.Options.Mailgun.Domain, l.Options.Mailgun.Sender) l.Options.Mailgun.Domain, l.Options.Mailgun.Sender)
l.mg = mailgun.NewMailgun(l.Options.Mailgun.Domain, l.Options.Mailgun.ApiKey) l.mg = mailgun.NewMailgun(l.Options.Mailgun.Domain, l.Options.Mailgun.ApiKey)
return l, nil l.IsSet = true
return nil
} }
func (l *MailGun) Send(ctx context.Context, params interfaces.Message) error { func (l *MailGun) Send(ctx context.Context, params interfaces.Message) error {
if l.mg == nil { if !l.IsSet {
return fmt.Errorf("Mailgun client not initialized") return fmt.Errorf("Mailgun client not initialized")
} }
+2 -2
View File
@@ -15,10 +15,10 @@ var (
) )
func TestSendEmail(t *testing.T) { func TestSendEmail(t *testing.T) {
gun := mailgun.NewMailGun() ini := mailgun.NewMailGun()
ctx := context.Background() ctx := context.Background()
ini, err := gun.SetOption(ctx, interfaces.SetMailgun(&interfaces.EmialConfigDataMailgun{ err := ini.SetOption(ctx, interfaces.SetMailgun(&interfaces.EmialConfigDataMailgun{
ApiKey: apikey, ApiKey: apikey,
Domain: domain, Domain: domain,
Sender: sender, Sender: sender,
+13 -13
View File
@@ -17,7 +17,7 @@ func TestConcurrentSend(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// 配置SMTP客户端 // 配置SMTP客户端
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.example.com", Host: "smtp.example.com",
Port: "587", Port: "587",
@@ -42,7 +42,7 @@ func TestConcurrentSend(t *testing.T) {
wg.Add(1) wg.Add(1)
go func(goroutineID int) { go func(goroutineID int) {
defer wg.Done() defer wg.Done()
for j := 0; j < emailsPerGoroutine; j++ { for j := 0; j < emailsPerGoroutine; j++ {
message := interfaces.Message{ message := interfaces.Message{
To: []string{fmt.Sprintf("test%d_%d@example.com", goroutineID, j)}, To: []string{fmt.Sprintf("test%d_%d@example.com", goroutineID, j)},
@@ -55,7 +55,7 @@ func TestConcurrentSend(t *testing.T) {
if err != nil { if err != nil {
errors <- fmt.Errorf("goroutine %d, email %d: %w", goroutineID, j, err) errors <- fmt.Errorf("goroutine %d, email %d: %w", goroutineID, j, err)
} }
// 模拟发送时间 // 模拟发送时间
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
} }
@@ -88,7 +88,7 @@ func TestMemoryUsage(t *testing.T) {
smtpClient := smtp.NewSmtp() smtpClient := smtp.NewSmtp()
ctx := context.Background() ctx := context.Background()
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.example.com", Host: "smtp.example.com",
Port: "587", Port: "587",
@@ -128,7 +128,7 @@ func TestConnectionReuse(t *testing.T) {
smtpClient := smtp.NewSmtp() smtpClient := smtp.NewSmtp()
ctx := context.Background() ctx := context.Background()
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.example.com", Host: "smtp.example.com",
Port: "587", Port: "587",
@@ -168,7 +168,7 @@ func TestErrorRecovery(t *testing.T) {
smtpClient := smtp.NewSmtp() smtpClient := smtp.NewSmtp()
ctx := context.Background() ctx := context.Background()
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "invalid-smtp-server.com", // 故意使用无效服务器 Host: "invalid-smtp-server.com", // 故意使用无效服务器
Port: "587", Port: "587",
@@ -187,7 +187,7 @@ func TestErrorRecovery(t *testing.T) {
} }
start := time.Now() start := time.Now()
// 这应该失败并触发重试机制 // 这应该失败并触发重试机制
err = smtpClient.Send(ctx, message) err = smtpClient.Send(ctx, message)
@@ -228,7 +228,7 @@ func TestLargeAttachment(t *testing.T) {
smtpClient := smtp.NewSmtp() smtpClient := smtp.NewSmtp()
ctx := context.Background() ctx := context.Background()
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.example.com", Host: "smtp.example.com",
Port: "587", Port: "587",
@@ -282,11 +282,11 @@ func BenchmarkConcurrentEmailCreation(b *testing.B) {
// 配置创建基准测试 // 配置创建基准测试
func BenchmarkSMTPConfiguration(b *testing.B) { func BenchmarkSMTPConfiguration(b *testing.B) {
ctx := context.Background() ctx := context.Background()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
smtpClient := smtp.NewSmtp() smtpClient := smtp.NewSmtp()
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.example.com", Host: "smtp.example.com",
Port: "587", Port: "587",
@@ -318,7 +318,7 @@ func TestLoadTesting(t *testing.T) {
smtpClient := smtp.NewSmtp() smtpClient := smtp.NewSmtp()
ctx := context.Background() ctx := context.Background()
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.example.com", Host: "smtp.example.com",
Port: "587", Port: "587",
@@ -342,7 +342,7 @@ func TestLoadTesting(t *testing.T) {
wg.Add(1) wg.Add(1)
go func(emailID int) { go func(emailID int) {
defer wg.Done() defer wg.Done()
// 获取信号量 // 获取信号量
semaphore <- struct{}{} semaphore <- struct{}{}
defer func() { <-semaphore }() defer func() { <-semaphore }()
@@ -367,4 +367,4 @@ func TestLoadTesting(t *testing.T) {
t.Logf("- Concurrency: %d", concurrency) t.Logf("- Concurrency: %d", concurrency)
t.Logf("- Duration: %v", duration) t.Logf("- Duration: %v", duration)
t.Logf("- Throughput: %.2f emails/second", float64(totalEmails)/duration.Seconds()) t.Logf("- Throughput: %.2f emails/second", float64(totalEmails)/duration.Seconds())
} }
+3 -3
View File
@@ -15,7 +15,7 @@ func ExampleUsage() {
smtpClient := NewSmtp() smtpClient := NewSmtp()
// 配置SMTP设置 // 配置SMTP设置
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.gmail.com", Host: "smtp.gmail.com",
Port: "587", // STARTTLS Port: "587", // STARTTLS
@@ -56,7 +56,7 @@ func ExampleSSLUsage() {
smtpClient := NewSmtp() smtpClient := NewSmtp()
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.gmail.com", Host: "smtp.gmail.com",
Port: "465", // SSL Port: "465", // SSL
@@ -87,7 +87,7 @@ func ExampleEnterpriseEmail() {
smtpClient := NewSmtp() smtpClient := NewSmtp()
// 企业邮箱通常使用587端口和STARTTLS // 企业邮箱通常使用587端口和STARTTLS
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.exmail.qq.com", // 腾讯企业邮箱 Host: "smtp.exmail.qq.com", // 腾讯企业邮箱
Port: "587", Port: "587",
+2 -2
View File
@@ -148,7 +148,7 @@ func TestMessageValidation2(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// 配置SMTP客户端 // 配置SMTP客户端
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.example.com", Host: "smtp.example.com",
Port: "587", Port: "587",
@@ -226,7 +226,7 @@ func TestMultipartEmailConstruction2(t *testing.T) {
smtpClient := NewSmtp() smtpClient := NewSmtp()
ctx := context.Background() ctx := context.Background()
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) { err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.example.com", Host: "smtp.example.com",
Port: "587", Port: "587",
+10 -8
View File
@@ -44,18 +44,18 @@ func NewSmtp() *Smtp {
return smtp return smtp
} }
func (l *Smtp) SetOption(ctx context.Context, opt ...interfaces.Option) (interfaces.EmailInterface, error) { func (l *Smtp) SetOption(ctx context.Context, opt ...interfaces.Option) error {
for _, o := range opt { for _, o := range opt {
o(&l.Options) o(&l.Options)
} }
if l.Options.Smtp == nil { if l.Options.Smtp == nil {
return nil, fmt.Errorf("SMTP configuration is required") return fmt.Errorf("SMTP configuration is required")
} }
// 验证配置 // 验证配置
if err := l.validateConfig(); err != nil { if err := l.validateConfig(); err != nil {
return nil, fmt.Errorf("invalid SMTP config: %w", err) return fmt.Errorf("invalid SMTP config: %w", err)
} }
// 初始化认证 // 初始化认证
@@ -65,11 +65,13 @@ func (l *Smtp) SetOption(ctx context.Context, opt ...interfaces.Option) (interfa
l.Options.Logger.Infof(ctx, "SMTP configured - Host:%s Port:%s Username:%s", l.Options.Logger.Infof(ctx, "SMTP configured - Host:%s Port:%s Username:%s",
l.Options.Smtp.Host, l.Options.Smtp.Port, l.Options.Smtp.Username) l.Options.Smtp.Host, l.Options.Smtp.Port, l.Options.Smtp.Username)
return l, nil l.IsSet = true
return nil
} }
func (l *Smtp) Send(ctx context.Context, message interfaces.Message) error { func (l *Smtp) Send(ctx context.Context, message interfaces.Message) error {
if l.Options.Smtp == nil { if !l.IsSet {
return fmt.Errorf("SMTP not initialized") return fmt.Errorf("SMTP not initialized")
} }
@@ -441,19 +443,19 @@ func (l *Smtp) writeBody(buffer *bytes.Buffer, boundary string, message interfac
altBoundary := "alt-" + boundary altBoundary := "alt-" + boundary
buffer.WriteString(fmt.Sprintf("--%s\r\n", boundary)) buffer.WriteString(fmt.Sprintf("--%s\r\n", boundary))
buffer.WriteString(fmt.Sprintf("Content-Type: multipart/alternative; boundary=%s\r\n\r\n", altBoundary)) buffer.WriteString(fmt.Sprintf("Content-Type: multipart/alternative; boundary=%s\r\n\r\n", altBoundary))
// 纯文本版本 // 纯文本版本
buffer.WriteString(fmt.Sprintf("--%s\r\n", altBoundary)) buffer.WriteString(fmt.Sprintf("--%s\r\n", altBoundary))
buffer.WriteString("Content-Type: text/plain; charset=utf-8\r\n") buffer.WriteString("Content-Type: text/plain; charset=utf-8\r\n")
buffer.WriteString("Content-Transfer-Encoding: quoted-printable\r\n\r\n") buffer.WriteString("Content-Transfer-Encoding: quoted-printable\r\n\r\n")
buffer.WriteString(message.TextBody + "\r\n") buffer.WriteString(message.TextBody + "\r\n")
// HTML版本 // HTML版本
buffer.WriteString(fmt.Sprintf("--%s\r\n", altBoundary)) buffer.WriteString(fmt.Sprintf("--%s\r\n", altBoundary))
buffer.WriteString("Content-Type: text/html; charset=utf-8\r\n") buffer.WriteString("Content-Type: text/html; charset=utf-8\r\n")
buffer.WriteString("Content-Transfer-Encoding: quoted-printable\r\n\r\n") buffer.WriteString("Content-Transfer-Encoding: quoted-printable\r\n\r\n")
buffer.WriteString(message.Body + "\r\n") buffer.WriteString(message.Body + "\r\n")
buffer.WriteString(fmt.Sprintf("--%s--\r\n", altBoundary)) buffer.WriteString(fmt.Sprintf("--%s--\r\n", altBoundary))
} else { } else {
// 单一内容类型 // 单一内容类型
+3 -3
View File
@@ -15,13 +15,13 @@ func TestSmtpEnhanced(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// 测试空配置 // 测试空配置
_, err := smtp.SetOption(ctx) err := smtp.SetOption(ctx)
if err == nil { if err == nil {
t.Error("Expected error for empty config") t.Error("Expected error for empty config")
} }
// 测试不完整配置 // 测试不完整配置
_, err = smtp.SetOption(ctx, func(opt *interfaces.Options) { err = smtp.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.example.com", Host: "smtp.example.com",
// 缺少用户名和密码 // 缺少用户名和密码
@@ -32,7 +32,7 @@ func TestSmtpEnhanced(t *testing.T) {
} }
// 测试完整配置 // 测试完整配置
_, err = smtp.SetOption(ctx, func(opt *interfaces.Options) { err = smtp.SetOption(ctx, func(opt *interfaces.Options) {
opt.Smtp = &interfaces.EmailConfigDataSmtp{ opt.Smtp = &interfaces.EmailConfigDataSmtp{
Host: "smtp.example.com", Host: "smtp.example.com",
Port: "587", Port: "587",
+2 -5
View File
@@ -10,7 +10,7 @@ import (
) )
func TestMail(t *testing.T) { func TestMail(t *testing.T) {
sm := smtp.NewSmtp() ini := smtp.NewSmtp()
ctx := context.Background() ctx := context.Background()
// ini, err := sm.SetOption(ctx, interfaces.SetSmtp(&interfaces.EmailConfigDataSmtp{ // ini, err := sm.SetOption(ctx, interfaces.SetSmtp(&interfaces.EmailConfigDataSmtp{
@@ -24,8 +24,7 @@ func TestMail(t *testing.T) {
// t.Fatal(err) // t.Fatal(err)
// } // }
err := ini.SetOption(ctx, interfaces.SetSmtp(&interfaces.EmailConfigDataSmtp{
ini, err := sm.SetOption(ctx, interfaces.SetSmtp(&interfaces.EmailConfigDataSmtp{
IsSSL: true, IsSSL: true,
Username: "demo@yunink.net", Username: "demo@yunink.net",
Password: "aAhVPzHydLoc9Fj8", Password: "aAhVPzHydLoc9Fj8",
@@ -37,8 +36,6 @@ func TestMail(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// req, err := http.Get("https://baidu.com") // req, err := http.Get("https://baidu.com")
// if err != nil { // if err != nil {
// t.Fatal(err) // t.Fatal(err)