From b39be7fca2ffbfc2c2de2037b03f01d6a23d240f Mon Sep 17 00:00:00 2001 From: houseme Date: Wed, 11 Oct 2023 22:04:23 +0800 Subject: [PATCH 01/13] fix --- miniprogram/message/consts.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/miniprogram/message/consts.go b/miniprogram/message/consts.go index a044078..496bb9c 100644 --- a/miniprogram/message/consts.go +++ b/miniprogram/message/consts.go @@ -22,9 +22,9 @@ const ( MsgTypeMiniProgramPage = "miniprogrampage" // MsgTypeEvent 事件 MsgTypeEvent MsgType = "event" - // DataTypeXML XML格式数据 + // DataTypeXML XML 格式数据 DataTypeXML = "xml" - // DataTypeJSON JSON格式数据 + // DataTypeJSON JSON 格式数据 DataTypeJSON = "json" ) From b1f31feff63e5e1bdc4a7695f00ab9c8989ae345 Mon Sep 17 00:00:00 2001 From: Ralph Maas Date: Wed, 11 Oct 2023 09:06:49 -0500 Subject: [PATCH 02/13] =?UTF-8?q?feat(miniapp):=20=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E8=AE=A2=E9=98=85=E6=B6=88=E6=81=AF=20(#429)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 用户订阅消息服务端回调处理 2. 用户订阅消息订阅通知事件推送 Co-authored-by: houseme --- miniprogram/message/consts.go | 50 ++++++++- miniprogram/message/reply.go | 15 +++ miniprogram/miniprogram.go | 10 ++ miniprogram/server/server.go | 199 ++++++++++++++++++++++++++++++++++ miniprogram/server/util.go | 29 +++++ 5 files changed, 301 insertions(+), 2 deletions(-) create mode 100644 miniprogram/message/reply.go create mode 100644 miniprogram/server/server.go create mode 100644 miniprogram/server/util.go diff --git a/miniprogram/message/consts.go b/miniprogram/message/consts.go index 496bb9c..adaf215 100644 --- a/miniprogram/message/consts.go +++ b/miniprogram/message/consts.go @@ -28,11 +28,25 @@ const ( DataTypeJSON = "json" ) +const ( + //EventSubscribePopup 用户操作订阅通知弹窗事件推送,用户在图文等场景内订阅通知的操作 + EventSubscribePopup EventType = "subscribe_msg_popup_event" + //EventSubscribeChange 用户管理订阅通知,用户在服务通知管理页面做通知管理时的操作 + EventSubscribeChange = "subscribe_msg_change_event" + //EventSubscribeSent 发送订阅通知,调用 bizsend 接口发送通知 + EventSubscribeSent = "subscribe_msg_sent_event" +) + +const ( + InfoTypeAcceptSubscribeMessage InfoType = "accept" + InfoTypeRejectSubscribeMessage = "reject" +) + // CommonToken 消息中通用的结构 type CommonToken struct { XMLName xml.Name `xml:"xml"` - ToUserName string `xml:"ToUserName"` - FromUserName string `xml:"FromUserName"` + ToUserName CDATA `xml:"ToUserName"` + FromUserName CDATA `xml:"FromUserName"` CreateTime int64 `xml:"CreateTime"` MsgType MsgType `xml:"MsgType"` } @@ -60,4 +74,36 @@ type MiniProgramMixMessage struct { // 进入会话事件 Event string `xml:"Event"` SessionFrom string `xml:"SessionFrom"` + + // 用户操作订阅通知弹窗消息回调 + SubscribeMsgPopupEvent struct { + List []SubscribeMessageList `xml:"List"` + } `xml:"SubscribeMsgPopupEvent"` + + // 用户管理订阅通知回调 + SubscribeMsgChangeEvent struct { + List []SubscribeMessageList `xml:"List"` + } `xml:"SubscribeMsgChangeEvent"` + + // 用户发送订阅通知回调 + SubscribeMsgSentEvent struct { + List []SubscribeMessageList `xml:"List"` + } `xml:"SubscribeMsgSentEvent"` } + +// SubscribeMessageList 订阅消息事件列表 +type SubscribeMessageList struct { + TemplateId string `xml:"TemplateId"` + SubscribeStatusString string `xml:"SubscribeStatusString"` + PopupScene string `xml:"PopupScene"` +} + +// EncryptedXMLMsg 安全模式下的消息体 +type EncryptedXMLMsg struct { + XMLName struct{} `xml:"xml" json:"-"` + ToUserName string `xml:"ToUserName" json:"toUserName"` + EncryptedMsg string `xml:"Encrypt" json:"Encrypt"` +} + +// CDATA 使用这种类型,在序列化 xml 时文本会被解析器忽略 +type CDATA string diff --git a/miniprogram/message/reply.go b/miniprogram/message/reply.go new file mode 100644 index 0000000..04c21bc --- /dev/null +++ b/miniprogram/message/reply.go @@ -0,0 +1,15 @@ +package message + +import "errors" + +//ErrInvalidReply 无效的回复 +var ErrInvalidReply = errors.New("无效的回复信息") + +//ErrUnsupportedReply 不支持的回复类型 +var ErrUnsupportedReply = errors.New("不支持的回复消息") + +//Reply 消息回复 +type Reply struct { + MsgType MsgType + MsgData interface{} +} diff --git a/miniprogram/miniprogram.go b/miniprogram/miniprogram.go index f6adaee..15d0ed2 100644 --- a/miniprogram/miniprogram.go +++ b/miniprogram/miniprogram.go @@ -15,6 +15,7 @@ import ( "github.com/silenceper/wechat/v2/miniprogram/order" "github.com/silenceper/wechat/v2/miniprogram/privacy" "github.com/silenceper/wechat/v2/miniprogram/qrcode" + "github.com/silenceper/wechat/v2/miniprogram/server" "github.com/silenceper/wechat/v2/miniprogram/riskcontrol" "github.com/silenceper/wechat/v2/miniprogram/security" "github.com/silenceper/wechat/v2/miniprogram/shortlink" @@ -24,6 +25,7 @@ import ( "github.com/silenceper/wechat/v2/miniprogram/urlscheme" "github.com/silenceper/wechat/v2/miniprogram/virtualpayment" "github.com/silenceper/wechat/v2/miniprogram/werun" + "net/http" ) // MiniProgram 微信小程序相关 API @@ -101,6 +103,14 @@ func (miniProgram *MiniProgram) GetWeRun() *werun.WeRun { return werun.NewWeRun(miniProgram.ctx) } +// GetServer 小程序微信回调处理,接收事件,回复消息管理 +func (miniProgram *MiniProgram) GetServer(req *http.Request, write http.ResponseWriter) *server.Server { + srv := server.NewServer(miniProgram.ctx) + srv.Request = req + srv.Write = write + return srv +} + // GetContentSecurity 内容安全接口 func (miniProgram *MiniProgram) GetContentSecurity() *content.Content { return content.NewContent(miniProgram.ctx) diff --git a/miniprogram/server/server.go b/miniprogram/server/server.go new file mode 100644 index 0000000..8fdc307 --- /dev/null +++ b/miniprogram/server/server.go @@ -0,0 +1,199 @@ +package server + +import ( + "encoding/xml" + "errors" + "fmt" + "github.com/silenceper/wechat/v2/miniprogram/context" + "github.com/silenceper/wechat/v2/miniprogram/message" + "github.com/silenceper/wechat/v2/util" + "io/ioutil" + "net/http" + "reflect" + "runtime/debug" + "strconv" +) + +// Server struct +type Server struct { + *context.Context + Write http.ResponseWriter + Request *http.Request + skipValidate bool + openID string + + messageHandler func(mixMessage *message.MiniProgramMixMessage) *message.Reply + + RequestRawXMLMsg []byte + RequestMsg *message.MiniProgramMixMessage + + ResponseRawXMLMsg []byte + ResponseMsg interface{} + + isSafeMode bool + random []byte + nonce string + timestamp int64 +} + +func NewServer(context *context.Context) *Server { + srv := new(Server) + srv.Context = context + return srv +} + +func (srv *Server) Server() error { + if !srv.Validate() { + return fmt.Errorf("请求签名校验失败") + } + echoStr := srv.Query("echostr") + if echoStr != "" { + srv.SetResponseWrite(echoStr) + return nil + } + + response, err := srv.handleRequest() + if err != nil { + return err + } + + return srv.buildResponse(response) + +} + +// SkipValidate 设置跳过签名校验 +func (srv *Server) SkipValidate(skip bool) { + srv.skipValidate = skip +} + +// Validate 校验请求是否合法 +func (srv *Server) Validate() bool { + if srv.skipValidate { + return true + } + timestamp := srv.Query("timestamp") + nonce := srv.Query("nonce") + signature := srv.Query("signature") + return signature == util.Signature(srv.Token, timestamp, nonce) +} + +func (srv *Server) handleRequest() (reply *message.Reply, err error) { + //set isSafeMode + srv.isSafeMode = false + encryptType := srv.Query("encrypt_type") + if encryptType == "aes" { + srv.isSafeMode = true + } + //set openID + srv.openID = srv.Query("openid") + + var msg interface{} + msg, err = srv.getMessage() + if err != nil { + return + } + mixMessage, success := msg.(*message.MiniProgramMixMessage) + if !success { + err = errors.New("消息类型转换失败") + } + srv.RequestMsg = mixMessage + reply = srv.messageHandler(mixMessage) + return +} + +//GetOpenID return openID +func (srv *Server) GetOpenID() string { + return srv.openID +} + +func (srv *Server) getMessage() (interface{}, error) { + var rawXMLMsgBytes []byte + var err error + if srv.isSafeMode { + var encryptedXMLMsg message.EncryptedXMLMsg + if err := xml.NewDecoder(srv.Request.Body).Decode(&encryptedXMLMsg); err != nil { + return nil, fmt.Errorf("从body中解析xml失败,err=%v", err) + } + //验证消息签名 + timestamp := srv.Query("timestamp") + srv.timestamp, err = strconv.ParseInt(timestamp, 10, 32) + if err != nil { + return nil, err + } + nonce := srv.Query("nonce") + srv.nonce = nonce + msgSignature := srv.Query("msg_signature") + msgSignatureGen := util.Signature(srv.Token, timestamp, nonce, encryptedXMLMsg.EncryptedMsg) + if msgSignature != msgSignatureGen { + return nil, fmt.Errorf("消息不合法,验证签名失败") + } + + // 解密 + srv.random, rawXMLMsgBytes, err = util.DecryptMsg(srv.AppID, encryptedXMLMsg.EncryptedMsg, srv.EncodingAESKey) + if err != nil { + return nil, fmt.Errorf("消息解密失败, err=%v", err) + } + } else { + rawXMLMsgBytes, err = ioutil.ReadAll(srv.Request.Body) + if err != nil { + return nil, fmt.Errorf("从body中解析xml失败, err=%v", err) + } + } + srv.RequestRawXMLMsg = rawXMLMsgBytes + return srv.parseRequestMessage(rawXMLMsgBytes) +} + +func (srv *Server) parseRequestMessage(rawXMLMsgBytes []byte) (msg *message.MiniProgramMixMessage, err error) { + msg = &message.MiniProgramMixMessage{} + err = xml.Unmarshal(rawXMLMsgBytes, msg) + return +} + +func (srv *Server) SetMessageHandler(handler func(*message.MiniProgramMixMessage) *message.Reply) { + srv.messageHandler = handler +} + +func (srv *Server) buildResponse(reply *message.Reply) (err error) { + defer func() { + if e := recover(); e != nil { + err = fmt.Errorf("panic error: %v\n%s", e, debug.Stack()) + } + }() + if reply == nil { + return nil + } + msgType := reply.MsgType + switch msgType { + case message.MsgTypeEvent: + case message.MsgTypeImage: + case message.MsgTypeLink: + case message.MsgTypeText: + case message.MsgTypeMiniProgramPage: + default: + err = message.ErrUnsupportedReply + return + } + msgData := reply.MsgData + value := reflect.ValueOf(msgData) + //msgData must be a ptr + kind := value.Kind().String() + if kind != "ptr" { + return message.ErrUnsupportedReply + } + params := make([]reflect.Value, 1) + params[0] = reflect.ValueOf(srv.RequestMsg.FromUserName) + value.MethodByName("SetToUserName").Call(params) + + params[0] = reflect.ValueOf(srv.RequestMsg.ToUserName) + value.MethodByName("SetFromUserName").Call(params) + + params[0] = reflect.ValueOf(srv.RequestMsg.MsgType) + value.MethodByName("SetMsgType").Call(params) + + params[0] = reflect.ValueOf(util.GetCurrTS()) + value.MethodByName("SetCreateTime").Call(params) + + srv.ResponseMsg = msgData + srv.ResponseRawXMLMsg, err = xml.Marshal(msgData) + return +} diff --git a/miniprogram/server/util.go b/miniprogram/server/util.go new file mode 100644 index 0000000..d7de965 --- /dev/null +++ b/miniprogram/server/util.go @@ -0,0 +1,29 @@ +package server + +import "net/http" + +var textContentType = []string{"text/plain; charset=utf-8"} + +//Set http response Content-Type +func setContentType(w http.ResponseWriter, value []string) { + header := w.Header() + if val := header["Content-Type"]; len(val) == 0 { + header["Content-Type"] = value + } +} + +// Query 查询 URL query string +func (srv *Server) Query(key string) string { + req := srv.Request + return req.URL.Query().Get(key) +} + +// SetResponseWrite 设置回调返回值 +func (srv *Server) SetResponseWrite(str string) { + setContentType(srv.Write, textContentType) + srv.Write.WriteHeader(http.StatusOK) + _, err := srv.Write.Write([]byte(str)) + if err != nil { + panic(err) + } +} From 7371d1649f532a3045d9e398fe9cbdf07bd822f8 Mon Sep 17 00:00:00 2001 From: houseme Date: Wed, 11 Oct 2023 23:51:24 +0800 Subject: [PATCH 03/13] feat: improve subscribe msg --- miniprogram/message/consts.go | 50 +----- miniprogram/message/customer_message.go | 4 +- miniprogram/message/message.go | 124 +++++++++++---- miniprogram/message/reply.go | 6 +- miniprogram/miniprogram.go | 10 -- miniprogram/server/server.go | 199 ------------------------ miniprogram/server/util.go | 29 ---- 7 files changed, 103 insertions(+), 319 deletions(-) delete mode 100644 miniprogram/server/server.go delete mode 100644 miniprogram/server/util.go diff --git a/miniprogram/message/consts.go b/miniprogram/message/consts.go index adaf215..496bb9c 100644 --- a/miniprogram/message/consts.go +++ b/miniprogram/message/consts.go @@ -28,25 +28,11 @@ const ( DataTypeJSON = "json" ) -const ( - //EventSubscribePopup 用户操作订阅通知弹窗事件推送,用户在图文等场景内订阅通知的操作 - EventSubscribePopup EventType = "subscribe_msg_popup_event" - //EventSubscribeChange 用户管理订阅通知,用户在服务通知管理页面做通知管理时的操作 - EventSubscribeChange = "subscribe_msg_change_event" - //EventSubscribeSent 发送订阅通知,调用 bizsend 接口发送通知 - EventSubscribeSent = "subscribe_msg_sent_event" -) - -const ( - InfoTypeAcceptSubscribeMessage InfoType = "accept" - InfoTypeRejectSubscribeMessage = "reject" -) - // CommonToken 消息中通用的结构 type CommonToken struct { XMLName xml.Name `xml:"xml"` - ToUserName CDATA `xml:"ToUserName"` - FromUserName CDATA `xml:"FromUserName"` + ToUserName string `xml:"ToUserName"` + FromUserName string `xml:"FromUserName"` CreateTime int64 `xml:"CreateTime"` MsgType MsgType `xml:"MsgType"` } @@ -74,36 +60,4 @@ type MiniProgramMixMessage struct { // 进入会话事件 Event string `xml:"Event"` SessionFrom string `xml:"SessionFrom"` - - // 用户操作订阅通知弹窗消息回调 - SubscribeMsgPopupEvent struct { - List []SubscribeMessageList `xml:"List"` - } `xml:"SubscribeMsgPopupEvent"` - - // 用户管理订阅通知回调 - SubscribeMsgChangeEvent struct { - List []SubscribeMessageList `xml:"List"` - } `xml:"SubscribeMsgChangeEvent"` - - // 用户发送订阅通知回调 - SubscribeMsgSentEvent struct { - List []SubscribeMessageList `xml:"List"` - } `xml:"SubscribeMsgSentEvent"` } - -// SubscribeMessageList 订阅消息事件列表 -type SubscribeMessageList struct { - TemplateId string `xml:"TemplateId"` - SubscribeStatusString string `xml:"SubscribeStatusString"` - PopupScene string `xml:"PopupScene"` -} - -// EncryptedXMLMsg 安全模式下的消息体 -type EncryptedXMLMsg struct { - XMLName struct{} `xml:"xml" json:"-"` - ToUserName string `xml:"ToUserName" json:"toUserName"` - EncryptedMsg string `xml:"Encrypt" json:"Encrypt"` -} - -// CDATA 使用这种类型,在序列化 xml 时文本会被解析器忽略 -type CDATA string diff --git a/miniprogram/message/customer_message.go b/miniprogram/message/customer_message.go index ce3830f..0f0abd3 100644 --- a/miniprogram/message/customer_message.go +++ b/miniprogram/message/customer_message.go @@ -28,7 +28,7 @@ type MediaText struct { Content string `json:"content"` } -// MediaResource 消息使用的临时素材id +// MediaResource 消息使用的临时素材 id type MediaResource struct { MediaID string `json:"media_id"` } @@ -51,7 +51,7 @@ type MediaLink struct { // CustomerMessage 客服消息 type CustomerMessage struct { - ToUser string `json:"touser"` // 接受者OpenID + ToUser string `json:"touser"` // 接受者 OpenID Msgtype MsgType `json:"msgtype"` // 客服消息类型 Text *MediaText `json:"text,omitempty"` // 可选 Image *MediaResource `json:"image,omitempty"` // 可选 diff --git a/miniprogram/message/message.go b/miniprogram/message/message.go index 89e4b8d..3b6782d 100644 --- a/miniprogram/message/message.go +++ b/miniprogram/message/message.go @@ -18,11 +18,11 @@ import ( type ConfirmReceiveMethod int8 const ( - // EventTypeTradeManageRemindAccessAPI 提醒接入发货信息管理服务API + // EventTypeTradeManageRemindAccessAPI 提醒接入发货信息管理服务 API // 小程序完成账期授权时/小程序产生第一笔交易时/已产生交易但从未发货的小程序,每天一次 EventTypeTradeManageRemindAccessAPI EventType = "trade_manage_remind_access_api" // EventTypeTradeManageRemindShipping 提醒需要上传发货信息 - // 曾经发过货的小程序,订单超过48小时未发货时 + // 曾经发过货的小程序,订单超过 48 小时未发货时 EventTypeTradeManageRemindShipping EventType = "trade_manage_remind_shipping" // EventTypeTradeManageOrderSettlement 订单将要结算或已经结算 // 订单完成发货时/订单结算时 @@ -39,14 +39,27 @@ const ( EventTypeXpayGoodsDeliverNotify EventType = "xpay_goods_deliver_notify" // EventTypeXpayCoinPayNotify 代币支付推送事件 EventTypeXpayCoinPayNotify EventType = "xpay_coin_pay_notify" + // EventSubscribePopup 用户操作订阅通知弹窗事件推送,用户在图文等场景内订阅通知的操作 + EventSubscribePopup EventType = "subscribe_msg_popup_event" + // EventSubscribeMsgChange 用户管理订阅通知,用户在服务通知管理页面做通知管理时的操作 + EventSubscribeMsgChange EventType = "subscribe_msg_change_event" + // EventSubscribeMsgSent 发送订阅通知,调用 bizsend 接口发送通知 + EventSubscribeMsgSent EventType = "subscribe_msg_sent_event" // ConfirmReceiveMethodAuto 自动确认收货 ConfirmReceiveMethodAuto ConfirmReceiveMethod = 1 // ConfirmReceiveMethodManual 手动确认收货 ConfirmReceiveMethodManual ConfirmReceiveMethod = 2 ) +const ( + // InfoTypeAcceptSubscribeMessage 接受订阅通知 + InfoTypeAcceptSubscribeMessage InfoType = "accept" + // InfoTypeRejectSubscribeMessage 拒绝订阅通知 + InfoTypeRejectSubscribeMessage InfoType = "reject" +) + // PushReceiver 接收消息推送 -// 暂仅支付Aes加密方式 +// 暂仅支付 Aes 加密方式 type PushReceiver struct { *context.Context } @@ -58,16 +71,16 @@ func NewPushReceiver(ctx *context.Context) *PushReceiver { } } -// GetMsg 获取接收到的消息(如果是加密的返回解密数据) +// GetMsg 获取接收到的消息 (如果是加密的返回解密数据) func (receiver *PushReceiver) GetMsg(r *http.Request) (string, []byte, error) { // 判断请求格式 var dataType string contentType := r.Header.Get("Content-Type") if strings.HasPrefix(contentType, "text/xml") { - // xml格式 + // xml 格式 dataType = DataTypeXML } else { - // json格式 + // json 格式 dataType = DataTypeJSON } @@ -108,7 +121,7 @@ func (receiver *PushReceiver) GetMsg(r *http.Request) (string, []byte, error) { return dataType, byteData, err } -// GetMsgData 获取接收到的消息(解密数据) +// GetMsgData 获取接收到的消息 (解密数据) func (receiver *PushReceiver) GetMsgData(r *http.Request) (MsgType, EventType, PushData, error) { dataType, decryptMsg, err := receiver.GetMsg(r) if err != nil { @@ -144,7 +157,7 @@ func (receiver *PushReceiver) GetMsgData(r *http.Request) (MsgType, EventType, P func (receiver *PushReceiver) getEvent(dataType string, eventType EventType, decryptMsg []byte) (PushData, error) { switch eventType { case EventTypeTradeManageRemindAccessAPI: - // 提醒接入发货信息管理服务API + // 提醒接入发货信息管理服务 API var pushData PushDataRemindAccessAPI err := receiver.unmarshal(dataType, decryptMsg, &pushData) return &pushData, err @@ -188,6 +201,21 @@ func (receiver *PushReceiver) getEvent(dataType string, eventType EventType, dec var pushData PushDataXpayCoinPayNotify err := receiver.unmarshal(dataType, decryptMsg, &pushData) return &pushData, err + case EventSubscribePopup: + // 用户操作订阅通知弹窗事件推送 + var pushData PushDataSubscribePopup + err := receiver.unmarshal(dataType, decryptMsg, &pushData) + return &pushData, err + case EventSubscribeMsgChange: + // 用户管理订阅通知事件推送 + var pushData PushDataSubscribeMsgChange + err := receiver.unmarshal(dataType, decryptMsg, &pushData) + return &pushData, err + case EventSubscribeMsgSent: + // 用户发送订阅通知事件推送 + var pushData PushDataSubscribeMsgSent + err := receiver.unmarshal(dataType, decryptMsg, &pushData) + return &pushData, err } // 暂不支持其他事件类型,直接返回解密后的数据,由调用方处理 return decryptMsg, nil @@ -206,7 +234,7 @@ type DataReceived struct { Encrypt string `json:"Encrypt" xml:"Encrypt"` // 加密的消息体 } -// PushData 推送的数据(已转对应的结构体) +// PushData 推送的数据 (已转对应的结构体) type PushData interface{} // CommonPushData 推送数据通用部分 @@ -216,7 +244,7 @@ type CommonPushData struct { Event EventType `json:"Event" xml:"Event"` // 事件类型 ToUserName string `json:"ToUserName" xml:"ToUserName"` // 小程序的原始 ID FromUserName string `json:"FromUserName" xml:"FromUserName"` // 发送方账号(一个 OpenID,此时发送方是系统账号) - CreateTime int64 `json:"CreateTime" xml:"CreateTime"` // 消息创建时间 (整型),时间戳 + CreateTime int64 `json:"CreateTime" xml:"CreateTime"` // 消息创建时间(整型),时间戳 } // MediaCheckAsyncData 媒体内容安全异步审查结果通知 @@ -272,7 +300,7 @@ type PushDataRemindShipping struct { Msg string `json:"msg" xml:"msg"` // 消息文本内容 } -// PushDataRemindAccessAPI 提醒接入发货信息管理服务API信息 +// PushDataRemindAccessAPI 提醒接入发货信息管理服务 API 信息 type PushDataRemindAccessAPI struct { CommonPushData Msg string `json:"msg" xml:"msg"` // 消息文本内容 @@ -281,9 +309,9 @@ type PushDataRemindAccessAPI struct { // PushDataAddExpressPath 运单轨迹更新信息 type PushDataAddExpressPath struct { CommonPushData - DeliveryID string `json:"DeliveryID" xml:"DeliveryID"` // 快递公司ID - WayBillID string `json:"WaybillId" xml:"WaybillId"` // 运单ID - OrderID string `json:"OrderId" xml:"OrderId"` // 订单ID + DeliveryID string `json:"DeliveryID" xml:"DeliveryID"` // 快递公司 ID + WayBillID string `json:"WaybillId" xml:"WaybillId"` // 运单 ID + OrderID string `json:"OrderId" xml:"OrderId"` // 订单 ID Version int `json:"Version" xml:"Version"` // 轨迹版本号(整型) Count int `json:"Count" xml:"Count"` // 轨迹节点数(整型) Actions []*PushDataAddExpressPathAction `json:"Actions" xml:"Actions"` // 轨迹节点列表 @@ -304,10 +332,10 @@ type PushDataSecVodUpload struct { // SecVodUploadEvent 短剧媒资上传完成事件 type SecVodUploadEvent struct { - MediaID string `json:"media_id" xml:"media_id"` // 媒资id + MediaID string `json:"media_id" xml:"media_id"` // 媒资 id SourceContext string `json:"source_context" xml:"source_context"` // 透传上传接口中开发者设置的值。 - Errcode int `json:"errcode" xml:"errcode"` // 错误码,上传失败时该值非 - Errmsg string `json:"errmsg" xml:"errmsg"` // 错误提示 + ErrCode int `json:"errcode" xml:"errcode"` // 错误码,上传失败时该值非 + ErrMsg string `json:"errmsg" xml:"errmsg"` // 错误提示 } // PushDataSecVodAudit 短剧媒资审核状态 @@ -318,14 +346,14 @@ type PushDataSecVodAudit struct { // SecVodAuditEvent 短剧媒资审核状态事件 type SecVodAuditEvent struct { - DramaID string `json:"drama_id" xml:"drama_id"` // 剧目id + DramaID string `json:"drama_id" xml:"drama_id"` // 剧目 id SourceContext string `json:"source_context" xml:"source_context"` // 透传上传接口中开发者设置的值 - AuditDetail DramaAuditDetail `json:"audit_detail" xml:"audit_detail"` // 剧目审核结果,单独每一集的审核结果可以根据drama_id查询剧集详情得到 + AuditDetail DramaAuditDetail `json:"audit_detail" xml:"audit_detail"` // 剧目审核结果,单独每一集的审核结果可以根据 drama_id 查询剧集详情得到 } // DramaAuditDetail 剧目审核结果 type DramaAuditDetail struct { - Status int `json:"status" xml:"status"` // 审核状态,0为无效值;1为审核中;2为最终失败;3为审核通过;4为驳回重填 + Status int `json:"status" xml:"status"` // 审核状态,0 为无效值;1 为审核中;2 为最终失败;3 为审核通过;4 为驳回重填 CreateTime int64 `json:"create_time" xml:"create_time"` // 提审时间戳 AuditTime int64 `json:"audit_time" xml:"audit_time"` // 审核时间戳 } @@ -333,9 +361,9 @@ type DramaAuditDetail struct { // PushDataXpayGoodsDeliverNotify 道具发货推送 type PushDataXpayGoodsDeliverNotify struct { CommonPushData - OpenID string `json:"OpenId" xml:"OpenId"` // 用户openid + OpenID string `json:"OpenId" xml:"OpenId"` // 用户 openid OutTradeNo string `json:"OutTradeNo" xml:"OutTradeNo"` // 业务订单号 - Env int `json:"Env" xml:"Env"` //,环境配置 0:现网环境(也叫正式环境)1:沙箱环境 + Env int `json:"Env" xml:"Env"` // ,环境配置 0:现网环境(也叫正式环境)1:沙箱环境 WeChatPayInfo WeChatPayInfo `json:"WeChatPayInfo" xml:"WeChatPayInfo"` // 微信支付信息 非微信支付渠道可能没有 GoodsInfo GoodsInfo `json:"GoodsInfo" xml:"GoodsInfo"` // 道具参数信息 } @@ -344,14 +372,14 @@ type PushDataXpayGoodsDeliverNotify struct { type WeChatPayInfo struct { MchOrderNo string `json:"MchOrderNo" xml:"MchOrderNo"` // 微信支付商户单号 TransactionID string `json:"TransactionId" xml:"TransactionId"` // 交易单号(微信支付订单号) - PaidTime int64 `json:"PaidTime" xml:"PaidTime"` // 用户支付时间,Linux秒级时间戳 + PaidTime int64 `json:"PaidTime" xml:"PaidTime"` // 用户支付时间,Linux 秒级时间戳 } // GoodsInfo 道具参数信息 type GoodsInfo struct { - ProductID string `json:"ProductId" xml:"ProductId"` // 道具ID + ProductID string `json:"ProductId" xml:"ProductId"` // 道具 ID Quantity int `json:"Quantity" xml:"Quantity"` // 数量 - OrigPrice int64 `json:"OrigPrice" xml:"OrigPrice"` // 物品原始价格 (单位:分) + OrigPrice int64 `json:"OrigPrice" xml:"OrigPrice"` // 物品原始价格(单位:分) ActualPrice int64 `json:"ActualPrice" xml:"ActualPrice"` // 物品实际支付价格(单位:分) Attach string `json:"Attach" xml:"Attach"` // 透传信息 } @@ -359,9 +387,9 @@ type GoodsInfo struct { // PushDataXpayCoinPayNotify 代币支付推送 type PushDataXpayCoinPayNotify struct { CommonPushData - OpenID string `json:"OpenId" xml:"OpenId"` // 用户openid + OpenID string `json:"OpenId" xml:"OpenId"` // 用户 openid OutTradeNo string `json:"OutTradeNo" xml:"OutTradeNo"` // 业务订单号 - Env int `json:"Env" xml:"Env"` //,环境配置 0:现网环境(也叫正式环境)1:沙箱环境 + Env int `json:"Env" xml:"Env"` // ,环境配置 0:现网环境(也叫正式环境)1:沙箱环境 WeChatPayInfo WeChatPayInfo `json:"WeChatPayInfo" xml:"WeChatPayInfo"` // 微信支付信息 非微信支付渠道可能没有 CoinInfo CoinInfo `json:"CoinInfo" xml:"CoinInfo"` // 代币参数信息 } @@ -369,7 +397,47 @@ type PushDataXpayCoinPayNotify struct { // CoinInfo 代币参数信息 type CoinInfo struct { Quantity int `json:"Quantity" xml:"Quantity"` // 数量 - OrigPrice int64 `json:"OrigPrice" xml:"OrigPrice"` // 物品原始价格 (单位:分) + OrigPrice int64 `json:"OrigPrice" xml:"OrigPrice"` // 物品原始价格(单位:分) ActualPrice int64 `json:"ActualPrice" xml:"ActualPrice"` // 物品实际支付价格(单位:分) Attach string `json:"Attach" xml:"Attach"` // 透传信息 } + +// PushDataSubscribePopup 用户操作订阅通知弹窗事件推送 +type PushDataSubscribePopup struct { + CommonPushData + SubscribeMsgPopupEvent SubscribeMsgPopupEvent `json:"SubscribeMsgPopup" xml:"SubscribeMsgPopup"` // 用户操作订阅通知弹窗消息回调 +} + +// SubscribeMsgPopupEvent 用户操作订阅通知弹窗消息回调 +type SubscribeMsgPopupEvent struct { + List []SubscribeMessageList `xml:"List" json:"List"` +} + +// PushDataSubscribeMsgChange 用户管理订阅通知事件推送 +type PushDataSubscribeMsgChange struct { + CommonPushData + SubscribeMsgChangeEvent SubscribeMsgChangeEvent `json:"SubscribeMsgChangeEvent" xml:"SubscribeMsgChangeEvent"` // 用户管理订阅通知回调 +} + +// SubscribeMsgChangeEvent 用户管理订阅通知回调 +type SubscribeMsgChangeEvent struct { + List []SubscribeMessageList `xml:"List" json:"List"` +} + +// PushDataSubscribeMsgSent 用户发送订阅通知事件推送 +type PushDataSubscribeMsgSent struct { + CommonPushData + SubscribeMsgSentEvent SubscribeMsgSentEvent `json:"SubscribeMsgSentEvent" xml:"SubscribeMsgSentEvent"` // 用户发送订阅通知回调 +} + +// SubscribeMsgSentEvent 用户发送订阅通知回调 +type SubscribeMsgSentEvent struct { + List []SubscribeMessageList `xml:"List"` +} + +// SubscribeMessageList 订阅消息事件列表 +type SubscribeMessageList struct { + TemplateID string `xml:"TemplateId" json:"TemplateId"` + SubscribeStatusString string `xml:"SubscribeStatusString" json:"SubscribeStatusString"` + PopupScene string `xml:"PopupScene" json:"PopupScene"` +} diff --git a/miniprogram/message/reply.go b/miniprogram/message/reply.go index 04c21bc..b7fba06 100644 --- a/miniprogram/message/reply.go +++ b/miniprogram/message/reply.go @@ -2,13 +2,13 @@ package message import "errors" -//ErrInvalidReply 无效的回复 +// ErrInvalidReply 无效的回复 var ErrInvalidReply = errors.New("无效的回复信息") -//ErrUnsupportedReply 不支持的回复类型 +// ErrUnsupportedReply 不支持的回复类型 var ErrUnsupportedReply = errors.New("不支持的回复消息") -//Reply 消息回复 +// Reply 消息回复 type Reply struct { MsgType MsgType MsgData interface{} diff --git a/miniprogram/miniprogram.go b/miniprogram/miniprogram.go index 15d0ed2..f6adaee 100644 --- a/miniprogram/miniprogram.go +++ b/miniprogram/miniprogram.go @@ -15,7 +15,6 @@ import ( "github.com/silenceper/wechat/v2/miniprogram/order" "github.com/silenceper/wechat/v2/miniprogram/privacy" "github.com/silenceper/wechat/v2/miniprogram/qrcode" - "github.com/silenceper/wechat/v2/miniprogram/server" "github.com/silenceper/wechat/v2/miniprogram/riskcontrol" "github.com/silenceper/wechat/v2/miniprogram/security" "github.com/silenceper/wechat/v2/miniprogram/shortlink" @@ -25,7 +24,6 @@ import ( "github.com/silenceper/wechat/v2/miniprogram/urlscheme" "github.com/silenceper/wechat/v2/miniprogram/virtualpayment" "github.com/silenceper/wechat/v2/miniprogram/werun" - "net/http" ) // MiniProgram 微信小程序相关 API @@ -103,14 +101,6 @@ func (miniProgram *MiniProgram) GetWeRun() *werun.WeRun { return werun.NewWeRun(miniProgram.ctx) } -// GetServer 小程序微信回调处理,接收事件,回复消息管理 -func (miniProgram *MiniProgram) GetServer(req *http.Request, write http.ResponseWriter) *server.Server { - srv := server.NewServer(miniProgram.ctx) - srv.Request = req - srv.Write = write - return srv -} - // GetContentSecurity 内容安全接口 func (miniProgram *MiniProgram) GetContentSecurity() *content.Content { return content.NewContent(miniProgram.ctx) diff --git a/miniprogram/server/server.go b/miniprogram/server/server.go deleted file mode 100644 index 8fdc307..0000000 --- a/miniprogram/server/server.go +++ /dev/null @@ -1,199 +0,0 @@ -package server - -import ( - "encoding/xml" - "errors" - "fmt" - "github.com/silenceper/wechat/v2/miniprogram/context" - "github.com/silenceper/wechat/v2/miniprogram/message" - "github.com/silenceper/wechat/v2/util" - "io/ioutil" - "net/http" - "reflect" - "runtime/debug" - "strconv" -) - -// Server struct -type Server struct { - *context.Context - Write http.ResponseWriter - Request *http.Request - skipValidate bool - openID string - - messageHandler func(mixMessage *message.MiniProgramMixMessage) *message.Reply - - RequestRawXMLMsg []byte - RequestMsg *message.MiniProgramMixMessage - - ResponseRawXMLMsg []byte - ResponseMsg interface{} - - isSafeMode bool - random []byte - nonce string - timestamp int64 -} - -func NewServer(context *context.Context) *Server { - srv := new(Server) - srv.Context = context - return srv -} - -func (srv *Server) Server() error { - if !srv.Validate() { - return fmt.Errorf("请求签名校验失败") - } - echoStr := srv.Query("echostr") - if echoStr != "" { - srv.SetResponseWrite(echoStr) - return nil - } - - response, err := srv.handleRequest() - if err != nil { - return err - } - - return srv.buildResponse(response) - -} - -// SkipValidate 设置跳过签名校验 -func (srv *Server) SkipValidate(skip bool) { - srv.skipValidate = skip -} - -// Validate 校验请求是否合法 -func (srv *Server) Validate() bool { - if srv.skipValidate { - return true - } - timestamp := srv.Query("timestamp") - nonce := srv.Query("nonce") - signature := srv.Query("signature") - return signature == util.Signature(srv.Token, timestamp, nonce) -} - -func (srv *Server) handleRequest() (reply *message.Reply, err error) { - //set isSafeMode - srv.isSafeMode = false - encryptType := srv.Query("encrypt_type") - if encryptType == "aes" { - srv.isSafeMode = true - } - //set openID - srv.openID = srv.Query("openid") - - var msg interface{} - msg, err = srv.getMessage() - if err != nil { - return - } - mixMessage, success := msg.(*message.MiniProgramMixMessage) - if !success { - err = errors.New("消息类型转换失败") - } - srv.RequestMsg = mixMessage - reply = srv.messageHandler(mixMessage) - return -} - -//GetOpenID return openID -func (srv *Server) GetOpenID() string { - return srv.openID -} - -func (srv *Server) getMessage() (interface{}, error) { - var rawXMLMsgBytes []byte - var err error - if srv.isSafeMode { - var encryptedXMLMsg message.EncryptedXMLMsg - if err := xml.NewDecoder(srv.Request.Body).Decode(&encryptedXMLMsg); err != nil { - return nil, fmt.Errorf("从body中解析xml失败,err=%v", err) - } - //验证消息签名 - timestamp := srv.Query("timestamp") - srv.timestamp, err = strconv.ParseInt(timestamp, 10, 32) - if err != nil { - return nil, err - } - nonce := srv.Query("nonce") - srv.nonce = nonce - msgSignature := srv.Query("msg_signature") - msgSignatureGen := util.Signature(srv.Token, timestamp, nonce, encryptedXMLMsg.EncryptedMsg) - if msgSignature != msgSignatureGen { - return nil, fmt.Errorf("消息不合法,验证签名失败") - } - - // 解密 - srv.random, rawXMLMsgBytes, err = util.DecryptMsg(srv.AppID, encryptedXMLMsg.EncryptedMsg, srv.EncodingAESKey) - if err != nil { - return nil, fmt.Errorf("消息解密失败, err=%v", err) - } - } else { - rawXMLMsgBytes, err = ioutil.ReadAll(srv.Request.Body) - if err != nil { - return nil, fmt.Errorf("从body中解析xml失败, err=%v", err) - } - } - srv.RequestRawXMLMsg = rawXMLMsgBytes - return srv.parseRequestMessage(rawXMLMsgBytes) -} - -func (srv *Server) parseRequestMessage(rawXMLMsgBytes []byte) (msg *message.MiniProgramMixMessage, err error) { - msg = &message.MiniProgramMixMessage{} - err = xml.Unmarshal(rawXMLMsgBytes, msg) - return -} - -func (srv *Server) SetMessageHandler(handler func(*message.MiniProgramMixMessage) *message.Reply) { - srv.messageHandler = handler -} - -func (srv *Server) buildResponse(reply *message.Reply) (err error) { - defer func() { - if e := recover(); e != nil { - err = fmt.Errorf("panic error: %v\n%s", e, debug.Stack()) - } - }() - if reply == nil { - return nil - } - msgType := reply.MsgType - switch msgType { - case message.MsgTypeEvent: - case message.MsgTypeImage: - case message.MsgTypeLink: - case message.MsgTypeText: - case message.MsgTypeMiniProgramPage: - default: - err = message.ErrUnsupportedReply - return - } - msgData := reply.MsgData - value := reflect.ValueOf(msgData) - //msgData must be a ptr - kind := value.Kind().String() - if kind != "ptr" { - return message.ErrUnsupportedReply - } - params := make([]reflect.Value, 1) - params[0] = reflect.ValueOf(srv.RequestMsg.FromUserName) - value.MethodByName("SetToUserName").Call(params) - - params[0] = reflect.ValueOf(srv.RequestMsg.ToUserName) - value.MethodByName("SetFromUserName").Call(params) - - params[0] = reflect.ValueOf(srv.RequestMsg.MsgType) - value.MethodByName("SetMsgType").Call(params) - - params[0] = reflect.ValueOf(util.GetCurrTS()) - value.MethodByName("SetCreateTime").Call(params) - - srv.ResponseMsg = msgData - srv.ResponseRawXMLMsg, err = xml.Marshal(msgData) - return -} diff --git a/miniprogram/server/util.go b/miniprogram/server/util.go deleted file mode 100644 index d7de965..0000000 --- a/miniprogram/server/util.go +++ /dev/null @@ -1,29 +0,0 @@ -package server - -import "net/http" - -var textContentType = []string{"text/plain; charset=utf-8"} - -//Set http response Content-Type -func setContentType(w http.ResponseWriter, value []string) { - header := w.Header() - if val := header["Content-Type"]; len(val) == 0 { - header["Content-Type"] = value - } -} - -// Query 查询 URL query string -func (srv *Server) Query(key string) string { - req := srv.Request - return req.URL.Query().Get(key) -} - -// SetResponseWrite 设置回调返回值 -func (srv *Server) SetResponseWrite(str string) { - setContentType(srv.Write, textContentType) - srv.Write.WriteHeader(http.StatusOK) - _, err := srv.Write.Write([]byte(str)) - if err != nil { - panic(err) - } -} From b3e2d624d9dd72a0b8ad8649cac1af98336f4d2a Mon Sep 17 00:00:00 2001 From: houseme Date: Wed, 11 Oct 2023 23:54:40 +0800 Subject: [PATCH 04/13] feat: add v1.21 and feature branch --- .github/workflows/go.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index b8d591f..c66040b 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -2,15 +2,15 @@ name: Go on: push: - branches: [ master,release-*,v2 ] + branches: [ master,release-*,v2,feature/** ] pull_request: - branches: [ master,release-*,v2 ] + branches: [ master,release-*,v2,feature/** ] jobs: golangci: strategy: matrix: - go-version: [ '1.16','1.17','1.18','1.19','1.20' ] + go-version: [ '1.16','1.17','1.18','1.19','1.20','1.21' ] name: golangci-lint runs-on: ubuntu-latest steps: @@ -42,7 +42,7 @@ jobs: # strategy set strategy: matrix: - go: [ '1.16','1.17','1.18','1.19','1.20' ] + go: [ '1.16','1.17','1.18','1.19','1.20','1.21' ] steps: - uses: actions/checkout@v3 From d214617446b4e6362381b9bcd0251ca0978da7c7 Mon Sep 17 00:00:00 2001 From: houseme Date: Thu, 12 Oct 2023 13:29:01 +0800 Subject: [PATCH 05/13] feat: improve code for subscribe --- miniprogram/message/message.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/miniprogram/message/message.go b/miniprogram/message/message.go index 3b6782d..285702b 100644 --- a/miniprogram/message/message.go +++ b/miniprogram/message/message.go @@ -405,7 +405,8 @@ type CoinInfo struct { // PushDataSubscribePopup 用户操作订阅通知弹窗事件推送 type PushDataSubscribePopup struct { CommonPushData - SubscribeMsgPopupEvent SubscribeMsgPopupEvent `json:"SubscribeMsgPopup" xml:"SubscribeMsgPopup"` // 用户操作订阅通知弹窗消息回调 + SubscribeMsgPopupEvent SubscribeMsgPopupEvent `json:"-" xml:"SubscribeMsgPopup"` // 用户操作订阅通知弹窗消息回调 + List []SubscribeMessageList `xml:"SubscribeMsgPopup>List" json:"List"` } // SubscribeMsgPopupEvent 用户操作订阅通知弹窗消息回调 @@ -416,7 +417,8 @@ type SubscribeMsgPopupEvent struct { // PushDataSubscribeMsgChange 用户管理订阅通知事件推送 type PushDataSubscribeMsgChange struct { CommonPushData - SubscribeMsgChangeEvent SubscribeMsgChangeEvent `json:"SubscribeMsgChangeEvent" xml:"SubscribeMsgChangeEvent"` // 用户管理订阅通知回调 + SubscribeMsgChangeEvent SubscribeMsgChangeEvent `json:"-" xml:"SubscribeMsgChangeEvent"` // 用户管理订阅通知回调 + List []SubscribeMessageList `xml:"SubscribeMsgChangeEvent>List" json:"List"` } // SubscribeMsgChangeEvent 用户管理订阅通知回调 @@ -427,12 +429,13 @@ type SubscribeMsgChangeEvent struct { // PushDataSubscribeMsgSent 用户发送订阅通知事件推送 type PushDataSubscribeMsgSent struct { CommonPushData - SubscribeMsgSentEvent SubscribeMsgSentEvent `json:"SubscribeMsgSentEvent" xml:"SubscribeMsgSentEvent"` // 用户发送订阅通知回调 + SubscribeMsgSentEvent SubscribeMsgSentEvent `json:"-" xml:"SubscribeMsgSentEvent"` // 用户发送订阅通知回调 + List []SubscribeMessageList `xml:"SubscribeMsgSentEvent>List" json:"List"` } // SubscribeMsgSentEvent 用户发送订阅通知回调 type SubscribeMsgSentEvent struct { - List []SubscribeMessageList `xml:"List"` + List []SubscribeMessageList `xml:"List" json:"List"` } // SubscribeMessageList 订阅消息事件列表 From 12dfb7051afb2e86375aa1d711c14df7e8ff4830 Mon Sep 17 00:00:00 2001 From: houseme Date: Sat, 14 Oct 2023 00:54:18 +0800 Subject: [PATCH 06/13] test --- miniprogram/message/message.go | 39 ++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/miniprogram/message/message.go b/miniprogram/message/message.go index 285702b..e577bb2 100644 --- a/miniprogram/message/message.go +++ b/miniprogram/message/message.go @@ -405,42 +405,53 @@ type CoinInfo struct { // PushDataSubscribePopup 用户操作订阅通知弹窗事件推送 type PushDataSubscribePopup struct { CommonPushData - SubscribeMsgPopupEvent SubscribeMsgPopupEvent `json:"-" xml:"SubscribeMsgPopup"` // 用户操作订阅通知弹窗消息回调 - List []SubscribeMessageList `xml:"SubscribeMsgPopup>List" json:"List"` + List string `xml:"SubscribeMsgPopupEvent>List" json:"List"` } // SubscribeMsgPopupEvent 用户操作订阅通知弹窗消息回调 type SubscribeMsgPopupEvent struct { - List []SubscribeMessageList `xml:"List" json:"List"` + List []SubscribeMsgPopupEventList `xml:"List" json:"List"` +} + +// SubscribeMsgPopupEventList 订阅消息事件列表 +type SubscribeMsgPopupEventList struct { + TemplateID string `xml:"TemplateId" json:"TemplateId"` + SubscribeStatusString string `xml:"SubscribeStatusString" json:"SubscribeStatusString"` + PopupScene string `xml:"PopupScene" json:"PopupScene"` } // PushDataSubscribeMsgChange 用户管理订阅通知事件推送 type PushDataSubscribeMsgChange struct { CommonPushData - SubscribeMsgChangeEvent SubscribeMsgChangeEvent `json:"-" xml:"SubscribeMsgChangeEvent"` // 用户管理订阅通知回调 - List []SubscribeMessageList `xml:"SubscribeMsgChangeEvent>List" json:"List"` + List []SubscribeMsgChangeList `xml:"SubscribeMsgChangeEvent>List" json:"List"` } // SubscribeMsgChangeEvent 用户管理订阅通知回调 type SubscribeMsgChangeEvent struct { - List []SubscribeMessageList `xml:"List" json:"List"` + List []SubscribeMsgChangeList `xml:"List" json:"List"` +} + +// SubscribeMsgChangeList 订阅消息事件列表 +type SubscribeMsgChangeList struct { + TemplateID string `xml:"TemplateId" json:"TemplateId"` + SubscribeStatusString string `xml:"SubscribeStatusString" json:"SubscribeStatusString"` } // PushDataSubscribeMsgSent 用户发送订阅通知事件推送 type PushDataSubscribeMsgSent struct { CommonPushData - SubscribeMsgSentEvent SubscribeMsgSentEvent `json:"-" xml:"SubscribeMsgSentEvent"` // 用户发送订阅通知回调 - List []SubscribeMessageList `xml:"SubscribeMsgSentEvent>List" json:"List"` + List []SubscribeMsgSentEventList `xml:"SubscribeMsgSentEvent>List" json:"List"` } // SubscribeMsgSentEvent 用户发送订阅通知回调 type SubscribeMsgSentEvent struct { - List []SubscribeMessageList `xml:"List" json:"List"` + List []SubscribeMsgSentEventList `xml:"List" json:"List"` } -// SubscribeMessageList 订阅消息事件列表 -type SubscribeMessageList struct { - TemplateID string `xml:"TemplateId" json:"TemplateId"` - SubscribeStatusString string `xml:"SubscribeStatusString" json:"SubscribeStatusString"` - PopupScene string `xml:"PopupScene" json:"PopupScene"` +// SubscribeMsgSentEventList 订阅消息事件列表 +type SubscribeMsgSentEventList struct { + TemplateID string `xml:"TemplateId" json:"TemplateId"` + MsgID string `xml:"MsgID" json:"MsgID"` + ErrorCode int `xml:"ErrorCode" json:"ErrorCode"` + ErrorStatus string `xml:"ErrorStatus" json:"ErrorStatus"` } From ef4e3a0a3e1acc74b74b68ca4f08278468778f52 Mon Sep 17 00:00:00 2001 From: houseme Date: Sat, 14 Oct 2023 00:59:32 +0800 Subject: [PATCH 07/13] test --- miniprogram/message/message.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/miniprogram/message/message.go b/miniprogram/message/message.go index e577bb2..0f8c93d 100644 --- a/miniprogram/message/message.go +++ b/miniprogram/message/message.go @@ -4,6 +4,7 @@ import ( "encoding/json" "encoding/xml" "errors" + "fmt" "io" "net/http" "sort" @@ -127,6 +128,7 @@ func (receiver *PushReceiver) GetMsgData(r *http.Request) (MsgType, EventType, P if err != nil { return "", "", nil, err } + fmt.Println("decryptMsg string", string(decryptMsg)) var ( msgType MsgType eventType EventType From 9e54a6a27b4a1dc18bb0591c6f1df62170f44ff2 Mon Sep 17 00:00:00 2001 From: houseme Date: Sat, 14 Oct 2023 01:05:01 +0800 Subject: [PATCH 08/13] fix --- miniprogram/message/message.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/miniprogram/message/message.go b/miniprogram/message/message.go index 0f8c93d..c58ac9d 100644 --- a/miniprogram/message/message.go +++ b/miniprogram/message/message.go @@ -4,12 +4,13 @@ import ( "encoding/json" "encoding/xml" "errors" - "fmt" "io" "net/http" "sort" "strings" + log "github.com/sirupsen/logrus" + "github.com/silenceper/wechat/v2/miniprogram/context" "github.com/silenceper/wechat/v2/miniprogram/security" "github.com/silenceper/wechat/v2/util" @@ -128,7 +129,7 @@ func (receiver *PushReceiver) GetMsgData(r *http.Request) (MsgType, EventType, P if err != nil { return "", "", nil, err } - fmt.Println("decryptMsg string", string(decryptMsg)) + log.Debug("decryptMsg string", string(decryptMsg)) var ( msgType MsgType eventType EventType @@ -407,7 +408,7 @@ type CoinInfo struct { // PushDataSubscribePopup 用户操作订阅通知弹窗事件推送 type PushDataSubscribePopup struct { CommonPushData - List string `xml:"SubscribeMsgPopupEvent>List" json:"List"` + List []SubscribeMsgPopupEventList `xml:"SubscribeMsgPopupEvent>List" json:"List"` } // SubscribeMsgPopupEvent 用户操作订阅通知弹窗消息回调 From 356fe3df4429e0b2e9127bcb407222b5d2160548 Mon Sep 17 00:00:00 2001 From: houseme Date: Sat, 14 Oct 2023 01:11:35 +0800 Subject: [PATCH 09/13] fix --- miniprogram/message/message.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/miniprogram/message/message.go b/miniprogram/message/message.go index c58ac9d..82a1cee 100644 --- a/miniprogram/message/message.go +++ b/miniprogram/message/message.go @@ -9,8 +9,6 @@ import ( "sort" "strings" - log "github.com/sirupsen/logrus" - "github.com/silenceper/wechat/v2/miniprogram/context" "github.com/silenceper/wechat/v2/miniprogram/security" "github.com/silenceper/wechat/v2/util" @@ -129,7 +127,6 @@ func (receiver *PushReceiver) GetMsgData(r *http.Request) (MsgType, EventType, P if err != nil { return "", "", nil, err } - log.Debug("decryptMsg string", string(decryptMsg)) var ( msgType MsgType eventType EventType From 5cc4af8fed0783afeb2a3c33a63ed4303bb2d794 Mon Sep 17 00:00:00 2001 From: houseme Date: Mon, 16 Oct 2023 13:50:37 +0800 Subject: [PATCH 10/13] improve comment --- officialaccount/message/message.go | 44 +++++++++++++++--------------- officialaccount/server/server.go | 6 ++-- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/officialaccount/message/message.go b/officialaccount/message/message.go index 41ffedc..e5fa245 100644 --- a/officialaccount/message/message.go +++ b/officialaccount/message/message.go @@ -27,15 +27,15 @@ const ( MsgTypeVideo MsgType = "video" // MsgTypeMiniprogrampage 表示小程序卡片消息 MsgTypeMiniprogrampage MsgType = "miniprogrampage" - // MsgTypeShortVideo 表示短视频消息[限接收] + // MsgTypeShortVideo 表示短视频消息 [限接收] MsgTypeShortVideo MsgType = "shortvideo" - // MsgTypeLocation 表示坐标消息[限接收] + // MsgTypeLocation 表示坐标消息 [限接收] MsgTypeLocation MsgType = "location" - // MsgTypeLink 表示链接消息[限接收] + // MsgTypeLink 表示链接消息 [限接收] MsgTypeLink MsgType = "link" - // MsgTypeMusic 表示音乐消息[限回复] + // MsgTypeMusic 表示音乐消息 [限回复] MsgTypeMusic MsgType = "music" - // MsgTypeNews 表示图文消息[限回复] + // MsgTypeNews 表示图文消息 [限回复] MsgTypeNews MsgType = "news" // MsgTypeTransfer 表示消息消息转发到客服 MsgTypeTransfer MsgType = "transfer_customer_service" @@ -91,7 +91,7 @@ const ( const ( // 微信开放平台需要用到 - // InfoTypeVerifyTicket 返回ticket + // InfoTypeVerifyTicket 返回 ticket InfoTypeVerifyTicket InfoType = "component_verify_ticket" // InfoTypeAuthorized 授权 InfoTypeAuthorized InfoType = "authorized" @@ -108,8 +108,8 @@ type MixMessage struct { CommonToken // 基本消息 - MsgID int64 `xml:"MsgId"` // 其他消息推送过来是MsgId - TemplateMsgID int64 `xml:"MsgID"` // 模板消息推送成功的消息是MsgID + MsgID int64 `xml:"MsgId"` // 其他消息推送过来是 MsgId + TemplateMsgID int64 `xml:"MsgID"` // 模板消息推送成功的消息是 MsgID Content string `xml:"Content"` Recognition string `xml:"Recognition"` PicURL string `xml:"PicUrl"` @@ -166,17 +166,17 @@ type MixMessage struct { // 事件相关:发布能力 PublishEventInfo struct { - PublishID int64 `xml:"publish_id"` // 发布任务id + PublishID int64 `xml:"publish_id"` // 发布任务 id PublishStatus freepublish.PublishStatus `xml:"publish_status"` // 发布状态 - ArticleID string `xml:"article_id"` // 当发布状态为0时(即成功)时,返回图文的 article_id,可用于“客服消息”场景 + ArticleID string `xml:"article_id"` // 当发布状态为 0 时(即成功)时,返回图文的 article_id,可用于“客服消息”场景 ArticleDetail struct { Count uint `xml:"count"` // 文章数量 Item []struct { Index uint `xml:"idx"` // 文章对应的编号 ArticleURL string `xml:"article_url"` // 图文的永久链接 } `xml:"item"` - } `xml:"article_detail"` // 当发布状态为0时(即成功)时,返回内容 - FailIndex []uint `xml:"fail_idx"` // 当发布状态为2或4时,返回不通过的文章编号,第一篇为 1;其他发布状态则为空 + } `xml:"article_detail"` // 当发布状态为 0 时(即成功)时,返回内容 + FailIndex []uint `xml:"fail_idx"` // 当发布状态为 2 或 4 时,返回不通过的文章编号,第一篇为 1;其他发布状态则为空 } `xml:"PublishEventInfo"` // 第三方平台相关 @@ -222,19 +222,19 @@ type MixMessage struct { TraceID string `xml:"trace_id"` StatusCode int `xml:"status_code"` - //小程序名称审核结果事件推送 - Ret int32 `xml:"ret"` //审核结果 2:失败,3:成功 - NickName string `xml:"nickname"` //小程序昵称 + // 小程序名称审核结果事件推送 + Ret int32 `xml:"ret"` // 审核结果 2:失败,3:成功 + NickName string `xml:"nickname"` // 小程序昵称 // 设备相关 device.MsgDevice - //小程序审核通知 - SuccTime int `xml:"SuccTime"` //审核成功时的时间戳 - FailTime int `xml:"FailTime"` //审核不通过的时间戳 - DelayTime int `xml:"DelayTime"` //审核延后时的时间戳 - Reason string `xml:"Reason"` //审核不通过的原因 - ScreenShot string `xml:"ScreenShot"` //审核不通过的截图示例。用 | 分隔的 media_id 的列表,可通过获取永久素材接口拉取截图内容 + // 小程序审核通知 + SuccTime int `xml:"SuccTime"` // 审核成功时的时间戳 + FailTime int `xml:"FailTime"` // 审核不通过的时间戳 + DelayTime int `xml:"DelayTime"` // 审核延后时的时间戳 + Reason string `xml:"Reason"` // 审核不通过的原因 + ScreenShot string `xml:"ScreenShot"` // 审核不通过的截图示例。用 | 分隔的 media_id 的列表,可通过获取永久素材接口拉取截图内容 } // SubscribeMsgPopupEvent 订阅通知事件推送的消息体 @@ -282,7 +282,7 @@ type ResponseEncryptedXMLMsg struct { Nonce string `xml:"Nonce" json:"Nonce"` } -// CDATA 使用该类型,在序列化为 xml 文本时文本会被解析器忽略 +// CDATA 使用该类型,在序列化为 xml 文本时文本会被解析器忽略 type CDATA string // MarshalXML 实现自己的序列化方法 diff --git a/officialaccount/server/server.go b/officialaccount/server/server.go index c741ee6..b0547df 100644 --- a/officialaccount/server/server.go +++ b/officialaccount/server/server.go @@ -73,7 +73,7 @@ func (srv *Server) Serve() error { if err != nil { return err } - // 非安全模式下,请求处理方法返回为nil则直接回复success给微信服务器 + // 非安全模式下,请求处理方法返回为 nil 则直接回复 success 给微信服务器 if response == nil && !srv.isSafeMode { srv.String("success") return nil @@ -198,7 +198,7 @@ func (srv *Server) parseRequestMessage(rawXMLMsgBytes []byte) (msg *message.MixM if err != nil { return } - // nonstandard json, 目前小程序订阅消息返回数据格式不标准,订阅消息模板单个List返回是对象,多个List返回是数组。 + // nonstandard json, 目前小程序订阅消息返回数据格式不标准,订阅消息模板单个 List 返回是对象,多个 List 返回是数组。 if msg.MsgType == message.MsgTypeEvent { listData := gjson.Get(string(rawXMLMsgBytes), "List") if listData.IsObject() { @@ -284,7 +284,7 @@ func (srv *Server) Send() (err error) { if err != nil { return } - // TODO 如果获取不到timestamp nonce 则自己生成 + // TODO 如果获取不到 timestamp nonce 则自己生成 timestamp := srv.timestamp timestampStr := strconv.FormatInt(timestamp, 10) msgSignature := util.Signature(srv.Token, timestampStr, srv.nonce, string(encryptedMsg)) From fe545a6de915853c11ac9e71448a501b3d621d98 Mon Sep 17 00:00:00 2001 From: houseme Date: Mon, 16 Oct 2023 13:51:20 +0800 Subject: [PATCH 11/13] improve code for message --- miniprogram/message/message.go | 131 +++++++++++++++++++++++++++++++-- 1 file changed, 124 insertions(+), 7 deletions(-) diff --git a/miniprogram/message/message.go b/miniprogram/message/message.go index 82a1cee..298bad9 100644 --- a/miniprogram/message/message.go +++ b/miniprogram/message/message.go @@ -9,6 +9,8 @@ import ( "sort" "strings" + "github.com/tidwall/gjson" + "github.com/silenceper/wechat/v2/miniprogram/context" "github.com/silenceper/wechat/v2/miniprogram/security" "github.com/silenceper/wechat/v2/util" @@ -205,16 +207,65 @@ func (receiver *PushReceiver) getEvent(dataType string, eventType EventType, dec // 用户操作订阅通知弹窗事件推送 var pushData PushDataSubscribePopup err := receiver.unmarshal(dataType, decryptMsg, &pushData) + if err == nil { + listData := gjson.Get(string(decryptMsg), "List") + if listData.IsObject() { + listItem := SubscribeMsgPopupEventList{} + if parseErr := json.Unmarshal([]byte(listData.Raw), &listItem); parseErr != nil { + return &pushData, parseErr + } + pushData.SetSubscribeMsgPopupEvents([]SubscribeMsgPopupEventList{listItem}) + } else if listData.IsArray() { + listItems := make([]SubscribeMsgPopupEventList, 0) + if parseErr := json.Unmarshal([]byte(listData.Raw), &listItems); parseErr != nil { + return &pushData, parseErr + } + pushData.SetSubscribeMsgPopupEvents(listItems) + } + } + return &pushData, err case EventSubscribeMsgChange: // 用户管理订阅通知事件推送 var pushData PushDataSubscribeMsgChange err := receiver.unmarshal(dataType, decryptMsg, &pushData) + if err == nil { + listData := gjson.Get(string(decryptMsg), "List") + if listData.IsObject() { + listItem := SubscribeMsgChangeList{} + if parseErr := json.Unmarshal([]byte(listData.Raw), &listItem); parseErr != nil { + return &pushData, parseErr + } + pushData.SetSubscribeMsgChangeEvents([]SubscribeMsgChangeList{listItem}) + } else if listData.IsArray() { + listItems := make([]SubscribeMsgChangeList, 0) + if parseErr := json.Unmarshal([]byte(listData.Raw), &listItems); parseErr != nil { + return &pushData, parseErr + } + pushData.SetSubscribeMsgChangeEvents(listItems) + } + } return &pushData, err case EventSubscribeMsgSent: // 用户发送订阅通知事件推送 var pushData PushDataSubscribeMsgSent err := receiver.unmarshal(dataType, decryptMsg, &pushData) + if err == nil { + listData := gjson.Get(string(decryptMsg), "List") + if listData.IsObject() { + listItem := SubscribeMsgSentList{} + if parseErr := json.Unmarshal([]byte(listData.Raw), &listItem); parseErr != nil { + return &pushData, parseErr + } + pushData.SetSubscribeMsgSentEvents([]SubscribeMsgSentList{listItem}) + } else if listData.IsArray() { + listItems := make([]SubscribeMsgSentList, 0) + if parseErr := json.Unmarshal([]byte(listData.Raw), &listItems); parseErr != nil { + return &pushData, parseErr + } + pushData.SetSubscribeMsgSentEvents(listItems) + } + } return &pushData, err } // 暂不支持其他事件类型,直接返回解密后的数据,由调用方处理 @@ -405,12 +456,13 @@ type CoinInfo struct { // PushDataSubscribePopup 用户操作订阅通知弹窗事件推送 type PushDataSubscribePopup struct { CommonPushData - List []SubscribeMsgPopupEventList `xml:"SubscribeMsgPopupEvent>List" json:"List"` + subscribeMsgPopupEventList []SubscribeMsgPopupEventList `json:"-"` + SubscribeMsgPopupEvent SubscribeMsgPopupEvent `xml:"SubscribeMsgPopupEvent"` } // SubscribeMsgPopupEvent 用户操作订阅通知弹窗消息回调 type SubscribeMsgPopupEvent struct { - List []SubscribeMsgPopupEventList `xml:"List" json:"List"` + List []SubscribeMsgPopupEventList `xml:"List"` } // SubscribeMsgPopupEventList 订阅消息事件列表 @@ -420,10 +472,32 @@ type SubscribeMsgPopupEventList struct { PopupScene string `xml:"PopupScene" json:"PopupScene"` } +// SetSubscribeMsgPopupEvents 设置订阅消息事件 +func (s *PushDataSubscribePopup) SetSubscribeMsgPopupEvents(list []SubscribeMsgPopupEventList) { + s.subscribeMsgPopupEventList = list +} + +// GetSubscribeMsgPopupEvents 获取订阅消息事件数据 +func (s *PushDataSubscribePopup) GetSubscribeMsgPopupEvents() []SubscribeMsgPopupEventList { + if s.subscribeMsgPopupEventList != nil { + return s.subscribeMsgPopupEventList + } + + if s.SubscribeMsgPopupEvent.List == nil { + return nil + } + list := make([]SubscribeMsgPopupEventList, len(s.SubscribeMsgPopupEvent.List)) + for i, item := range s.SubscribeMsgPopupEvent.List { + list[i] = item + } + return list +} + // PushDataSubscribeMsgChange 用户管理订阅通知事件推送 type PushDataSubscribeMsgChange struct { CommonPushData - List []SubscribeMsgChangeList `xml:"SubscribeMsgChangeEvent>List" json:"List"` + SubscribeMsgChangeEvent SubscribeMsgChangeEvent `xml:"SubscribeMsgChangeEvent"` + subscribeMsgChangeList []SubscribeMsgChangeList `json:"-"` } // SubscribeMsgChangeEvent 用户管理订阅通知回调 @@ -437,21 +511,64 @@ type SubscribeMsgChangeList struct { SubscribeStatusString string `xml:"SubscribeStatusString" json:"SubscribeStatusString"` } +// SetSubscribeMsgChangeEvents 设置订阅消息事件 +func (s *PushDataSubscribeMsgChange) SetSubscribeMsgChangeEvents(list []SubscribeMsgChangeList) { + s.subscribeMsgChangeList = list +} + +// GetSubscribeMsgChangeEvents 获取订阅消息事件数据 +func (s *PushDataSubscribeMsgChange) GetSubscribeMsgChangeEvents() []SubscribeMsgChangeList { + if s.subscribeMsgChangeList != nil { + return s.subscribeMsgChangeList + } + + if s.SubscribeMsgChangeEvent.List == nil { + return nil + } + list := make([]SubscribeMsgChangeList, len(s.SubscribeMsgChangeEvent.List)) + for i, item := range s.SubscribeMsgChangeEvent.List { + list[i] = item + } + return list +} + // PushDataSubscribeMsgSent 用户发送订阅通知事件推送 type PushDataSubscribeMsgSent struct { CommonPushData - List []SubscribeMsgSentEventList `xml:"SubscribeMsgSentEvent>List" json:"List"` + SubscribeMsgSentEvent SubscribeMsgSentEvent `xml:"SubscribeMsgSentEvent"` + subscribeMsgSentEventList []SubscribeMsgSentList `json:"-"` } // SubscribeMsgSentEvent 用户发送订阅通知回调 type SubscribeMsgSentEvent struct { - List []SubscribeMsgSentEventList `xml:"List" json:"List"` + List []SubscribeMsgSentList `xml:"List" json:"List"` } -// SubscribeMsgSentEventList 订阅消息事件列表 -type SubscribeMsgSentEventList struct { +// SubscribeMsgSentList 订阅消息事件列表 +type SubscribeMsgSentList struct { TemplateID string `xml:"TemplateId" json:"TemplateId"` MsgID string `xml:"MsgID" json:"MsgID"` ErrorCode int `xml:"ErrorCode" json:"ErrorCode"` ErrorStatus string `xml:"ErrorStatus" json:"ErrorStatus"` } + +// SetSubscribeMsgSentEvents 设置订阅消息事件 +func (s *PushDataSubscribeMsgSent) SetSubscribeMsgSentEvents(list []SubscribeMsgSentList) { + s.subscribeMsgSentEventList = list +} + +// GetSubscribeMsgSentEvents 获取订阅消息事件数据 +func (s *PushDataSubscribeMsgSent) GetSubscribeMsgSentEvents() []SubscribeMsgSentList { + if s.subscribeMsgSentEventList != nil { + return s.subscribeMsgSentEventList + } + + if s.SubscribeMsgSentEvent.List == nil { + return nil + } + list := make([]SubscribeMsgSentList, len(s.SubscribeMsgSentEvent.List)) + for i, item := range s.SubscribeMsgSentEvent.List { + list[i] = item + } + return list +} From 3d9cb1670992ccde6f81643327028be6466e7cb6 Mon Sep 17 00:00:00 2001 From: houseme Date: Mon, 16 Oct 2023 15:12:05 +0800 Subject: [PATCH 12/13] improve code for unmarshal message --- miniprogram/message/message.go | 135 ++++++++++++++++++--------------- 1 file changed, 75 insertions(+), 60 deletions(-) diff --git a/miniprogram/message/message.go b/miniprogram/message/message.go index 298bad9..5c1d99b 100644 --- a/miniprogram/message/message.go +++ b/miniprogram/message/message.go @@ -205,81 +205,96 @@ func (receiver *PushReceiver) getEvent(dataType string, eventType EventType, dec return &pushData, err case EventSubscribePopup: // 用户操作订阅通知弹窗事件推送 - var pushData PushDataSubscribePopup - err := receiver.unmarshal(dataType, decryptMsg, &pushData) - if err == nil { - listData := gjson.Get(string(decryptMsg), "List") - if listData.IsObject() { - listItem := SubscribeMsgPopupEventList{} - if parseErr := json.Unmarshal([]byte(listData.Raw), &listItem); parseErr != nil { - return &pushData, parseErr - } - pushData.SetSubscribeMsgPopupEvents([]SubscribeMsgPopupEventList{listItem}) - } else if listData.IsArray() { - listItems := make([]SubscribeMsgPopupEventList, 0) - if parseErr := json.Unmarshal([]byte(listData.Raw), &listItems); parseErr != nil { - return &pushData, parseErr - } - pushData.SetSubscribeMsgPopupEvents(listItems) - } - } - - return &pushData, err + return receiver.unmarshalSubscribePopup(dataType, decryptMsg) case EventSubscribeMsgChange: // 用户管理订阅通知事件推送 - var pushData PushDataSubscribeMsgChange - err := receiver.unmarshal(dataType, decryptMsg, &pushData) - if err == nil { - listData := gjson.Get(string(decryptMsg), "List") - if listData.IsObject() { - listItem := SubscribeMsgChangeList{} - if parseErr := json.Unmarshal([]byte(listData.Raw), &listItem); parseErr != nil { - return &pushData, parseErr - } - pushData.SetSubscribeMsgChangeEvents([]SubscribeMsgChangeList{listItem}) - } else if listData.IsArray() { - listItems := make([]SubscribeMsgChangeList, 0) - if parseErr := json.Unmarshal([]byte(listData.Raw), &listItems); parseErr != nil { - return &pushData, parseErr - } - pushData.SetSubscribeMsgChangeEvents(listItems) - } - } - return &pushData, err + return receiver.unmarshalSubscribeMsgChange(dataType, decryptMsg) case EventSubscribeMsgSent: // 用户发送订阅通知事件推送 - var pushData PushDataSubscribeMsgSent - err := receiver.unmarshal(dataType, decryptMsg, &pushData) - if err == nil { - listData := gjson.Get(string(decryptMsg), "List") - if listData.IsObject() { - listItem := SubscribeMsgSentList{} - if parseErr := json.Unmarshal([]byte(listData.Raw), &listItem); parseErr != nil { - return &pushData, parseErr - } - pushData.SetSubscribeMsgSentEvents([]SubscribeMsgSentList{listItem}) - } else if listData.IsArray() { - listItems := make([]SubscribeMsgSentList, 0) - if parseErr := json.Unmarshal([]byte(listData.Raw), &listItems); parseErr != nil { - return &pushData, parseErr - } - pushData.SetSubscribeMsgSentEvents(listItems) - } - } - return &pushData, err + return receiver.unmarshalSubscribeMsgSent(dataType, decryptMsg) } // 暂不支持其他事件类型,直接返回解密后的数据,由调用方处理 return decryptMsg, nil } // unmarshal 解析推送的数据 -func (receiver *PushReceiver) unmarshal(dateType string, decryptMsg []byte, pushData interface{}) error { - if dateType == DataTypeXML { +func (receiver *PushReceiver) unmarshal(dataType string, decryptMsg []byte, pushData interface{}) error { + if dataType == DataTypeXML { return xml.Unmarshal(decryptMsg, pushData) } return json.Unmarshal(decryptMsg, pushData) } +// unmarshalSubscribePopup +func (receiver *PushReceiver) unmarshalSubscribePopup(dataType string, decryptMsg []byte) (PushData, error) { + var pushData PushDataSubscribePopup + err := receiver.unmarshal(dataType, decryptMsg, &pushData) + if err == nil { + listData := gjson.Get(string(decryptMsg), "List") + if listData.IsObject() { + listItem := SubscribeMsgPopupEventList{} + if parseErr := json.Unmarshal([]byte(listData.Raw), &listItem); parseErr != nil { + return &pushData, parseErr + } + pushData.SetSubscribeMsgPopupEvents([]SubscribeMsgPopupEventList{listItem}) + } else if listData.IsArray() { + listItems := make([]SubscribeMsgPopupEventList, 0) + if parseErr := json.Unmarshal([]byte(listData.Raw), &listItems); parseErr != nil { + return &pushData, parseErr + } + pushData.SetSubscribeMsgPopupEvents(listItems) + } + } + + return &pushData, err +} + +// unmarshalSubscribeMsgChange 解析用户管理订阅通知事件推送 +func (receiver *PushReceiver) unmarshalSubscribeMsgChange(dataType string, decryptMsg []byte) (PushData, error) { + var pushData PushDataSubscribeMsgChange + err := receiver.unmarshal(dataType, decryptMsg, &pushData) + if err == nil { + listData := gjson.Get(string(decryptMsg), "List") + if listData.IsObject() { + listItem := SubscribeMsgChangeList{} + if parseErr := json.Unmarshal([]byte(listData.Raw), &listItem); parseErr != nil { + return &pushData, parseErr + } + pushData.SetSubscribeMsgChangeEvents([]SubscribeMsgChangeList{listItem}) + } else if listData.IsArray() { + listItems := make([]SubscribeMsgChangeList, 0) + if parseErr := json.Unmarshal([]byte(listData.Raw), &listItems); parseErr != nil { + return &pushData, parseErr + } + pushData.SetSubscribeMsgChangeEvents(listItems) + } + } + return &pushData, err +} + +// unmarshalSubscribeMsgSent 解析用户发送订阅通知事件推送 +func (receiver *PushReceiver) unmarshalSubscribeMsgSent(dataType string, decryptMsg []byte) (PushData, error) { + var pushData PushDataSubscribeMsgSent + err := receiver.unmarshal(dataType, decryptMsg, &pushData) + if err == nil { + listData := gjson.Get(string(decryptMsg), "List") + if listData.IsObject() { + listItem := SubscribeMsgSentList{} + if parseErr := json.Unmarshal([]byte(listData.Raw), &listItem); parseErr != nil { + return &pushData, parseErr + } + pushData.SetSubscribeMsgSentEvents([]SubscribeMsgSentList{listItem}) + } else if listData.IsArray() { + listItems := make([]SubscribeMsgSentList, 0) + if parseErr := json.Unmarshal([]byte(listData.Raw), &listItems); parseErr != nil { + return &pushData, parseErr + } + pushData.SetSubscribeMsgSentEvents(listItems) + } + } + return &pushData, err +} + // DataReceived 接收到的数据 type DataReceived struct { Encrypt string `json:"Encrypt" xml:"Encrypt"` // 加密的消息体 From 1090563e0b19937a3cc58dec0ec96b09a0615994 Mon Sep 17 00:00:00 2001 From: houseme Date: Mon, 16 Oct 2023 15:17:03 +0800 Subject: [PATCH 13/13] improve code for message --- domain/openapi/mgnt.go | 20 ++++++++++---------- miniprogram/message/message.go | 26 ++++++++------------------ 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/domain/openapi/mgnt.go b/domain/openapi/mgnt.go index 02621d4..9e0754d 100644 --- a/domain/openapi/mgnt.go +++ b/domain/openapi/mgnt.go @@ -2,12 +2,12 @@ package openapi import "github.com/silenceper/wechat/v2/util" -// GetAPIQuotaParams 查询API调用额度参数 +// GetAPIQuotaParams 查询 API 调用额度参数 type GetAPIQuotaParams struct { - CgiPath string `json:"cgi_path"` // api的请求地址,例如"/cgi-bin/message/custom/send";不要前缀“https://api.weixin.qq.com” ,也不要漏了"/",否则都会76003的报错 + CgiPath string `json:"cgi_path"` // api 的请求地址,例如"/cgi-bin/message/custom/send";不要前缀“https://api.weixin.qq.com” ,也不要漏了"/",否则都会 76003 的报错 } -// APIQuota API调用额度 +// APIQuota API 调用额度 type APIQuota struct { util.CommonError Quota struct { @@ -17,20 +17,20 @@ type APIQuota struct { } `json:"quota"` // 详情 } -// GetRidInfoParams 查询rid信息参数 +// GetRidInfoParams 查询 rid 信息参数 type GetRidInfoParams struct { - Rid string `json:"rid"` // 调用接口报错返回的rid + Rid string `json:"rid"` // 调用接口报错返回的 rid } -// RidInfo rid信息 +// RidInfo rid 信息 type RidInfo struct { util.CommonError Request struct { InvokeTime int64 `json:"invoke_time"` // 发起请求的时间戳 CostInMs int64 `json:"cost_in_ms"` // 请求毫秒级耗时 - RequestURL string `json:"request_url"` // 请求的URL参数 - RequestBody string `json:"request_body"` // post请求的请求参数 + RequestURL string `json:"request_url"` // 请求的 URL 参数 + RequestBody string `json:"request_body"` // post 请求的请求参数 ResponseBody string `json:"response_body"` // 接口请求返回参数 - ClientIP string `json:"client_ip"` // 接口请求的客户端ip - } `json:"request"` // 该rid对应的请求详情 + ClientIP string `json:"client_ip"` // 接口请求的客户端 ip + } `json:"request"` // 该 rid 对应的请求详情 } diff --git a/miniprogram/message/message.go b/miniprogram/message/message.go index 5c1d99b..77dccfe 100644 --- a/miniprogram/message/message.go +++ b/miniprogram/message/message.go @@ -498,14 +498,10 @@ func (s *PushDataSubscribePopup) GetSubscribeMsgPopupEvents() []SubscribeMsgPopu return s.subscribeMsgPopupEventList } - if s.SubscribeMsgPopupEvent.List == nil { + if s.SubscribeMsgPopupEvent.List == nil || len(s.SubscribeMsgPopupEvent.List) < 1 { return nil } - list := make([]SubscribeMsgPopupEventList, len(s.SubscribeMsgPopupEvent.List)) - for i, item := range s.SubscribeMsgPopupEvent.List { - list[i] = item - } - return list + return s.SubscribeMsgPopupEvent.List } // PushDataSubscribeMsgChange 用户管理订阅通知事件推送 @@ -537,14 +533,11 @@ func (s *PushDataSubscribeMsgChange) GetSubscribeMsgChangeEvents() []SubscribeMs return s.subscribeMsgChangeList } - if s.SubscribeMsgChangeEvent.List == nil { + if s.SubscribeMsgChangeEvent.List == nil || len(s.SubscribeMsgChangeEvent.List) < 1 { return nil } - list := make([]SubscribeMsgChangeList, len(s.SubscribeMsgChangeEvent.List)) - for i, item := range s.SubscribeMsgChangeEvent.List { - list[i] = item - } - return list + + return s.SubscribeMsgChangeEvent.List } // PushDataSubscribeMsgSent 用户发送订阅通知事件推送 @@ -578,12 +571,9 @@ func (s *PushDataSubscribeMsgSent) GetSubscribeMsgSentEvents() []SubscribeMsgSen return s.subscribeMsgSentEventList } - if s.SubscribeMsgSentEvent.List == nil { + if s.SubscribeMsgSentEvent.List == nil || len(s.SubscribeMsgSentEvent.List) < 1 { return nil } - list := make([]SubscribeMsgSentList, len(s.SubscribeMsgSentEvent.List)) - for i, item := range s.SubscribeMsgSentEvent.List { - list[i] = item - } - return list + + return s.SubscribeMsgSentEvent.List }