作者 唐旭辉

新增,修改

@@ -6,7 +6,7 @@ require ( @@ -6,7 +6,7 @@ require (
6 github.com/astaxie/beego v1.10.0 6 github.com/astaxie/beego v1.10.0
7 github.com/go-sql-driver/mysql v1.4.1 7 github.com/go-sql-driver/mysql v1.4.1
8 github.com/prometheus/client_golang v1.1.0 8 github.com/prometheus/client_golang v1.1.0
9 - github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 9 + github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect
10 gitlab.fjmaimaimai.com/mmm-go/gocomm v0.0.1 10 gitlab.fjmaimaimai.com/mmm-go/gocomm v0.0.1
11 google.golang.org/appengine v1.6.2 // indirect 11 google.golang.org/appengine v1.6.2 // indirect
12 ) 12 )
@@ -65,6 +65,7 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -65,6 +65,7 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
65 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 65 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
66 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 66 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
67 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 67 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
  68 +github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
68 github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 69 github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
69 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 70 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
70 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 71 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@@ -89,8 +90,10 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 @@ -89,8 +90,10 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
89 github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 90 github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
90 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 91 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
91 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 92 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
  93 +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
92 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 94 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
93 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 95 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
  96 +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
94 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 97 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
95 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 98 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
96 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 99 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
  1 +package datastore
  2 +
  3 +//redis 缓存
  1 +package datastore
  2 +
  3 +// 内存
  1 +package filestore
  2 +
  3 +//IFileStore TODO文件存储相关
  4 +type IFileStore interface {
  5 + Auth() error //在获取文件前进行对请求进行检查
  6 + ReadFile() (string, error) //实际获取文件操作
  7 + StoreFile() (string, error) //实际存储文件操作
  8 + DeleteFile() error
  9 +}
  10 +
  11 +// type LocalFileStore struct {
  12 +// }
  13 +
  14 +// type XXXFileStore struct {
  15 +// }
@@ -3,22 +3,34 @@ package sms @@ -3,22 +3,34 @@ package sms
3 import ( 3 import (
4 "encoding/json" 4 "encoding/json"
5 "errors" 5 "errors"
  6 + "fmt"
  7 + "regexp"
  8 +
  9 + "github.com/astaxie/beego"
  10 + "github.com/astaxie/beego/httplib"
6 ) 11 )
7 12
  13 +//ISmsServe 短信服务接口协议
8 type ISmsServe interface { 14 type ISmsServe interface {
9 Send() error //调用远端接口发送短信 15 Send() error //调用远端接口发送短信
10 TextContent(v ...interface{}) (string, error) //构建短信文本内容 16 TextContent(v ...interface{}) (string, error) //构建短信文本内容
11 ValidReturn() (string, error) //检查调用远端接口后的返回内容,可用于记日志等操作 17 ValidReturn() (string, error) //检查调用远端接口后的返回内容,可用于记日志等操作
  18 + Name() string
12 } 19 }
13 20
  21 +//SmsServe 生产线上使用的短信服务
