package wechat import ( "bytes" "context" "encoding/json" "errors" "eta/eta_mini_api/models" "eta/eta_mini_api/utils" "fmt" "io" "net/http" "time" ) var ( TemplateMsgSendUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s" TemplateMsgClearQuotaUrl = "https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=%s" ) type TemplateMsgSendClient struct { AccessToken string Data []byte } type SendTemplateResponse struct { Errcode int `json:"errcode"` Errmsg string `json:"errmsg"` MsgID int `json:"msgid"` } type ClearQuotaResponse struct { Errcode int `json:"errcode"` Errmsg string `json:"errmsg"` } type OpenIdList struct { OpenId string UserId int } // TemplateMsgSendClient.ClearQuota 清除发送超过当日10万次限制 func (c *TemplateMsgSendClient) ClearQuota() (result *ClearQuotaResponse, err error) { key := "CACHE_SendTemplateMsg_ERR" exists, _ := utils.Redis.Exists(context.TODO(), key).Result() if exists == 1 { return } _ = utils.Redis.SetEX(context.TODO(), key, 1, 6*time.Minute) sendUrl := fmt.Sprintf(TemplateMsgClearQuotaUrl, c.AccessToken) client := http.Client{} clearData := make(map[string]interface{}) clearData["appid"] = WxAppId clearJson, _ := json.Marshal(clearData) resp, err := client.Post(sendUrl, "application/json", bytes.NewBuffer(clearJson)) if err != nil { return } defer func() { _ = resp.Body.Close() }() clearBody, err := io.ReadAll(resp.Body) err = json.Unmarshal(clearBody, &result) return } // TemplateMsgSendClient.SendMsg 推送消息 func (c *TemplateMsgSendClient) SendMsg() (sendRes *SendTemplateResponse, err error) { // 请求接口 sendUrl := fmt.Sprintf(TemplateMsgSendUrl, c.AccessToken) client := http.Client{} resp, err := client.Post(sendUrl, "application/json", bytes.NewBuffer(c.Data)) if err != nil { return } defer func() { _ = resp.Body.Close() }() body, _ := io.ReadAll(resp.Body) if err = json.Unmarshal(body, &sendRes); err != nil { return } // 模板消息发送超过当日10万次限制错误处理 if sendRes.Errcode == 45009 { // 发送提示邮件 // 清理限制 clearRes, e := c.ClearQuota() if e != nil { err = e return } if clearRes.Errcode != 0 { clearJson, er := json.Marshal(clearRes) if er != nil { return nil, er } fmt.Println("自动清理模板消息限制接口, 调用失败, ClearQuotaResponse: " + string(clearJson)) return } // 发送成功邮件 // 重新推送 go func() { _, e := c.SendMsg() if e != nil { return } }() } if sendRes.Errcode != 0 { err = errors.New("推送模板消息失败, SendTemplateResponse: " + string(body)) } return } // AddUserTemplateRecord 新增模板消息推送记录 func AddUserTemplateRecord(userId, sendStatus, sendType int, openid, resource, sendData, result string) (err error) { item := &models.UserTemplateRecord{ UserID: userId, OpenID: openid, Resource: resource, SendData: sendData, Result: result, CreateDate: time.Now().Format(utils.FormatDate), CreateTime: time.Now().Format(utils.FormatDateTime), SendStatus: sendStatus, SendType: sendType, } err = item.Insert() return } // SendMultiTemplateMsg 推送模板消息至多个用户 func SendMultiTemplateMsg(sendMap map[string]interface{}, items []*OpenIdList, resource string, sendType int) (err error) { ws := GetWxChat() accessToken, err := ws.GetAccessToken() if err != nil { return } for _, item := range items { sendMap["touser"] = item.OpenId data, e := json.Marshal(sendMap) if e != nil { err = e return } ts := &TemplateMsgSendClient{ AccessToken: accessToken, Data: data, } result, e := ts.SendMsg() if result == nil { return } // 推送消息记录 { go func(v *OpenIdList) { sendStatus := 1 if e != nil { sendStatus = 0 } resultJson, _ := json.Marshal(result) _ = AddUserTemplateRecord(v.UserId, sendStatus, sendType, v.OpenId, resource, string(data), string(resultJson)) }(item) } if e != nil { err = e return } } return }