87 lines
2.6 KiB
Go
87 lines
2.6 KiB
Go
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
|
|
}
|