C#开发微信门户及应用(30)--消息的群发处理和预览功能

在很多场合下,我们可能需要利用微信公众号的优势,定期给指定用户群发送一些推广消息或者新闻内容,以便给关注客户一种经常更新公众号内容的感觉,同时也方便我们经常和用户进行互动。微信公众号的高级群发接口就是为了处理这个场景的,本文介绍在C#代码中如何封装消息的群发和预览等功能。

1、消息群发的功能和限制

对于公众号中的服务号和订阅号,群发的消息有一定的限制,具体规则如下所示。

1、对于认证订阅号,群发接口每天可成功调用1次,此次群发可选择发送给全部用户或某个分组;
2、对于认证服务号虽然开发者使用高级群发接口的每日调用限制为100次,但是用户每月只能接收4条,无论在公众平台网站上,还是使用接口群发,用户每月只能接收4条群发消息,多于4条的群发将对该用户发送失败;
3、具备微信支付权限的公众号,在使用群发接口上传、群发图文消息类型时,可使用<a>标签加入外链;
4、开发者可以使用预览接口校对消息样式和排版,通过预览接口可发送编辑好的消息给指定用户校验效果。

群发图文消息的过程如下:

1、首先,预先将图文消息中需要用到的图片,使用上传图文消息内图片接口,上传成功并获得图片URL
2、上传图文消息素材,需要用到图片时,请使用上一步获取的图片URL
3、使用对用户分组的群发,或对OpenID列表的群发,将图文消息群发出去
4、在上述过程中,如果需要,还可以预览图文消息、查询群发状态,或删除已群发的消息等

群发图片、文本等其他消息类型的过程如下:

1、如果是群发文本消息,则直接根据下面的接口说明进行群发即可
2、如果是群发图片、视频等消息,则需要预先通过素材管理接口准备好mediaID

2、消息的群发处理

虽然群发的消息类型有几种,如包括图文消息、文本消息、图片、视频、语音、卡劵等等,不过消息群发方式分为两类:根据群组发送消息和根据OpenID发送消息两种。

根据微信接口的定义,我们设计了对上面两种不同方式的发送接口,我们把不同类型的消息放到枚举MassMessageType 进行定义。

        /// <summary>
        /// 根据分组进行群发消息(图文消息、文本消息、语音消息、视频消息、图片、卡劵等)
        /// </summary>
        /// <param name="accessToken">访问凭证</param>
        /// <param name="mediaIdOrContent">群发媒体文件时传入mediaId,群发文本消息时传入content,群发卡券时传入cardId</param>
        /// <param name="groupId">群发到的分组的group_id</param>
        /// <param name="isToAll">
        /// 使用is_to_all为true且成功群发,会使得此次群发进入历史消息列表。
        /// 设置is_to_all为false时是可以多次群发的,但每个用户只会收到最多4条,且这些群发不会进入历史消息列表</param>
        /// <returns></returns>
        MassMessageResult SendByGroup(string accessToken, MassMessageType messageType, string mediaIdOrContent, string groupId, bool isToAll = false);

        /// <summary>
        /// 根据OpenId进行群发消息(视频消息需要单独)
        /// </summary>
        /// <param name="accessToken">访问凭证</param>
        /// <param name="messageType">消息类型</param>
        /// <param name="mediaIdOrContent">用于群发的消息的media_id</param>
        /// <param name="openIdList">openId字符串数组</param>
        /// <returns></returns>
        MassMessageResult SendByOpenId(string accessToken, MassMessageType messageType, string mediaIdOrContent, List<string> openIdList);

其中枚举MassMessageType定义代码如下所示。

   /// <summary>
    /// 群发消息的类型
    /// </summary>
    public enum MassMessageType
    {
        /// <summary>
        /// 图文消息
        /// </summary>
        mpnews,

        /// <summary>
        /// 文本消息
        /// </summary>
        text,

        /// <summary>
        /// 图片
        /// </summary>
        image,

        /// <summary>
        /// 语音
        /// </summary>
        voice,

        /// <summary>
        /// 音乐
        /// </summary>
        music,

        /// <summary>
        /// 视频
        /// </summary>
        video,

        /// <summary>
        /// 卡劵
        /// </summary>
        wxcard
    }

