package email import ( "errors" "sync" ) /* 系统描述:邮件系统,现支持qq邮箱以及gmail邮箱的邮件发送 */ type ( ISys interface { SendMail(title, content string, to ...string) error SendMailForFile(title string, content string, filepath string, filename string, to ...string) (err error) } ) // defsys 由 OnInit(启动) 与 Reload(运行时热更) 写、SendMail* 读,故必须加锁: // 热重载会整个换掉客户端(SMTP 的 *Email 与 Resend 的 *Resend 是不同具体类型), // 无锁并发读写接口值是 data race。 var ( mu sync.RWMutex defsys ISys ) var errNotInit = errors.New("email 系统未初始化") func get() ISys { mu.RLock() defer mu.RUnlock() return defsys } func set(sys ISys) { mu.Lock() defsys = sys mu.Unlock() } func OnInit(config map[string]interface{}, option ...Option) (err error) { sys, err := newSys(newOptions(config, option...)) if err != nil { return } set(sys) return } // Reload 运行时热替换邮件客户端(console 后台改配置后由业务侧调用)。 // 与 OnInit 的关键差别:配置不合法时返回 error 而非 panic,且**保留原客户端不动**, // 保证一次误保存不会让线上服务失去发信能力。 func Reload(config map[string]interface{}, option ...Option) error { options, err := newOptionsChecked(config, option...) if err != nil { return err } sys, err := newSys(options) if err != nil { return err } set(sys) return nil } func NewSys(option ...Option) (sys ISys, err error) { sys, err = newSys(newOptionsByOption(option...)) return } func SendMail(title, content string, to ...string) error { sys := get() if sys == nil { return errNotInit } return sys.SendMail(title, content, to...) } func SendMailForFile(title, content string, filepath string, filename string, to ...string) error { sys := get() if sys == nil { return errNotInit } return sys.SendMailForFile(title, content, filepath, filename, to...) }