package youngee_talent_service import ( "bytes" "crypto/sha256" "crypto/tls" "encoding/base64" "fmt" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/net/ghttp" uuid "github.com/satori/go.uuid" "io/ioutil" "math/rand" "net/http" "net/url" "strings" "time" "youngmini_server/app/model/youngee_talent_model" "youngmini_server/app/utils" ) // 无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值 const WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"" // 无需修改,用于格式化鉴权头域,给"Authorization"参数赋值 const AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"" type SendcodeReq struct { Phone string `json:"phone"` } // 接口调用此函数 func SendSmsCode(r *ghttp.Request) *TalentHttpResult { l := SendcodeReq{} err := r.ParseForm(&l) if err != nil { return &TalentHttpResult{Code: -1, Msg: "请求体解析失败"} } vcode := GetCode() err = SendCodeImp(l.Phone, vcode) if err != nil { return &TalentHttpResult{Code: -1, Msg: "短信发送失败"} } //验证码存redis _ = SetRedis(l.Phone, vcode) return &TalentHttpResult{Code: 0, Msg: "sendSmsCodeSuccess", Data: vcode} } func SetRedis(phone string, vcode string) error { err, _ := g.Redis().Do("SETEX", getRedisKey(phone), 120, vcode) //第三个参数以秒为单位 if err != nil { fmt.Println("redis存储错误") } return nil } func getRedisKey(phone string) string { return fmt.Sprintf("%s%s", "c_user:", phone) } // 发送验证码具体实现 func SendCodeImp(phone string, vcode string) error { //必填,请参考"开发准备"获取如下数据,替换为实际值 apiAddress := "https://smsapi.cn-south-1.myhuaweicloud.com:443/sms/batchSendSms/v1" //APP接入地址(在控制台"应用管理"页面获取)+接口访问URI appKey := "NETTvTJJie9ax03v9K5T4DFB9EV6" //APP_Key appSecret := "txi9kXIrxW0dVNMyAulrJf7XFNP7" //APP_Secret sender := "8823022707732" //国内短信签名通道号或国际/港澳台短信通道号 templateId := "7103cdd480d14d0aa8c68954a7dbeb6e" //模板ID //条件必填,国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称 //国际/港澳台短信不用关注该参数 signature := "样叽" //签名名称 //必填,全局号码格式(包含国家码),示例:+86151****6789,多个号码之间用英文逗号分隔 receiver := "+86" + phone //短信接收人号码 //选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告 statusCallBack := "" /* * 选填,使用无变量模板时请赋空值 string templateParas = ""; * 单变量模板示例:模板内容为"您的验证码是${1}"时,templateParas可填写为"[\"369751\"]" * 双变量模板示例:模板内容为"您有${1}件快递请到${2}领取"时,templateParas可填写为"[\"3\",\"人民公园正门\"]" * 模板中的每个变量都必须赋值,且取值不能为空 * 查看更多模板和变量规范:产品介绍>模板和变量规范 */ templateParas := "[\"" + vcode + "\"]" //模板变量,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。 // templateParas := "[\"12345678\"]" //模板变量,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。 body := buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature) headers := make(map[string]string) headers["Content-Type"] = "application/x-www-form-urlencoded" headers["Authorization"] = AUTH_HEADER_VALUE headers["X-WSSE"] = buildWsseHeader(appKey, appSecret) resp, err := post(apiAddress, []byte(body), headers) if err != nil { return err } fmt.Println(resp) return nil } func buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature string) string { param := "from=" + url.QueryEscape(sender) + "&to=" + url.QueryEscape(receiver) + "&templateId=" + url.QueryEscape(templateId) if templateParas != "" { param += "&templateParas=" + url.QueryEscape(templateParas) } if statusCallBack != "" { param += "&statusCallback=" + url.QueryEscape(statusCallBack) } if signature != "" { param += "&signature=" + url.QueryEscape(signature) } return param } func post(url string, param []byte, headers map[string]string) (string, error) { tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client := &http.Client{Transport: tr} req, err := http.NewRequest("POST", url, bytes.NewBuffer(param)) if err != nil { return "", err } for key, header := range headers { req.Header.Set(key, header) } resp, err := client.Do(req) defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", err } return string(body), nil } func buildWsseHeader(appKey, appSecret string) string { var cTime = time.Now().Format("2006-01-02T15:04:05Z") var nonce = uuid.NewV4().String() nonce = strings.ReplaceAll(nonce, "-", "") h := sha256.New() h.Write([]byte(nonce + cTime + appSecret)) passwordDigestBase64Str := base64.StdEncoding.EncodeToString(h.Sum(nil)) return fmt.Sprintf(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, cTime) } // 随机生成6位数验证码 func GetCode() string { rnd := rand.New(rand.NewSource(time.Now().UnixNano())) vcode := fmt.Sprintf("%06v", rnd.Int31n(1000000)) return vcode } type ChangeReq struct { Phone string `json:"phone"` Vcode string `json:"vcode"` } // 更换手机号 func ChangePhone(r *ghttp.Request) *TalentHttpResult { tid, err := utils.SessionTalentInfo.GetTalentIdFromSession(r) if err != nil { return &TalentHttpResult{Code: -1, Msg: "Get talent info failed"} } l := ChangeReq{} err = r.ParseForm(&l) if err != nil { fmt.Printf("前端数据解析错误") } //手机号是否被绑定 existingTalent := &youngee_talent_model.TalentInfo{} record, err := g.DB().Model(existingTalent).Where("talent_phone_number = ?", l.Phone).One() if err != nil { fmt.Println("查询错误:", err) return &TalentHttpResult{Code: -3, Msg: "查询失败"} } // 检查是否找到了记录 if record != nil { fmt.Println("手机号已存在:", record["talent_phone_number"]) return &TalentHttpResult{Code: -1, Msg: "手机号已绑定"} } else { VcodeKey := fmt.Sprintf("%s%s", "c_user:", l.Phone) Vcode, _ := g.Redis().DoVar("GET", VcodeKey) //验证码正确 if Vcode.String() == l.Vcode { //修改youngee_talent_info表中手机号 _, err := g.DB().Model(existingTalent).Where("id=?", tid).Update(g.Map{"talent_phone_number": l.Phone}) if err != nil { fmt.Println("更新失败") return &TalentHttpResult{Code: -4, Msg: "手机号更新失败"} } return &TalentHttpResult{Code: 0, Msg: "更新成功"} } else { return &TalentHttpResult{Code: -2, Msg: "验证码错误"} } } }