然后我们根据上面的接口实现相关的处理函数,群发消息的类定义代码如下所示。

    /// <summary>
    /// 消息群发.
    /// 在公众平台网站上,为订阅号提供了每天1条的群发权限,为服务号提供每月(自然月)4条的群发权限。
    /// 而对于某些具备开发能力的公众号运营者,可以通过高级群发接口,实现更灵活的群发能力。
    /// </summary>
    public class MassSendApi : IMassSendApi

对于图文消息的群发规则,微信接口定义如下。

接口调用请求说明

http请求方式: POST
https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token=ACCESS_TOKEN

POST数据说明

POST数据示例如下:

图文消息(注意图文消息的media_id需要通过上述方法来得到):

{
   "filter":{
      "is_to_all":false,
      "group_id":2
   },
   "mpnews":{
      "media_id":"123dsdajkasd231jhksad"
   },
    "msgtype":"mpnews"
}

其他类似文本消息、图片、视频、语音、卡劵等发送方式类似,都是提供一个不同的JSON字符串,然后提交到对应的连接地址就可以了,因此我们可以把它们进行统一的封装处理。

我们可以在一个条件语句里面对内容进行组装,例如对于图文消息的处理代码如下所示。

switch (messageType)
            {
                case MassMessageType.mpnews://图文消息
                    postData = new
                    {
                        filter = new
                        {
                            is_to_all = isToAll, //是否让此次群发进入历史消息列表
                            group_id = groupId //群发到的分组的group_id
                        },
                        mpnews = new
                        {
                            media_id = mediaIdOrContent  //用于群发的消息的media_id
                        },
                        msgtype = "mpnews"
                    }.ToJson();
                    break;

对于文本消息的组装如下所示。

                case MassMessageType.text://文本消息
                    postData = new
                    {
                        filter = new
                        {
                            is_to_all = isToAll, //是否让此次群发进入历史消息列表
                            group_id = groupId //群发到的分组的group_id
                        },
                        text = new
                        {
                            content = mediaIdOrContent  //用于群发的消息的内容
                        },
                        msgtype = "text"
                    }.ToJson();
                    break;

最后我们通过代码进行提交JSON数据,并获取返回结果即可,如下代码所示。

            string url = string.Format("https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token={0}", accessToken);

            MassMessageResult result = JsonHelper<MassMessageResult>.ConvertJson(url, postData);
            return result;

这样,整合各个消息类型的处理,我们就可以得到一个完整的消息群发操作了。

群发给openid的操作也是类似上面的处理方式,也是通过一个switch的条件语句,进行不同内容的构建,然后统一发送即可。

请注意:在返回成功时,意味着群发任务提交成功,并不意味着此时群发已经结束,所以,仍有可能在后续的发送过程中出现异常情况导致用户未收到消息,如消息有时会进行审核、服务器不稳定等。此外,群发任务一般需要较长的时间才能全部发送完毕,请耐心等待

由于群发任务提交后,群发任务可能在一定时间后才完成,因此,群发接口调用时,仅会给出群发任务是否提交成功的提示,若群发任务提交成功,则在群发任务结束时,会向开发者在公众平台填写的开发者URL(callback URL)推送事件。

推送的XML结构如下(发送成功时):

<xml>
<ToUserName><![CDATA[gh_3e8adccde292]]></ToUserName>
<FromUserName><![CDATA[oR5Gjjl_eiZoUpGozMo7dbBJ362A]]></FromUserName>
<CreateTime>1394524295</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[MASSSENDJOBFINISH]]></Event>
<MsgID>1988</MsgID>
<Status><![CDATA[sendsuccess]]></Status>
<TotalCount>100</TotalCount>
<FilterCount>80</FilterCount>
<SentCount>75</SentCount>
<ErrorCount>5</ErrorCount>
</xml>

