You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

427 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package third_part
import (
"WechatGateWay/global"
"WechatGateWay/utils"
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"math"
"mime/multipart"
"net/http"
"os"
"reflect"
)
var ctx = global.CTX
var GetTokenApi = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s"
var SendMessageApi = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=%s"
var UploadMediaApi = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=%s&type=%s"
type Msg struct {
Content string `json:"content"`
}
type Pic struct {
MediaId string `json:"media_id"`
}
type JsonData struct {
ToUser string `json:"touser"`
AgentId string `json:"agentid"`
MsgType string `json:"msgtype"`
DuplicateCheckInterval int `json:"duplicate_check_interval"`
Text Msg `json:"text"`
Image Pic `json:"image"`
}
// GetRemoteToken 从企业微信服务端API获取access_token存在redis服务则缓存
func GetRemoteToken() string {
log.Println("WechatAccessToken开始更新")
corpId := global.WechatCorpId
appSecret := global.WechatSendSecret
getTokenUrl := fmt.Sprintf(GetTokenApi, corpId, appSecret)
//log.Println("getTokenUrl==>", getTokenUrl)
resp, err := http.Get(getTokenUrl)
if err != nil {
log.Println(err)
}
respData, err := io.ReadAll(resp.Body)
if err != nil {
log.Println(err)
}
tokenResponse := utils.ParseJson(string(respData))
resp.Body.Close()
var accessToken string
_, ok := tokenResponse["access_token"]
if ok {
accessToken = fmt.Sprintf("%s", tokenResponse["access_token"])
//存到gloabl
global.WechatAccessToken = accessToken
log.Println("WechatAccessToken已更新")
}
return accessToken
}
// PostMsg 推送消息
func PostMsg(postData JsonData, postUrl string) string {
postJson, _ := json.Marshal(postData)
msgReq, err := http.NewRequest("POST", postUrl, bytes.NewBuffer(postJson))
if err != nil {
log.Println(err)
}
msgReq.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(msgReq)
if err != nil {
log.Fatalln("企业微信发送应用消息接口报错==>", err)
}
body, _ := io.ReadAll(resp.Body)
msgReq.Body.Close()
//mediaResp := utils.ParseJson(string(body))
//log.Println("企业微信发送应用消息接口返回==>", mediaResp)
return string(body)
}
// UploadMediaFile 上传临时素材并返回mediaId
func UploadMediaFile(msgType string, filename string, accessToken string) (string, float64) {
// 企业微信图片上传不能大于2M
//if pic.Size > 2*1024*1024 {
// log.Fatalln("图片超过2M 无法发送")
// // 自定义code无效的图片文件
// return "", 400
//
//}
//图片类型
//fileExt := strings.ToLower(path.Ext(pic.Filename))
//if fileExt != ".png" && fileExt != ".jpg" && fileExt != ".gif" && fileExt != ".jpeg" {
// log.Fatalln("上传失败!只允许png,jpg,gif,jpeg文件")
// return "", 400
//}
// 读取上传的文件流。
fd, err := os.Open(filename)
if err != nil {
fmt.Println("Error:", err)
return "", 400
}
defer fd.Close()
fileInfo, err := os.Stat(filename)
if err != nil {
fmt.Println("Error:", err)
return "", 400
}
buf := new(bytes.Buffer)
writer := multipart.NewWriter(buf)
if createFormFile, err := writer.CreateFormFile("media", fileInfo.Name()); err == nil {
readAll, _ := io.ReadAll(fd)
createFormFile.Write(readAll)
}
writer.Close()
uploadMediaUrl := fmt.Sprintf(UploadMediaApi, accessToken, msgType)
newRequest, _ := http.NewRequest("POST", uploadMediaUrl, buf)
newRequest.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, err := client.Do(newRequest)
respData, _ := io.ReadAll(resp.Body)
mediaResp := utils.ParseJson(string(respData))
if err != nil {
log.Fatalln("上传临时素材出错==>", err)
return "", mediaResp["errcode"].(float64)
} else {
return mediaResp["media_id"].(string), float64(0)
}
}
// UploadMedia 上传临时素材并返回mediaId
func UploadMedia(msgType string, pic *multipart.FileHeader, accessToken string) (string, float64) {
// 企业微信图片上传不能大于2M
//if pic.Size > 2*1024*1024 {
// log.Fatalln("图片超过2M 无法发送")
// // 自定义code无效的图片文件
// return "", 400
//
//}
//图片类型
//fileExt := strings.ToLower(path.Ext(pic.Filename))
//if fileExt != ".png" && fileExt != ".jpg" && fileExt != ".gif" && fileExt != ".jpeg" {
// log.Fatalln("上传失败!只允许png,jpg,gif,jpeg文件")
// return "", 400
//}
// 读取上传的文件流。
fd, err := pic.Open()
if err != nil {
fmt.Println("Error:", err)
return "", 400
}
defer fd.Close()
buf := new(bytes.Buffer)
writer := multipart.NewWriter(buf)
if createFormFile, err := writer.CreateFormFile("media", pic.Filename); err == nil {
readAll, _ := ioutil.ReadAll(fd)
createFormFile.Write(readAll)
}
writer.Close()
uploadMediaUrl := fmt.Sprintf(UploadMediaApi, accessToken, msgType)
log.Println("uploadMediaUrl==>", uploadMediaUrl)
newRequest, _ := http.NewRequest("POST", uploadMediaUrl, buf)
newRequest.Header.Set("Content-Type", writer.FormDataContentType())
log.Println("Content-Type ", writer.FormDataContentType())
client := &http.Client{}
resp, err := client.Do(newRequest)
respData, _ := ioutil.ReadAll(resp.Body)
mediaResp := utils.ParseJson(string(respData))
log.Println("企业微信上传临时素材接口返回==>", mediaResp)
if err != nil {
log.Fatalln("上传临时素材出错==>", err)
return "", mediaResp["errcode"].(float64)
} else {
return mediaResp["media_id"].(string), float64(0)
}
}
// ValidateToken 判断accessToken是否失效
// true-未失效, false-失效需重新获取
func ValidateToken(errcode any) bool {
codeTyp := reflect.TypeOf(errcode)
log.Println("errcode的数据类型==>", codeTyp)
if !codeTyp.Comparable() {
log.Printf("type is not comparable: %v", codeTyp)
return true
}
// 如果errcode为42001表明token已失效则清空redis中的token缓存
// 已知codeType为float64
if math.Abs(errcode.(float64)-float64(42001)) < 1e-3 {
log.Println("现需重新获取token")
return false
}
return true
}
// GetAccessToken 获取企业微信的access_token
func GetAccessToken() string {
accessToken := global.WechatAccessToken
if accessToken == "" {
log.Println("get access_token from remote API")
accessToken = GetRemoteToken()
global.WechatAccessToken = accessToken
} else {
log.Println("get access_token from redis")
}
return accessToken
}
// InitJsonData 初始化Json公共部分数据
func InitJsonData(msgType, toUser string) JsonData {
var WecomAid = global.WechatSendAid
return JsonData{
ToUser: toUser,
AgentId: WecomAid,
MsgType: msgType,
DuplicateCheckInterval: 600,
}
}
// SendTextPost 发送文本消息
func SendTextPost(toUser, msg string) {
// 获取token
accessToken := GetAccessToken()
// 准备发送应用消息所需参数
postData := InitJsonData("text", toUser)
postData.Text = Msg{
Content: msg,
}
//for i := 0; i <= 3; i++ {
// sendMessageUrl := fmt.Sprintf(SendMessageApi, accessToken)
// postStatus = PostMsg(postData, sendMessageUrl)
// postResponse := utils.ParseJson(postStatus)
// errcode := postResponse["errcode"]
// log.Println("发送应用消息接口返回errcode==>", errcode)
// tokenValid = ValidateToken(errcode)
// // token有效则跳出循环继续执行否则重试3次
// if tokenValid {
// break
// }
// // 刷新token
// accessToken = GetAccessToken()
//}
sendMessageUrl := fmt.Sprintf(SendMessageApi, accessToken)
postStatus := PostMsg(postData, sendMessageUrl)
msg_req, err := http.NewRequest("POST", sendMessageUrl, bytes.NewBuffer([]byte(postStatus)))
if err != nil {
log.Println(err)
}
msg_req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
_, err = client.Do(msg_req)
if err != nil {
panic(err)
}
defer msg_req.Body.Close()
}
// due func
func SendTextBak(toUser, msg string) {
// 获取token
accessToken := GetAccessToken()
// 默认token有效
tokenValid := true
// 准备发送应用消息所需参数
postData := InitJsonData("text", toUser)
postData.Text = Msg{
Content: msg,
}
postStatus := ""
for i := 0; i <= 3; i++ {
sendMessageUrl := fmt.Sprintf(SendMessageApi, accessToken)
postStatus = PostMsg(postData, sendMessageUrl)
postResponse := utils.ParseJson(postStatus)
errcode := postResponse["errcode"]
log.Println("发送应用消息接口返回errcode==>", errcode)
tokenValid = ValidateToken(errcode)
// token有效则跳出循环继续执行否则重试3次
if tokenValid {
break
}
// 刷新token
accessToken = GetAccessToken()
}
var res http.ResponseWriter
res.Header().Set("Content-type", "application/json")
_, _ = res.Write([]byte(postStatus))
}
// SendPicMediaId send pic
func SendPicFile(toUser string, filename string) {
token := global.WechatAccessToken
// 准备发送应用消息所需参数
mid, _ := UploadMediaFile("image", filename, token)
log.Println("上传图片返回的media_id==>", mid)
postData := InitJsonData("image", toUser)
postData.Image = Pic{
MediaId: mid,
}
sendMessageUrl := fmt.Sprintf(SendMessageApi, token)
postStatus := PostMsg(postData, sendMessageUrl)
//postResponse := utils.ParseJson(postStatus)
msg_req, err := http.NewRequest("POST", sendMessageUrl, bytes.NewBuffer([]byte(postStatus)))
if err != nil {
log.Println(err)
}
msg_req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
_, err = client.Do(msg_req)
if err != nil {
panic(err)
}
defer msg_req.Body.Close()
}
// SendPicMediaId send pic
func SendPicMid(toUser string, mid string) {
log.Println("send pic mid:", mid)
token := global.WechatAccessToken
// 准备发送应用消息所需参数
postData := InitJsonData("image", toUser)
postData.Image = Pic{
MediaId: mid,
}
sendMessageUrl := fmt.Sprintf(SendMessageApi, token)
postStatus := PostMsg(postData, sendMessageUrl)
//postResponse := utils.ParseJson(postStatus)
msg_req, err := http.NewRequest("POST", sendMessageUrl, bytes.NewBuffer([]byte(postStatus)))
if err != nil {
log.Println(err)
}
msg_req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
_, err = client.Do(msg_req)
if err != nil {
panic(err)
}
defer msg_req.Body.Close()
}
// wechat send pic
func SendPic(toUser string, pic *multipart.FileHeader) {
var res http.ResponseWriter
// 获取token
accessToken := GetAccessToken()
// 默认token有效
tokenValid := true
msgType := "image"
// 默认mediaId为空
mediaId := ""
// token有效则跳出循环继续执行否则重试3次
for i := 0; i <= 3; i++ {
var errcode float64
mediaId, errcode = UploadMedia(msgType, pic, accessToken)
log.Printf("企业微信上传临时素材接口返回的media_id==>[%s], errcode==>[%f]\n", mediaId, errcode)
tokenValid = ValidateToken(errcode)
if tokenValid {
break
}
accessToken = GetAccessToken()
}
// 准备发送应用消息所需参数
postData := InitJsonData("image", toUser)
postData.Image = Pic{
MediaId: mediaId,
}
postStatus := ""
for i := 0; i <= 3; i++ {
sendMessageUrl := fmt.Sprintf(SendMessageApi, accessToken)
postStatus = PostMsg(postData, sendMessageUrl)
postResponse := utils.ParseJson(postStatus)
errcode := postResponse["errcode"]
log.Println("发送应用消息接口返回errcode==>", errcode)
tokenValid = ValidateToken(errcode)
// token有效则跳出循环继续执行否则重试3次
if tokenValid {
break
}
// 刷新token
accessToken = GetAccessToken()
}
res.Header().Set("Content-type", "application/json")
_, _ = res.Write([]byte(postStatus))
}