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 weimobTokenExpires = 7200 * time.Second yilianyunTokenExpires = 30 * 24 * 3600 * time.Second maxRefreshGap = 5 * 60 * time.Second errRefreshGap = 10 * time.Second minRefreshGap = 1 * time.Second ) 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 { needRefreshGap := expiresTime * 8 / 10 sleepGap := expiresTime / 10 if sleepGap > maxRefreshGap { sleepGap = maxRefreshGap } refreshFunc := func() (sleepDuration time.Duration) { sleepDuration = sleepGap 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 errRefreshGap } 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 globals.IsProductEnv() { globals.SugarLogger.Infof("RefreshConfig %s get empty token", configKey) sleepDuration = errRefreshGap } else { globals.SugarLogger.Infof("RefreshConfig %s get empty token", configKey) } return sleepDuration } configSetter(curConfig.Token) if curConfig.Date == "" { curConfig.Date = utils.GetCurTimeStr() } else { sleepDuration = expiresTime - time.Now().Sub(utils.Str2Time(curConfig.Date)) } 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) } } if sleepDuration < minRefreshGap { sleepDuration = minRefreshGap } else if sleepDuration > maxRefreshGap { sleepDuration = maxRefreshGap } return sleepDuration } utils.CallFuncAsync(func() { for { sleepGap := refreshFunc() time.Sleep(sleepGap) } }) return nil } 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.IsMainProductEnv() { 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 { if tokenInfo := getWXTokenFromRemote(api.WeixinAPI.CBGetToken()); tokenInfo != nil { expireTimeStr = utils.Time2Str(time.Now().Add(-weixinTokenExpires)) token = tokenInfo.Token } } return token, expireTimeStr }, func(value string) { globals.SugarLogger.Debugf("RefreshWeixinToken setter value:%s", value) syseventhub.SysEventHub.OnNewWXToken(value) api.WeixinAPI.CBSetToken(value) }) } return err } func RefreshWeixin2Token() (err error) { if api.WeixinMiniAPI2 != nil { err = RefreshConfig("wechat2", weixinTokenExpires, func() (token string, expireTimeStr string) { globals.SugarLogger.Debugf("RefreshWeixin2Token RunMode:%s", beego.BConfig.RunMode) if globals.IsMainProductEnv() { if tokenInfo, err := api.WeixinMiniAPI2.CBRetrieveToken(); err == nil { globals.SugarLogger.Debugf("RefreshWeixin2Token tokenInfo:%s", utils.Format4Output(tokenInfo, true)) token = tokenInfo.AccessToken } else { globals.SugarLogger.Errorf("RefreshWeixin2Token RefreshToken failed with error:%v", err) } } else { if tokenInfo := getWX2TokenFromRemote(api.WeixinMiniAPI2.CBGetToken()); tokenInfo != nil { expireTimeStr = utils.Time2Str(time.Now().Add(-weixinTokenExpires)) token = tokenInfo.Token } } return token, expireTimeStr }, func(value string) { globals.SugarLogger.Debugf("RefreshWeixinToken setter value:%s", value) syseventhub.SysEventHub.OnNewWX2Token(value) api.WeixinMiniAPI2.CBSetToken(value) }) } return err } func RefreshWeixin3Token() (err error) { if api.WeixinMiniAPI3 != nil { err = RefreshConfig("wechat3", weixinTokenExpires, func() (token string, expireTimeStr string) { globals.SugarLogger.Debugf("RefreshWeixin3Token RunMode:%s", beego.BConfig.RunMode) if globals.IsMainProductEnv() { if tokenInfo, err := api.WeixinMiniAPI3.CBRetrieveToken(); err == nil { globals.SugarLogger.Debugf("RefreshWeixin3Token tokenInfo:%s", utils.Format4Output(tokenInfo, true)) token = tokenInfo.AccessToken } else { globals.SugarLogger.Errorf("RefreshWeixin3Token RefreshToken failed with error:%v", err) } } else { if tokenInfo := getWX3TokenFromRemote(api.WeixinMiniAPI3.CBGetToken()); tokenInfo != nil { expireTimeStr = utils.Time2Str(time.Now().Add(-weixinTokenExpires)) token = tokenInfo.Token } } return token, expireTimeStr }, func(value string) { globals.SugarLogger.Debugf("RefreshWeixinToken setter value:%s", value) syseventhub.SysEventHub.OnNewWX3Token(value) api.WeixinMiniAPI3.CBSetToken(value) }) } return err } func RefreshWeimobToken() (err error) { if api.WeimobAPI != nil { err = RefreshConfig("weimob", weimobTokenExpires, func() (token string, expireTimeStr string) { globals.SugarLogger.Debugf("RefreshWeimobToken RunMode:%s", beego.BConfig.RunMode) if globals.IsMainProductEnv() { 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)) } else { globals.SugarLogger.Debugf("RefreshWeimobToken err:%s", err) } } else { curToken := "" if tokenInfo := api.WeimobAPI.GetToken(); tokenInfo != nil { curToken = string(utils.MustMarshal(tokenInfo)) } if tokenInfo := getWeimobTokenFromRemote(curToken); tokenInfo != nil { expireTimeStr = utils.Time2Str(time.Now().Add(-weimobTokenExpires)) token = tokenInfo.Token } } return token, expireTimeStr }, func(value string) { var tokenInfo *weimobapi.TokenInfo err := utils.UnmarshalUseNumber([]byte(value), &tokenInfo) if err == nil { syseventhub.SysEventHub.OnNewWeimobToken(string(utils.MustMarshal(tokenInfo))) 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 { //globals.IsProductEnv() { 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() (token string, expireTimeStr string) { globals.SugarLogger.Debugf("RefreshYilianyunToken RunMode:%s", beego.BConfig.RunMode) if globals.IsMainProductEnv() { // 只有京西菜市刷新易联云key if tokenInfo, err := api.YilianyunAPI.RetrieveToken(); err == nil { token = string(utils.MustMarshal(tokenInfo)) } else { globals.SugarLogger.Errorf("RefreshYilianyunToken RefreshToken failed with error:%v", err) } } else { if tokenInfo := getYLYTokenFromRemote(api.YilianyunAPI.GetToken()); tokenInfo != nil { expireTimeStr = utils.Time2Str(time.Now().Add(-yilianyunTokenExpires)) token = tokenInfo.Token } } return token, expireTimeStr }, func(value string) { token := value var tokenInfo *yilianyunapi.TokenInfo if err := utils.TryUnmarshalUseNumber([]byte(value), &tokenInfo); err == nil { token = tokenInfo.AccessToken } syseventhub.SysEventHub.OnNewYLYToken(token) api.YilianyunAPI.SetToken(token) }) } func PollingRemotEvent(remoteURL string, waitSecond int, params map[string]interface{}) (tokenInfo *syseventhub.TokenInfo) { if waitSecond == 0 { waitSecond = 5 * 60 } params2 := utils.MergeMaps(params, map[string]interface{}{ "accessKey": globals.GetWeixinTokenKey, "waitSecond": waitSecond, }) for { globals.SugarLogger.Debugf("PollingRemotEvent %s", remoteURL) response, err := http.Get(utils.GenerateGetURL(remoteURL, "", params2)) globals.SugarLogger.Debugf("PollingRemotEvent2 %s error:%v", remoteURL, err) 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([]byte(result.Data), &tokenInfo); err == nil && tokenInfo != nil { globals.SugarLogger.Debugf("PollingRemotEvent %s:%s", remoteURL, utils.Format4Output(tokenInfo, false)) break } } } else { err = fmt.Errorf("PollingRemotEvent %s return code is:%s", remoteURL, result.Code) } } } } else { err = platformapi.ErrHTTPCodeIsNot200 } } globals.SugarLogger.Infof("PollingRemotEvent %s failed with error:%v", remoteURL, err) if err != nil { time.Sleep(errRefreshGap) } } return tokenInfo } func getWXTokenFromRemote(oldToken string) (tokenInfo *syseventhub.TokenInfo) { if !globals.IsMainProductEnv() && globals.GetWeixinTokenKey != "" && globals.GetWeixinTokenURL != "" { tokenInfo = PollingRemotEvent(globals.GetWeixinTokenURL, 0, map[string]interface{}{ "oldToken": oldToken, }) } return tokenInfo } func getWX2TokenFromRemote(oldToken string) (tokenInfo *syseventhub.TokenInfo) { if !globals.IsMainProductEnv() && globals.GetWeixinTokenKey != "" && globals.GetWeixinTokenURL != "" { tokenInfo = PollingRemotEvent(globals.GetWeixinTokenURL, 0, map[string]interface{}{ "oldToken": oldToken, }) } return tokenInfo } func getWX3TokenFromRemote(oldToken string) (tokenInfo *syseventhub.TokenInfo) { if !globals.IsMainProductEnv() && globals.GetWeixinTokenKey != "" && globals.GetWeixinTokenURL != "" { tokenInfo = PollingRemotEvent(globals.GetWeixinTokenURL, 0, map[string]interface{}{ "oldToken": oldToken, }) } return tokenInfo } func getYLYTokenFromRemote(oldToken string) (tokenInfo *syseventhub.TokenInfo) { if !globals.IsMainProductEnv() && globals.GetWeixinTokenKey != "" && globals.GetYLYTokenURL != "" { tokenInfo = PollingRemotEvent(globals.GetYLYTokenURL, 0, map[string]interface{}{ "oldToken": oldToken, }) } return tokenInfo } func getWeimobTokenFromRemote(oldToken string) (tokenInfo *syseventhub.TokenInfo) { if !globals.IsMainProductEnv() && globals.GetWeixinTokenKey != "" && globals.GetWeimobTokenURL != "" { tokenInfo = PollingRemotEvent(globals.GetWeimobTokenURL, 0, map[string]interface{}{ "oldToken": oldToken, }) } return tokenInfo }