Files
jx-callback/business/jxutils/tasks/configrefresh.go
2019-04-27 15:44:26 +08:00

283 lines
8.6 KiB
Go

package tasks
import (
"fmt"
"io/ioutil"
"net/http"
"time"
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/jx-callback/business/jxutils/eventhub/syseventhub"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/model/legacymodel"
"git.rosy.net.cn/jx-callback/globals/api"
"git.rosy.net.cn/baseapi/platformapi/weimobapi"
"git.rosy.net.cn/baseapi/platformapi/yilianyunapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/globals"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
)
const (
weixinTokenExpires = 7200 * time.Second
dingdingTokenExpires = 7200 * time.Second
elmTokenExpires = 20 * 24 * 3600 * time.Second
weimobTokenExpires = 7200 * time.Second
maxRefreshGap = 5 * 60 * time.Second
yilianyunTokenExpires = 30 * 24 * 3600 * time.Second
)
type ElmTokenForCompatible struct {
Error string `json:"error"`
ErrorDescription string `json:"error_description"`
AccessToken string `json:"accessToken"`
TokenType string `json:"tokenType"`
Expires int `json:"expires"`
RefreshToken string `json:"refreshToken"`
Success bool `json:"success"`
}
type CallResult struct {
Code string `json:"code"`
Desc string `json:"desc"`
Data string `json:"data"`
}
func RefreshConfig(configKey string, expiresTime time.Duration, configGetter func() (string, string), configSetter func(value string)) error {
sleepGap := expiresTime / 10
needRefreshGap := expiresTime * 8 / 10
if sleepGap > maxRefreshGap {
sleepGap = maxRefreshGap
}
refreshFunc := func() (string, error) {
curConfig := &legacymodel.Config{
Thirdparty: configKey,
}
db := orm.NewOrm()
// 0: don't refresh, 1 update, 2 insert
handleType := 0
if err := db.Read(curConfig, "Thirdparty"); err != nil {
if err != orm.ErrNoRows {
globals.SugarLogger.Errorf("db error:%v, curConfig:%v", err, curConfig)
return "", err
}
handleType = 2
} else {
configSetter(curConfig.Token)
if curConfig.Date <= utils.Time2Str(time.Now().Add(-needRefreshGap)) {
handleType = 1
}
}
if handleType != 0 {
if curConfig.Token, curConfig.Date = configGetter(); curConfig.Token == "" {
if beego.BConfig.RunMode == "prod" {
globals.SugarLogger.Errorf("RefreshConfig %s get empty token", configKey)
} else {
globals.SugarLogger.Infof("RefreshConfig %s get empty token", configKey)
}
return "", nil
}
globals.SugarLogger.Debugf("RefreshConfig refresh %s, value:%s", configKey, curConfig.Token)
if curConfig.Date == "" {
curConfig.Date = utils.GetCurTimeStr()
}
var num int64
var err error
if handleType == 1 {
num, err = db.Update(curConfig, "Token", "Date")
} else {
num, err = db.Insert(curConfig)
}
if err != nil || num == 0 {
globals.SugarLogger.Errorf("db error:%v, num:%d, curConfig:%v", err, num, curConfig)
return "", err
}
configSetter(curConfig.Token)
}
return curConfig.Token, nil
}
token, err := refreshFunc() // 这样写的目的是强制第一次调用时要刷新一次
if err == nil {
if token != "" {
configSetter(token)
}
utils.CallFuncAsync(func() {
for {
time.Sleep(sleepGap)
refreshFunc()
}
})
}
return err
}
func RefreshWeixinToken() (err error) {
if api.WeixinAPI != nil {
err = RefreshConfig("wechat", weixinTokenExpires, func() (token string, expireTimeStr string) {
globals.SugarLogger.Debugf("RefreshWeixinToken RunMode:%s", beego.BConfig.RunMode)
if globals.IsProductEnv() {
if beego.BConfig.RunMode == "prod" {
if tokenInfo, err := api.WeixinAPI.CBRetrieveToken(); err == nil {
globals.SugarLogger.Debugf("RefreshWeixinToken tokenInfo:%s", utils.Format4Output(tokenInfo, true))
token = tokenInfo.AccessToken
} else {
globals.SugarLogger.Errorf("RefreshWeixinToken RefreshToken failed with error:%v", err)
}
} else {
tokenInfo := getWXTokenFromProd(api.WeixinAPI.CBGetToken())
if !tokenInfo.IsNew {
expireTimeStr = utils.Time2Str(time.Now().Add(-weixinTokenExpires))
}
token = tokenInfo.Token
}
}
return token, expireTimeStr
}, func(value string) {
syseventhub.SysEventHub.OnNewWXToken(value)
api.WeixinAPI.CBSetToken(value)
})
}
return err
}
func RefreshElmToken() (err error) {
if api.ElmAPI != nil {
err = RefreshConfig("eleme", elmTokenExpires, func() (string, string) {
if beego.BConfig.RunMode == "prod" {
if tokenInfo, err := api.ElmAPI.RefreshTokenIndividual(); err == nil {
tokenInfo2 := &ElmTokenForCompatible{
Error: "",
ErrorDescription: "",
AccessToken: tokenInfo.AccessToken,
TokenType: tokenInfo.TokenType,
Expires: tokenInfo.ExpiresIn,
RefreshToken: "",
Success: true,
}
return string(utils.MustMarshal(tokenInfo2)), ""
}
}
return "", ""
}, func(value string) {
var tokenInfo ElmTokenForCompatible
err := utils.UnmarshalUseNumber([]byte(value), &tokenInfo)
if err == nil {
api.ElmAPI.SetToken(tokenInfo.AccessToken)
}
})
}
return err
}
func RefreshWeimobToken() (err error) {
if api.WeimobAPI != nil {
err = RefreshConfig("weimob", weimobTokenExpires, func() (string, string) {
if beego.BConfig.RunMode == "prod" {
if tokenInfo, err := api.WeimobAPI.RefreshTokenByRefreshToken(); err == nil {
return string(utils.MustMarshal(tokenInfo)), utils.Time2Str(time.Now().Add((time.Duration(tokenInfo.ExpiresIn) - weimobTokenExpires/time.Second) * time.Second))
}
}
return "", ""
}, func(value string) {
var tokenInfo *weimobapi.TokenInfo
err := utils.UnmarshalUseNumber([]byte(value), &tokenInfo)
if err == nil {
api.WeimobAPI.SetToken(tokenInfo)
}
})
}
return err
}
func RefreshDingDingToken() (err error) {
api.DingDingAPI.RetrieveToken()
return RefreshConfig("dingding", dingdingTokenExpires, func() (string, string) {
globals.SugarLogger.Debugf("RefreshDingDingToken RunMode:%s", beego.BConfig.RunMode)
if true { //beego.BConfig.RunMode == "prod" {
if token, err := api.DingDingAPI.RetrieveToken(); err == nil {
globals.SugarLogger.Debugf("RefreshDingDingToken tokenInfo:%s", token)
return token, ""
} else {
globals.SugarLogger.Errorf("RefreshDingDingToken RefreshToken failed with error:%v", err)
}
}
return "", ""
}, func(value string) {
api.DingDingAPI.SetToken(value)
})
}
func SaveWeimobToken(token *weimobapi.TokenInfo) (err error) {
db := dao.GetDB()
config := &legacymodel.Config{
Thirdparty: "weimob",
Token: string(utils.MustMarshal(token)),
Date: utils.Time2Str(time.Now().Add((time.Duration(token.ExpiresIn) - weimobTokenExpires/time.Second) * time.Second)),
LastOperator: model.AdminName,
}
return dao.CreateOrUpdate(db, config)
}
func RefreshYilianyunToken() (err error) {
return RefreshConfig("yilianyun", yilianyunTokenExpires, func() (string, string) {
globals.SugarLogger.Debugf("RefreshYilianyunToken RunMode:%s", beego.BConfig.RunMode)
if beego.BConfig.RunMode == "prod" {
if tokenInfo, err := api.YilianyunAPI.RetrieveToken(); err == nil {
return string(utils.MustMarshal(tokenInfo)), ""
} else {
globals.SugarLogger.Errorf("RefreshYilianyunToken RefreshToken failed with error:%v", err)
}
}
return "", ""
}, func(value string) {
var tokenInfo *yilianyunapi.TokenInfo
err := utils.UnmarshalUseNumber([]byte(value), &tokenInfo)
if err == nil {
api.YilianyunAPI.SetToken(tokenInfo.AccessToken)
}
})
}
func getWXTokenFromProd(oldToken string) (tokenInfo *syseventhub.WXTokenInfo) {
if globals.GetWeixinTokenKey != "" && globals.GetWeixinTokenURL != "" {
for {
waitSecond := 5 * 60
response, err := http.Get(fmt.Sprintf("%s?accessKey=%s&oldToken=%s&waitSecond=%d", globals.GetWeixinTokenURL, globals.GetWeixinTokenKey, oldToken, waitSecond))
if err == nil {
defer response.Body.Close()
if response.StatusCode == http.StatusOK {
data, err2 := ioutil.ReadAll(response.Body)
if err = err2; err == nil {
var result CallResult
if err = utils.UnmarshalUseNumber(data, &result); err == nil {
if result.Code == "0" {
if result.Data != "" {
if err = utils.UnmarshalUseNumber(data, &tokenInfo); err == nil && tokenInfo != nil {
break
}
}
} else {
err = fmt.Errorf("return code is:%s", result.Code)
}
}
}
} else {
err = platformapi.ErrHTTPCodeIsNot200
}
}
if err != nil {
globals.SugarLogger.Infof("getWXTokenFromProd failed with error:%v", err)
time.Sleep(30 * time.Second)
}
}
}
return tokenInfo
}