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 }