Loading

golang 小游戏添加客服(微信版)

教程只是提供思路, 让你快速完成你想要的功能, 不要全部依赖于此.
不喜勿碰.

大致思路:

  1. 上传图片 获取 media_id (当然你发送不是图片你可以不获取这个media_id)
  2. 获取 access_token
  3. 判定用户发送的消息类型, 做出相应的消息回复.

这里我说明一下, 我这里的需求是要送图片的.
我这里的思路是:
1.先将要发送的图片上传到 mongodb 数据库.
2.从数据库中读取图片数据
3.获取media_id, 并保存media_id, 因为这 media_id 有个时效性, 你可以保存一份, 减少请求,加快相应速度.
4.获取access_token, 并保存, 原因同上.
5.判定用户发送的消息类型, 做出相应的消息回复.

基础代码

// 对内置的 http的 Post 和 Get 做了一个简单的封装

func HttpPost(url, params string, contentType string, retMap interface{}) error {
	if contentType == "" {
		contentType = "application/json"
		// "application/x-www-form-urlencoded"
	}
	resp, err := http.Post(url,
		contentType,
		strings.NewReader(params))
	if err != nil {
		return err
	}

	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(body, retMap)
	if err != nil {
		return err
	}
	return nil
}

func HttpGet(url string, retMap interface{}) error {
	resp, err := http.Get(url)
	if err != nil {
		return err
	}

	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(body, retMap)
	if err != nil {
		return err
	}
	return nil
}

客服消息

type MediaResp struct {
	ErrorCode int    `json:"errcode"`
	ErrorMsg  string `json:"errmsg"`
	Type      string `json:"type"`
	MediaID   string `json:"media_id"`
	CreatedAt int64  `json:"created_at"`
}

type StoreMdeiaKV struct {
	MediaId    string
	Expires    int64
	CreateTime int64
}

func getWechatMediaId(fileName, appid, appSecret, projName, platform string, data []byte, retMap *MediaResp) error {
	buf := new(bytes.Buffer)
	w := multipart.NewWriter(buf)

	err := w.WriteField("contentType", "image/jpeg")
	if err != nil {
		return err
	}

	fw, err := w.CreateFormFile("value", fileName)
	if err != nil {
		return err
	}

	// image/jpeg
	_, err = fw.Write(data)
	if err != nil {
		return err
	}
	_ = w.Close()

	err, retToken := checkAndGetTokenWechat(fmt.Sprintf("%s-%s", projName, platform), appid, appSecret)
	if err != nil {
		return err
	}
	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/media/upload?access_token=%s&type=image", retToken.Token)
	resp, err := http.Post(url,
		w.FormDataContentType(),
		buf)
	if err != nil {
		return err
	}

	defer func() {
		_ = resp.Body.Close()
	}()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	err = json.Unmarshal(body, retMap)
	if err != nil {
		return err
	}
	return nil
}

var _mediaMap sync.Map = sync.Map{}

func checkMediaIdTime(key string, fileName, appid, appSecret, projName, platform string, data []byte) (error, *StoreMdeiaKV) {

	retMap := &MediaResp{}
	if val, ok := _mediaMap.Load(key); !ok {

		err := getWechatMediaId(fileName, appid, appSecret, projName, platform, data, retMap)
		if err != nil {
			return err, nil
		}

		d := &StoreMdeiaKV{
			MediaId:    retMap.MediaID,
			Expires:    time.Now().Unix() + int64(3*24*259200-3600),
			CreateTime: time.Now().Unix(),
		}
		fmt.Println(fmt.Sprintf("media_id key not exist | expires ==> %d | key ==>%s", d.Expires, key))

		_mediaMap.Store(key, d)
		return nil, d
	} else {
		d := val.(*StoreMdeiaKV)
		t := time.Now().Unix()
		if t >= d.Expires {
			fmt.Println(fmt.Sprintf("expires d ==> %d, t ==> %d, key ==> %s", d.Expires, t, key))
			err := getWechatMediaId(fileName, appid, appSecret, projName, platform, data, retMap)
			if err != nil {
				return err, nil
			}

			d := &StoreMdeiaKV{
				MediaId:    retMap.MediaID,
				Expires:    time.Now().Unix() + int64(3*24*259200-3600),
				CreateTime: time.Now().Unix(),
			}
			_mediaMap.Store(key, d)
			return nil, d
		} else {
			return nil, d
		}
	}
}

func CheckMediaId(projName, platform string) (bool, *StoreMdeiaKV) {
	key := fmt.Sprintf("%s-%s", projName, platform)
	if val, ok := _mediaMap.Load(key); !ok {
		return false, nil
	} else {
		d := val.(*StoreMdeiaKV)
		t := time.Now().Unix()
		if t >= d.Expires {
			return false, nil
		} else {
			return true, d
		}
	}
}

