package wechat import ( "encoding/json" "eta/eta_mini_ht_api/common/component/config" logger "eta/eta_mini_ht_api/common/component/log" "eta/eta_mini_ht_api/common/component/wechat/we_http" "eta/eta_mini_ht_api/common/contants" "eta/eta_mini_ht_api/common/exception" "eta/eta_mini_ht_api/common/utils/client" "fmt" "io" "net/url" "sync" ) const ( baseURL = "https://api.weixin.qq.com" codeAPI = "/sns/jscode2session" ) const ( // WeChatServerError 微信服务器错误时返回返回消息 ) var ( wechatClient *Client once sync.Once ) type Client struct { // HTTP请求客户端 client *client.HttpClient // 小程序后台配置: 小程序ID appid string // 小程序后台配置: 小程序密钥 secret string } func GetInstance() *Client { once.Do(func() { wechatConf, ok := config.GetConfig(contants.WECHAT).(*config.WechatConfig) if !ok { // 处理错误情况,比如记录日志或返回错误信息 logger.Info("加载wechat配置失败") return // 或者采取其他适当的错误处理措施 } // 默认配置 wechatClient = NewClient(wechatConf.GetAppid(), wechatConf.GetSecret(), WithHttpClient(client.DefaultClient())) }) return wechatClient } // AccessTokenGetter 用户自定义获取access_token的方法 type AccessTokenGetter func(appid, secret string) (token string, expireIn uint) // NewClient 初始化客户端并用自定义配置替换默认配置 func NewClient(appid, secret string, opts ...func(*Client)) *Client { cli := &Client{ appid: appid, secret: secret, } // 执行额外的配置函数 for _, fn := range opts { fn(cli) } return cli } // WithHttpClient 自定义 HTTP Client func WithHttpClient(hc *client.HttpClient) func(*Client) { return func(cli *Client) { cli.client = hc } } type loginResponse struct { we_http.BaseResponse we_http.LoginResponse } // Login 小程序登录 func (cli *Client) Login(code string) (wxUser WxUser, err error) { if code == "" { err = exception.New(exception.WeChatCodeEmpty) return } api, err := code2url(cli.appid, cli.secret, code) if err != nil { err = exception.New(exception.WeChatIllegalRequest) return } //请求微信接口 res, err := cli.client.Get(api) if err != nil || res.StatusCode != 200 { if res != nil { logger.Error("获取微信用户信息失败:%v", res.Status) } else { logger.Error("获取微信用户信息失败:%v", err) } err = exception.New(exception.WeChatServerError) return } defer func(Body io.ReadCloser) { Body.Close() }(res.Body) var data loginResponse err = json.NewDecoder(res.Body).Decode(&data) if err != nil { logger.Error("解析微信应答失败:%v", err) err = exception.New(exception.WeChatResponseError) return } if data.Errcode != 0 { logger.Error("获取用户信息失败:%v", data.Errmsg) err = exception.New(exception.WechatUserInfoFailed) return } wxUser = WxUser{ OpenId: data.LoginResponse.OpenID, UnionId: data.LoginResponse.UnionID, } return } type WxUser struct { OpenId string UnionId string } // 拼接 获取 session_key 的 URL func code2url(appID, secret, code string) (string, error) { url, err := url.Parse(baseURL + codeAPI) if err != nil { return "", err } query := url.Query() query.Set("appid", appID) query.Set("secret", secret) query.Set("js_code", code) query.Set("grant_type", "authorization_code") url.RawQuery = query.Encode() return url.String(), nil } type WxUserInfo struct { OpenId string `json:"openid"` AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` Scope string `json:"scope"` ErrCode int ErrMsg string } // Login 小程序登录 func (cli *Client) GzhLogin(code string) (wxUser WxUser, err error) { if code == "" { err = exception.New(exception.WeChatCodeEmpty) return } api, err := code2url(cli.appid, cli.secret, code) if err != nil { err = exception.New(exception.WeChatIllegalRequest) return } //请求微信接口 res, err := cli.client.Get(api) defer func(Body io.ReadCloser) { Body.Close() }(res.Body) if err != nil || res.StatusCode != 200 { logger.Error("获取微信用户信息失败:%v", res.Status) err = exception.New(exception.WeChatServerError) return } var data loginResponse err = json.NewDecoder(res.Body).Decode(&data) if err != nil { logger.Error("解析微信应答失败:%v", err) err = exception.New(exception.WeChatResponseError) return } fmt.Println(data) if data.Errcode != 0 { logger.Error("获取用户信息失败:%v", data.Errmsg) err = exception.New(exception.WechatUserInfoFailed) return } wxUser = WxUser{ OpenId: data.LoginResponse.OpenID, UnionId: data.LoginResponse.UnionID, } return }