Merge pull request #32 from fcbhank/dev

fixes #25: 当access_token失效时,主动重新获取(最多重试3次)
This commit is contained in:
Easy 2021-08-21 11:07:23 +08:00 committed by GitHub
commit 1872580421
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 74 additions and 34 deletions

View File

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"math"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"os" "os"
@ -38,6 +39,8 @@ var UploadMediaApi = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_to
/*------------------------------- 企业微信服务端API end -------------------------------*/ /*------------------------------- 企业微信服务端API end -------------------------------*/
const RedisTokenKey = "access_token"
type Msg struct { type Msg struct {
Content string `json:"content"` Content string `json:"content"`
} }
@ -89,13 +92,13 @@ func GetRemoteToken(corpId, appSecret string) string {
} }
tokenResponse := ParseJson(string(respData)) tokenResponse := ParseJson(string(respData))
log.Println("企业微信获取access_token接口返回==>", tokenResponse) log.Println("企业微信获取access_token接口返回==>", tokenResponse)
accessToken := tokenResponse["access_token"].(string) accessToken := tokenResponse[RedisTokenKey].(string)
if RedisStat == "ON" { if RedisStat == "ON" {
log.Println("prepare to set redis key") log.Println("prepare to set redis key")
rdb := RedisClient() rdb := RedisClient()
// access_token有效时间为7200秒(2小时) // access_token有效时间为7200秒(2小时)
set, err := rdb.SetNX(ctx, "access_token", accessToken, 7000*time.Second).Result() set, err := rdb.SetNX(ctx, RedisTokenKey, accessToken, 7000*time.Second).Result()
log.Println(set) log.Println(set)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
@ -136,20 +139,16 @@ func PostMsg(postData JsonData, postUrl string) string {
return string(body) return string(body)
} }
// CheckOrUploadMedia 核对消息类型如果为图片则上传临时素材并返回mediaId // UploadMedia 上传临时素材并返回mediaId
func CheckOrUploadMedia(msgType string, req *http.Request, accessToken string) string { func UploadMedia(msgType string, req *http.Request, accessToken string) (string, float64) {
if msgType != "image" {
log.Println("消息类型不是图片")
return ""
}
// 企业微信图片上传不能大于2M // 企业微信图片上传不能大于2M
_ = req.ParseMultipartForm(2 << 20) _ = req.ParseMultipartForm(2 << 20)
imgFile, imgHeader, err := req.FormFile("media") imgFile, imgHeader, err := req.FormFile("media")
log.Printf("文件大小==>%d字节", imgHeader.Size) log.Printf("文件大小==>%d字节", imgHeader.Size)
if err != nil { if err != nil {
log.Fatalln("图片文件出错==>", err) log.Fatalln("图片文件出错==>", err)
return "" // 自定义code无效的图片文件
return "", 400
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
writer := multipart.NewWriter(buf) writer := multipart.NewWriter(buf)
@ -171,28 +170,44 @@ func CheckOrUploadMedia(msgType string, req *http.Request, accessToken string) s
log.Println("企业微信上传临时素材接口返回==>", mediaResp) log.Println("企业微信上传临时素材接口返回==>", mediaResp)
if err != nil { if err != nil {
log.Fatalln("上传临时素材出错==>", err) log.Fatalln("上传临时素材出错==>", err)
return "" return "", mediaResp["errcode"].(float64)
} else { } else {
return mediaResp["media_id"].(string) return mediaResp["media_id"].(string), float64(0)
} }
} }
// IsZero 判断企业微信服务端是否正常响应 // ValidateToken 判断accessToken是否失效
func IsZero(v interface{}) (bool, error) { // true-未失效, false-失效需重新获取
t := reflect.TypeOf(v) func ValidateToken(errcode interface{}) bool {
if !t.Comparable() { codeTyp := reflect.TypeOf(errcode)
return false, fmt.Errorf("type is not comparable: %v", t) log.Println("errcode的数据类型==>", codeTyp)
if !codeTyp.Comparable() {
log.Printf("type is not comparable: %v", codeTyp)
return true
} }
return v == reflect.Zero(t).Interface(), nil
// 如果errcode为42001表明token已失效则清空redis中的token缓存
// 已知codeType为float64
if math.Abs(errcode.(float64)-float64(42001)) < 1e-3 {
if RedisStat == "ON" {
log.Printf("token已失效开始删除redis中的key==>%s", RedisTokenKey)
rdb := RedisClient()
rdb.Del(ctx, RedisTokenKey)
log.Printf("删除redis中的key==>%s完毕", RedisTokenKey)
}
log.Println("现需重新获取token")
return false
}
return true
} }
// 获取企业微信的access_token // GetAccessToken 获取企业微信的access_token
func getAccessToken() string { func GetAccessToken() string {
accessToken := "" accessToken := ""
if RedisStat == "ON" { if RedisStat == "ON" {
log.Println("尝试从redis获取token") log.Println("尝试从redis获取token")
rdb := RedisClient() rdb := RedisClient()
value, err := rdb.Get(ctx, "access_token").Result() value, err := rdb.Get(ctx, RedisTokenKey).Result()
if err == redis.Nil { if err == redis.Nil {
log.Println("access_token does not exist, need get it from remote API") log.Println("access_token does not exist, need get it from remote API")
} }
@ -222,6 +237,11 @@ func main() {
// 设置日志内容显示文件名和行号 // 设置日志内容显示文件名和行号
log.SetFlags(log.LstdFlags | log.Lshortfile) log.SetFlags(log.LstdFlags | log.Lshortfile)
wecomChan := func(res http.ResponseWriter, req *http.Request) { wecomChan := func(res http.ResponseWriter, req *http.Request) {
// 获取token
accessToken := GetAccessToken()
// 默认token有效
tokenValid := true
_ = req.ParseForm() _ = req.ParseForm()
sendkey := req.FormValue("sendkey") sendkey := req.FormValue("sendkey")
if sendkey != Sendkey { if sendkey != Sendkey {
@ -230,10 +250,24 @@ func main() {
msgContent := req.FormValue("msg") msgContent := req.FormValue("msg")
msgType := req.FormValue("msg_type") msgType := req.FormValue("msg_type")
log.Println("mes_type=", msgType) log.Println("mes_type=", msgType)
// 刷新token // 默认mediaId为空
accessToken := getAccessToken() mediaId := ""
mediaId := CheckOrUploadMedia(msgType, req, accessToken) if msgType != "image" {
log.Println("企业微信上传临时素材接口返回的media_id==>", mediaId) log.Println("消息类型不是图片")
} else {
// token有效则跳出循环继续执行否则重试3次
for i := 0; i <= 3; i++ {
var errcode float64
mediaId, errcode = UploadMedia(msgType, req, accessToken)
log.Printf("企业微信上传临时素材接口返回的media_id==>[%s], errcode==>[%f]\n", mediaId, errcode)
tokenValid = ValidateToken(errcode)
if tokenValid {
break
}
accessToken = GetAccessToken()
}
}
// 准备发送应用消息所需参数 // 准备发送应用消息所需参数
postData := InitJsonData(msgType) postData := InitJsonData(msgType)
@ -243,17 +277,23 @@ func main() {
postData.Image = Pic{ postData.Image = Pic{
MediaId: mediaId, MediaId: mediaId,
} }
// 再次刷新token
accessToken = getAccessToken()
sendMessageUrl := fmt.Sprintf(SendMessageApi, accessToken)
postStatus := PostMsg(postData, sendMessageUrl) postStatus := ""
postResponse := ParseJson(postStatus) for i := 0; i <= 3; i++ {
errcode := postResponse["errcode"] sendMessageUrl := fmt.Sprintf(SendMessageApi, accessToken)
_, err := IsZero(errcode) postStatus = PostMsg(postData, sendMessageUrl)
if err != nil { postResponse := ParseJson(postStatus)
log.Printf("%v", err) errcode := postResponse["errcode"]
log.Println("发送应用消息接口返回errcode==>", errcode)
tokenValid = ValidateToken(errcode)
// token有效则跳出循环继续执行否则重试3次
if tokenValid {
break
}
// 刷新token
accessToken = GetAccessToken()
} }
res.Header().Set("Content-type", "application/json") res.Header().Set("Content-type", "application/json")
_, _ = res.Write([]byte(postStatus)) _, _ = res.Write([]byte(postStatus))
} }