func GetMediaId(fileName, appid, appSecret, projName, platform string, data []byte) (error, *StoreMdeiaKV) {
	return checkMediaIdTime(fmt.Sprintf("%s-%s", projName, platform), fileName, appid, appSecret, projName, platform, data)
}

var _tokenMap sync.Map = sync.Map{}

type storeTokenKV struct {
	Token      string
	Expires    int64
	CreateTime int64
}

type WechatTokenRetMap struct {
	Token   string `json:"access_token"`
	Expires int64  `json:"expires_in"`
	Errcode int    `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
}

func getTokenWechat(appid, appSecret string, retMap *WechatTokenRetMap) error {

	url := "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"

	getUrl := fmt.Sprintf(url, appid, appSecret)

	err := HttpGet(getUrl, retMap)

	if err != nil {
		return err
	}

	if retMap.Errcode != 0 {
		return errors.New(fmt.Sprintf("code:%d,errmsg:%s", retMap.Errcode, retMap.ErrMsg))
	}

	return nil
}

func checkAndGetTokenWechat(key, appid, appSecret string) (error, *storeTokenKV) {
	if val, ok := _tokenMap.Load(key); !ok {

		retToken := &WechatTokenRetMap{}
		err := getTokenWechat(appid, appSecret, retToken)
		if err != nil {
			return err, nil
		}

		d := &storeTokenKV{
			Token:      retToken.Token,
			Expires:    retToken.Expires,
			CreateTime: time.Now().Unix(),
		}

		_tokenMap.Store(key, d)
		return nil, d
	} else {
		d := val.(*storeTokenKV)
		t := time.Now().Unix() - d.CreateTime + 60
		if t >= d.Expires {
			retToken := &WechatTokenRetMap{}
			err := getTokenWechat(appid, appSecret, retToken)
			if err != nil {
				return err, nil
			}
			d := &storeTokenKV{
				Token:      retToken.Token,
				Expires:    retToken.Expires,
				CreateTime: time.Now().Unix(),
			}
			_tokenMap.Store(key, d)
			return nil, d
		} else {
			return nil, d
		}
	}
}

type SendServiceMsgResp struct {
	ErrorCode int    `json:"errCode"`
	ErrorMsg  string `json:"errMsg"`
}

type SendServiceMsgInnerReq struct {
	MediaId string `json:"media_id"`
}

type SendServiceMsgReq struct {
	Touser  string                 `json:"touser"`
	MsgType string                 `json:"msgtype"`
	Image   SendServiceMsgInnerReq `json:"image"`
}

func SendServiceMessage(appid, appSecret, projName, platform string, retMap *SendServiceMsgResp, d interface{}) error {
	// https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN

	err, retToken := checkAndGetTokenWechat(fmt.Sprintf("%s-%s", projName, platform), appid, appSecret)
	if err != nil {
		return err
	}

	//err, retMedia := GetMediaId(fileName, appid, appSecret, projName, platform, data)
	//
	//if err != nil {
	//	return err
	//}

	fmt.Println(fmt.Sprintf("token ==> %s", retToken))

	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=%s", retToken.Token)
	dJson, _ := json.Marshal(d)
	sJson := string(dJson)
	err = HttpPost(url, sJson, "", retMap)
	if err != nil {
		return err
	}

	return nil
}

解释微信数据

我这里没有验证是不是来自微信. 直接返回

type Cs struct {
	FromUserName string `json:"FromUserName"`
	ToUserName   string `json:"ToUserName"`
	Content      string `json:"Content"`
	CreateTime   int64  `json:"CreateTime"`
	MsgId        int64  `json:"MsgId"`
	MsgType      string `json:"MsgType"`
	Encrypt      string `json:"Encrypt"`
}

func CustomerService(w http.ResponseWriter, r *http.Request) {
    Log.Log.Debugf("come in wechat msg | request method ==> %s", r.Method)
    if r.Method == "GET" {
          query := r.URL.Query()
          // query["signature"][0]
          // query["timestamp"][0]
          // query["nonce"][0]
          echostr := query["echostr"][0]
          _, _ = w.Write([]byte(echostr))
      } else {
          req := &Cs{}

          if err := json.NewDecoder(r.Body).Decode(req); err != nil {
                Log.Log.Errorf(fmt.Sprintf("errr ==> %s", err.Error()))
                _, _ = w.Write([]byte("success"))
                return
          }
}

调用例子

func test() {
    resp := &Common.SendServiceMsgResp{}
    data := map[string]interface{}{
          "touser":  req.FromUserName,
          "msgtype": "text",
          "text": map[string]interface{}{
                "content": "消息测试",
          },
    }
    err := Common.SendServiceMessage(g.AppId, g.AppSecret, g.ProjName, g.Platform, resp, data)
    if err != nil {
          Log.Log.Errorf("send customer service msg fail | err ===> %s", err.Error())
          return Common.CodeError, nil
    }
}
posted @ 2020-05-19 15:01  丫丫魏  阅读(178)  评论(0编辑  收藏  举报