package integration import ( "context" "regexp" "strings" "github.com/go-kratos/kratos/v3/errors" ) var ( ErrSMSInvalid = errors.BadRequest("SMS_INVALID", "短信参数无效") ErrSMSDisabled = errors.BadRequest("SMS_DISABLED", "短信渠道未配置或未启用") ErrSMSFailed = errors.BadRequest("SMS_SEND_FAILED", "短信提交失败,请检查服务商配置和账户状态") ) // SMSMessage carries one recipient and provider-specific template parameters. type SMSMessage struct { Provider string Phone string NamedParams map[string]string Params []string } type SMSResult struct { RequestID string MessageID string } type SMSSender interface { Send(context.Context, *SMSMessage) (*SMSResult, error) } type SMSUsecase struct{ sender SMSSender } func NewSMSUsecase(sender SMSSender) *SMSUsecase { return &SMSUsecase{sender: sender} } var smsPhonePattern = regexp.MustCompile(`^\+[1-9][0-9]{6,14}$`) func ValidateSMSMessage(m *SMSMessage) error { if m == nil || (m.Provider != "aliyun" && m.Provider != "tencent") || !smsPhonePattern.MatchString(m.Phone) { return ErrSMSInvalid } // The Aliyun adapter uses the mainland China SendSms API. if m.Provider == "aliyun" && (!strings.HasPrefix(m.Phone, "+861") || len(m.Phone) != 14 || len(m.Params) != 0) { return errors.BadRequest("SMS_INVALID", "阿里云短信请使用 +86 开头的中国大陆手机号及对象格式模板参数") } if m.Provider == "tencent" && len(m.NamedParams) != 0 { return errors.BadRequest("SMS_INVALID", "腾讯云短信模板参数必须是按模板顺序排列的字符串数组") } if len(m.NamedParams) > 20 || len(m.Params) > 20 { return ErrSMSInvalid } for key, value := range m.NamedParams { if strings.TrimSpace(key) == "" || len(key) > 64 || len(value) > 500 { return ErrSMSInvalid } } for _, value := range m.Params { if len(value) > 500 { return ErrSMSInvalid } } return nil } func (uc *SMSUsecase) Send(ctx context.Context, m *SMSMessage) (*SMSResult, error) { if err := ValidateSMSMessage(m); err != nil { return nil, err } return uc.sender.Send(ctx, m) } func validateSMSConfig(provider string, values map[string]any) error { definition, ok := IntegrationDefinition(IntegrationKindSMS, provider) if !ok { return errors.BadRequest("SMS_PROVIDER_INVALID", "不支持的短信服务商") } for _, field := range definition.Fields { value, ok := values[field.Key].(string) if !ok || len(value) > 512 || (field.Required && strings.TrimSpace(value) == "") { return errors.BadRequest("SMS_CONFIG_INVALID", "请填写有效的"+field.Label) } } return nil }