对应的字段说明如下所示。

参数 说明
ToUserName 公众号的微信号
FromUserName 公众号群发助手的微信号,为mphelper
CreateTime 创建时间的时间戳
MsgType 消息类型,此处为event
Event 事件信息,此处为MASSSENDJOBFINISH
MsgID 群发的消息ID
Status 群发的结构,为“send success”或“send fail”或“err(num)”。但send success时,也有可能因用户拒收公众号的消息、系统错误等原因造成少量用户接收失败。err(num)是审核失败的具体原因
TotalCount group_id下粉丝数;或者openid_list中的粉丝数
FilterCount 过滤(过滤是指特定地区、性别的过滤、用户设置拒收的过滤,用户接收已超4条的过滤)后,准备发送的粉丝数,原则上,FilterCount = SentCount + ErrorCount
SentCount 发送成功的粉丝数
ErrorCount 发送失败的粉丝数

因此我们需要通过处理消息群发的发送完成操作,定义一个实体类来承载这个消息。

    public class RequestMassSendJobFinish : BaseEvent
    {
        public RequestMassSendJobFinish()
        {
            this.MsgType = RequestMsgType.Event.ToString().ToLower();
            this.Event = RequestEvent.MASSSENDJOBFINISH.ToString();
        }

        /// <summary>
        /// 群发的消息ID
        /// </summary>
        public int MsgID { get; set; }

        /// <summary>
        /// 返回状态。
        /// </summary>
        public string Status { get; set; }

        /// <summary>
        /// group_id下粉丝数;或者openid_list中的粉丝数
        /// </summary>
        public int TotalCount { get; set; }

        /// <summary>
        /// 过滤(过滤是指,有些用户在微信设置不接收该公众号的消息)后,准备发送的粉丝数,原则上,FilterCount = SentCount + ErrorCount
        /// </summary>
        public int FilterCount { get; set; }

        /// <summary>
        /// 发送成功的粉丝数
        /// </summary>
        public int SendCount { get; set; }

        /// <summary>
        /// 发送失败的粉丝数
        /// </summary>
        public int ErrorCount { get; set; }
    }

在我们需要记录或者更新处理这种群发消息的状态的时候,我们可以在整个微信的消息链里面对这样的请求事件进行处理,如下代码是处理这种群发消息的通知的。

                            case RequestEvent.MASSSENDJOBFINISH:
                                {
                                    //由于群发任务彻底完成需要较长时间,将会在群发任务即将完成的时候,就推送群发结果,此时的推送人数数据将会与实际情形存在一定误差
                                    RequestMassSendJobFinish info = XmlConvertor.XmlToObject(postStr, typeof(RequestMassSendJobFinish)) as RequestMassSendJobFinish;
                                    if(info != null)
                                    {
                                        //在此记录群发完成的处理
                                    }
                                    LogTextHelper.Info(eventName + ((info == null) ? "info is null" : info.ToJson()));
                                }
                                break;

3、待群发消息的预览

在很多时候,我们群发消息之前,我们希望通过自己的微信号来看看具体的群发消息效果,如果没有问题我们在统一群发,相当于一个真实的审核过程,这样对于我们发送高质量的消息是一个很好的习惯。

对于普通的消息预览,我们定义的接口如下所示。

        /// <summary>
        /// 预览接口【订阅号与服务号认证后均可用】。
        /// 开发者可通过该接口发送消息给指定用户,在手机端查看消息的样式和排版。
        /// 为了满足第三方平台开发者的需求,在保留对openID预览能力的同时,增加了对指定微信号发送预览的能力,但该能力每日调用次数有限制(100次),请勿滥用。
        /// </summary>
        /// <param name="accessToken">访问凭证</param>
        /// <param name="messageType">消息类型</param>
        /// <param name="media_id">用于群发的消息的media_id</param>
        /// <param name="touserOpenId">接收消息用户对应该公众号的openid</param>
        /// <param name="towxname">可以针对微信号进行预览(而非openID),towxname和touser同时赋值时,以towxname优先</param>
        /// <returns></returns>
        MassMessageResult PreviewMessage(string accessToken, MassMessageType messageType, string media_id, string touserOpenId, string towxname = null);

