更新
This commit is contained in:
+218
@@ -0,0 +1,218 @@
|
||||
package mailx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SenderInfo 描述一个已注册通道实例的信息
|
||||
type SenderInfo struct {
|
||||
// Name 实例名,是管理器中的唯一标识,用于 SendWith/Unregister/SetDefault 路由
|
||||
Name string
|
||||
// Type 通道类型(底层 Sender.Name()),如 "smtp" / "aliyun" / "aws" / "mailgun"
|
||||
Type string
|
||||
}
|
||||
|
||||
// senderEntry 管理器内部存储的通道条目
|
||||
type senderEntry struct {
|
||||
instance string // 实例名(管理器内的唯一标识)
|
||||
sender Sender
|
||||
}
|
||||
|
||||
// Manager 多通道管理器:注册多个发送通道实例,发送时按实例名路由
|
||||
type Manager struct {
|
||||
mu sync.RWMutex
|
||||
senders map[string]senderEntry // key 为实例名
|
||||
def string
|
||||
|
||||
loggerMu sync.RWMutex
|
||||
logger Logger
|
||||
}
|
||||
|
||||
// NewManager 创建多通道管理器
|
||||
func NewManager() *Manager {
|
||||
return &Manager{
|
||||
senders: make(map[string]senderEntry),
|
||||
logger: noopLogger{},
|
||||
}
|
||||
}
|
||||
|
||||
// New 创建多通道管理器(NewManager 的别名,简化调用)
|
||||
func New() *Manager {
|
||||
return NewManager()
|
||||
}
|
||||
|
||||
// Register 注册一个发送通道,实例名即通道的 Name()。
|
||||
// 若需同一类型通道注册多份不同配置,请使用 RegisterNamed。
|
||||
func (m *Manager) Register(s Sender) error {
|
||||
if s == nil {
|
||||
return ErrInvalidConfig
|
||||
}
|
||||
return m.RegisterNamed(s.Name(), s)
|
||||
}
|
||||
|
||||
// RegisterNamed 以指定实例名注册一个发送通道。
|
||||
// 实例名是管理器内的唯一标识;允许同一通道类型(如 smtp)注册多份不同配置,
|
||||
// 例如 "smtp-main" 与 "smtp-backup"。
|
||||
func (m *Manager) RegisterNamed(name string, s Sender) error {
|
||||
if s == nil {
|
||||
return ErrInvalidConfig
|
||||
}
|
||||
if name == "" {
|
||||
return fmt.Errorf("%w: sender instance name is empty", ErrInvalidConfig)
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if _, ok := m.senders[name]; ok {
|
||||
return fmt.Errorf("%w: %q already registered", ErrInvalidConfig, name)
|
||||
}
|
||||
m.senders[name] = senderEntry{instance: name, sender: s}
|
||||
if m.def == "" {
|
||||
m.def = name
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unregister 注销指定实例名的通道
|
||||
func (m *Manager) Unregister(name string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
delete(m.senders, name)
|
||||
if m.def == name {
|
||||
m.def = ""
|
||||
for n := range m.senders {
|
||||
m.def = n
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetDefault 设置默认通道实例
|
||||
func (m *Manager) SetDefault(name string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if _, ok := m.senders[name]; !ok {
|
||||
return fmt.Errorf("%w: %q", ErrSenderNotFound, name)
|
||||
}
|
||||
m.def = name
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sender 按实例名获取已注册的通道;name 为空时返回默认实例
|
||||
func (m *Manager) Sender(name string) (Sender, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if name == "" {
|
||||
name = m.def
|
||||
}
|
||||
if name == "" {
|
||||
return nil, ErrSenderNotFound
|
||||
}
|
||||
e, ok := m.senders[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %q", ErrSenderNotFound, name)
|
||||
}
|
||||
return e.sender, nil
|
||||
}
|
||||
|
||||
// Registered 判断指定实例名的通道是否已注册
|
||||
func (m *Manager) Registered(name string) bool {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
_, ok := m.senders[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Names 返回所有已注册通道的实例名(顺序不保证稳定)。
|
||||
// 如需包含通道类型信息,请使用 Senders。
|
||||
func (m *Manager) Names() []string {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
names := make([]string, 0, len(m.senders))
|
||||
for n := range m.senders {
|
||||
names = append(names, n)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// Senders 返回所有已注册通道实例的信息列表(含实例名与通道类型)。
|
||||
func (m *Manager) Senders() []SenderInfo {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
infos := make([]SenderInfo, 0, len(m.senders))
|
||||
for _, e := range m.senders {
|
||||
infos = append(infos, SenderInfo{Name: e.instance, Type: e.sender.Name()})
|
||||
}
|
||||
return infos
|
||||
}
|
||||
|
||||
// Default 返回当前默认通道实例名(无默认时返回空串)
|
||||
func (m *Manager) Default() string {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.def
|
||||
}
|
||||
|
||||
// SetLogger 设置管理器全局日志器
|
||||
func (m *Manager) SetLogger(l Logger) *Manager {
|
||||
if l != nil {
|
||||
m.loggerMu.Lock()
|
||||
m.logger = l
|
||||
m.loggerMu.Unlock()
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Logger 返回管理器当前的日志器
|
||||
func (m *Manager) Logger() Logger {
|
||||
m.loggerMu.RLock()
|
||||
defer m.loggerMu.RUnlock()
|
||||
return m.logger
|
||||
}
|
||||
|
||||
// Send 使用默认通道发送
|
||||
func (m *Manager) Send(ctx context.Context, msg *Message) error {
|
||||
return m.SendWith(ctx, "", msg)
|
||||
}
|
||||
|
||||
// SendWith 使用指定实例名的通道发送(发送时指定配置/通道)
|
||||
func (m *Manager) SendWith(ctx context.Context, name string, msg *Message) error {
|
||||
s, err := m.Sender(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.dispatch(ctx, s, msg)
|
||||
}
|
||||
|
||||
// SendBy 使用临时传入的通道发送,无需提前注册(发送时指定配置)
|
||||
func (m *Manager) SendBy(ctx context.Context, s Sender, msg *Message) error {
|
||||
if s == nil {
|
||||
return ErrInvalidConfig
|
||||
}
|
||||
return m.dispatch(ctx, s, msg)
|
||||
}
|
||||
|
||||
// dispatch 统一校验消息并注入日志器后发送
|
||||
func (m *Manager) dispatch(ctx context.Context, s Sender, msg *Message) error {
|
||||
if msg == nil {
|
||||
return ErrInvalidMessage
|
||||
}
|
||||
if err := msg.Validate(); err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrInvalidMessage, err)
|
||||
}
|
||||
logger := m.Logger()
|
||||
start := time.Now()
|
||||
err := s.Send(WithLogger(ctx, logger), msg)
|
||||
elapsed := time.Since(start)
|
||||
if err != nil {
|
||||
logger.Errorf(ctx, "mailx: send via %q failed in %v: %v", s.Name(), elapsed, err)
|
||||
return err
|
||||
}
|
||||
logger.Infof(ctx, "mailx: sent via %q to %v in %v", s.Name(), msg.To, elapsed)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user