1
0
mirror of https://github.com/silenceper/wechat.git synced 2026-02-04 12:52:27 +08:00
Files
wechat/miniprogram/subscribe/subscribe.go
markwang 8a810837a4 feat: 微信小程序-动态消息及订阅消息 (#835)
* feat: 微信小程序-动态消息及订阅消息

* feat: 微信小程序-动态消息及订阅消息

* feat: 微信小程序-动态消息及订阅消息
2025-06-18 16:16:43 +08:00

439 lines
14 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package subscribe
import (
"encoding/json"
"fmt"
"github.com/silenceper/wechat/v2/miniprogram/context"
"github.com/silenceper/wechat/v2/util"
)
const (
// 发送订阅消息
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html
subscribeSendURL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send"
// 获取当前帐号下的个人模板列表
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getTemplateList.html
getTemplateURL = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate"
// 添加订阅模板
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.addTemplate.html
addTemplateURL = "https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate"
// 删除私有模板
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.deleteTemplate.html
delTemplateURL = "https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate"
// 统一服务消息
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/uniform-message/uniformMessage.send.html
uniformMessageSend = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send"
// getCategoryURL 获取类目
getCategoryURL = "https://api.weixin.qq.com/wxaapi/newtmpl/getcategory?access_token=%s"
// getPubTemplateKeyWordsByIDURL 获取关键词列表
getPubTemplateKeyWordsByIDURL = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatekeywords?access_token=%s&tid=%s"
// getPubTemplateTitleListURL 获取所属类目下的公共模板
getPubTemplateTitleListURL = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatetitles?access_token=%s&ids=%s&start=%d&limit=%d"
// setUserNotifyURL 激活与更新服务卡片
setUserNotifyURL = "https://api.weixin.qq.com/wxa/set_user_notify?access_token=%s"
// setUserNotifyExtURL 更新服务卡片扩展信息
setUserNotifyExtURL = "https://api.weixin.qq.com/wxa/set_user_notifyext?access_token=%s"
// getUserNotifyURL 查询服务卡片状态
getUserNotifyURL = "https://api.weixin.qq.com/wxa/get_user_notify?access_token=%s"
)
// Subscribe 订阅消息
type Subscribe struct {
*context.Context
}
// NewSubscribe 实例化
func NewSubscribe(ctx *context.Context) *Subscribe {
return &Subscribe{Context: ctx}
}
// Message 订阅消息请求参数
type Message struct {
ToUser string `json:"touser"` // 必选,接收者(用户)的 openid
TemplateID string `json:"template_id"` // 必选所需下发的订阅模板id
Page string `json:"page"` // 可选,点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,示例index?foo=bar。该字段不填则模板无跳转。
Data map[string]*DataItem `json:"data"` // 必选, 模板内容
MiniprogramState string `json:"miniprogram_state"` // 可选跳转小程序类型developer为开发版trial为体验版formal为正式版默认为正式版
Lang string `json:"lang"` // 入小程序查看”的语言类型支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文)默认为zh_CN
}
// DataItem 模版内某个 .DATA 的值
type DataItem struct {
Value interface{} `json:"value"`
Color string `json:"color"`
}
// TemplateItem template item
type TemplateItem struct {
PriTmplID string `json:"priTmplId"`
Title string `json:"title"`
Content string `json:"content"`
Example string `json:"example"`
Type int64 `json:"type"`
KeywordEnumValueList []KeywordEnumValue `json:"keywordEnumValueList"`
}
// KeywordEnumValue 枚举参数值范围
type KeywordEnumValue struct {
EnumValueList []string `json:"enumValueList"`
KeywordCode string `json:"keywordCode"`
}
// TemplateList template list
type TemplateList struct {
util.CommonError
Data []TemplateItem `json:"data"`
}
// resTemplateSend 发送获取 msg id
type resTemplateSend struct {
util.CommonError
MsgID int64 `json:"msgid"`
}
// Send 发送订阅消息
func (s *Subscribe) Send(msg *Message) (err error) {
var accessToken string
accessToken, err = s.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", subscribeSendURL, accessToken)
response, err := util.PostJSON(uri, msg)
if err != nil {
return
}
return util.DecodeWithCommonError(response, "Send")
}
// SendGetMsgID 发送订阅消息返回 msgid
func (s *Subscribe) SendGetMsgID(msg *Message) (msgID int64, err error) {
var accessToken string
accessToken, err = s.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", subscribeSendURL, accessToken)
response, err := util.PostJSON(uri, msg)
if err != nil {
return
}
var result resTemplateSend
if err = json.Unmarshal(response, &result); err != nil {
return
}
if result.ErrCode != 0 {
err = fmt.Errorf("template msg send error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg)
return
}
msgID = result.MsgID
return
}
// ListTemplates 获取当前帐号下的个人模板列表
// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.getTemplateList.html
func (s *Subscribe) ListTemplates() (*TemplateList, error) {
accessToken, err := s.GetAccessToken()
if err != nil {
return nil, err
}
uri := fmt.Sprintf("%s?access_token=%s", getTemplateURL, accessToken)
response, err := util.HTTPGet(uri)
if err != nil {
return nil, err
}
templateList := TemplateList{}
err = util.DecodeWithError(response, &templateList, "ListTemplates")
if err != nil {
return nil, err
}
return &templateList, nil
}
// UniformMessage 统一服务消息
type UniformMessage struct {
ToUser string `json:"touser"`
WeappTemplateMsg struct {
TemplateID string `json:"template_id"`
Page string `json:"page"`
FormID string `json:"form_id"`
Data map[string]*DataItem `json:"data"`
EmphasisKeyword string `json:"emphasis_keyword"`
} `json:"weapp_template_msg"`
MpTemplateMsg struct {
Appid string `json:"appid"`
TemplateID string `json:"template_id"`
URL string `json:"url"`
Miniprogram struct {
Appid string `json:"appid"`
Pagepath string `json:"pagepath"`
} `json:"miniprogram"`
Data map[string]*DataItem `json:"data"`
} `json:"mp_template_msg"`
}
// UniformSend 发送统一服务消息
func (s *Subscribe) UniformSend(msg *UniformMessage) (err error) {
var accessToken string
accessToken, err = s.GetAccessToken()
if err != nil {
return
}
uri := fmt.Sprintf("%s?access_token=%s", uniformMessageSend, accessToken)
response, err := util.PostJSON(uri, msg)
if err != nil {
return
}
return util.DecodeWithCommonError(response, "UniformSend")
}
type resSubscribeAdd struct {
util.CommonError
TemplateID string `json:"priTmplId"`
}
// Add 添加订阅消息模板
func (s *Subscribe) Add(ShortID string, kidList []int, sceneDesc string) (templateID string, err error) {
var accessToken string
accessToken, err = s.GetAccessToken()
if err != nil {
return
}
var msg = struct {
TemplateIDShort string `json:"tid"`
SceneDesc string `json:"sceneDesc"`
KidList []int `json:"kidList"`
}{TemplateIDShort: ShortID, SceneDesc: sceneDesc, KidList: kidList}
uri := fmt.Sprintf("%s?access_token=%s", addTemplateURL, accessToken)
var response []byte
response, err = util.PostJSON(uri, msg)
if err != nil {
return
}
var result resSubscribeAdd
err = util.DecodeWithError(response, &result, "AddSubscribe")
return result.TemplateID, err
}
// Delete 删除私有模板
func (s *Subscribe) Delete(templateID string) (err error) {
var accessToken string
accessToken, err = s.GetAccessToken()
if err != nil {
return
}
var msg = struct {
TemplateID string `json:"priTmplId"`
}{TemplateID: templateID}
uri := fmt.Sprintf("%s?access_token=%s", delTemplateURL, accessToken)
var response []byte
response, err = util.PostJSON(uri, msg)
if err != nil {
return
}
return util.DecodeWithCommonError(response, "DeleteSubscribe")
}
// GetCategoryResponse 获取类目响应
type GetCategoryResponse struct {
util.CommonError
Data []Category `json:"data"`
}
// Category 类目
type Category struct {
ID int64 `json:"id"`
Name string `json:"name"`
}
// GetCategory 获取类目
// see https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/getCategory.html
func (s *Subscribe) GetCategory() ([]Category, error) {
var (
accessToken string
err error
)
if accessToken, err = s.GetAccessToken(); err != nil {
return nil, err
}
var response []byte
if response, err = util.HTTPGet(fmt.Sprintf(getCategoryURL, accessToken)); err != nil {
return nil, err
}
result := &GetCategoryResponse{}
err = util.DecodeWithError(response, result, "GetCategory")
return result.Data, err
}
// GetPubTemplateKeywordsByIDResponse 获取关键词列表响应
type GetPubTemplateKeywordsByIDResponse struct {
util.CommonError
Count int64 `json:"count"`
Data []PubTemplateKeywords `json:"data"`
}
// PubTemplateKeywords 关键词
type PubTemplateKeywords struct {
KID int64 `json:"kid"`
Name string `json:"name"`
Example string `json:"example"`
Rule string `json:"rule"`
}
// GetPubTemplateKeywordsByID 获取关键词列表
// see https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/getPubTemplateKeyWordsById.html
func (s *Subscribe) GetPubTemplateKeywordsByID(tid string) (*GetPubTemplateKeywordsByIDResponse, error) {
var (
accessToken string
err error
)
if accessToken, err = s.GetAccessToken(); err != nil {
return nil, err
}
var response []byte
if response, err = util.HTTPGet(fmt.Sprintf(getPubTemplateKeyWordsByIDURL, accessToken, tid)); err != nil {
return nil, err
}
result := &GetPubTemplateKeywordsByIDResponse{}
err = util.DecodeWithError(response, result, "GetPubTemplateKeywordsByID")
return result, err
}
// GetPubTemplateTitleListRequest 获取所属类目下的公共模板请求
type GetPubTemplateTitleListRequest struct {
Start int64
Limit int64
IDs string
}
// GetPubTemplateTitleListResponse 获取所属类目下的公共模板响应
type GetPubTemplateTitleListResponse struct {
util.CommonError
Count int64 `json:"count"`
Data []PubTemplateTitle `json:"data"`
}
// PubTemplateTitle 模板标题
type PubTemplateTitle struct {
Type int64 `json:"type"`
TID string `json:"tid"`
Title string `json:"title"`
CategoryID string `json:"categoryId"`
}
// GetPubTemplateTitleList 获取所属类目下的公共模板
// see https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/getPubTemplateTitleList.html
func (s *Subscribe) GetPubTemplateTitleList(req *GetPubTemplateTitleListRequest) (*GetPubTemplateTitleListResponse, error) {
var (
accessToken string
err error
)
if accessToken, err = s.GetAccessToken(); err != nil {
return nil, err
}
var response []byte
if response, err = util.HTTPGet(fmt.Sprintf(getPubTemplateTitleListURL, accessToken, req.IDs, req.Start, req.Limit)); err != nil {
return nil, err
}
result := &GetPubTemplateTitleListResponse{}
err = util.DecodeWithError(response, result, "GetPubTemplateTitleList")
return result, err
}
// SetUserNotifyRequest 激活与更新服务卡片请求
type SetUserNotifyRequest struct {
OpenID string `json:"openid"`
NotifyType int64 `json:"notify_type"`
NotifyCode string `json:"notify_code"`
ContentJSON string `json:"content_json"`
CheckJSON string `json:"check_json,omitempty"`
}
// SetUserNotify 激活与更新服务卡片
// see https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/setUserNotify.html
func (s *Subscribe) SetUserNotify(req *SetUserNotifyRequest) error {
var (
accessToken string
err error
)
if accessToken, err = s.GetAccessToken(); err != nil {
return err
}
var response []byte
if response, err = util.PostJSON(fmt.Sprintf(setUserNotifyURL, accessToken), req); err != nil {
return err
}
return util.DecodeWithCommonError(response, "SetUserNotify")
}
// SetUserNotifyExtRequest 更新服务卡片扩展信息请求
type SetUserNotifyExtRequest struct {
OpenID string `json:"openid"`
NotifyType int64 `json:"notify_type"`
NotifyCode string `json:"notify_code"`
ExtJSON string `json:"ext_json"`
}
// SetUserNotifyExt 更新服务卡片扩展信息
// see https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/setUserNotifyExt.html
func (s *Subscribe) SetUserNotifyExt(req *SetUserNotifyExtRequest) error {
var (
accessToken string
err error
)
if accessToken, err = s.GetAccessToken(); err != nil {
return err
}
var response []byte
if response, err = util.PostJSON(fmt.Sprintf(setUserNotifyExtURL, accessToken), req); err != nil {
return err
}
return util.DecodeWithCommonError(response, "SetUserNotifyExt")
}
// GetUserNotifyRequest 查询服务卡片状态请求
type GetUserNotifyRequest struct {
OpenID string `json:"openid"`
NotifyType int64 `json:"notify_type"`
NotifyCode string `json:"notify_code"`
}
// GetUserNotifyResponse 查询服务卡片状态响应
type GetUserNotifyResponse struct {
util.CommonError
NotifyInfo NotifyInfo `json:"notify_info"`
}
// NotifyInfo 卡片状态
type NotifyInfo struct {
NotifyType int64 `json:"notify_type"`
ContentJSON string `json:"content_json"`
CodeState int64 `json:"code_state"`
CodeExpireTime int64 `json:"code_expire_time"`
}
// GetUserNotify 查询服务卡片状态
// see https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/getUserNotify.html
func (s *Subscribe) GetUserNotify(req *GetUserNotifyRequest) (*GetUserNotifyResponse, error) {
var (
accessToken string
err error
)
if accessToken, err = s.GetAccessToken(); err != nil {
return nil, err
}
var response []byte
if response, err = util.PostJSON(fmt.Sprintf(getUserNotifyURL, accessToken), req); err != nil {
return nil, err
}
result := &GetUserNotifyResponse{}
err = util.DecodeWithError(response, result, "GetUserNotify")
return result, err
}