具体的实现也就是针对不同的消息类型,构建一个不同的处理机制,把它们差异性的JSON构造出来,然后统一调用就可以了,具体代码如下所示。

        public MassMessageResult PreviewMessage(string accessToken, MassMessageType messageType, string media_id, string touserOpenId, string towxname = null)
        {
            string postData = "";
            switch (messageType)
            {
                case MassMessageType.mpnews://图文消息
                    postData = new
                    {
                        touser = touserOpenId,
                        towxname = towxname,
                        mpnews = new
                        {
                            media_id = media_id
                        },
                        msgtype = "mpnews"
                    }.ToJson();
                    break;

                case MassMessageType.text://文本消息
                    postData = new
                    {
                        touser = touserOpenId,
                        towxname = towxname,
                        text = new
                        {
                            content = media_id
                        },
                        msgtype = "text"
                    }.ToJson();
                    break;

                case MassMessageType.voice://语音
                    postData = new
                    {
                        touser = touserOpenId,
                        towxname = towxname,
                        voice = new
                        {
                            media_id = media_id
                        },
                        msgtype = "voice"
                    }.ToJson();
                    break;

                case MassMessageType.image://图片
                    postData = new
                    {
                        touser = touserOpenId,
                        towxname = towxname,
                        image = new
                        {
                            media_id = media_id
                        },
                        msgtype = "image"
                    }.ToJson();
                    break;

                case MassMessageType.video://视频
                    postData = new
                    {
                        touser = touserOpenId,
                        towxname = towxname,
                        mpvideo = new
                        {
                            media_id = media_id
                        },
                        msgtype = "mpvideo"
                    }.ToJson();
                    break;

                case MassMessageType.wxcard: //卡劵
                    throw new WeixinException("发送卡券息请使用PreviewCardMessage方法。");
                    break;
            }

            var url = string.Format("https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token={0}", accessToken);
            return JsonHelper<MassMessageResult>.ConvertJson(url, postData);
        }

消息的预览在我们正式群发消息前的审核是比较有用的,我们可以通过接口进行一个消息的预览,可以在微信公众号上看到的效果与正式群发后的消息是一样的。

例如我们通过下面的代码进行一个简单的预览消息操作。

        /// <summary>
        /// 群发消息的预览
        /// </summary>
        private void btnPreviewMass_Click(object sender, EventArgs e)
        {
            //上传图片
            btnUpload_Click(null, null);

            //上传图文消息
            btnUploadNews_Click(null, null);

            //消息群发前的预览操作
            List<string> list = new List<string>() { openId };
            IMassSendApi api = new MassSendApi();
            var mediaId = this.news_mediaId;
            MassMessageResult result = api.PreviewMessage(token, MassMessageType.mpnews, mediaId, openId);
            if (result != null)
            {
                Console.WriteLine(result.msg_id);
            }
        }

最后可以看到例子代码的预览效果如下所示。

如果对这个《C#开发微信门户及应用》系列感兴趣,可以关注我的其他文章,系列随笔如下所示:

C#开发微信门户及应用(29)--微信个性化菜单的实现

C#开发微信门户及应用(28)--微信“摇一摇·周边”功能的使用和接口的实现

C#开发微信门户及应用(27)-公众号模板消息管理

C#开发微信门户及应用(26)-公众号微信素材管理

C#开发微信门户及应用(25)-微信企业号的客户端管理功能

C#开发微信门户及应用(24)-微信小店货架信息管理

C#开发微信门户及应用(23)-微信小店商品管理接口的封装和测试

C#开发微信门户及应用(22)-微信小店的开发和使用

C#开发微信门户及应用(21)-微信企业号的消息和事件的接收处理及解密

