package sms import ( "context" "errors" "fmt" ) var ( ErrProviderConfigInvalid = errors.New("sms provider config invalid") ErrProviderRateLimited = errors.New("sms provider rate limited") ) // TemplateRequest 描述一条模板短信。SignName 留空时使用 Provider 自身配置的默认签名。 type TemplateRequest struct { Phone string SignName string TemplateCode string Params map[string]string } type Provider interface { SendLoginCode(ctx context.Context, phone string, code string) error // SendTemplate 发送模板短信,用于登录验证码以外的业务节点触达。 // 阿里云要求模板必须提前报备审核,因此调用方只传模板 CODE 与变量,不拼接文案。 SendTemplate(ctx context.Context, req TemplateRequest) error } type ProviderError struct { Code string Message string Err error } func (e *ProviderError) Error() string { if e.Message != "" { return fmt.Sprintf("sms provider rejected: %s: %s", e.Code, e.Message) } if e.Code != "" { return fmt.Sprintf("sms provider rejected: %s", e.Code) } return "sms provider rejected" } func (e *ProviderError) Unwrap() error { return e.Err } func IsProviderRateLimited(err error) bool { return errors.Is(err, ErrProviderRateLimited) }