14 type SmsServe struct { 22 type SmsServe struct {
15 - ToPhone string  
16 - UseTpl string  
17 - smsContent string //非导出  
18 - serveReturn *YunPainReturn //非导出 23 + ToPhone string //对方的电话号码
  24 + UseTpl string //使用的短信模板
  25 + smsContent string //非导出,最终发送的短信内容
  26 + serveReturn *YunPianReturn //非导出,调用远端接口后的响应内容
19 } 27 }
20 28
21 -type YunPainReturn struct { 29 +var (
  30 + _ ISmsServe = &SmsServe{}
  31 +)
  32 +
  33 +type YunPianReturn struct {
22 Code int `json:"code"` //0代表发送成功,其他code代表出错,详细见"返回值说明"页面 34 Code int `json:"code"` //0代表发送成功,其他code代表出错,详细见"返回值说明"页面
23 Msg string `json:"msg"` //例如""发送成功"",或者相应错误信息 35 Msg string `json:"msg"` //例如""发送成功"",或者相应错误信息
24 Count int `json:"count"` //发送成功短信的计费条数(计费条数:70个字一条,超出70个字时按每67字一条计费) 36 Count int `json:"count"` //发送成功短信的计费条数(计费条数:70个字一条,超出70个字时按每67字一条计费)
@@ -36,17 +48,34 @@ func NewSmsServe(toPhone string, useTpl string) *SmsServe { @@ -36,17 +48,34 @@ func NewSmsServe(toPhone string, useTpl string) *SmsServe {
36 } 48 }
37 49
38 //Send 短信服务发送动作 50 //Send 短信服务发送动作
  51 +//实现接口 ISmsServe
39 func (s *SmsServe) Send() error { 52 func (s *SmsServe) Send() error {
40 if len(s.ToPhone) == 0 { 53 if len(s.ToPhone) == 0 {
41 return nil 54 return nil
42 } 55 }
43 - //TODO 56 + //校验号码合法性/^1[3456789][0-9]{9}$/
  57 + ok, err := regexp.MatchString(`^1[3456789][0-9]{9}$`, s.ToPhone)
  58 + if !ok {
  59 + return fmt.Errorf("ToPhone err:%w", err)
  60 + }
44 //发送短信 61 //发送短信
  62 + var (
  63 + resp *YunPianReturn
  64 + )
  65 + post := httplib.Post(beego.AppConfig.String("yunpian_sms_sdk_url"))
  66 + post.Param("apikey", beego.AppConfig.String("yunpian_app_key"))
  67 + post.Param("mobile", s.ToPhone)
  68 + post.Param("text", s.smsContent)
  69 + if err := post.ToJSON(&resp); err != nil {
  70 + return fmt.Errorf("parse yunpian response err:%w ", err)
  71 + }
45 //获取并设置返回值 72 //获取并设置返回值
  73 + s.serveReturn = resp
46 return nil 74 return nil
47 } 75 }
48 76
49 // TextContent 短信正文内容设置 77 // TextContent 短信正文内容设置
  78 +//实现接口 ISmsServe
50 func (s *SmsServe) TextContent(v ...interface{}) (string, error) { 79 func (s *SmsServe) TextContent(v ...interface{}) (string, error) {
51 tpl, err := getSmsTpl(s.UseTpl) 80 tpl, err := getSmsTpl(s.UseTpl)
52 if err != nil { 81 if err != nil {
@@ -61,6 +90,7 @@ func (s *SmsServe) TextContent(v ...interface{}) (string, error) { @@ -61,6 +90,7 @@ func (s *SmsServe) TextContent(v ...interface{}) (string, error) {
61 } 90 }
62 91
63 //ValidReturn 校验调用远端接口后返回的响应内容 92 //ValidReturn 校验调用远端接口后返回的响应内容
  93 +//实现接口 ISmsServe
64 func (s *SmsServe) ValidReturn() (string, error) { 94 func (s *SmsServe) ValidReturn() (string, error) {
65 if s.serveReturn == nil { 95 if s.serveReturn == nil {
66 return "", errors.New("serveReturn is nil") 96 return "", errors.New("serveReturn is nil")
@@ -71,3 +101,69 @@ func (s *SmsServe) ValidReturn() (string, error) { @@ -71,3 +101,69 @@ func (s *SmsServe) ValidReturn() (string, error) {
71 } 101 }
72 return string(str), nil 102 return string(str), nil
73 } 103 }
  104 +
  105 +func (s *SmsServe) Name() string {
  106 + return "SmsServe"
  107 +}
  108 +
  109 +//DevSmsServe 开发调试使用的短信服务
  110 +type DevSmsServe struct {
  111 + ToPhone string //对方的电话号码
  112 + UseTpl string //使用的短信模板
  113 + smsContent string //非导出,最终发送的短信内容
  114 + serveReturn string //非导出,调用远端接口后的响应内容
  115 +}
  116 +
  117 +var (
  118 + _ ISmsServe = &DevSmsServe{}
  119 +)
  120 +
  121 +func NewDevSmsServe(toPhone string, useTpl string) *DevSmsServe {
  122 + return &DevSmsServe{
  123 + ToPhone: toPhone,
  124 + UseTpl: useTpl,
  125 + serveReturn: "",
  126 + }
  127 +}
  128 +
  129 +//Send 实现接口 ISmsServe
  130 +func (s *DevSmsServe) Send() error {
  131 + return nil
  132 +}
  133 +
  134 +//TextContent 实现接口 ISmsServe
  135 +func (s *DevSmsServe) TextContent(v ...interface{}) (string, error) {
  136 + tpl, err := getSmsTpl(s.UseTpl)
  137 + if err != nil {
  138 + return "", err
  139 + }
  140 + t, err := tpl.ParseTpl(v)
  141 + if err != nil {
  142 + return "", err
  143 + }
  144 + s.smsContent = t
  145 + return t, nil
  146 +}
  147 +
  148 +//ValidReturn 实现接口 ISmsServe
  149 +func (s *DevSmsServe) ValidReturn() (string, error) {
  150 + return "ok", nil
  151 +}
  152 +
  153 +func (s *DevSmsServe) Name() string {
  154 + return "DevSmsServe"
  155 +}
  156 +
  157 +//SendSms 发送短信调用出口 TODO
  158 +func SendSms(phone string, code string, param ...interface{}) error {
  159 + var serve ISmsServe
  160 + serve = NewSmsServe(phone, code)
  161 + // serve = NewDevSmsServe(phone, code)
  162 +
  163 + _, err := serve.TextContent(param)
  164 + if err != nil {
  165 + return err
  166 + }
  167 +
  168 + return serve.Send()
  169 +}
1 package sms 1 package sms
2 2
3 import ( 3 import (
  4 + "bytes"
4 "errors" 5 "errors"
5 "fmt" 6 "fmt"
  7 + "html/template"
6 ) 8 )
7 9
8 type ISmsTpl interface { 10 type ISmsTpl interface {
@@ -13,6 +15,9 @@ type ISmsTpl interface { @@ -13,6 +15,9 @@ type ISmsTpl interface {
13 //注册短信模板 15 //注册短信模板
14 var smsTpl = map[string]ISmsTpl{ 16 var smsTpl = map[string]ISmsTpl{
15 "10001": SmsTpl{"【云片网】您的验证码是%s", 1}, 17 "10001": SmsTpl{"【云片网】您的验证码是%s", 1},
  18 + "10002": SmsTplUseHtml{
  19 + `【买买买信息科技】{{.Code}}({{.AppName}}手机验证码,请完成验证),如非本人操作,请忽略本短信`,
  20 + "10002"},
16 } 21 }
17 22
18 const ( 23 const (
@@ -21,8 +26,8 @@ const ( @@ -21,8 +26,8 @@ const (
21 ) 26 )
22 27
23 type SmsTpl struct { 28 type SmsTpl struct {
24 - Formate string  
25 - ParamNum int 29 + Formate string //模板字符串
  30 + ParamNum int //模板限定参数数量
26 } 31 }
27 32
28 var ( 33 var (
@@ -42,6 +47,35 @@ func (t SmsTpl) String() string { @@ -42,6 +47,35 @@ func (t SmsTpl) String() string {
42 return t.Formate 47 return t.Formate
43 } 48 }
44 49
  50 +//SmsTplUseHtml 以html模板方式构建短信模板
  51 +type SmsTplUseHtml struct {
  52 + Formate string //模板字符串
  53 + Name string
  54 +}
  55 +
  56 +var (
  57 + _ ISmsTpl = SmsTplUseHtml{}
  58 +)
  59 +
  60 +func (t SmsTplUseHtml) ParseTpl(v ...interface{}) (string, error) {
  61 + var param interface{}
  62 + if len(v) > 0 {
  63 + param = v[0]
  64 + }
  65 + tpl, err := template.New(t.Name).Parse(t.Formate)
  66 + if err != nil {
  67 + return "", err
  68 + }
  69 + btTpl := bytes.NewBuffer(nil)
  70 + if err = tpl.Execute(btTpl, param); err != nil {
  71 + return "", err
  72 + }
  73 + return btTpl.String(), nil
  74 +}
  75 +func (t SmsTplUseHtml) String() string {
  76 + return t.Formate
  77 +}
  78 +
45 //getSmsTpl 获取样板 79 //getSmsTpl 获取样板
46 //TODO 待优化 80 //TODO 待优化
47 func getSmsTpl(code string) (ISmsTpl, error) { 81 func getSmsTpl(code string) (ISmsTpl, error) {