C#开发微信门户及应用(29)--微信个性化菜单的实现
有一段时间没有接着微信的主题继续介绍里面的功能模块了,这段时间来,微信也做了不少的变化改动,针对这些特性我全面核对了一下相关的微信公众号和企业号的接口,对原有的微信API和系统管理做了全面的更新,本随笔以及后面的随笔就是基于这个工作上的总结,以期把微信涉及的功能模块,都使用C#实现的方式来介绍。本随笔主要介绍微信公众号的个性化菜单的实现,虽然目前微信公众号和企业号已经在功能上接近一致,不过在企业号上还没有个性化菜单的相关接口。
1、个性化菜单介绍
我们先了解一下个性化菜单的介绍,根据官方的资料,如下介绍。
为了帮助公众号实现灵活的业务运营,微信公众平台新增了个性化菜单接口,开发者可以通过该接口,让公众号的不同用户群体看到不一样的自定义菜单。该接口开放给已认证订阅号和已认证服务号。
开发者可以通过以下条件来设置用户看到的菜单:
- 1、用户分组(开发者的业务需求可以借助用户分组来完成)
- 2、性别
- 3、手机操作系统
- 4、地区(用户在微信客户端设置的地区)
- 5、语言(用户在微信客户端设置的语言)
个性化菜单接口说明:
- 1、个性化菜单要求用户的微信客户端版本在iPhone6.2.2,Android 6.2.4以上。
- 2、菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。
- 3、普通公众号的个性化菜单的新增接口每日限制次数为2000次,删除接口也是2000次,测试个性化菜单匹配结果接口为20000次
- 4、出于安全考虑,一个公众号的所有个性化菜单,最多只能设置为跳转到3个域名下的链接
- 5、创建个性化菜单之前必须先创建默认菜单(默认菜单是指使用普通自定义菜单创建接口创建的菜单)。如果删除默认菜单,个性化菜单也会全部删除
个性化菜单匹配规则说明:
- 当公众号创建多个个性化菜单时,将按照发布顺序,由新到旧逐一匹配,直到用户信息与matchrule相符合。如果全部个性化菜单都没有匹配成功,则返回默认菜单。
- 例如公众号先后发布了默认菜单,个性化菜单1,个性化菜单2,个性化菜单3。那么当用户进入公众号页面时,将从个性化菜单3开始匹配,如果个性化菜单3匹配成功,则直接返回个性化菜单3,否则继续尝试匹配个性化菜单2,直到成功匹配到一个菜单。
- 根据上述匹配规则,为了避免菜单生效时间的混淆,决定不予提供个性化菜单编辑API,开发者需要更新菜单时,需将完整配置重新发布一轮。
除正常的默认菜单外,个性化菜单提供了下面几个处理操作。
1 创建个性化菜单
2 删除个性化菜单
3 测试个性化菜单匹配结果
4 查询个性化菜单
5 删除所有菜单
个性化自定义菜单功能,是指商家可以根据粉丝的分组、性别、手机操作系统,甚至地区,来分别展示公众号的菜单。
比如,一家健身会所的公众号,将自定义菜单根据粉丝性别进行了分开设置。男性粉丝进入公众号会展示男生们比较感兴趣菜单内容,跆拳道、搏击等;若是女性粉丝,则会展示瑜伽、舞蹈、防身术等女性粉丝感兴趣的菜单课程等。
2、个性化菜单的C#设计处理
微信的菜单是微信处理中很重要的一环,因为它是微信给用户入口体验重要的一环,也是很直观的界面呈现之一,菜单的展示就是需要我们绞尽脑汁来精简的界面元素了,由于菜单的重要性以及功能的丰富性,我在之前好几篇随笔专门介绍了菜单的各种处理。
在《C#开发微信门户及应用(6)--微信门户菜单的管理操作》、《C#开发微信门户及应用(9)-微信门户菜单管理及提交到微信服务器》、《C#开发微信门户及应用(11)--微信菜单的多种表现方式介绍》、《C#开发微信门户及应用(20)-微信企业号的菜单管理》分别对相关的菜单有过介绍,有需要可以参考了解一下。
菜单模块,在微信公众号和企业号,它们都可以共用一个模型,因此我们把这些内容都放在微信共用模块里面,包括它的实体类信息(如菜单模块),虽然有些地方,部分字段内容没有,但是我们可以通过实体类的JSON标识来进行处理,从而实现比较弹性化的实体类信息承载。
如下标识代码所示。
- /// <summary>
- /// 菜单基本信息(公众号、企业号公用,其他部分一样)
- /// </summary>
- public class MenuJson : BaseJsonResult
- {
- /// <summary>
- /// 按钮描述,既按钮名字,不超过16个字节,子菜单不超过40个字节
- /// </summary>
- public string name { get; set; }
- /// <summary>
- /// 按钮类型(click或view)
- /// </summary>
- [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
- public string type { get; set; }
- /// <summary>
- /// 按钮KEY值,用于消息接口(event类型)推送,不超过128字节
- /// </summary>
- [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
- public string key { get; set; }
- /// <summary>
- /// 网页链接,用户点击按钮可打开链接,不超过256字节
- /// </summary>
- [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
- public string url { get; set; }
- /// <summary>
- /// media_id类型和view_limited类型必须,调用新增永久素材接口返回的合法media_id
- /// </summary>
- [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
- public string media_id { get; set; }
- /// <summary>
- /// 子按钮数组,按钮个数应为2~5个
- /// </summary>
- [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
- public List<MenuJson> sub_button { get; set; }
上面这个实体类是菜单信息的基础信息,我们需要从这个基础上扩展出来几个实体类,方便在构建,获取菜单信息的时候进行信息转换,如下几个类图的设计所示。
通过这几个类的关系,我们就可以适应所有的默认菜单和个性化菜单,以及企业号的菜单信息转换处理了,通过JSON转换为对应的实体类,实现信息的强类型处理。
其中我们菜单处理的API实现类代码如下所示。
- /// <summary>
- /// 菜单的相关操作
- /// </summary>
- public class MenuApi : IMenuApi
这样我们在IMenuApi的基础上增加几个个性化的接口。
- /// <summary>
- /// 创建个性化菜单
- /// </summary>
- /// <param name="accessToken">调用接口凭证</param>
- /// <param name="menuJson">菜单对象</param>
- /// <returns></returns>
- string CreateConditionalMenu(string accessToken, MenuListJson menuJson);
- /// <summary>
- /// 根据菜单ID删除个性化菜单
- /// </summary>
- /// <param name="accessToken">调用接口凭证</param>
- /// <param name="menuid">菜单ID</param>
- /// <returns></returns>
- CommonResult DeleteConditionalMenu(string accessToken, string menuid);
- /// <summary>
- /// 测试个性化菜单匹配结果
- /// </summary>
- /// <param name="accessToken">调用接口凭证</param>
- /// <returns></returns>
- MenuListJson TryMatchConditionalMenu(string accessToken, string user_id);
然后在MenuApi实现类里面实现相关的逻辑处理即可。
下面是创建个性化菜单的接口说明。
创建个性化菜单
http请求方式:POST(请使用https协议)
请求示例
- {
- "button":[
- {
- "type":"click",
- "name":"今日歌曲",
- "key":"V1001_TODAY_MUSIC"
- },
- {
- "name":"菜单",
- "sub_button":[
- {
- "type":"view",
- "name":"搜索",
- "url":"http://www.soso.com/"
- },
- {
- "type":"view",
- "name":"视频",
- "url":"http://v.qq.com/"
- },
- {
- "type":"click",
- "name":"赞一下我们",
- "key":"V1001_GOOD"
- }]
- }],
- "matchrule":{
- "group_id":"2",
- "sex":"1",
- "country":"中国",
- "province":"广东",
- "city":"广州",
- "client_platform_type":"2"
- "language":"zh_CN"
- }
- }
其中上面的信息,转换为我们的实体类对象信息就是如下所示。
- MenuListJson menuJson
它API返回结果如下所示。
- {
- "menuid":"208379533"
- }
为了承载这个信息,我们需要定义一个实体类来承载这个信息。
- /// <summary>
- /// 返回MenuId的结果
- /// </summary>
- public class MenuIdResult
- {
- /// <summary>
- /// 菜单的ID
- /// </summary>
- public string menuid { get; set; }
- }
最后我们的API接口只需要获取里面的菜单ID值,字符串类型的就可以了,API具体实现代码如下所示。
- /// <summary>
- /// 创建个性化菜单
- /// </summary>
- /// <param name="accessToken">调用接口凭证</param>
- /// <param name="menuJson">菜单对象</param>
- /// <returns></returns>
- public string CreateConditionalMenu(string accessToken, MenuListJson menuJson)
- {
- string result = "";
- var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/addconditional?access_token={0}", accessToken);
- //matchrule不能为空
- ArgumentValidation.CheckForNullReference(menuJson.matchrule, "matchrule");
- string postData = menuJson.ToJson();
- MenuIdResult list = JsonHelper<MenuIdResult>.ConvertJson(url, postData);
- if (list != null)
- {
- result = list.menuid;
- }
- return result;
- }
而删除个性化菜单的接口也是类似的处理,如下所示。
- /// <summary>
- /// 根据菜单ID删除个性化菜单
- /// </summary>
- /// <param name="accessToken">调用接口凭证</param>
- /// <param name="menuid">菜单ID</param>
- /// <returns></returns>
- public CommonResult DeleteConditionalMenu(string accessToken, string menuid)
- {
- var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/delconditional?access_token={0}", accessToken);
- var data = new
- {
- menuid = menuid
- };
- string postData = data.ToJson();
- return Helper.GetExecuteResult(url, postData);
- }
另外,对于个性化菜单来说,查询、删除所有的菜单操作接口和普通默认菜单是一样的,因此可以重用这两个接口,不过由于我们扩展了查询返回的信息类,所以如果是个性化菜单,那么它返回的JSON字符串里面,包含了conditionalmenu的属性,这个是一个集合 ,也就是说个性化菜单是多个规则的个性化菜单的集合。
菜单的JSON数据结构如下所示。
3、个性化菜单封装接口的使用
有了上面接口的封装,我们可以在创建个性化菜单的时候很方便了,构建个性化菜单的时候,我们需要把全部需要出现的菜单都需要放在一个conditionalmenu的单元里面,虽然这样的单元在集合里面出现多个。
如下面是构建一个个性化的菜单项目,和普通的默认菜单不同,它需要指定一个matchrule的对象,如下代码所示。
- MenuJson conditional = new MenuJson("相关链接", new MenuJson[] {
- new MenuJson("个性化菜单", ButtonType.click, "event_company")
- });
- MatchRule rule = new MatchRule()
- {
- sex = ""
- };
- MenuListJson menuJson = new MenuListJson();
- menuJson.button.AddRange(new MenuJson[] { conditional });
- menuJson.matchrule = rule;
通过上面的代码了解,我们如果需要增加多个个性化菜单,那么就是通过这个方法,每次创建一个个性化菜单的方式进行构建,当然如果我们使用查询个性化菜单的结果可以看到,如果创建多个,那么conditionalmenu集合里面是多个对象的。
调用代码每次添加一个个性化菜单的操作如下所示。
- string menuid = menuBLL.CreateConditionalMenu(token, menuJson);
- Console.WriteLine("创建菜单:" + (!string.IsNullOrEmpty(menuid) ? "成功:" + menuid : "失败"));
我们创建多个规则后,可以通过TryMatch的方式来检查匹配的结果,也就是对应不同用户(openid)会出现不同的个性化菜单列表了。
- var myResult = menuBLL.TryMatchConditionalMenu(token, openId);
- if (myResult != null)
- {
- Console.WriteLine(myResult.ToJson());
- }
- var userid_female = "oSiLnt2J4mYkhVG3aLTdMIF1hv-s";//女性的ID
- myResult = menuBLL.TryMatchConditionalMenu(token, userid_female);
- if (myResult != null)
- {
- Console.WriteLine(myResult.ToJson());
- }
如果对这个《C#开发微信门户及应用》系列感兴趣,可以关注我的其他文章,系列随笔如下所示:
C#开发微信门户及应用(28)--微信“摇一摇·周边”功能的使用和接口的实现
C#开发微信门户及应用(23)-微信小店商品管理接口的封装和测试
C#开发微信门户及应用(21)-微信企业号的消息和事件的接收处理及解密
C#开发微信门户及应用(19)-微信企业号的消息发送(文本、图片、文件、语音、视频、图文消息等)
C#开发微信门户及应用(18)-微信企业号的通讯录管理开发之成员管理
C#开发微信门户及应用(17)-微信企业号的通讯录管理开发之部门管理
C#开发微信门户及应用(15)-微信菜单增加扫一扫、发图片、发地理位置功能
C#开发微信门户及应用(14)-在微信菜单中采用重定向获取用户数据
C#开发微信门户及应用(11)--微信菜单的多种表现方式介绍
C#开发微信门户及应用(10)--在管理系统中同步微信用户分组信息
C#开发微信门户及应用(9)-微信门户菜单管理及提交到微信服务器
C#开发微信门户及应用(29)--微信个性化菜单的实现的更多相关文章
- C#开发微信门户及应用(25)-微信企业号的客户端管理功能
我们知道,微信公众号和企业号都提供了一个官方的Web后台,方便我们对微信账号的配置,以及相关数据的管理功能,对于微信企业号来说,有通讯录中的组织架构管理.标签管理.人员管理.以及消息的发送等功能,其中 ...
- C#开发微信门户及应用(38)--微信摇一摇红包功能
摇一摇周边红包接口是为线下商户提供的发红包功能.用户可以在商家门店等线下场所通过摇一摇周边领取商家发放的红包.我曾经在<C#开发微信门户及应用(28)--微信“摇一摇·周边”功能的使用和接口的实 ...
- C#开发微信门户及应用(37)--微信公众号标签管理功能
微信公众号,仿照企业号的思路,增加了标签管理的功能,对关注的粉丝可以设置标签管理,实现更加方便的分组管理功能.开发者可以使用用户标签管理的相关接口,实现对公众号的标签进行创建.查询.修改.删除等操作, ...
- C#开发微信门户及应用(36)--微信卡劵管理的封装操作
前面几篇介绍了微信支付方面的内容,本篇继续微信接口的一些其他方面的内容:卡劵管理.卡劵管理是微信接口里面非常复杂的一个部分,里面的接口非常多,我花了不少时间对它进行了封装处理,重构优化等等工作,卡劵在 ...
- C#开发微信门户及应用(35)--微信支付之企业付款封装操作
在前面几篇随笔,都是介绍微信支付及红包相关的内容,其实支付部分的内容还有很多,例如企业付款.公众号支付或刷卡支付.摇一摇红包.代金券等方面的内容,这些都是微信接口支持的内容,本篇继续微信支付这一主题, ...
- C#开发微信门户及应用(34)--微信裂变红包
在上篇随笔<C#开发微信门户及应用(33)--微信现金红包的封装及使用>介绍了普通现金红包的封装和使用,这种红包只能单独一次发给一个人,用户获取了红包就完成了,如果我们让用户收到红包后,可 ...
- C#开发微信门户及应用(33)--微信现金红包的封装及使用
我在上篇随笔<C#开发微信门户及应用(32)--微信支付接入和API封装使用>介绍为微信支付的API封装及使用,其中介绍了如何配置好支付环境,并对扫码支付的两种方式如何在C#开发中使用进行 ...
- C#开发微信门户及应用(32)--微信支付接入和API封装使用
在微信的应用上,微信支付是一个比较有用的部分,但也是比较复杂的技术要点,在微商大行其道的年代,自己的商店没有增加微信支付好像也说不过去,微信支付旨在为广大微信用户及商户提供更优质的支付服务,微信的支付 ...
- C#开发微信门户及应用(24)-微信小店货架信息管理
在前面微信小店系列篇<C#开发微信门户及应用(22)-微信小店的开发和使用>里面介绍了一些微信小店的基础知识,以及<C#开发微信门户及应用(23)-微信小店商品管理接口的封装和测试& ...
随机推荐
- SQL Server 权限管理
标签:SQL SERVER/MSSQL SERVER/数据库/DBA/权限控制/管理/分配/登入名/数据库用户/角色 概述 对数据库系统而言,保证数据的安全性永远都是最重要的问题之一.一个好的数据库环 ...
- .NET core for docker
本文描述下 .net core 在 docker 里面的玩法 首先按照官方文档先 拉取镜像 docker pull microsoft/dotnet:latest 然后就有了 dotnet 这个运行时 ...
- Android学习——windows下搭建Cygwin环境
在上一篇博文<Android学习——windows下搭建NDK_r9环境>中,我们详细的讲解了在windows下进行Android NDK开发环境的配置,我们也讲到了在NDk r7以后,我 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (21) -----第四章 ASP.NET MVC中使用实体框架之在页面中创建查询和使用ASP.NET URL路由过虑
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 4.2. 构建一个搜索查询 搜索数据是几乎所有应用的一个基本功能.它一般是动态的,因 ...
- springboot之filter/listener/servlet
简介 SpringBoot可以简化开发流程,但是在其中如何使用传统的J2EE servlet/listener/filter呢 @Bean配置 在Configuration类中加入filter和ser ...
- 【转】Spark常见问题汇总
原文地址:https://my.oschina.net/tearsky/blog/629201 摘要: 1.Operation category READ is not supported in st ...
- Hadoop HDFS 用户指南
This document is a starting point for users working with Hadoop Distributed File System (HDFS) eithe ...
- Elasticsearch查询——布尔查询Bool Query
Elasticsearch在2.x版本的时候把filter查询给摘掉了,因此在query dsl里面已经找不到filter query了.其实es并没有完全抛弃filter query,而是它的设计与 ...
- AngularJS 源码分析2
上一篇地址 本文主要分析RootScopeProvider和ParseProvider RootScopeProvider简介 今天这个rootscope可是angularjs里面比较活跃的一个pro ...
- Linux更改用户密码
登录虚拟机后,使用passwd密令更改用户密码,新密码需要输入两次才能更改成功.不多说,直接上代码 [root@localhost Desktop]# passwd //使用passwd密令 Chan ...