|
@@ -15,6 +15,7 @@ import (
|
|
"net/url"
|
|
"net/url"
|
|
"os"
|
|
"os"
|
|
"path/filepath"
|
|
"path/filepath"
|
|
|
|
+ "qfw/util/redis"
|
|
"regexp"
|
|
"regexp"
|
|
"sort"
|
|
"sort"
|
|
"time"
|
|
"time"
|
|
@@ -275,6 +276,9 @@ type Weixin struct {
|
|
appId string
|
|
appId string
|
|
appSecret string
|
|
appSecret string
|
|
refreshToken bool
|
|
refreshToken bool
|
|
|
|
+ //
|
|
|
|
+ refreshTokenChan chan string
|
|
|
|
+ refreshTicketChan chan bool
|
|
}
|
|
}
|
|
|
|
|
|
// Convert qr scene to url
|
|
// Convert qr scene to url
|
|
@@ -291,9 +295,27 @@ func New(token string, appid string, secret string) *Weixin {
|
|
wx.refreshToken = false
|
|
wx.refreshToken = false
|
|
if len(appid) > 0 && len(secret) > 0 {
|
|
if len(appid) > 0 && len(secret) > 0 {
|
|
wx.tokenChan = make(chan accessToken)
|
|
wx.tokenChan = make(chan accessToken)
|
|
- go createAccessToken(wx.tokenChan, appid, secret, &wx.refreshToken)
|
|
|
|
|
|
+ wx.refreshTokenChan = make(chan string)
|
|
|
|
+ go createAccessToken(wx.tokenChan, appid, &wx.refreshToken, wx.refreshTokenChan)
|
|
wx.ticketChan = make(chan jsApiTicket)
|
|
wx.ticketChan = make(chan jsApiTicket)
|
|
- go createJsApiTicket(wx.tokenChan, wx.ticketChan)
|
|
|
|
|
|
+ wx.refreshTicketChan = make(chan bool)
|
|
|
|
+ go createJsApiTicket(appid, wx.ticketChan, wx.refreshTicketChan)
|
|
|
|
+ go func() {
|
|
|
|
+ for {
|
|
|
|
+ select {
|
|
|
|
+ case t := <-wx.refreshTokenChan:
|
|
|
|
+ log.Println("重新获取 access token")
|
|
|
|
+ c := redis.RedisPool["sso"].Get()
|
|
|
|
+ c.Do("PUBLISH", "WxRefreshSub", fmt.Sprintf("token_%s %s", wx.appId, t))
|
|
|
|
+ c.Close()
|
|
|
|
+ case <-wx.refreshTicketChan:
|
|
|
|
+ log.Println("重新获取 jsApi ticket")
|
|
|
|
+ c := redis.RedisPool["sso"].Get()
|
|
|
|
+ c.Do("PUBLISH", "WxRefreshSub", fmt.Sprintf("ticket_%s", wx.appId))
|
|
|
|
+ c.Close()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }()
|
|
}
|
|
}
|
|
return wx
|
|
return wx
|
|
}
|
|
}
|
|
@@ -313,8 +335,12 @@ func (wx *Weixin) GetAppSecret() string {
|
|
}
|
|
}
|
|
|
|
|
|
func (wx *Weixin) RefreshAccessToken() {
|
|
func (wx *Weixin) RefreshAccessToken() {
|
|
- wx.refreshToken = true
|
|
|
|
- <-wx.tokenChan
|
|
|
|
|
|
+ for i := 0; i < 10; i++ {
|
|
|
|
+ go func() {
|
|
|
|
+ wx.refreshToken = true
|
|
|
|
+ <-wx.tokenChan
|
|
|
|
+ }()
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
// Register request callback.
|
|
// Register request callback.
|
|
@@ -333,6 +359,11 @@ func (wx *Weixin) GetToken() string {
|
|
return token.token
|
|
return token.token
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+func (wx *Weixin) GetTicket() string {
|
|
|
|
+ ticket := <-wx.ticketChan
|
|
|
|
+ return ticket.ticket
|
|
|
|
+}
|
|
|
|
+
|
|
// Post text message
|
|
// Post text message
|
|
func (wx *Weixin) PostText(touser string, text string) error {
|
|
func (wx *Weixin) PostText(touser string, text string) error {
|
|
var msg struct {
|
|
var msg struct {
|
|
@@ -815,7 +846,27 @@ func checkSignature(t string, w http.ResponseWriter, r *http.Request) bool {
|
|
return fmt.Sprintf("%x", h.Sum(nil)) == signature
|
|
return fmt.Sprintf("%x", h.Sum(nil)) == signature
|
|
}
|
|
}
|
|
|
|
|
|
-func authAccessToken(appid string, secret string) (string, time.Duration, int64) {
|
|
|
|
|
|
+func authAccessToken(appid, t string, r chan string) (string, int64, int64) {
|
|
|
|
+ r <- t
|
|
|
|
+ ret, err := redis.GetNewBytes("sso", fmt.Sprintf("WxToken_%s", appid))
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Println("redis中获取Token出错", err)
|
|
|
|
+ } else if ret != nil {
|
|
|
|
+ var res struct {
|
|
|
|
+ AccessToken string `json:"access_token"`
|
|
|
|
+ ExpiresIn int64 `json:"expires_in"`
|
|
|
|
+ LastTime int64 `json:"last_time"`
|
|
|
|
+ }
|
|
|
|
+ if err := json.Unmarshal(*ret, &res); err != nil {
|
|
|
|
+ log.Println("Parse access token failed: ", err)
|
|
|
|
+ } else {
|
|
|
|
+ return res.AccessToken, res.ExpiresIn, res.LastTime
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return "", 0, 0
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*func authAccessToken(appid string, secret string) (string, time.Duration, int64) {
|
|
resp, err := http.Get(weixinHost + "/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret)
|
|
resp, err := http.Get(weixinHost + "/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret)
|
|
if err != nil {
|
|
if err != nil {
|
|
log.Println("Get access token failed: ", err)
|
|
log.Println("Get access token failed: ", err)
|
|
@@ -838,9 +889,30 @@ func authAccessToken(appid string, secret string) (string, time.Duration, int64)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return "", 0, 0
|
|
return "", 0, 0
|
|
|
|
+}*/
|
|
|
|
+func getJsApiTicket(appid string, r chan bool) (*jsApiTicket, error) {
|
|
|
|
+ r <- true
|
|
|
|
+ ret, err := redis.GetNewBytes("sso", fmt.Sprintf("WxTicket_%s", appid))
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Println("redis中获取Token出错", err)
|
|
|
|
+ } else if ret != nil {
|
|
|
|
+ var res struct {
|
|
|
|
+ Ticket string `json:"ticket"`
|
|
|
|
+ ExpiresIn int64 `json:"expires_in"`
|
|
|
|
+ }
|
|
|
|
+ if err := json.Unmarshal(*ret, &res); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ var ticket jsApiTicket
|
|
|
|
+ ticket.ticket = res.Ticket
|
|
|
|
+ ticket.expires = time.Unix(res.ExpiresIn, 0)
|
|
|
|
+ return &ticket, nil
|
|
|
|
+ }
|
|
|
|
+ time.Sleep(500 * time.Millisecond)
|
|
|
|
+ return nil, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func getJsApiTicket(c chan accessToken) (*jsApiTicket, error) {
|
|
|
|
|
|
+/*func getJsApiTicket(c chan accessToken) (*jsApiTicket, error) {
|
|
reply, err := sendGetRequest(weixinJsApiTicketURL+"?type=jsapi&access_token=", c)
|
|
reply, err := sendGetRequest(weixinJsApiTicketURL+"?type=jsapi&access_token=", c)
|
|
if err != nil {
|
|
if err != nil {
|
|
return nil, err
|
|
return nil, err
|
|
@@ -857,61 +929,58 @@ func getJsApiTicket(c chan accessToken) (*jsApiTicket, error) {
|
|
ticket.expires = time.Now().Add(time.Duration(res.ExpiresIn * 1000 * 1000 * 1000))
|
|
ticket.expires = time.Now().Add(time.Duration(res.ExpiresIn * 1000 * 1000 * 1000))
|
|
return &ticket, nil
|
|
return &ticket, nil
|
|
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-type countTocken struct {
|
|
|
|
- allTimes int64
|
|
|
|
- dayTimes int64
|
|
|
|
- day int
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-var CountT = countTocken{0, 0, time.Now().Day()}
|
|
|
|
-
|
|
|
|
-func init() {
|
|
|
|
- go func() {
|
|
|
|
- for {
|
|
|
|
- if time.Now().Day() != CountT.day {
|
|
|
|
- CountT.dayTimes = 0
|
|
|
|
- CountT.day = time.Now().Day()
|
|
|
|
- log.Println("wx-tocken改变统计时间:", CountT.day, ",次数:", CountT.dayTimes)
|
|
|
|
- }
|
|
|
|
- time.Sleep(30 * time.Second)
|
|
|
|
- }
|
|
|
|
- }()
|
|
|
|
-}
|
|
|
|
|
|
+}*/
|
|
|
|
|
|
-func createAccessToken(c chan accessToken, appid string, secret string, refresh *bool) {
|
|
|
|
|
|
+func createAccessToken(c chan accessToken, appid string, refresh *bool, r chan string) {
|
|
lt := int64(0)
|
|
lt := int64(0)
|
|
token := accessToken{"", time.Now(), <}
|
|
token := accessToken{"", time.Now(), <}
|
|
c <- token
|
|
c <- token
|
|
for {
|
|
for {
|
|
- if *refresh || (time.Now().Unix()-*token.lasttime) > 90*60 || time.Since(token.expires).Seconds() >= 0 {
|
|
|
|
- CountT.allTimes++
|
|
|
|
- CountT.dayTimes++
|
|
|
|
- *refresh = false
|
|
|
|
- var expires time.Duration
|
|
|
|
- var lasttime int64
|
|
|
|
- token.token, expires, lasttime = authAccessToken(appid, secret)
|
|
|
|
- log.Println("获取tocken:", CountT.dayTimes, "token", token.token)
|
|
|
|
- token.expires = time.Now().Add(expires)
|
|
|
|
- *token.lasttime = lasttime
|
|
|
|
|
|
+ reTry := 0
|
|
|
|
+ for {
|
|
|
|
+ if *refresh || (time.Now().Unix()-*token.lasttime) > 5400 || time.Since(token.expires).Seconds() >= 0 {
|
|
|
|
+ if reTry > 0 {
|
|
|
|
+ time.Sleep(time.Second)
|
|
|
|
+ }
|
|
|
|
+ reTry++
|
|
|
|
+ subToken := ""
|
|
|
|
+ if *refresh || *token.lasttime == -1 {
|
|
|
|
+ subToken = token.token
|
|
|
|
+ }
|
|
|
|
+ *refresh = false
|
|
|
|
+ var expires int64
|
|
|
|
+ var lasttime int64
|
|
|
|
+ token.token, expires, lasttime = authAccessToken(appid, subToken, r)
|
|
|
|
+ if (*refresh || *token.lasttime == -1) && token.expires.Unix() == expires {
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ token.expires = time.Unix(expires, 0)
|
|
|
|
+ *token.lasttime = lasttime
|
|
|
|
+ } else {
|
|
|
|
+ break
|
|
|
|
+ }
|
|
}
|
|
}
|
|
c <- token
|
|
c <- token
|
|
- if CountT.dayTimes > 100 {
|
|
|
|
- log.Println(">>-->>获取tocken%d次...<<--<<", CountT.dayTimes)
|
|
|
|
- time.Sleep(1 * time.Minute)
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-func createJsApiTicket(cin chan accessToken, c chan jsApiTicket) {
|
|
|
|
|
|
+func createJsApiTicket(appid string, c chan jsApiTicket, r chan bool) {
|
|
ticket := jsApiTicket{"", time.Now()}
|
|
ticket := jsApiTicket{"", time.Now()}
|
|
c <- ticket
|
|
c <- ticket
|
|
for {
|
|
for {
|
|
- if time.Since(ticket.expires).Seconds() >= 0 {
|
|
|
|
- t, err := getJsApiTicket(cin)
|
|
|
|
- if err == nil {
|
|
|
|
- ticket = *t
|
|
|
|
|
|
+ reTry := 0
|
|
|
|
+ for {
|
|
|
|
+ if time.Since(ticket.expires).Seconds() >= 0 {
|
|
|
|
+ if reTry > 0 {
|
|
|
|
+ time.Sleep(time.Second)
|
|
|
|
+ }
|
|
|
|
+ reTry++
|
|
|
|
+ t, err := getJsApiTicket(appid, r)
|
|
|
|
+ if err == nil && t != nil {
|
|
|
|
+ ticket = *t
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
c <- ticket
|
|
c <- ticket
|
|
@@ -939,7 +1008,7 @@ func sendGetRequest(reqURL string, c chan accessToken) ([]byte, error) {
|
|
case 0:
|
|
case 0:
|
|
return reply, nil
|
|
return reply, nil
|
|
case 42001, 40001:
|
|
case 42001, 40001:
|
|
- *token.lasttime = 0
|
|
|
|
|
|
+ *token.lasttime = -1
|
|
<-c
|
|
<-c
|
|
log.Println("reget the tocken by ", result.ErrorCode, result.ErrorMessage)
|
|
log.Println("reget the tocken by ", result.ErrorCode, result.ErrorMessage)
|
|
continue
|
|
continue
|
|
@@ -972,7 +1041,7 @@ func postRequest(reqURL string, c chan accessToken, data []byte) ([]byte, error)
|
|
case 0:
|
|
case 0:
|
|
return reply, nil
|
|
return reply, nil
|
|
case 42001, 40001:
|
|
case 42001, 40001:
|
|
- *token.lasttime = 0
|
|
|
|
|
|
+ *token.lasttime = -1
|
|
<-c
|
|
<-c
|
|
log.Println("reget the tocken by ", result.ErrorCode, result.ErrorMessage)
|
|
log.Println("reget the tocken by ", result.ErrorCode, result.ErrorMessage)
|
|
continue
|
|
continue
|