作者 yangfu

yunpian sms

1 -appname = ability 1 +appname = 能力展示
2 httpport = 8080 2 httpport = 8080
3 runmode = dev 3 runmode = dev
4 4
@@ -5,4 +5,8 @@ data_source = "root:123456@tcp(127.0.0.1:3306)/ability_display" @@ -5,4 +5,8 @@ data_source = "root:123456@tcp(127.0.0.1:3306)/ability_display"
5 5
6 #redis相关配置 6 #redis相关配置
7 redis_add_port = "127.0.0.1:6379" 7 redis_add_port = "127.0.0.1:6379"
8 -redis_auth = "123456"  
  8 +redis_auth = "123456"
  9 +
  10 +#sms相关配置
  11 +yunpian_sms_sdk_url ="https://sms.yunpian.com/v2/sms/single_send.json"
  12 +yunpian_app_key ="0bf6fb10a11a68a95dee80901eb545b5"
@@ -7,6 +7,8 @@ const ( @@ -7,6 +7,8 @@ const (
7 LoginSmdcode ="signInCaptcha" 7 LoginSmdcode ="signInCaptcha"
8 ) 8 )
9 9
  10 +var Nums =[]byte("0123456789")
  11 +
10 type RequestHeader struct { 12 type RequestHeader struct {
11 TimeStamp string 13 TimeStamp string
12 Uuid string 14 Uuid string
@@ -32,6 +34,9 @@ type LoginResponse struct { @@ -32,6 +34,9 @@ type LoginResponse struct {
32 /*SmsCode*/ 34 /*SmsCode*/
33 type SmsCodeRequest struct { 35 type SmsCodeRequest struct {
34 Phone string `json:"phone" valid:"Required;Mobile"` 36 Phone string `json:"phone" valid:"Required;Mobile"`
  37 + Content string `json:"-"`
  38 + SendType string `json:"send_type"`//sms_login_code sms_change_mobile
  39 +
35 } 40 }
36 type SmsCodeResponse struct { 41 type SmsCodeResponse struct {
37 } 42 }
  1 +package protocol
  2 +
  3 +//短信类型
  4 +const (
  5 + SmsLoginCode ="sms_login_code"
  6 + SmsChangeMobile="sms_change_mobile"
  7 +)
  8 +
  9 +type SmsInfo struct {
  10 + Code string `json:"code"`
  11 + Count int `json:"count"`
  12 + ErrorCount int `json:"error_count"`
  13 + LastTime int64 `json:"last_time"`
  14 + CreateTime int64 `json:"create_time"`
  15 +}
1 package auth 1 package auth
2 2
3 import ( 3 import (
  4 + "bytes"
4 "fmt" 5 "fmt"
  6 + "github.com/astaxie/beego"
5 "gitlab.fjmaimaimai.com/mmm-go/ability/internal/repository" 7 "gitlab.fjmaimaimai.com/mmm-go/ability/internal/repository"
6 "gitlab.fjmaimaimai.com/mmm-go/ability/models" 8 "gitlab.fjmaimaimai.com/mmm-go/ability/models"
7 "gitlab.fjmaimaimai.com/mmm-go/ability/protocol" 9 "gitlab.fjmaimaimai.com/mmm-go/ability/protocol"
8 "gitlab.fjmaimaimai.com/mmm-go/gocomm/common" 10 "gitlab.fjmaimaimai.com/mmm-go/gocomm/common"
9 "gitlab.fjmaimaimai.com/mmm-go/gocomm/pkg/log" 11 "gitlab.fjmaimaimai.com/mmm-go/gocomm/pkg/log"
  12 + "gitlab.fjmaimaimai.com/mmm-go/gocomm/pkg/redis"
  13 + s_sms "gitlab.fjmaimaimai.com/mmm-go/ability/services/sms"
  14 + comm_time "gitlab.fjmaimaimai.com/mmm-go/gocomm/time"
  15 + "html/template"
10 "strings" 16 "strings"
  17 + "encoding/json"
  18 + "time"
11 ) 19 )
12 20
13 type IAuthService interface { 21 type IAuthService interface {
@@ -28,7 +36,7 @@ func assertImplement(){ @@ -28,7 +36,7 @@ func assertImplement(){
28 36
29 var( 37 var(
30 //服务 38 //服务
31 - //sms s_sms.ISmsService = &s_sms.SmsService{} 39 + sms s_sms.ISmsService = &s_sms.YunPianSmsService{}
32 40
33 //仓储 41 //仓储
34 UserRepository repository.IUserRepository =&repository.UserRepository{} 42 UserRepository repository.IUserRepository =&repository.UserRepository{}
@@ -39,6 +47,7 @@ func (s *AuthService)Login(request *protocol.LoginRequest)(rsp *protocol.LoginRe @@ -39,6 +47,7 @@ func (s *AuthService)Login(request *protocol.LoginRequest)(rsp *protocol.LoginRe
39 var ( 47 var (
40 user *models.Users 48 user *models.Users
41 userInfo *models.UserInfo 49 userInfo *models.UserInfo
  50 + result bool
42 ) 51 )
43 user,err =UserRepository.GetUsersByMobile(request.Phone) 52 user,err =UserRepository.GetUsersByMobile(request.Phone)
44 if err!=nil{ 53 if err!=nil{
@@ -56,7 +65,11 @@ func (s *AuthService)Login(request *protocol.LoginRequest)(rsp *protocol.LoginRe @@ -56,7 +65,11 @@ func (s *AuthService)Login(request *protocol.LoginRequest)(rsp *protocol.LoginRe
56 } 65 }
57 break 66 break
58 case protocol.LoginSmdcode: 67 case protocol.LoginSmdcode:
59 - goto Success 68 + if result,err=CheckSmsCode(request);result && err==nil{
  69 + goto Success
  70 + }else{
  71 + return
  72 + }
60 default: 73 default:
61 err =fmt.Errorf("grantType error") 74 err =fmt.Errorf("grantType error")
62 return 75 return
@@ -168,5 +181,103 @@ func (s *AuthService)CheckUuid(request *protocol.CheckUuidRequest)(rsp *protocol @@ -168,5 +181,103 @@ func (s *AuthService)CheckUuid(request *protocol.CheckUuidRequest)(rsp *protocol
168 } 181 }
169 //短信验证码 182 //短信验证码
170 func (s *AuthService)SmsCode(request *protocol.SmsCodeRequest)(rsp *protocol.SmsCodeResponse,err error){ 183 func (s *AuthService)SmsCode(request *protocol.SmsCodeRequest)(rsp *protocol.SmsCodeResponse,err error){
171 - return nil,nil 184 + var(
  185 + value,key,msgContent string
  186 + smsInfo *protocol.SmsInfo
  187 + )
  188 + msgContent = `【买买买信息科技】{{.Code}}({{.AppName}}手机验证码,请完成验证),如非本人操作,请忽略本短信`
  189 + switch request.SendType {
  190 + case protocol.SmsLoginCode:
  191 + case protocol.SmsChangeMobile:
  192 + default:
  193 + err = common.NewErrorWithMsg(2,"send_type error.")
  194 + return
  195 + }
  196 + key = request.SendType
  197 + //check user phone exists
  198 + if !redis.Hexists(key,request.Phone){
  199 + smsInfo = &protocol.SmsInfo{
  200 + CreateTime:time.Now().Unix(),
  201 + }
  202 + goto Send
  203 + }else{
  204 + if value,err =redis.Hget(key,request.Phone);err!=nil{
  205 + log.Error(err)
  206 + return
  207 + }
  208 + if err=json.Unmarshal([]byte(value),&smsInfo);err!=nil{
  209 + log.Error(err)
  210 + return
  211 + }
  212 + //第二天重置
  213 + if smsInfo.LastTime<comm_time.GetUnixTimeByYyyymmdd(){
  214 + smsInfo.Count=0
  215 + smsInfo.CreateTime = time.Now().Unix()
  216 + }
  217 + if smsInfo.Count>100{//TODO:limit send time
  218 + return
  219 + }
  220 + goto Send
  221 + }
  222 + Send:
  223 + {
  224 + smsInfo.Code = common.RandomStringWithChars(6,string(protocol.Nums))
  225 + smsInfo.LastTime=time.Now().Unix()
  226 + smsInfo.ErrorCount =0
  227 + //Todo Lock
  228 + smsInfo.Count +=1
  229 + if err=redis.Hset(key,request.Phone,common.AssertJson(smsInfo),-1);err!=nil{
  230 + return
  231 + }
  232 + tp :=template.New("sms_code")
  233 + tp.Parse(msgContent)
  234 + buf :=bytes.NewBuffer(nil)
  235 + tp.Execute(
  236 + buf,
  237 + map[string]string{
  238 + "Code":smsInfo.Code,
  239 + "AppName":beego.BConfig.AppName,
  240 + },)
  241 + request.Content = buf.String()
  242 + err = sms.Send(request)
  243 + }
  244 + return
  245 +}
  246 +//验证短信验证码
  247 +func CheckSmsCode(request *protocol.LoginRequest)(result bool,err error){
  248 + var(
  249 + value string
  250 + smsInfo *protocol.SmsInfo
  251 + )
  252 + result =false
  253 + if value,err =redis.Hget(protocol.SmsLoginCode,request.Phone);err!=nil{
  254 + err = common.NewErrorWithMsg(1009,"smscode expire")
  255 + return
  256 + }
  257 + if err=json.Unmarshal([]byte(value),&smsInfo);err!=nil{
  258 + return
  259 + }
  260 + if smsInfo.ErrorCount>=5{
  261 + err = common.NewErrorWithMsg(1011,"smscode over error times")
  262 + return
  263 + }
  264 + if smsInfo.LastTime+60*5<time.Now().Unix(){
  265 + err = common.NewErrorWithMsg(1009,"smscode expire")
  266 + goto Fail
  267 + }
  268 + if smsInfo.Code == request.Code{
  269 + result = true
  270 + return
  271 + }else{
  272 + err = common.NewErrorWithMsg(1012,"smscode error")
  273 + goto Fail
  274 + }
  275 + Fail:
  276 + {
  277 + smsInfo.ErrorCount +=1
  278 + if err=redis.Hset(protocol.SmsLoginCode,request.Phone,common.AssertJson(smsInfo),-1);err!=nil{
  279 + return
  280 + }
  281 + }
  282 + return
172 } 283 }
1 package sms 1 package sms
2 2
3 -type ISmsService interface { 3 +import (
  4 + "github.com/astaxie/beego"
  5 + "github.com/astaxie/beego/httplib"
  6 + "gitlab.fjmaimaimai.com/mmm-go/ability/protocol"
  7 + "gitlab.fjmaimaimai.com/mmm-go/gocomm/common"
  8 + "gitlab.fjmaimaimai.com/mmm-go/gocomm/pkg/log"
  9 +)
4 10
  11 +type ISmsService interface {
  12 + Send(request *protocol.SmsCodeRequest)(err error)
5 } 13 }
6 14
7 -type SmsService struct {} 15 +type YunPianSmsService struct {}
8 16
9 func assertImplement(){ 17 func assertImplement(){
10 - var _ ISmsService = (*SmsService)(nil) 18 + var _ ISmsService = (*YunPianSmsService)(nil)
11 } 19 }
12 //发送 20 //发送
  21 +
  22 +
  23 +func(s *YunPianSmsService)Send(request *protocol.SmsCodeRequest)(err error){
  24 + var (
  25 + resp *YunPianResponse
  26 + )
  27 + log.Debug("[sms] mobile:",request.Phone," content:",request.Content)
  28 + if beego.BConfig.RunMode!="prod"{
  29 + return
  30 + }
  31 + post:= httplib.Post(beego.AppConfig.String("yunpian_sms_sdk_url"))
  32 + post.Param("apikey",beego.AppConfig.String("yunpian_app_key"))
  33 + post.Param("mobile",request.Phone)
  34 + post.Param("text",request.Content)
  35 + if err= post.ToJSON(&resp);err!=nil{
  36 + return
  37 + }
  38 + if resp.Code!=0 || resp.Mobile!=request.Phone{
  39 + log.Error("yunpian send sms code:",resp.Code," error msg:",resp.Msg)
  40 + err = common.NewErrorWithMsg(1,resp.Msg)
  41 + }
  42 + return nil
  43 +}
  44 +
  45 +type YunPianResponse struct {
  46 + Code int `json:"code"` //0代表发送成功,其他code代表出错,详细见"返回值说明"页面
  47 + Msg string `json:"msg"`//例如""发送成功"",或者相应错误信息
  48 + Count int `json:"count"`//发送成功短信的计费条数(计费条数:70个字一条,超出70个字时按每67字一条计费)
  49 + Mobile string `json:"string"`//发送手机号
  50 + Fee float64 `json:"fee"` //扣费金额,单位:元,类型:双精度浮点型/double
  51 + Sid int64 `json:"sid"` //短信id,64位整型
  52 +}