Loading

golang 订阅消息提醒

这里不只于 微信平台, 还有QQ平台.

这里说一下大致的思路吧, 按着平台的文档就差不错可以了,

  1. 获取access_token
  2. 获取模板id 和模板的字段名字
  3. 装配结构体
  4. 发送请求

下面是代码实现例子

基础代码

// 对内置的 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
}

微信

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 WechatSubscribeRetMap struct {
	Token   string `json:"access_token"`
	Expires int    `json:"expires_in"`
	Errcode int    `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
}

func SendSubscribeWechat(appid, appSecret, projName, platform string, data interface{}, retMap *WechatSubscribeRetMap) error {

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

	fmt.Println("token ==>", retToken)
	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=%s", retToken.Token)

	dJson, _ := json.Marshal(data)
	sJson := string(dJson)
	err = HttpPost(url, sJson, "", retMap)
	if err != nil {
		return err
	}
	return nil
}

调用列子

我这里使用的结构体转配的 你可以使用 map 这种的更方便.

type ValS struct {
	Value string `json:"value"`
}

type SignInTT struct {
	V1 ValS `json:"phrase1"` // 签到状态
	V2 ValS `json:"date4"`   // 通知日期
	V3 ValS `json:"thing7"`  // 功能名称
	V4 ValS `json:"thing8"`  // 功能描述
}
type SubScribeSendS struct {
	Touser     string   `json:"touser"`
	TemplateId string   `json:"template_id"`
	Page       string   `json:"page"`
	Data       SignInTT `json:"data"`
}

func test() {
      d := &common.WechatSubscribeRetMap{}
      sTime := time.Now()
      hLog.Debug("======================", item.TemplateName, item.OpenId)
      err := common.SendSubscribeWechat("appid", "秘钥", "根据的项目定义这个字段", "根据的项目定义这个字段", &SubScribeSendS{
            Touser:     "<用户的Openid>",
            TemplateId: "<微信开发平台的模板id>",
            Page:       "<跳转路径>", // 这里配置了跳转路径会点击消息模板直接进入游戏, 建议配置 // 例如: index
            Data: SignInTT{
                  V1: ValS{
                        Value: "未签到",
                  },
                  V2: ValS{
			Value: sTime.Format("2006年01月02日"),
                  },
                  V3: ValS{
			Value: "七日签到",
                  },
                  V4: ValS{
			Value: "累计签到获取奖励",
                  },
            },
      }, d)

// 用map 模拟那个data结构体
d := map[string]interface{} {
      "Touser": "<用户的Openid>",
      "TemplateId": "<微信开发平台的模板id>",
      "Page": "<跳转路径>", // 这里配置了跳转路径会点击消息模板直接进入游戏, 建议配置 // 例如: index
      "Data": map[string]interface{} {
            "phrase1": map[string]interface{} {
                  "value": 未签到,
            },
            // 其他的字段类似
      },
}

我这里更新一下QQ平台的订阅消息

这里说明一下 QQ 平台的订阅消息和 微信的 调用方式基本保持一致, 这里唯一不同有两点
1.请求的地址不一样
2.请求订阅消息传参的格式不一样

上面的第一点我就不多说了. 主要说一下第二点
因为第二点 QQ小游戏 官方文档说的不清晰需要调试, 而且一个账号一天只能发送一个订阅消息.

QQ 订阅消息传递的结构体如下.
在传递Data这个字段的时候 微信是指定 对象里面的 key, QQ 是直接使用 订阅消息模板里面的关键字作为key

d:= map[string]interface{}{
      "touser":      "3B77F77EC13BA7243DD53BD4BF632CAD",
      "template_id": "8de968e05b257800a0833f36d16ca5f4",
      "page":        "index?env=s&type=signIn",
      "data": map[string]interface{}{
            "离线收益": map[string]interface{}{
                  "value": "未签到",
            },
            "通知日期": map[string]interface{}{
                  "value": sTime.Format("2006年01月02日"),
            },
            "通知时间": map[string]interface{}{
                  "value": "七日签到",
            },
            "温馨提示": map[string]interface{}{
                  "value": "累计签到获取奖励",
            },
      },
}
posted @ 2020-05-19 14:33  丫丫魏  阅读(320)  评论(0编辑  收藏  举报