调整项目结构,factory只负责暴露方法,不实现业务细节
This commit is contained in:
269
sms/sms.go
269
sms/sms.go
@@ -16,59 +16,6 @@ import (
|
||||
"git.toowon.com/jimmy/go-common/config"
|
||||
)
|
||||
|
||||
// SMS 短信发送器
|
||||
type SMS struct {
|
||||
config *config.SMSConfig
|
||||
}
|
||||
|
||||
// NewSMS 创建短信发送器
|
||||
func NewSMS(cfg *config.SMSConfig) (*SMS, error) {
|
||||
if cfg == nil {
|
||||
return nil, fmt.Errorf("SMS config is nil")
|
||||
}
|
||||
|
||||
if cfg.AccessKeyID == "" {
|
||||
return nil, fmt.Errorf("AccessKeyID is required")
|
||||
}
|
||||
|
||||
if cfg.AccessKeySecret == "" {
|
||||
return nil, fmt.Errorf("AccessKeySecret is required")
|
||||
}
|
||||
|
||||
if cfg.SignName == "" {
|
||||
return nil, fmt.Errorf("SignName is required")
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if cfg.Region == "" {
|
||||
cfg.Region = "cn-hangzhou"
|
||||
}
|
||||
if cfg.Timeout == 0 {
|
||||
cfg.Timeout = 10
|
||||
}
|
||||
|
||||
return &SMS{
|
||||
config: cfg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SendRequest 发送短信请求
|
||||
type SendRequest struct {
|
||||
// PhoneNumbers 手机号列表
|
||||
PhoneNumbers []string
|
||||
|
||||
// TemplateCode 模板代码(如果为空,使用配置中的模板代码)
|
||||
TemplateCode string
|
||||
|
||||
// TemplateParam 模板参数(可以是map或JSON字符串)
|
||||
// 如果是map,会自动转换为JSON字符串
|
||||
// 如果是string,直接使用(必须是有效的JSON字符串)
|
||||
TemplateParam interface{}
|
||||
|
||||
// SignName 签名(如果为空,使用配置中的签名)
|
||||
SignName string
|
||||
}
|
||||
|
||||
// SendResponse 发送短信响应
|
||||
type SendResponse struct {
|
||||
// RequestID 请求ID
|
||||
@@ -84,48 +31,123 @@ type SendResponse struct {
|
||||
BizID string `json:"BizId"`
|
||||
}
|
||||
|
||||
// SendRaw 发送原始请求(允许外部完全控制请求参数)
|
||||
// params: 请求参数map,工具只负责添加必要的系统参数(如签名、时间戳等)并发送
|
||||
func (s *SMS) SendRaw(params map[string]string) (*SendResponse, error) {
|
||||
if params == nil {
|
||||
params = make(map[string]string)
|
||||
// SMS 短信发送器
|
||||
type SMS struct {
|
||||
config *config.SMSConfig
|
||||
}
|
||||
|
||||
// NewSMS 创建短信发送器
|
||||
func NewSMS(cfg *config.Config) *SMS {
|
||||
if cfg == nil || cfg.SMS == nil {
|
||||
return &SMS{config: nil}
|
||||
}
|
||||
return &SMS{config: cfg.SMS}
|
||||
}
|
||||
|
||||
// getSMSConfig 获取短信配置(内部方法)
|
||||
func (s *SMS) getSMSConfig() (*config.SMSConfig, error) {
|
||||
if s.config == nil {
|
||||
return nil, fmt.Errorf("SMS config is nil")
|
||||
}
|
||||
|
||||
// 确保必要的系统参数存在
|
||||
if params["Action"] == "" {
|
||||
params["Action"] = "SendSms"
|
||||
if s.config.AccessKeyID == "" {
|
||||
return nil, fmt.Errorf("AccessKeyID is required")
|
||||
}
|
||||
if params["Version"] == "" {
|
||||
params["Version"] = "2017-05-25"
|
||||
|
||||
if s.config.AccessKeySecret == "" {
|
||||
return nil, fmt.Errorf("AccessKeySecret is required")
|
||||
}
|
||||
if params["RegionId"] == "" {
|
||||
params["RegionId"] = s.config.Region
|
||||
|
||||
if s.config.SignName == "" {
|
||||
return nil, fmt.Errorf("SignName is required")
|
||||
}
|
||||
if params["AccessKeyId"] == "" {
|
||||
params["AccessKeyId"] = s.config.AccessKeyID
|
||||
|
||||
// 设置默认值
|
||||
if s.config.Region == "" {
|
||||
s.config.Region = "cn-hangzhou"
|
||||
}
|
||||
if params["Format"] == "" {
|
||||
params["Format"] = "JSON"
|
||||
if s.config.Timeout == 0 {
|
||||
s.config.Timeout = 10
|
||||
}
|
||||
if params["SignatureMethod"] == "" {
|
||||
params["SignatureMethod"] = "HMAC-SHA1"
|
||||
|
||||
return s.config, nil
|
||||
}
|
||||
|
||||
// SendSMS 发送短信
|
||||
// phoneNumbers: 手机号列表
|
||||
// templateParam: 模板参数(map或JSON字符串)
|
||||
// templateCode: 模板代码(可选,如果为空使用配置中的模板代码)
|
||||
func (s *SMS) SendSMS(phoneNumbers []string, templateParam interface{}, templateCode ...string) (*SendResponse, error) {
|
||||
cfg, err := s.getSMSConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if params["SignatureVersion"] == "" {
|
||||
params["SignatureVersion"] = "1.0"
|
||||
|
||||
if len(phoneNumbers) == 0 {
|
||||
return nil, fmt.Errorf("phone numbers are required")
|
||||
}
|
||||
if params["SignatureNonce"] == "" {
|
||||
params["SignatureNonce"] = fmt.Sprint(time.Now().UnixNano())
|
||||
|
||||
// 使用配置中的模板代码(如果请求中未指定)
|
||||
templateCodeValue := ""
|
||||
if len(templateCode) > 0 && templateCode[0] != "" {
|
||||
templateCodeValue = templateCode[0]
|
||||
} else {
|
||||
templateCodeValue = cfg.TemplateCode
|
||||
}
|
||||
if params["Timestamp"] == "" {
|
||||
params["Timestamp"] = time.Now().UTC().Format("2006-01-02T15:04:05Z")
|
||||
if templateCodeValue == "" {
|
||||
return nil, fmt.Errorf("template code is required")
|
||||
}
|
||||
|
||||
signName := cfg.SignName
|
||||
|
||||
// 处理模板参数
|
||||
var templateParamJSON string
|
||||
if templateParam != nil {
|
||||
switch v := templateParam.(type) {
|
||||
case string:
|
||||
// 直接使用字符串(必须是有效的JSON)
|
||||
templateParamJSON = v
|
||||
case map[string]string:
|
||||
// 转换为JSON字符串
|
||||
paramBytes, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal template param: %w", err)
|
||||
}
|
||||
templateParamJSON = string(paramBytes)
|
||||
default:
|
||||
// 尝试JSON序列化
|
||||
paramBytes, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal template param: %w", err)
|
||||
}
|
||||
templateParamJSON = string(paramBytes)
|
||||
}
|
||||
} else {
|
||||
templateParamJSON = "{}"
|
||||
}
|
||||
|
||||
// 构建请求参数
|
||||
params := make(map[string]string)
|
||||
params["Action"] = "SendSms"
|
||||
params["Version"] = "2017-05-25"
|
||||
params["RegionId"] = cfg.Region
|
||||
params["AccessKeyId"] = cfg.AccessKeyID
|
||||
params["Format"] = "JSON"
|
||||
params["SignatureMethod"] = "HMAC-SHA1"
|
||||
params["SignatureVersion"] = "1.0"
|
||||
params["SignatureNonce"] = fmt.Sprint(time.Now().UnixNano())
|
||||
params["Timestamp"] = time.Now().UTC().Format("2006-01-02T15:04:05Z")
|
||||
params["PhoneNumbers"] = strings.Join(phoneNumbers, ",")
|
||||
params["SignName"] = signName
|
||||
params["TemplateCode"] = templateCodeValue
|
||||
params["TemplateParam"] = templateParamJSON
|
||||
|
||||
// 计算签名
|
||||
signature := s.calculateSignature(params, "POST")
|
||||
signature := s.calculateSignature(params, "POST", cfg.AccessKeySecret)
|
||||
params["Signature"] = signature
|
||||
|
||||
// 构建请求URL
|
||||
endpoint := s.config.Endpoint
|
||||
endpoint := cfg.Endpoint
|
||||
if endpoint == "" {
|
||||
endpoint = "https://dysmsapi.aliyuncs.com"
|
||||
}
|
||||
@@ -145,7 +167,7 @@ func (s *SMS) SendRaw(params map[string]string) (*SendResponse, error) {
|
||||
httpReq.Header.Set("Accept", "application/json")
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: time.Duration(s.config.Timeout) * time.Second,
|
||||
Timeout: time.Duration(cfg.Timeout) * time.Second,
|
||||
}
|
||||
|
||||
resp, err := client.Do(httpReq)
|
||||
@@ -174,70 +196,8 @@ func (s *SMS) SendRaw(params map[string]string) (*SendResponse, error) {
|
||||
return &sendResp, nil
|
||||
}
|
||||
|
||||
// Send 发送短信(使用SendRequest结构)
|
||||
// 注意:如果需要完全控制请求参数,请使用SendRaw方法
|
||||
func (s *SMS) Send(req *SendRequest) (*SendResponse, error) {
|
||||
if req == nil {
|
||||
return nil, fmt.Errorf("request is nil")
|
||||
}
|
||||
|
||||
if len(req.PhoneNumbers) == 0 {
|
||||
return nil, fmt.Errorf("phone numbers are required")
|
||||
}
|
||||
|
||||
// 使用配置中的模板代码和签名(如果请求中未指定)
|
||||
templateCode := req.TemplateCode
|
||||
if templateCode == "" {
|
||||
templateCode = s.config.TemplateCode
|
||||
}
|
||||
if templateCode == "" {
|
||||
return nil, fmt.Errorf("template code is required")
|
||||
}
|
||||
|
||||
signName := req.SignName
|
||||
if signName == "" {
|
||||
signName = s.config.SignName
|
||||
}
|
||||
|
||||
// 处理模板参数
|
||||
var templateParamJSON string
|
||||
if req.TemplateParam != nil {
|
||||
switch v := req.TemplateParam.(type) {
|
||||
case string:
|
||||
// 直接使用字符串(必须是有效的JSON)
|
||||
templateParamJSON = v
|
||||
case map[string]string:
|
||||
// 转换为JSON字符串
|
||||
paramBytes, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal template param: %w", err)
|
||||
}
|
||||
templateParamJSON = string(paramBytes)
|
||||
default:
|
||||
// 尝试JSON序列化
|
||||
paramBytes, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal template param: %w", err)
|
||||
}
|
||||
templateParamJSON = string(paramBytes)
|
||||
}
|
||||
} else {
|
||||
templateParamJSON = "{}"
|
||||
}
|
||||
|
||||
// 构建请求参数
|
||||
params := make(map[string]string)
|
||||
params["PhoneNumbers"] = strings.Join(req.PhoneNumbers, ",")
|
||||
params["SignName"] = signName
|
||||
params["TemplateCode"] = templateCode
|
||||
params["TemplateParam"] = templateParamJSON
|
||||
|
||||
// 使用SendRaw发送
|
||||
return s.SendRaw(params)
|
||||
}
|
||||
|
||||
// calculateSignature 计算签名
|
||||
func (s *SMS) calculateSignature(params map[string]string, method string) string {
|
||||
func (s *SMS) calculateSignature(params map[string]string, method, accessKeySecret string) string {
|
||||
// 对参数进行排序
|
||||
keys := make([]string, 0, len(params))
|
||||
for k := range params {
|
||||
@@ -260,31 +220,10 @@ func (s *SMS) calculateSignature(params map[string]string, method string) string
|
||||
stringToSign := method + "&" + url.QueryEscape("/") + "&" + url.QueryEscape(queryString)
|
||||
|
||||
// 计算HMAC-SHA1签名
|
||||
mac := hmac.New(sha1.New, []byte(s.config.AccessKeySecret+"&"))
|
||||
mac := hmac.New(sha1.New, []byte(accessKeySecret+"&"))
|
||||
mac.Write([]byte(stringToSign))
|
||||
signature := base64.StdEncoding.EncodeToString(mac.Sum(nil))
|
||||
|
||||
return signature
|
||||
}
|
||||
|
||||
// SendSimple 发送简单短信(便捷方法)
|
||||
// phoneNumbers: 手机号列表
|
||||
// templateParam: 模板参数
|
||||
func (s *SMS) SendSimple(phoneNumbers []string, templateParam map[string]string) (*SendResponse, error) {
|
||||
return s.Send(&SendRequest{
|
||||
PhoneNumbers: phoneNumbers,
|
||||
TemplateParam: templateParam,
|
||||
})
|
||||
}
|
||||
|
||||
// SendWithTemplate 使用指定模板发送短信(便捷方法)
|
||||
// phoneNumbers: 手机号列表
|
||||
// templateCode: 模板代码
|
||||
// templateParam: 模板参数
|
||||
func (s *SMS) SendWithTemplate(phoneNumbers []string, templateCode string, templateParam map[string]string) (*SendResponse, error) {
|
||||
return s.Send(&SendRequest{
|
||||
PhoneNumbers: phoneNumbers,
|
||||
TemplateCode: templateCode,
|
||||
TemplateParam: templateParam,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user