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)) }