template_msg.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. package wechat
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "errors"
  7. "eta/eta_mini_api/models"
  8. "eta/eta_mini_api/utils"
  9. "fmt"
  10. "io"
  11. "net/http"
  12. "time"
  13. )
  14. var (
  15. TemplateMsgSendUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s"
  16. TemplateMsgClearQuotaUrl = "https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=%s"
  17. )
  18. type TemplateMsgSendClient struct {
  19. AccessToken string
  20. Data []byte
  21. }
  22. type SendTemplateResponse struct {
  23. Errcode int `json:"errcode"`
  24. Errmsg string `json:"errmsg"`
  25. MsgID int `json:"msgid"`
  26. }
  27. type ClearQuotaResponse struct {
  28. Errcode int `json:"errcode"`
  29. Errmsg string `json:"errmsg"`
  30. }
  31. type OpenIdList struct {
  32. OpenId string
  33. UserId int
  34. }
  35. // TemplateMsgSendClient.ClearQuota 清除发送超过当日10万次限制
  36. func (c *TemplateMsgSendClient) ClearQuota() (result *ClearQuotaResponse, err error) {
  37. key := "CACHE_SendTemplateMsg_ERR"
  38. exists, _ := utils.Redis.Exists(context.TODO(), key).Result()
  39. if exists == 1 {
  40. return
  41. }
  42. _ = utils.Redis.SetEX(context.TODO(), key, 1, 6*time.Minute)
  43. sendUrl := fmt.Sprintf(TemplateMsgClearQuotaUrl, c.AccessToken)
  44. client := http.Client{}
  45. clearData := make(map[string]interface{})
  46. clearData["appid"] = WxAppId
  47. clearJson, _ := json.Marshal(clearData)
  48. resp, err := client.Post(sendUrl, "application/json", bytes.NewBuffer(clearJson))
  49. if err != nil {
  50. return
  51. }
  52. defer func() {
  53. _ = resp.Body.Close()
  54. }()
  55. clearBody, err := io.ReadAll(resp.Body)
  56. err = json.Unmarshal(clearBody, &result)
  57. return
  58. }
  59. // TemplateMsgSendClient.SendMsg 推送消息
  60. func (c *TemplateMsgSendClient) SendMsg() (sendRes *SendTemplateResponse, err error) {
  61. // 请求接口
  62. sendUrl := fmt.Sprintf(TemplateMsgSendUrl, c.AccessToken)
  63. client := http.Client{}
  64. resp, err := client.Post(sendUrl, "application/json", bytes.NewBuffer(c.Data))
  65. if err != nil {
  66. return
  67. }
  68. defer func() {
  69. _ = resp.Body.Close()
  70. }()
  71. body, _ := io.ReadAll(resp.Body)
  72. if err = json.Unmarshal(body, &sendRes); err != nil {
  73. return
  74. }
  75. // 模板消息发送超过当日10万次限制错误处理
  76. if sendRes.Errcode == 45009 {
  77. // 发送提示邮件
  78. // go alarm_msg.SendAlarmMsg("模板消息发送超过当日10万次限制, SendTemplateResponse: "+string(body), 3)
  79. // 清理限制
  80. clearRes, e := c.ClearQuota()
  81. if e != nil {
  82. err = e
  83. return
  84. }
  85. if clearRes.Errcode != 0 {
  86. clearJson, er := json.Marshal(clearRes)
  87. if er != nil {
  88. return nil, er
  89. }
  90. fmt.Println("自动清理模板消息限制接口, 调用失败, ClearQuotaResponse: " + string(clearJson))
  91. // go alarm_msg.SendAlarmMsg("自动清理模板消息限制接口, 调用失败, ClearQuotaResponse: "+string(clearJson), 3)
  92. return
  93. }
  94. // 发送成功邮件
  95. // go alarm_msg.SendAlarmMsg("自动清理模板消息限制接口, 调用成功", 1)
  96. // 重新推送
  97. go func() {
  98. _, e := c.SendMsg()
  99. if e != nil {
  100. return
  101. // reSendJson, _ := json.Marshal(reSend)
  102. // alarm_msg.SendAlarmMsg("重新推送模板消息失败, SendTemplateResponse: "+string(reSendJson), 3)
  103. }
  104. }()
  105. }
  106. if sendRes.Errcode != 0 {
  107. err = errors.New("推送模板消息失败, SendTemplateResponse: " + string(body))
  108. }
  109. return
  110. }
  111. // AddUserTemplateRecord 新增模板消息推送记录
  112. func AddUserTemplateRecord(userId, sendStatus, sendType int, openid, resource, sendData, result string) (err error) {
  113. item := &models.UserTemplateRecord{
  114. UserID: userId,
  115. OpenID: openid,
  116. Resource: resource,
  117. SendData: sendData,
  118. Result: result,
  119. CreateDate: time.Now().Format(utils.FormatDate),
  120. CreateTime: time.Now().Format(utils.FormatDateTime),
  121. SendStatus: sendStatus,
  122. SendType: sendType,
  123. }
  124. err = item.Insert()
  125. return
  126. }
  127. // SendMultiTemplateMsg 推送模板消息至多个用户
  128. func SendMultiTemplateMsg(sendMap map[string]interface{}, items []*OpenIdList, resource string, sendType int) (err error) {
  129. ws := GetWxChat()
  130. accessToken, err := ws.GetAccessToken()
  131. if err != nil {
  132. return
  133. }
  134. for _, item := range items {
  135. sendMap["touser"] = item.OpenId
  136. data, e := json.Marshal(sendMap)
  137. if e != nil {
  138. err = e
  139. return
  140. }
  141. ts := &TemplateMsgSendClient{
  142. AccessToken: accessToken,
  143. Data: data,
  144. }
  145. result, e := ts.SendMsg()
  146. if result == nil {
  147. return
  148. }
  149. // 推送消息记录
  150. {
  151. go func(v *OpenIdList) {
  152. sendStatus := 1
  153. if e != nil {
  154. sendStatus = 0
  155. }
  156. resultJson, _ := json.Marshal(result)
  157. _ = AddUserTemplateRecord(v.UserId, sendStatus, sendType, v.OpenId, resource, string(data), string(resultJson))
  158. }(item)
  159. }
  160. if e != nil {
  161. err = e
  162. return
  163. }
  164. }
  165. return
  166. }