C#开发微信门户及应用(20)-微信企业号的菜单管理

C#开发微信门户及应用(19)-微信企业号的消息发送(文本、图片、文件、语音、视频、图文消息等)

C#开发微信门户及应用(18)-微信企业号的通讯录管理开发之成员管理

C#开发微信门户及应用(17)-微信企业号的通讯录管理开发之部门管理

C#开发微信门户及应用(16)-微信企业号的配置和使用

C#开发微信门户及应用(15)-微信菜单增加扫一扫、发图片、发地理位置功能

C#开发微信门户及应用(14)-在微信菜单中采用重定向获取用户数据

C#开发微信门户及应用(13)-使用地理位置扩展相关应用

C#开发微信门户及应用(12)-使用语音处理

C#开发微信门户及应用(11)--微信菜单的多种表现方式介绍

C#开发微信门户及应用(10)--在管理系统中同步微信用户分组信息

C#开发微信门户及应用(9)-微信门户菜单管理及提交到微信服务器

C#开发微信门户及应用(8)-微信门户应用管理系统功能介绍

C#开发微信门户及应用(7)-微信多客服功能及开发集成

C#开发微信门户及应用(6)--微信门户菜单的管理操作

C#开发微信门户及应用(5)--用户分组信息管理

C#开发微信门户及应用(4)--关注用户列表及详细信息管理

C#开发微信门户及应用(3)--文本消息和图文消息的应答

C#开发微信门户及应用(2)--微信消息的处理和应答

C#开发微信门户及应用(1)--开始使用微信接口

时间: 2024-10-14 06:52:49

C#开发微信门户及应用(30)--消息的群发处理和预览功能的相关文章

C#开发微信门户及应用(32)--微信支付接入和API封装使用

在微信的应用上,微信支付是一个比较有用的部分,但也是比较复杂的技术要点,在微商大行其道的年代,自己的商店没有增加微信支付好像也说不过去,微信支付旨在为广大微信用户及商户提供更优质的支付服务,微信的支付和安全系统由腾讯财付通提供支持.本文主要介绍如何在微信公众号上实现微信支付的接入.微信支付API的封装,以及API的调用,实现我们一些常见的业务调用. 1.开通微信支付并配置 微信支付是需要微信公众号的认证基础,也就是只对认证的公众号开放,微信认证需要签署相关的资料,并且进行对账认证,一般会有电话联

《C#开发微信门户及应用》

C#开发微信门户及应用(40)--使用微信JSAPI实现微信支付功能 C#开发微信门户及应用(39)--使用微信JSSDK实现签到的功能 C#开发微信门户及应用(38)--微信摇一摇红包功能 C#开发微信门户及应用(37)--微信公众号标签管理功能 C#开发微信门户及应用(36)--微信卡劵管理的封装操作 C#开发微信门户及应用(35)--微信支付之企业付款封装操作 C#开发微信门户及应用(34)--微信裂变红包 C#开发微信门户及应用(33)--微信现金红包的封装及使用 C#开发微信门户及应用

C#开发微信门户及应用(27)-公众号模板消息管理

通过模板消息接口,公众号能向关注其账号的用户发送预设模板的消息.模板消息仅用于公众号向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等.不支持广告等营销类消息以及其它所有可能对用户造成骚扰的消息.本文主要介绍基于C#开发实现公众号模板消息的管理功能. “模板消息功能的推出,将极大地增强服务号的服务通知能力”,在一些一直期待微信模板消息功能开放的公众号运营者看来,微信一对一沟通的社交属性,让信息推送的触达率更加精准,这也让企业在成本.服务效率.性能上有了

C#开发微信门户及应用(19)-微信企业号的消息发送(文本、图片、文件、语音、视频、图文消息等)

