Files
arenax-server/utility/wechat/wechat.go

195 lines
5.4 KiB
Go

package wechat
import (
"context"
"io"
"os"
"server/utility/ecode"
"sync"
"time"
"github.com/go-resty/resty/v2"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/glog"
"github.com/google/uuid"
)
type weChatClient struct {
AppId string
AppSecret string
TicketExpire int
Token string
accessToken string
expiresIn int
lastUpdated time.Time
mu sync.RWMutex
}
var (
instance *weChatClient
once sync.Once
)
func GetWeChatClient() *weChatClient {
return instance
}
func init() {
once.Do(func() {
ctx := context.Background()
instance = &weChatClient{
AppId: g.Config().MustGet(ctx, "wechat.appId").String(),
AppSecret: g.Config().MustGet(ctx, "wechat.appSecret").String(),
TicketExpire: g.Config().MustGet(ctx, "wechat.ticketExpire").Int(),
Token: g.Config().MustGet(ctx, "wechat.token").String(),
}
go instance.autoRefreshToken(ctx)
})
}
func (c *weChatClient) getAccessToken() error {
result := struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
}{}
ctx := context.Background()
resp, err := resty.New().R().
SetQueryParams(g.MapStrStr{
"grant_type": "client_credential",
"appid": c.AppId,
"secret": c.AppSecret,
}).
SetResult(&result).
Get("https://api.weixin.qq.com/cgi-bin/token")
if err != nil {
glog.Errorf(ctx, "发起 get access_token 请求出现异常: %+v", err)
return ecode.Fail.Sub("发起 get access_token 请求出现异常")
}
if resp.StatusCode() != 200 {
glog.Errorf(ctx, "获取微信 access_token 响应异常: %+v", resp.Status())
return ecode.Fail.Sub("获取微信 access_token 失败")
}
c.mu.Lock()
defer c.mu.Unlock()
c.accessToken = result.AccessToken
c.expiresIn = result.ExpiresIn
c.lastUpdated = time.Now()
glog.Infof(ctx, "获取微信 access_token 成功: %+v", c.accessToken)
return nil
}
func (c *weChatClient) autoRefreshToken(ctx context.Context) {
for {
// 初次获取
if err := c.getAccessToken(); err != nil {
time.Sleep(1 * time.Minute)
continue
}
// 等待 expiresIn - 300 秒(即提前 5 分钟刷新)
c.mu.RLock()
expiresIn := c.expiresIn
c.mu.RUnlock()
refreshAfter := time.Duration(expiresIn-300) * time.Second
time.Sleep(refreshAfter)
}
}
func (c *weChatClient) GetTicket(sceneId string) (string, error) {
body := map[string]interface{}{
"expire_seconds": c.TicketExpire,
"action_name": "QR_STR_SCENE",
"action_info": map[string]interface{}{
"scene": map[string]string{
"scene_str": sceneId,
},
},
}
result := struct {
Ticket string `json:"ticket"`
ExpireSeconds int `json:"expire_seconds"`
Url string `json:"url"`
}{}
resp, err := resty.New().R().
SetQueryParams(g.MapStrStr{"access_token": c.accessToken}).
SetBody(body).
SetResult(&result).
Post("https://api.weixin.qq.com/cgi-bin/qrcode/create")
if err != nil {
glog.Errorf(context.Background(), "发起 get ticket 请求出现异常: %+v", err)
return "", ecode.Fail.Sub("发起 get ticket 请求出现异常")
}
if resp.StatusCode() != 200 {
glog.Errorf(context.Background(), "获取微信 ticket 响应异常: %+v", resp.Status())
return "", ecode.Fail.Sub("获取微信 ticket 失败")
}
glog.Infof(context.Background(), "获取微信 ticket 成功: %+v", result.Ticket)
return result.Ticket, nil
}
func (c *weChatClient) GetQrCode(ticket string) (imagePath string, err error) {
qrCodeUrl := "https://mp.weixin.qq.com/cgi-bin/showqrcode"
resp, err := resty.New().R().
SetDoNotParseResponse(true).
SetQueryParams(g.MapStrStr{"ticket": ticket}).
Get(qrCodeUrl)
if err != nil {
glog.Errorf(context.Background(), "获取二维码图片请求异常: %+v", err)
return "", ecode.Fail.Sub("获取二维码图片请求失败")
}
if resp.StatusCode() != 200 {
glog.Errorf(context.Background(), "获取二维码图片响应异常: %+v", resp.Status())
return "", ecode.Fail.Sub("获取二维码图片失败")
}
imagePath = uuid.New().String() + ".jpg"
data, readErr := io.ReadAll(resp.RawBody())
if readErr != nil {
glog.Errorf(context.Background(), "读取二维码图片失败: %+v", readErr)
return "", ecode.Fail.Sub("读取二维码图片失败")
}
defer resp.RawBody().Close()
writeErr := os.WriteFile(imagePath, data, 0644)
if writeErr != nil {
glog.Errorf(context.Background(), "保存二维码图片失败: %+v", writeErr)
return "", ecode.Fail.Sub("保存二维码图片失败")
}
glog.Infof(context.Background(), "二维码图片保存成功: %s", imagePath)
return imagePath, nil
}
func (c *weChatClient) GetToken() string {
return c.Token
}
func (c *weChatClient) GetUserUnionId(ctx context.Context, openid string) (unionId string, err error) {
// TODO 获取唯一UnionId
//result := struct {
// UnionId string `json:"unionid"`
//}{}
//
//resp, err := resty.New().R().
// SetQueryParams(g.MapStrStr{"access_token": c.accessToken}).SetQueryParam("openid", openid).
// SetResult(&result).
// Get("https://api.weixin.qq.com/cgi-bin/user/info")
//
//if err != nil {
// glog.Errorf(ctx, "发起 get ticket 请求出现异常: %+v", err)
// return "", ecode.Fail.Sub("发起 get ticket 请求出现异常")
//}
//if resp.StatusCode() != 200 {
// glog.Errorf(ctx, "获取微信 ticket 响应异常: %+v", resp.Status())
// return "", ecode.Fail.Sub("获取微信 ticket 失败")
//}
return openid, nil
}