wechatHelper.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. package wechat
  2. import (
  3. "context"
  4. "crypto/md5"
  5. "dsbqj-admin/pkg/cache"
  6. "dsbqj-admin/pkg/logger"
  7. "dsbqj-admin/pkg/util"
  8. "encoding/hex"
  9. "fmt"
  10. "github.com/goccy/go-json"
  11. "github.com/silenceper/wechat"
  12. wechatCtx "github.com/silenceper/wechat/context"
  13. "os"
  14. "sort"
  15. "strconv"
  16. "strings"
  17. "sync/atomic"
  18. "time"
  19. wechatCache "dsbqj-admin/pkg/cache/wechat"
  20. )
  21. var GOpenid OpenidMgr
  22. type OpenidMgr struct {
  23. V atomic.Value //值
  24. Over int64 //过期时间
  25. }
  26. type WechatHelper struct {
  27. ctx context.Context
  28. Wechat *wechat.Wechat
  29. AppId string
  30. AppSecret string
  31. }
  32. const (
  33. subscribeUrl = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token="
  34. accessTokenURL = "http://fx.wumiao666.com/cp/getaccesstoken"
  35. )
  36. type SignResult struct {
  37. Param string
  38. Line string
  39. Sign string
  40. }
  41. func AppSign(params map[string]string, key string) SignResult {
  42. var builder strings.Builder
  43. // 1. 排序 key
  44. keys := make([]string, 0, len(params))
  45. for k := range params {
  46. keys = append(keys, k)
  47. }
  48. sort.Strings(keys)
  49. // 2. 拼接 key=value&
  50. for _, k := range keys {
  51. builder.WriteString(k)
  52. builder.WriteString("=")
  53. builder.WriteString(params[k])
  54. builder.WriteString("&")
  55. }
  56. strForWX := builder.String()
  57. str := strings.TrimSuffix(strForWX, "&")
  58. line := str + key
  59. sign := md5Hex(line)
  60. return SignResult{
  61. Param: str,
  62. Line: line,
  63. Sign: strings.ToLower(sign),
  64. }
  65. }
  66. func md5Hex(s string) string {
  67. sum := md5.Sum([]byte(s))
  68. return hex.EncodeToString(sum[:])
  69. }
  70. type TokenData struct {
  71. AccessToken string `json:"accessToken"`
  72. ExpiresSecond *int64 `json:"expiresSecond"`
  73. }
  74. type ResAccessToken struct {
  75. Msg string `json:"msg"`
  76. Code string `json:"code"`
  77. Data *TokenData `json:"data"`
  78. }
  79. func GetAccessTokenFromServer(ctx *wechatCtx.Context) (resAccessToken ResAccessToken, err error) {
  80. var params = map[string]string{}
  81. params["platformId"] = os.Getenv("SDK_PLATFORMID")
  82. params["appId"] = os.Getenv("SDK_APPID")
  83. params["wxAppId"] = os.Getenv("WECHAT_APPID")
  84. params["clearCache"] = os.Getenv("SDK_CLEARCACHE")
  85. params["requestTime"] = strconv.Itoa(int(util.GetNowSecond()))
  86. res := AppSign(params, os.Getenv("SDK_APPKEY"))
  87. url := fmt.Sprintf("%s?%s&sign=%s", accessTokenURL, res.Param, res.Sign)
  88. var body []byte
  89. body, err = util.HTTPGet(url)
  90. if err != nil {
  91. return
  92. }
  93. err = json.Unmarshal(body, &resAccessToken)
  94. if err != nil {
  95. return
  96. }
  97. if resAccessToken.Code != "0" {
  98. err = fmt.Errorf("get access_token error : errcode=%v , errormsg=%v", resAccessToken.Msg)
  99. return
  100. }
  101. accessTokenCacheKey := fmt.Sprintf("access_token_%s", ctx.AppID)
  102. var expires int64 = 600
  103. if resAccessToken.Data.ExpiresSecond != nil {
  104. expires = *resAccessToken.Data.ExpiresSecond
  105. }
  106. err = ctx.Cache.Set(accessTokenCacheKey, resAccessToken.Data.AccessToken, time.Duration(expires)*time.Second)
  107. return
  108. }
  109. func MyAccessTokenFunc(ctx *wechatCtx.Context) (accessToken string, err error) {
  110. accessTokenCacheKey := fmt.Sprintf("access_token_%s", ctx.AppID)
  111. val := ctx.Cache.Get(accessTokenCacheKey)
  112. if val != nil {
  113. accessToken = val.(string)
  114. return
  115. }
  116. //从微信服务器获取
  117. var resAccessToken ResAccessToken
  118. resAccessToken, err = GetAccessTokenFromServer(ctx)
  119. if err != nil {
  120. return
  121. }
  122. accessToken = resAccessToken.Data.AccessToken
  123. return
  124. }
  125. func NewWechatHelper() *WechatHelper {
  126. appID := os.Getenv("WECHAT_APPID")
  127. appSecret := os.Getenv("WECHAT_SECRET")
  128. wx := wechat.NewWechat(&wechat.Config{
  129. AppID: appID,
  130. AppSecret: appSecret,
  131. Cache: wechatCache.NewAccessToken(cache.RedisReport),
  132. })
  133. wx.Context.SetGetAccessTokenFunc(MyAccessTokenFunc)
  134. return &WechatHelper{
  135. ctx: context.Background(),
  136. AppId: appID,
  137. AppSecret: appSecret,
  138. Wechat: wx,
  139. }
  140. }
  141. type errmsg struct {
  142. Errcode int `json:"errcode"` //错误码
  143. Errmsg string `json:"errmsg"` //错误信息
  144. Result struct {
  145. Suggest string `json:"suggest"` //建议,有risky、pass、review三种值
  146. Label int `json:"label"` //命中标签枚举值,100 正常;10001 广告;20001 时政;20002 色情;20003 辱骂;20006 违法犯罪;20008 欺诈;20012 低俗;20013 版权;21000 其他
  147. } `json:"result"`
  148. }
  149. type Subscribe struct {
  150. ToUser string `json:"touser"`
  151. TemplateId string `json:"template_id"`
  152. MiniProgramState string `json:"miniprogram_state"`
  153. Lang string `json:"lang"`
  154. Data interface{} `json:"data"`
  155. }
  156. func (this *WechatHelper) SendWechatSubscribe(openid string, template string, msg interface{}) int {
  157. token, _ := this.Wechat.GetAccessToken()
  158. url := subscribeUrl + token
  159. logger.Info("send msg use template %s, url %s", template, url)
  160. reqData := Subscribe{ToUser: openid, TemplateId: template, Lang: "zh_CN"}
  161. reqData.Data = msg
  162. data, _ := json.Marshal(reqData)
  163. buf, err := util.HTTPPost(url, string(data))
  164. if err != nil {
  165. logger.Info("[ERROR] CheckWechatMsg post err!err:", err.Error())
  166. return 101
  167. }
  168. var re = new(errmsg)
  169. err = json.Unmarshal(buf, re)
  170. if err != nil {
  171. logger.Info("[ERROR] CheckWechatMsg Unmarshal err!err:", err)
  172. return 103
  173. }
  174. if re.Errcode != 0 {
  175. if re.Errcode == 40003 || re.Errcode == 43101 { //openid无效,appid与openid不匹配, 用户未订阅消息(用户未在近两小时访问小程序)
  176. logger.Info("CheckWechatMsg msg: %s ret: %d errmsg: %s", msg, re.Errcode, re.Errmsg)
  177. return 0
  178. }
  179. } else {
  180. if re.Result.Label != 100 && re.Result.Label != 0 {
  181. logger.Info("[ERROR]CheckWechatMsg msg:", msg, " ret:", re.Result.Label, " ,suggest:", re.Result.Suggest)
  182. return re.Result.Label
  183. }
  184. }
  185. logger.Info("==============> %v", re)
  186. return re.Errcode
  187. }