wechat_client.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. package wechat
  2. import (
  3. "eta_mini_ht_api/component/cache"
  4. "eta_mini_ht_api/component/log"
  5. "github.com/medivhzhan/weapp/v3/auth"
  6. "github.com/medivhzhan/weapp/v3/request"
  7. "github.com/mitchellh/mapstructure"
  8. "net/http"
  9. "sync"
  10. )
  11. const (
  12. baseURL = "https://api.weixin.qq.com"
  13. )
  14. var (
  15. wechatClient *Client
  16. once sync.Once
  17. )
  18. type Client struct {
  19. // HTTP请求客户端
  20. request *request.Request
  21. // 数据缓存器
  22. cache *cache.RedisCache
  23. // 日志记录器
  24. logger logger.Logger
  25. // 小程序后台配置: 小程序ID
  26. appid string
  27. // 小程序后台配置: 小程序密钥
  28. secret string
  29. // 用户自定义获取access_token的方法
  30. accessTokenGetter AccessTokenGetter
  31. }
  32. func GetInstance() *Client {
  33. once.Do(func() {
  34. // 默认配置
  35. wechatClient = NewClient("", "",
  36. WithHttpClient(http.DefaultClient),
  37. WithCache(cache.GetInstance()),
  38. WithLogger(logger.GetInstance()),
  39. )
  40. })
  41. return wechatClient
  42. }
  43. // 用户自定义获取access_token的方法
  44. type AccessTokenGetter func(appid, secret string) (token string, expireIn uint)
  45. // 初始化客户端并用自定义配置替换默认配置
  46. func NewClient(appid, secret string, opts ...func(*Client)) *Client {
  47. cli := &Client{
  48. appid: appid,
  49. secret: secret,
  50. }
  51. // 执行额外的配置函数
  52. for _, fn := range opts {
  53. fn(cli)
  54. }
  55. if cli.cache == nil {
  56. //cli.cache = cache.NewMemoryCache()
  57. }
  58. if cli.request == nil {
  59. //cli.request = request.NewRequest(http.DefaultClient, request.ContentTypeJSON, cli.Logger)
  60. }
  61. if cli.logger == nil {
  62. //cli.logger = logger.NewLogger(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Info, true)
  63. }
  64. return cli
  65. }
  66. // WithHttpClient 自定义 HTTP Client
  67. func WithHttpClient(hc *http.Client) func(*Client) {
  68. return func(cli *Client) {
  69. //cli.request = request.NewRequest(hc, request.ContentTypeJSON, cli.Logger)
  70. }
  71. }
  72. // WithCache 自定义缓存
  73. func WithCache(cc *cache.RedisCache) func(*Client) {
  74. return func(cli *Client) {
  75. cli.cache = cc
  76. }
  77. }
  78. // WithAccessTokenSetter 自定义获取access_token的方法
  79. func WithAccessTokenSetter(getter AccessTokenGetter) func(*Client) {
  80. return func(cli *Client) {
  81. cli.accessTokenGetter = getter
  82. }
  83. }
  84. // WithLogger 自定义日志
  85. func WithLogger(logger logger.Logger) func(*Client) {
  86. return func(cli *Client) {
  87. cli.logger = logger
  88. }
  89. }
  90. // POST 参数
  91. type requestParams map[string]interface{}
  92. // URL 参数
  93. type requestQueries map[string]interface{}
  94. // tokenAPI 获取带 token 的 API 地址
  95. func tokenAPI(api, token string) (string, error) {
  96. queries := requestQueries{
  97. "access_token": token,
  98. }
  99. return request.EncodeURL(api, queries)
  100. }
  101. // convert bool to int
  102. func bool2int(ok bool) uint8 {
  103. if ok {
  104. return 1
  105. }
  106. return 0
  107. }
  108. // Logger 获取日志记录器
  109. func (cli *Client) Logger() logger.Logger { return cli.logger }
  110. // AccessToken 获取小程序全局唯一后台接口调用凭据(access_token)。
  111. // 调调用绝大多数后台接口时都需使用 access_token,开发者需要进行妥善保存,注意缓存。
  112. func (cli *Client) AccessToken() (string, error) {
  113. return "", nil
  114. ////key := cli.tokenCacheKey()
  115. //data, ok := cli.cache.Get(key)
  116. //if ok {
  117. // return data.(string), nil
  118. //}
  119. //
  120. //if cli.accessTokenGetter != nil {
  121. //// token, expireIn := cli.accessTokenGetter(cli.appid, cli.secret)
  122. // //cli.cache.Set(key, token, time.Duration(expireIn)*time.Second)
  123. //// return token, nil
  124. //} else {
  125. //
  126. // req := auth.GetStableAccessTokenRequest{
  127. // Appid: cli.appid,
  128. // Secret: cli.secret,
  129. // GrantType: "client_credential",
  130. // }
  131. // rsp, err := cli.NewAuth().GetStableAccessToken(&req)
  132. // if err != nil {
  133. // return "", err
  134. // }
  135. //
  136. // if err := rsp.GetResponseError(); err != nil {
  137. // return "", err
  138. // }
  139. //
  140. // err = cli.cache.SetString(key, rsp.AccessToken, time.Duration(rsp.ExpiresIn)*time.Second)
  141. // if err != nil {
  142. // return "", err
  143. // }
  144. // return rsp.AccessToken, nil
  145. //}
  146. }
  147. // 拼凑完整的 URI
  148. func (cli *Client) combineURI(url string, req interface{}, withToken bool) (string, error) {
  149. output := make(map[string]interface{})
  150. config := &mapstructure.DecoderConfig{
  151. Metadata: nil,
  152. Result: &output,
  153. TagName: "query",
  154. }
  155. decoder, err := mapstructure.NewDecoder(config)
  156. if err != nil {
  157. return "", err
  158. }
  159. err = decoder.Decode(req)
  160. if err != nil {
  161. return "", err
  162. }
  163. if withToken {
  164. token, err := cli.AccessToken()
  165. if err != nil {
  166. return "", err
  167. }
  168. output["access_token"] = token
  169. }
  170. return request.EncodeURL(baseURL+url, output)
  171. }
  172. // NewAuth 用户信息
  173. func (cli *Client) NewAuth() *auth.Auth {
  174. return auth.NewAuth(cli.request, cli.combineURI)
  175. }