SendSmsCode.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. package youngee_talent_service
  2. import (
  3. "bytes"
  4. "crypto/sha256"
  5. "crypto/tls"
  6. "encoding/base64"
  7. "fmt"
  8. "github.com/gogf/gf/frame/g"
  9. "github.com/gogf/gf/net/ghttp"
  10. uuid "github.com/satori/go.uuid"
  11. "io/ioutil"
  12. "math/rand"
  13. "net/http"
  14. "net/url"
  15. "strings"
  16. "time"
  17. "youngmini_server/app/model/youngee_talent_model"
  18. "youngmini_server/app/utils"
  19. )
  20. // 无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
  21. const WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\""
  22. // 无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
  23. const AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\""
  24. type SendcodeReq struct {
  25. Phone string `json:"phone"`
  26. }
  27. // 接口调用此函数
  28. func SendSmsCode(r *ghttp.Request) *TalentHttpResult {
  29. l := SendcodeReq{}
  30. err := r.ParseForm(&l)
  31. if err != nil {
  32. fmt.Printf("前端数据解析错误")
  33. }
  34. vcode := GetCode()
  35. fmt.Println("验证码为=====>", vcode)
  36. err = SendCodeImp(l.Phone, vcode)
  37. if err != nil {
  38. return &TalentHttpResult{Code: -1, Msg: "短信发送失败"}
  39. }
  40. //验证码存redis
  41. _ = SetRedis(l.Phone, vcode)
  42. return &TalentHttpResult{Code: 1, Msg: "sendSmsCodeSuccess"}
  43. }
  44. func SetRedis(phone string, vcode string) error {
  45. err, _ := g.Redis().Do("SETEX", getRedisKey(phone), vcode, 120) //第三个参数以秒为单位
  46. if err != nil {
  47. fmt.Println("redis存储错误")
  48. }
  49. return nil
  50. }
  51. func getRedisKey(phone string) string {
  52. return fmt.Sprintf("%s%s", "c_user:", phone)
  53. }
  54. // 发送验证码具体实现
  55. func SendCodeImp(phone string, vcode string) error {
  56. //必填,请参考"开发准备"获取如下数据,替换为实际值
  57. apiAddress := "https://smsapi.cn-south-1.myhuaweicloud.com:443/sms/batchSendSms/v1" //APP接入地址(在控制台"应用管理"页面获取)+接口访问URI
  58. appKey := "NETTvTJJie9ax03v9K5T4DFB9EV6" //APP_Key
  59. appSecret := "txi9kXIrxW0dVNMyAulrJf7XFNP7" //APP_Secret
  60. sender := "8823022707732" //国内短信签名通道号或国际/港澳台短信通道号
  61. templateId := "7103cdd480d14d0aa8c68954a7dbeb6e" //模板ID
  62. //条件必填,国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称
  63. //国际/港澳台短信不用关注该参数
  64. signature := "样叽" //签名名称
  65. //必填,全局号码格式(包含国家码),示例:+86151****6789,多个号码之间用英文逗号分隔
  66. receiver := "+86" + phone //短信接收人号码
  67. //选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
  68. statusCallBack := ""
  69. /*
  70. * 选填,使用无变量模板时请赋空值 string templateParas = "";
  71. * 单变量模板示例:模板内容为"您的验证码是${1}"时,templateParas可填写为"[\"369751\"]"
  72. * 双变量模板示例:模板内容为"您有${1}件快递请到${2}领取"时,templateParas可填写为"[\"3\",\"人民公园正门\"]"
  73. * 模板中的每个变量都必须赋值,且取值不能为空
  74. * 查看更多模板和变量规范:产品介绍>模板和变量规范
  75. */
  76. templateParas := "[\"" + vcode + "\"]" //模板变量,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。
  77. // templateParas := "[\"12345678\"]" //模板变量,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。
  78. body := buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature)
  79. headers := make(map[string]string)
  80. headers["Content-Type"] = "application/x-www-form-urlencoded"
  81. headers["Authorization"] = AUTH_HEADER_VALUE
  82. headers["X-WSSE"] = buildWsseHeader(appKey, appSecret)
  83. resp, err := post(apiAddress, []byte(body), headers)
  84. if err != nil {
  85. return err
  86. }
  87. fmt.Println(resp)
  88. return nil
  89. }
  90. func buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature string) string {
  91. param := "from=" + url.QueryEscape(sender) + "&to=" + url.QueryEscape(receiver) + "&templateId=" + url.QueryEscape(templateId)
  92. if templateParas != "" {
  93. param += "&templateParas=" + url.QueryEscape(templateParas)
  94. }
  95. if statusCallBack != "" {
  96. param += "&statusCallback=" + url.QueryEscape(statusCallBack)
  97. }
  98. if signature != "" {
  99. param += "&signature=" + url.QueryEscape(signature)
  100. }
  101. return param
  102. }
  103. func post(url string, param []byte, headers map[string]string) (string, error) {
  104. tr := &http.Transport{
  105. TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
  106. }
  107. client := &http.Client{Transport: tr}
  108. req, err := http.NewRequest("POST", url, bytes.NewBuffer(param))
  109. if err != nil {
  110. return "", err
  111. }
  112. for key, header := range headers {
  113. req.Header.Set(key, header)
  114. }
  115. resp, err := client.Do(req)
  116. defer resp.Body.Close()
  117. body, err := ioutil.ReadAll(resp.Body)
  118. if err != nil {
  119. return "", err
  120. }
  121. return string(body), nil
  122. }
  123. func buildWsseHeader(appKey, appSecret string) string {
  124. var cTime = time.Now().Format("2006-01-02T15:04:05Z")
  125. var nonce = uuid.NewV4().String()
  126. nonce = strings.ReplaceAll(nonce, "-", "")
  127. h := sha256.New()
  128. h.Write([]byte(nonce + cTime + appSecret))
  129. passwordDigestBase64Str := base64.StdEncoding.EncodeToString(h.Sum(nil))
  130. return fmt.Sprintf(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, cTime)
  131. }
  132. // 随机生成6位数验证码
  133. func GetCode() string {
  134. rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
  135. vcode := fmt.Sprintf("%06v", rnd.Int31n(1000000))
  136. return vcode
  137. }
  138. type ChangeReq struct {
  139. Phone string `json:"phone"`
  140. Vcode string `json:"vcode"`
  141. }
  142. // 更换手机号
  143. func ChangePhone(r *ghttp.Request) *TalentHttpResult {
  144. tid, err := utils.SessionTalentInfo.GetTalentIdFromSession(r)
  145. if err != nil {
  146. return &TalentHttpResult{Code: -1, Msg: "Get talent info failed"}
  147. }
  148. l := ChangeReq{}
  149. err = r.ParseForm(&l)
  150. if err != nil {
  151. fmt.Printf("前端数据解析错误")
  152. }
  153. //手机号是否被绑定
  154. existingTalent := &youngee_talent_model.TalentInfo{}
  155. record, err := g.DB().Model(existingTalent).Where("talent_phone_number = ?", l.Phone).One()
  156. if err != nil {
  157. fmt.Println("查询错误:", err)
  158. return &TalentHttpResult{Code: -3, Msg: "查询失败"}
  159. }
  160. // 检查是否找到了记录
  161. if record != nil {
  162. fmt.Println("手机号已存在:", record["talent_phone_number"])
  163. return &TalentHttpResult{Code: -1, Msg: "手机号已绑定"}
  164. } else {
  165. VcodeKey := fmt.Sprintf("%s%s", "c_user:", l.Phone)
  166. Vcode, _ := g.Redis().DoVar("GET", VcodeKey)
  167. //验证码正确
  168. if Vcode.String() == l.Vcode {
  169. //修改youngee_talent_info表中手机号
  170. _, err := g.DB().Model(existingTalent).Where("id=?", tid).Update(g.Map{"talent_phone_number": l.Phone})
  171. if err != nil {
  172. fmt.Println("更新失败")
  173. return &TalentHttpResult{Code: -4, Msg: "手机号更新失败"}
  174. }
  175. return &TalentHttpResult{Code: 0, Msg: "更新成功"}
  176. } else {
  177. return &TalentHttpResult{Code: -2, Msg: "验证码错误"}
  178. }
  179. }
  180. }