Files
kra-oa/internal/biz/integration/sms.go
T
2026-09-29 18:15:35 +08:00

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
}