123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- package service
-
- import (
- "bytes"
- "encoding/base64"
- "encoding/json"
- "io/ioutil"
- "math/rand"
- "net/http"
- "strconv"
- "strings"
- "time"
-
- "Xcx_New/utils"
-
- "github.com/astaxie/beego"
- )
-
- type NewSMSServiceError struct {
- Err string
- }
-
- func (e *NewSMSServiceError) Error() string {
- return e.Err
- }
-
- // 有如下两个外部可调用的接口:SendSMSUseTemplate、SendSMSWithCustomContent两个函数
- // 但是实际上我打算将 SendSMSUseTemplate 作为底层函数使用,不直接提供外界:
- // 这需要提供数个默认模板,通过为这几个模板创建独立便利函数,封装 SendSMSUseTemplate 的参数 defaultTemplateID 和 params
-
- // 发送验证码短信
- // 参数 aespass 是加密后的地址信息,用于限制频繁调用
- func SendVerificationCodeSMS(mobile string, aespass string) error {
- if len(mobile) == 0 {
- return &SMSServiceError{Err: "手机号为空"}
- }
- if err := newCheckVerificationCodeSMSLimit(aespass, mobile); err != nil {
- return err
- }
-
- var code_str string
- for i := 0; i < 6; i++ {
- rand.Seed(time.Now().UnixNano())
- code_str += strconv.Itoa(rand.Intn(10))
- }
- templateID, _ := beego.AppConfig.Int("sms_verification_code_templateid")
- utils.TraceLog("验证码为%v", code_str)
- _, _, err := batchSendMessage(templateID, []string{code_str}, []string{mobile})
- if err == nil {
- redisClient := RedisClient()
- defer redisClient.Close()
- cur_date := time.Now().Format("2006-01-02")
- redisClient.Set("code_msg_"+mobile, code_str, time.Minute*10)
- redisClient.Incr("code_msg_" + mobile + "_" + cur_date).Result()
- // 取出地址信息,因为上面已经验证过,这里就直接解密而不做错误判断了
- bytesPass, _ := base64.StdEncoding.DecodeString(aespass)
- tpass := utils.AESDecrypt(bytesPass)
- redisClient.Incr("ip:host_" + cur_date + "_" + tpass).Result()
- }
- return err
- }
-
- func newCheckVerificationCodeSMSLimit(aespass string, mobile string) error {
- redisClient := RedisClient()
- defer redisClient.Close()
- bytesPass, err := base64.StdEncoding.DecodeString(aespass)
- utils.TraceLog("bytesPass:%v err:%v", bytesPass, err)
- utils.TraceLog("aespass:%v", aespass)
- if err != nil {
- return &SMSServiceError{Err: "缺少关键参数1"}
- }
- tpass := utils.AESDecrypt(bytesPass)
- if len(tpass) == 0 {
- return &SMSServiceError{Err: "缺少关键参数2"}
- }
-
- cur_date := time.Now().Format("2006-01-02")
- add_redis, err := redisClient.Get("ip:host_" + cur_date + "_" + tpass).Result()
- if err != nil {
- return &SMSServiceError{Err: "缺少关键参数3"}
- }
- ip_max_send_count, _ := beego.AppConfig.Int("ip_max_send_count")
- if add_count, _ := strconv.Atoi(add_redis); add_count >= ip_max_send_count {
- return &SMSServiceError{Err: "当前IP发送短信超过限制"}
- }
-
- moblie_count, _ := redisClient.Get("code_msg_" + mobile + "_" + cur_date).Result()
- moblie_count_int, _ := strconv.Atoi(moblie_count)
- if moblie_max, _ := beego.AppConfig.Int("moblie_max_send_count"); moblie_count_int >= moblie_max {
- return &SMSServiceError{Err: "当前手机号发送短信超过限制"}
- }
-
- return nil
- }
-
- // 指定模板群发短信
- // 返回值为发送了 n 条短信、短信平台返回的 report 数组[{"code":"0", "msg":"OK", "smsid":"f96f79240e372587e9284cd580d8f953", "mobile":"18011984299", "count":"1"}]
- func batchSendMessage(templateID int, params []string, mobiles []string) (int, []interface{}, error) {
- sms_api := beego.AppConfig.String("sms_baseUrl") + "sendsms"
- mobileStr := strings.Join(mobiles, ",")
- appID, sid, token := getSMSConfig()
- requestParams := make(map[string]interface{})
- requestParams["appid"] = appID
- requestParams["sid"] = sid
- requestParams["token"] = token
- requestParams["templateid"] = strconv.Itoa(templateID)
- requestParams["mobile"] = mobileStr
- if params != nil && len(params) != 0 {
- paramStr := strings.Join(params, ",")
- requestParams["param"] = paramStr
- }
-
- paramsBytes, _ := json.Marshal(requestParams)
- resp, requestErr := http.Post(sms_api, "application/json", bytes.NewBuffer(paramsBytes))
-
- if requestErr != nil {
- utils.ErrorLog("短信平台模板群发接口调用失败: %v", requestErr)
- return 0, nil, requestErr
- }
- defer resp.Body.Close()
- body, ioErr := ioutil.ReadAll(resp.Body)
- if ioErr != nil {
- utils.ErrorLog("短信平台模板群发接口返回数据读取失败: %v", ioErr)
- return 0, nil, ioErr
- }
- var respJSON map[string]interface{}
- utils.InfoLog(string(body))
- if err := json.Unmarshal([]byte(string(body)), &respJSON); err != nil {
- utils.ErrorLog("短信平台模板群发接口返回数据解析JSON失败: %v", err)
- return 0, nil, err
- }
- if respJSON["code"].(string) != "000000" {
- msg := respJSON["msg"].(string)
- utils.ErrorLog("短信平台模板群发接口请求失败: %v", msg)
- return 0, nil, &SMSServiceError{"短信平台模板群发接口请求失败"}
-
- } else {
- utils.SuccessLog("短信发送成功 report: %v", respJSON["report"])
- if len(mobiles) > 1 {
- count, _ := strconv.Atoi(respJSON["count_sum"].(string))
- return count, respJSON["report"].([]interface{}), nil
- } else {
- return 1, nil, nil
- }
- }
- }
|