我们知道,企业号主要是面向企业需求而生的,因此内部消息的交流显得非常重要,而且发送.回复消息数量应该很可观,对于大企业尤其如此,因此可以结合企业号实现内部消息的交流.企业号具有关注安全.消息无限制等特点,很适合企业内部的环境.本文主要介绍如何利用企业号实现文本.图片.文件.语音.视频.图文消息等消息的发送操作. 1.企业号特点 对于企业号,有以下一些特点: 1)关注更安全 –只有企业通讯录的成员才能关注企业号,分级管理员.保密消息等各种特性确保企业内部信息的安全. 企业可以设置自行验证关注者身份

C#开发微信门户及应用(3)--文本消息和图文消息的应答

C#开发微信门户及应用(3)--文本消息和图文消息的应答 微信应用如火如荼,很多公司都希望搭上信息快车,这个是一个商机,也是一个技术的方向,因此,有空研究下.学习下微信的相关开发,也就成为计划的安排事情之一了.本系列文章希望从一个循序渐进的角度上,全面介绍微信的相关开发过程和相关经验总结,希望给大家了解一下相关的开发历程. 在前面两篇两篇随笔<C#开发微信门户及应用(1)--开始使用微信接口>和<C#开发微信门户及应用(2)--微信消息的处理和应答>里面,大致介绍了我微信应用的框架

C#开发微信门户及应用(2)--微信消息的处理和应答

原文:C#开发微信门户及应用(2)--微信消息的处理和应答 微信应用如火如荼,很多公司都希望搭上信息快车,这个是一个商机,也是一个技术的方向,因此,有空研究下.学习下微信的相关开发,也就成为计划的安排事情之一了.本系列文章希望从一个循序渐进的角度上,全面介绍微信的相关开发过程和相关经验总结,希望给大家了解一下相关的开发历程.本篇随笔主要基于上一篇<C#开发微信门户及应用(1)--开始使用微信接口>的基础上进行深入的介绍,介绍微信消息的处理和应答的过程. 1.微信的消息应答交互 我们知道,微信的

C#开发微信门户及应用(21)-微信企业号的消息和事件的接收处理及解密

原文:C#开发微信门户及应用(21)-微信企业号的消息和事件的接收处理及解密 在上篇随笔<C#开发微信门户及应用(19)-微信企业号的消息发送(文本.图片.文件.语音.视频.图文消息等)>介绍了有关企业号的消息发送,官方特别声明消息是不用加密发送的.但是在回调的服务器上,也就是我们网站的服务器上,微信传过来的消息是加密的,需要我们调用类库对消息和事件进行解密操作,由于官方的例子不全,因此摸索了不少时间,最终顺利解密收到的各种消息和事件.本文主要介绍 微信企业号的消息和事件的接收处理及解密操作.

C#开发微信门户及应用(5)--用户分组信息管理

在上个月的对C#开发微信门户及应用做了介绍,写过了几篇的随笔进行分享,由于时间关系,间隔了一段时间没有继续写这个系列的博客了,并不是对这个方面停止了研究,而是继续深入探索这方面的技术,为了更好的应用起来,专心做好底层的技术开发.本篇继续上一篇的介绍,主要介绍分组管理方面的开发应用,这篇的内容和上一篇,作为一个完整的用户信息和分组信息管理的组合. 1.用户分组管理内容 用户分组的引入,主要是方便管理关注者列表,以及方便向不同的组别发送消息的操作的,一个公众账号,最多支持创建500个分组. 用户分组

C#开发微信门户及应用(23)-微信小店商品管理接口的封装和测试

在上篇<C#开发微信门户及应用(22)-微信小店的开发和使用>里面介绍了一些微信小店的基础知识,以及对应的对象模型,本篇继续微信小店的主题,介绍其中API接口的封装和测试使用.微信小店的相关对象模型,基本上包括了常规的商品.商品分组.货架.库存.订单这些模型,还有商品分类,商品分类属性.商品分类SKU.快递邮寄模板.图片管理等功能.本文介绍的接口封装也就是基于这些内容进行的,并针对接口的实现进行测试和使用. 1.商品管理接口的定义 前面文章介绍了微信小店的对象模型,如下所示. 这个图形基本上覆