C#开发微信门户及应用教程javascript
C#开发微信门户及应用(1)--开始使用微信接口... 6php
一、微信帐号... 6html
二、微信菜单定义... 7java
三、接入微信的连接处理... 8web
四、使用开发方式建立菜单... 14ajax
五、我建立的菜单案例... 17数据库
C#开发微信门户及应用(2)--微信消息的处理和应答... 18json
一、微信的消息应答交互... 18api
二、微信的管理接口... 25数组
C#开发微信门户及应用(3)--文本消息和图文消息的应答... 29
一、实体信息关系及定义... 30
二、消息的回复处理... 37
C#开发微信门户及应用(4)--关注用户列表及详细信息管理... 41
一、关注用户列表及用户分组信息... 41
二、获取AIP调用者的的Token.. 47
三、获取关注用户列表... 50
四、获取用户详细信息... 59
C#开发微信门户及应用(5)--用户分组信息管理... 62
一、用户分组管理内容... 62
二、用户分组管理接口的实现... 67
三、用户分组接口的调用... 79
C#开发微信门户及应用(6)--微信门户菜单的管理操做... 82
一、菜单的基础信息... 82
二、菜单的实体类定义... 85
三、菜单管理操做的接口实现... 91
C#开发微信门户及应用(7)-微信多客服功能及开发集成... 100
一、多客服准备工做... 101
二、使用多客服客户端或助手操做... 102
三、微信多客服的开发使用... 103
C#开发微信门户及应用(8)-微信门户应用管理系统功能介绍... 108
一、微信菜单管理... 109
二、菜单事件的处理... 112
三、微信消息内容管理... 116
四、应答指令的维护... 121
五、订阅用户管理... 129
六、用户分组管理... 134
七、多媒体管理... 136
八、图文消息处理... 139
九、会话消息管理... 145
十、群发消息管理... 147
C#开发微信门户及应用(9)-微信门户菜单管理及提交到微信服务器... 149
一、微信菜单的要求及相关界面设计... 150
二、提交菜单到微信服务器的操做... 153
C#开发微信门户及应用(10)--在管理系统中同步微信用户分组信息... 160
一、用户分组,在管理系统中的界面设计... 161
二、分组同步操做代码展现... 163
C#开发微信门户及应用(11)--微信菜单的多种表现方式介绍... 172
一、微信自定义菜单的分类... 172
二、重定向类型菜单的URL. 174
三、重定向连接菜单的用途... 182
C#开发微信门户及应用(12)-使用语音处理... 182
一、微信语音接口的定义0. 183
二、语音的处理操做... 186
C#开发微信门户及应用(13)-使用地理位置扩展相关应用... 197
一、微信的地理位置信息... 198
二、地址位置的应用处理... 205
三、地址位置应用扩展... 208
C#开发微信门户及应用(14)-在微信菜单中采用重定向获取用户数据... 223
一、微信重定向菜单的配置... 224
二、脚本转换操做的实现代码... 227
三、重定向页面的设计及处理... 230
C#开发微信门户及应用(15)-微信菜单增长扫一扫、发图片、发地理位置功能... 233
一、微信几个功能的官方介绍... 234
二、微信新菜单功能的测试公众号... 236
三、改进菜单对象和提交菜单... 238
四、微信扫一扫功能集成... 245
五、新菜单功能测试发现的问题... 250
C#开发微信门户及应用(16)-微信企业号的配置和使用... 251
一、微信企业号的注册和登录... 251
二、设置开发回调模式... 256
三、实现回调页面的功能开发... 259
C#开发微信门户及应用(17)-微信企业号的通信录管理开发之部门管理... 266
一、企业组织的建立和配置... 266
二、API访问的全局惟一票据AccessToken的获取... 270
二、通信录管理之部门信息的维护... 272
三、部门管理的API调用... 278
C#开发微信门户及应用(18)-微信企业号的通信录管理开发之成员管理... 281
一、成员的建立操做... 281
二、成员的更新操做... 287
三、成员的删除、成员的获取、部门成员的获取操做... 290
七、综合例子调用代码... 295
C#开发微信门户及应用(19)-微信企业号的消息发送(文本、图片、文件、语音、视频、图文消息等) 299
一、企业号特色... 299
二、企业号的管理接口内容... 300
三、企业号消息和事件的处理... 302
四、企业号消息管理... 304
五、消息接口的定义和实现... 310
六、消息的发送操做和实际效果... 313
C#开发微信门户及应用(20)-微信企业号的菜单管理... 317
一、菜单的整体介绍... 318
二、菜单的实体类定义和接口定义处理... 319
三、企业号菜单管理接口的调用和处理效果... 324
微信应用如火如荼,不少公司都但愿搭上信息快车,这个是一个商机,也是一个技术的方向,所以,有空研究下、学习下微信的相关开发,也就 成为平常计划的重要事情之一了。本系列文章但愿从一个按部就班的角度上,全面介绍微信的相关开发过程和相关经验总结,但愿给你们了解一下相关的开发历程。 本随笔主要针对微信开发过程的前期准备和一些初始的工做的介绍。
在写下本文的以前一周时间里,我主要就是参考一些介绍文章以及微信公众平台的相关接口说明,并结合C#的代码开发,整理了本身公司的门 户界面,实现了微信工做号的初步用户交互和信息展现工做,随着工做的进一步开展,愈来愈多的功能可能加入,并但愿从应用角度上扩展微信的接口,从而实现我 对微信接口的技术探秘和了解过程。
要开发使用微信的平台API,就须要到微信的公众平台(https://mp.weixin.qq.com/)去注册,拥有一个服务号或者订阅号,服务号主要面对企业和组织,订阅号主要面向组织和我的,他们之间有必定的差别,根据不一样的须要本身申请对应的帐号便可。
为了使用一些高级的接口,你可能须要拥有服务号和高级的认证。帐号注册过程,须要下载一个申请表格,打印并盖公章,另外还须要申请人拿着身份证拍照(有点怪异,呵呵),而后上传到服务器进行审核,通常很快就能获取批复。
我以公司名义申请了服务号,帐号注册后,会在主界面上显示你的相关信息,另外给你申请一个二维码的东西,扫描二维码便可进入公司的微信关注确认对话框,很是方便。以下就是我申请后的公司帐号二维码,能够直接使用扫描。
微信有两种方式的菜单定义,一种是编辑模式,一种是开发模式,二者互斥,也就是说,一旦咱们采用了开发模式,就不能使用编辑模式了,反过来也同样。编辑下的菜单,其实也是能够管理的,可是微信不支持,以为很不爽。
通常状况下,若是咱们刚刚申请了微信号码,能够使用编辑菜单测试一下,根听说明编辑一些菜单试试。虽然微信说24小时内更新,不过通常很快,最快可能一两分钟就更新了,感受仍是不错的。
使用开发者模式,你须要根据微信的要求,在服务器上放置一个页面连接,使用C#开发的,能够采用***.ashx的命名方式,使用Asp.NET的通常处理程序便可,不须要使用普通的页面。
使用开发模式的菜单,也就是能够调用微信API进行菜单建立的工做,对于调用微信的API(微信有不少API能够调用),咱们须要知道,有几个参数的重要性,因此在开发模式打开的时候,会给你列出这些参数,以下所示。
上面说了,你申请开发模式对菜单或者对其余API的调用,你须要顺利经过接入微信的测试,也就是确认你填写的连接存在并能顺利通过微信的回调测试。微信提供了一个PHP的页面处理例子,若是咱们是C#开发的呢,能够搜一下就会获得答案,个人处理方式以下所示。
建立一个通常处理程序,而后在其处理页面里面增长一个处理逻辑,若是是非POST方式的内容,就是表示微信进行的Get测试,你须要增长一些处理逻辑,把它给你的内容传回去便可,若是是POST方式的,就是微信服务器对接口消息的请求操做了,后面介绍。
///<summary>
/// 微信接口。统一接收并处理信息的入口。
///</summary>
publicclass wxapi : IHttpHandler
{
publicvoid ProcessRequest(HttpContext context)
{
string postString = string.Empty;
if (HttpContext.Current.Request.HttpMethod.ToUpper() == "POST")
{
using (Stream stream = HttpContext.Current.Request.InputStream)
{
Byte[] postBytes = new Byte[stream.Length];
stream.Read(postBytes, 0, (Int32)stream.Length);
postString = Encoding.UTF8.GetString(postBytes);
}
if (!string.IsNullOrEmpty(postString))
{
Execute(postString);
}
}
else
{
Auth(); //微信接入的测试
}
}
通常来讲,Auth函数里面,就是要对相关的参数进行获取,而后进行处理返回给微信服务器。
string token = "****";//你申请的时候填写的Token
string echoString = HttpContext.Current.Request.QueryString["echoStr"];
string signature = HttpContext.Current.Request.QueryString["signature"];
string timestamp = HttpContext.Current.Request.QueryString["timestamp"];
string nonce = HttpContext.Current.Request.QueryString["nonce"];
完整的Author函数代码以下所示,其中我把业务逻辑进行进一步抽取到了一个新的类里面,方便业务逻辑的管理。
///<summary>
/// 成为开发者的第一步,验证并相应服务器的数据
///</summary>
privatevoid Auth()
{
string token = ConfigurationManager.AppSettings["WeixinToken"];//从配置文件获取Token
if (string.IsNullOrEmpty(token))
{
LogTextHelper.Error(string.Format("WeixinToken 配置项没有配置!"));
}
string echoString = HttpContext.Current.Request.QueryString["echoStr"];
string signature = HttpContext.Current.Request.QueryString["signature"];
string timestamp = HttpContext.Current.Request.QueryString["timestamp"];
string nonce = HttpContext.Current.Request.QueryString["nonce"];
if (new BasicApi().CheckSignature(token, signature, timestamp, nonce))
{
if (!string.IsNullOrEmpty(echoString))
{
HttpContext.Current.Response.Write(echoString);
HttpContext.Current.Response.End();
}
}
}
而对微信参数的签名并返回的操做CheckSignature,代码以下所示。
///<summary>
/// 验证微信签名
///</summary>
publicbool CheckSignature(string token, string signature, string timestamp, string nonce)
{
string[] ArrTmp = { token, timestamp, nonce };
Array.Sort(ArrTmp);
string tmpStr = string.Join("", ArrTmp);
tmpStr = FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1");
tmpStr = tmpStr.ToLower();
if (tmpStr == signature)
{
returntrue;
}
else
{
returnfalse;
}
}
一旦你顺利经过微信的认证,那么它就让你以开发方式调用它的API,而且能够随意建立你的菜单了。
建立菜单的方式,你能够经过下面的位置进入到他的API处理界面里面。
进入后,你会发现微信把不少消息的处理,分门别类放到不一样的分类里面了。
其实咱们如今初步要作的就是如何看看,使用代码方式调用建立菜单,进入菜单的API调试界面里面。
你会发现里面还须要输入一个Access_Token的东西,这个是一个会话身份认证,所以你还须要到接口里面去找这个如何建立的。下面图中的两个红色部分,就是咱们开始的时候,微信提示咱们“开发者凭据”的两个关键参数。
弄完这些,你就能够根据得到的Access_Token进行菜单的建立工做了,根据菜单的定义,它分为几类,能够分为URL方式(View),事件方式(Click)。
click:用户点击click类型按钮后,微信服务器会经过消息接口推送消息类型为event 的结构给开发者(参考消息接口指南),而且带上按钮中开发者填写的key值,开发者能够经过自定义的key值与用户进行交互;
view:用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的url值 (即网页连接),达到打开网页的目的,建议与网页受权获取用户基本信息接口结合,得到用户的登入我的信息。
在随笔的开始,我公布了一个二维码,一旦使用微信扫一扫,进行关注服务号后,那么就能够看到我本身建立的菜单了。主菜单通常最多三列,每一个主菜单还能够有子菜单,他们的文字都有所限制的。
咱们来看看我公司的微信门户菜单,看起来是否是很酷呢。
微信应用如火如荼,不少公司都但愿搭上信息快车,这个是一个商机,也是一个技术的方向,所以,有空研究下、学习下微信的相关开发,也就 成为计划的安排事情之一了。本系列文章但愿从一个按部就班的角度上,全面介绍微信的相关开发过程和相关经验总结,但愿给你们了解一下相关的开发历程。本篇 随笔主要基于上一篇《C#开发微信门户及应用(1)--开始使用微信接口》的基础上进行深刻的介绍,介绍微信消息的处理和应答的过程。
咱们知道,微信的服务器架起了客户手机和开发者服务器的一个桥梁,经过消息的传递和响应,实现了与用户的交互操做,下面是它的消息流程图。
微信向开发者服务器请求的消息包含了多种类型,不过基原本说,分为了文本消息处理、事件消息处理、语音消息的识别,以及成为开发者以前的那个消息认证操做基本分类,下面是我绘制的一个消息分类图,其中介绍了这几种关系,以及各自的消息细化分类。
对于这些消息的请求,咱们在开发服务器端,须要编写相关的逻辑进行对应给的处理,而后给微信服务器平台回应消息便可。
在前一篇的随笔里面我贴过代码,介绍微信消息处理的入口操做,代码以下所示。
publicvoid ProcessRequest(HttpContext context)
{
//WHC.Framework.Commons.LogTextHelper.Info("测试记录");
string postString = string.Empty;
if (HttpContext.Current.Request.HttpMethod.ToUpper() == "POST")
{
using (Stream stream = HttpContext.Current.Request.InputStream)
{
Byte[] postBytes = new Byte[stream.Length];
stream.Read(postBytes, 0, (Int32)stream.Length);
postString = Encoding.UTF8.GetString(postBytes);
}
if (!string.IsNullOrEmpty(postString))
{
Execute(postString);
}
}
else
{
Auth();
}
}
其中的Execute(postString);就是对消息的处理函数,它实现了对不一样消息的分发处理过程。‘
///<summary>
/// 处理各类请求信息并应答(经过POST的请求)
///</summary>
///<param name="postStr">POST方式提交的数据</param>
privatevoid Execute(string postStr)
{
WeixinApiDispatch dispatch = new WeixinApiDispatch();
string responseContent = dispatch.Execute(postStr);
HttpContext.Current.Response.ContentEncoding = Encoding.UTF8;
HttpContext.Current.Response.Write(responseContent);
}
里面的WeixinApiDispatch就是一个分发的管理类,它提取请求消息的内容,并构建不一样类型的消息参数,传递给不一样的响应函数进行处理,而后返回封装好的XML内容,做为响应。
具体的代码处理逻辑以下图所示。
这个消息处理接口,其实就是定义好一系列的对请求消息的处理操做,参数是不一样给的消息对象,具体的代码定义以下所示(因为篇幅缘由,省略部分接口,具体能够参考上图)。
///<summary>
/// 客户端请求的数据接口
///</summary>
publicinterface IWeixinAction
{
///<summary>
/// 对文本请求信息进行处理
///</summary>
///<param name="info">文本信息实体</param>
///<returns></returns>
string HandleText(RequestText info);
///<summary>
/// 对图片请求信息进行处理
///</summary>
///<param name="info">图片信息实体</param>
///<returns></returns>
string HandleImage(RequestImage info);
...........................
///<summary>
/// 对订阅请求事件进行处理
///</summary>
///<param name="info">订阅请求事件信息实体</param>
///<returns></returns>
string HandleEventSubscribe(RequestEventSubscribe info);
///<summary>
/// 对菜单单击请求事件进行处理
///</summary>
///<param name="info">菜单单击请求事件信息实体</param>
///<returns></returns>
string HandleEventClick(RequestEventClick info);
..............................
}
从上面的代码能够看出,不一样的消息,处处理函数这里,就以不一样的消息实体类的方式传递过来了(注意:实体类是我根据程序开发须要本身定义的,非微信自己的实体类),这样很是方便咱们处理操做,不然每次须要解析不一样的消息内容,很容易出现问题,这样强类型的数据类型,提升了咱们开发微信应用的强壮型和高效性。这些实体类的对象有必定的继承关系的,他们的继承关系以下所示。
上面的消息分类是微信服务器向开发者服务器发送的消息请求操做,还有一种消息,是咱们开发者服务器向微信服务器进行的消息请求或者响应,这种这里暂且称之为微信的管理接口,它代表了咱们能够经过这些接口进行相关的消息回复或者数据管理操做。它的分类图以下所示。
微信的回复消息处理,它也和上面小节的信息同样,它也是继承自BaseMessage实体类的(一样,下图的实体类及其继承关系也是自定义的,方便程序开发),它的关系以下所示
回复的消息,通常用的最多的是文本消息和图文消息。
文本消息的效果以下所示。
图文消息,能够增长图片,还能够增长详细的连接页面,是很是好看的一种效果,对于一些内容比较多,但愿展示更好效果的,通常采用这种,效果以下所示。
微信应用如火如荼,不少公司都但愿搭上信息快车,这个是一个商机,也是一个技术的方向,所以,有空研究下、学习下微信的相关开发,也就 成为计划的安排事情之一了。本系列文章但愿从一个按部就班的角度上,全面介绍微信的相关开发过程和相关经验总结,但愿给你们了解一下相关的开发历程。
在前面两篇两篇随笔《C#开发微信门户及应用(1)--开始使用微信接口》和《C#开发微信门户及应用(2)--微信消息的处理和应答》里面,大体介绍了我微信应用的框架构建,本随笔继续介绍这一主题,介绍消息应答里面的文本应答和图文应答的过程。
咱们知道,给手机用户发送响应消息,它能够分为好多种方式,如回复文本消息、回复图片消息、回复语音消息、回复视频消息、回复音乐消息、回复图文消息等,以下所示。
而其中图片、视频、语音这三种方式,是须要开通微信认证才能够向用户发送存在微信服务器上的媒体信息,通常没有认证的公众号或者服务号,是不能发送这几种内容的。
在上一篇微信开发的随笔中,我展现了对接收消息和回复消息的应用实体类,这些实体类是我根据须要,根据开发须要,在应用层面对它们进行了封装,如回复的消息关系以下所示。
消息基类BaseMessage的实体类定义以下所示,它对日期构造了一个整形数值,并具有了一些常规的属性,而且还有一个重要的ToXML方法,用来给方法传递这些XML数据的。
///<summary>
/// 基础消息内容
///</summary>
[XmlRoot(ElementName = "xml")]
publicclass BaseMessage
{
///<summary>
/// 初始化一些内容,如建立时间为整形,
///</summary>
public BaseMessage()
{
this.CreateTime = DateTime.Now.DateTimeToInt();
}
///<summary>
/// 开发者微信号
///</summary>
publicstring ToUserName { get; set; }
///<summary>
/// 发送方账号(一个OpenID)
///</summary>
publicstring FromUserName { get; set; }
///<summary>
/// 消息建立时间 (整型)
///</summary>
publicint CreateTime { get; set; }
///<summary>
/// 消息类型
///</summary>
publicstring MsgType { get; set; }
publicvirtualstring ToXml()
{
this.CreateTime = DateTime.Now.DateTimeToInt();//从新更新
return MyXmlHelper.ObjectToXml(this);
}
}
回复的文本消息实体类代码以下所示,咱们能够看到,它继承了不少通用的实体属性,而且还具有了一个ToXml的通用方法,咱们须要把它转换为响应的XML的时候,就使用这个方法就能够了。
///<summary>
/// 回复文本消息
///</summary>
[System.Xml.Serialization.XmlRoot(ElementName = "xml")]
publicclass ResponseText : BaseMessage
{
public ResponseText()
{
this.MsgType = ResponseMsgType.Text.ToString().ToLower();
}
public ResponseText(BaseMessage info) : this()
{
this.FromUserName = info.ToUserName;
this.ToUserName = info.FromUserName;
}
///<summary>
/// 内容
///</summary>
publicstring Content { get; set; }
}
而图文消息对象类ResponseNews,它包含更多的信息定义
///<summary>
/// 回复图文消息
///</summary>
[System.Xml.Serialization.XmlRoot(ElementName = "xml")]
publicclass ResponseNews : BaseMessage
{
public ResponseNews()
{
this.MsgType = ResponseMsgType.News.ToString().ToLower();
this.Articles = new List<ArticleEntity>();
}
public ResponseNews(BaseMessage info) : this()
{
this.FromUserName = info.ToUserName;
this.ToUserName = info.FromUserName;
}
///<summary>
/// 图文消息个数,限制为10条之内
///</summary>
publicint ArticleCount
{
get
{
returnthis.Articles.Count;
}
set
{
;//增长这个步骤才出来XML内容
}
}
///<summary>
/// 图文列表。
/// 多条图文消息信息,默认第一个item为大图,注意,若是图文数超过10,则将会无响应
///</summary>
[System.Xml.Serialization.XmlArrayItem("item")]
public List<ArticleEntity> Articles { get; set; }
}
而其中的图文列表集合中的对象,它也是一个实体类型,包含了一些图文的连接,标题等信息,不在赘述。
如对于文本消息,咱们能够用如下的方式进行处理。
ResponseText response = new ResponseText(info);
response.Content = "抱歉,此功能暂未开通。";
result = response.ToXml();
对于图文消息,咱们可能须要录入更多的消息才能返回更好的效果。
注意图文的消息,图片的尺寸最好按照官方的标准,不然在手机上看起来很差看,官方的标准好像是宽高是(360,200)像素
///<summary>
/// 订阅或者显示公司信息
///</summary>
///<param name="info"></param>
///<returns></returns>
privatestring ShowCompanyInfo(BaseMessage info)
{
string result = "";
//使用在微信平台上的图文信息(单图文信息)
ResponseNews response = new ResponseNews(info);
ArticleEntity entity = new ArticleEntity();
entity.Title = "广州爱奇迪软件科技有限公司";
entity.Description = "欢迎关注广州爱奇迪软件--专业的单位信息化软件和软件开发框架提供商,咱们立志于为客户提供最好的软件及服务。\r\n";
entity.Description += "咱们是一家极富创新性的软件科技公司,从事研究、开发并销售最可靠的、安全易用的技术产品及优质专业的服务,帮助全球客户和合做伙伴取得成功。\r\n......(此处省略1000字,哈哈)";
entity.PicUrl = "http://www.iqidi.com/WeixinImage/company.png";
entity.Url = "http://www.iqidi.com";
response.Articles.Add(entity);
result = response.ToXml();
return result;
}
咱们来看看我公司的微信门户菜单,看起来是否是很酷呢。
对于这两种(文本消息、图文消息)用的地方是最多,不少微信门户,都主要是使用这两种方式进行响应。固然,咱们还能够根据客户手机提交上来的各类消息进行不一样的处理,请求消息的类型我在上一篇的随笔有介绍,以下所示。
须要关注了解总体效果,能够使用微信直接扫描二维码便可。
在上个月的对C#开发微信门户及应用作了介绍,写过了几篇的随笔进行分享,因为时间关系,间隔了一段时间没有继续写这个系列的博客了,并非对这个方面中止了研究,而是继续深刻探索这方面的技术,为了更好的应用起来,专心作好底层的技术开发。
微信的很重要的一个特色就是可以利用其平台庞大的用户群体,所以很容易整合在CRM(客户关系管理)系统里面,服务号和订阅好都可以向关注者推送相 关的产品消息,还能和48小时内响应消息和事件的活跃用户进行交互对话,所以用户信息是微信API里面很是重要的一环,本随笔主要介绍获取关注用户、查看 用户信息、分组管理等方面的开发应用。
在微信的管理平台上,咱们能够看到本身帐号的关注者用户,以及用户分组信息,以下所示。
上面的管理界面,能看到关注者用户的基础信息,可是使用微信API获取到的是一个称之为OpenID的列表,咱们先了解这个东西是什么?微信API的说明给出下面的解析:
关注者列表由一串OpenID(加密后的微信号,每一个用户对每一个公众号的OpenID是惟一的。对于不一样公众号,同一用户的openid不一样)组成。公众号可经过本接口来根据OpenID获取用户基本信息,包括昵称、头像、性别、所在城市、语言和关注时间。
上面的解析意思很清楚了,就是一个用户关注咱们的公众号,那么无论他是第几回关注,对咱们公众号来讲,都是一个肯定的值;可是,一个用户对其余公众号,却有着其余不一样的OpenID。
微信提供了为数很少的几个关键字信息,用来记录用户的相关内容,根据用户的相关定义,咱们定义一个实体类,用来放置获取回来的用户信息。
///<summary>
/// 高级接口获取的用户信息。
/// 在关注者与公众号产生消息交互后,公众号可得到关注者的OpenID
/// (加密后的微信号,每一个用户对每一个公众号的OpenID是惟一的。对于不一样公众号,同一用户的openid不一样)。
/// 公众号可经过本接口来根据OpenID获取用户基本信息,包括昵称、头像、性别、所在城市、语言和关注时间。
///</summary>
publicclass UserJson : BaseJsonResult
{
///<summary>
/// 用户是否订阅该公众号标识,值为0时,表明此用户没有关注该公众号,拉取不到其他信息。
///</summary>
publicint subscribe { get; set; }
///<summary>
/// 用户的标识,对当前公众号惟一
///</summary>
publicstring openid { get; set; }
///<summary>
/// 用户的昵称
///</summary>
publicstring nickname { get; set; }
///<summary>
/// 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
///</summary>
publicint sex { get; set; }
///<summary>
/// 用户的语言,简体中文为zh_CN
///</summary>
publicstring language { get; set; }
///<summary>
/// 用户所在城市
///</summary>
publicstring city { get; set; }
///<summary>
/// 用户所在省份
///</summary>
publicstring province { get; set; }
///<summary>
/// 用户所在国家
///</summary>
publicstring country { get; set; }
///<summary>
/// 用户头像,最后一个数值表明正方形头像大小(有0、4六、6四、9六、132数值可选,0表明640*640正方形头像),用户没有头像时该项为空
///</summary>
publicstring headimgurl { get; set; }
///<summary>
/// 用户关注时间,为时间戳。若是用户曾屡次关注,则取最后关注时间
///</summary>
publiclong subscribe_time { get; set; }
}
根据分组信息定义,咱们定义一个分组的实体类信息。
///<summary>
/// 分组信息
///</summary>
publicclass GroupJson : BaseJsonResult
{
///<summary>
/// 分组id,由微信分配
///</summary>
publicint id { get; set; }
///<summary>
/// 分组名字,UTF8编码
///</summary>
publicstring name { get; set; }
}
在作微信API的开发,不少时候,咱们都须要传入一个AccessToken,这个就是区分调用者和记录会话信息的字符串,所以,在学习全部API开发以前,咱们须要很好理解这个访问控制参数。
这个对象的定义,咱们能够从微信的API说明中了解。
access_token是公众号的全局惟一票据,公众号调用各接口时都需使用access_token。正常状况下access_token有效期为7200秒,重复获取将致使上次获取的access_token失效。因为获取access_token的api调用次数很是有限,建议开发者全局存储与更新access_token,频繁刷新access_token会致使api调用受限,影响自身业务。
根据上面的说明定义,咱们能够看到,它是一个和身份,以及会话时间有关的一个参数,并且它的产生次数有限制,所以要求咱们须要对它进行缓存并重复利用,在会话到期以前,咱们应该尽量重用这个参数,避免反复请求,增长服务器压力,以及调用的时间。
我定义了一个方法,用来构造生成相关的Access Token,并且它具备缓存的功能,但具体如何缓存及使用,对我API的调用是透明的,咱们只要用的时候,就对它调用就是了。
/// 获取凭证接口
///</summary>
///<param name="appid">第三方用户惟一凭证</param>
///<param name="secret">第三方用户惟一凭证密钥,既appsecret</param>
string GetAccessToken(string appid, string secret);
缓存主要是基于.NET4增长的类库MemoryCache,这个是一个很是不错的缓存类。
个人获取AccessToken的操做实现代码以下所示。
///<summary>
/// 获取每次操做微信API的Token访问令牌
///</summary>
///<param name="appid">应用ID</param>
///<param name="secret">开发者凭据</param>
///<returns></returns>
publicstring GetAccessToken(string appid, string secret)
{
//正常状况下access_token有效期为7200秒,这里使用缓存设置短于这个时间便可
string access_token = MemoryCacheHelper.GetCacheItem<string>("access_token", delegate()
{
string grant_type = "client_credential";
var url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type={0}&appid={1}&secret={2}",
grant_type, appid, secret);
HttpHelper helper = new HttpHelper();
string result = helper.GetHtml(url);
string regex = "\"access_token\":\"(?<token>.*?)\"";
string token = CRegex.GetText(result, regex, "token");
return token;
},
new TimeSpan(0, 0, 7000)//7000秒过时
);
return access_token;
}
因为咱们知道,AccessToken默认是7200秒过时,所以在这个时间段里面,咱们尽量使用缓存来记录它的值,若是超过了这个时间,咱们调用这个方法的时候,它会自动从新获取一个新的值给咱们了。
获取关注用户列表,一次拉取API调用,最多拉取10000个关注者的OpenID,能够经过屡次拉取的方式来知足需求。微信的接口定义以下所示。
http请求方式: GET(请使用https协议)
https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID
这个接口返回的数据是
{"total":2,"count":2,"data":{"openid":["","OPENID1","OPENID2"]},"next_openid":"NEXT_OPENID"}
根据返回的Json数据定义,咱们还须要定义两个实体类,用来存放返回的结果。
///<summary>
/// 获取关注用户列表的Json结果
///</summary>
publicclass UserListJsonResult : BaseJsonResult
{
///<summary>
/// 关注该公众帐号的总用户数
///</summary>
publicint total { get; set; }
///<summary>
/// 拉取的OPENID个数,最大值为10000
///</summary>
publicint count { get; set; }
///<summary>
/// 列表数据,OPENID的列表
///</summary>
public OpenIdListData data { get; set; }
///<summary>
/// 拉取列表的后一个用户的OPENID
///</summary>
publicstring next_openid { get; set; }
}
///<summary>
/// 列表数据,OPENID的列表
///</summary>
publicclass OpenIdListData
{
///<summary>
/// OPENID的列表
///</summary>
public List<string> openid { get; set; }
}
为了获取相关的用户信息,我定义了一个接口,用来获取用户的信息,接口定义以下所示。
///<summary>
/// 微信用户管理的API接口
///</summary>
publicinterface IUserApi
{
///<summary>
/// 获取关注用户列表
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="nextOpenId">第一个拉取的OPENID,不填默认从头开始拉取</param>
///<returns></returns>
List<string> GetUserList(string accessToken, string nextOpenId = null);
///<summary>
/// 获取用户基本信息
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="openId">普通用户的标识,对当前公众号惟一</param>
///<param name="lang">返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语</param>
UserJson GetUserDetail(string accessToken, string openId, Language lang = Language.zh_CN);
而后在实现类里面,咱们分别对上面两个接口进行实现,获取用户列表信息以下所示。
///<summary>
/// 获取关注用户列表
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="nextOpenId">第一个拉取的OPENID,不填默认从头开始拉取</param>
///<returns></returns>
public List<string> GetUserList(string accessToken, string nextOpenId = null)
{
List<string> list = new List<string>();
string url = string.Format("https://api.weixin.qq.com/cgi-bin/user/get?access_token={0}", accessToken);
if (!string.IsNullOrEmpty(nextOpenId))
{
url += "&next_openid=" + nextOpenId;
}
UserListJsonResult result = JsonHelper<UserListJsonResult>.ConvertJson(url);
if (result != null&& result.data != null)
{
list.AddRange(result.data.openid);
}
return list;
}
咱们看到,转换的逻辑已经放到了JsonHelper里面去了,这个辅助类里面分别对数值进行了获取内容,验证返回值,而后转换正确实体类几个部分的操做。
获取内容,经过辅助类HttpHelper进行,这个在个人公用类库里面,里面的逻辑主要就是经过HttpRequest进行数据的获取操做,不在赘述。
HttpHelper helper = new HttpHelper();
string content = helper.GetHtml(url);
因为返回的内容,咱们须要判断它是否正确返回所需的结果,若是没有,抛出自定义的相关异常,方便处理,具体以下所示。
///<summary>
/// 检查返回的记录,若是返回没有错误,或者结果提示成功,则不抛出异常
///</summary>
///<param name="content">返回的结果</param>
///<returns></returns>
privatestaticbool VerifyErrorCode(string content)
{
if (content.Contains("errcode"))
{
ErrorJsonResult errorResult = JsonConvert.DeserializeObject<ErrorJsonResult>(content);
//非成功操做才记录异常,由于有些操做是返回正常的结果({"errcode": 0, "errmsg": "ok"})
if (errorResult != null&& errorResult.errcode != ReturnCode.请求成功)
{
string error = string.Format("微信请求发生错误!错误代码:{0},说明:{1}", (int)errorResult.errcode, errorResult.errmsg);
LogTextHelper.Error(errorResult);
thrownew WeixinException(error);//抛出错误
}
}
returntrue;
}
而后转换为相应的格式,就是经过Json.NET的类库进行转换。
T result = JsonConvert.DeserializeObject<T>(content);
return result;
这样咱们就能够在ConvertJson函数实体里面,完整的进行处理和转换了,转换完整的函数代码以下所示。
///<summary>
/// Json字符串操做辅助类
///</summary>
publicclass JsonHelper<T>where T : class, new()
{
///<summary>
/// 检查返回的记录,若是返回没有错误,或者结果提示成功,则不抛出异常
///</summary>
///<param name="content">返回的结果</param>
///<returns></returns>
privatestaticbool VerifyErrorCode(string content)
{
if (content.Contains("errcode"))
{
ErrorJsonResult errorResult = JsonConvert.DeserializeObject<ErrorJsonResult>(content);
//非成功操做才记录异常,由于有些操做是返回正常的结果({"errcode": 0, "errmsg": "ok"})
if (errorResult != null&& errorResult.errcode != ReturnCode.请求成功)
{
string error = string.Format("微信请求发生错误!错误代码:{0},说明:{1}", (int)errorResult.errcode, errorResult.errmsg);
LogTextHelper.Error(errorResult);
thrownew WeixinException(error);//抛出错误
}
}
returntrue;
}
///<summary>
/// 转换Json字符串到具体的对象
///</summary>
///<param name="url">返回Json数据的连接地址</param>
///<returns></returns>
publicstatic T ConvertJson(string url)
{
HttpHelper helper = new HttpHelper();
string content = helper.GetHtml(url);
VerifyErrorCode(content);
T result = JsonConvert.DeserializeObject<T>(content);
return result;
}
}
调用这个API的界面层代码以下所示(测试代码)
IUserApi userBLL = new UserApi();
List<string> userList = userBLL.GetUserList(token)
上面的获取列表操做,相对比较简单,并且不用POST任何数据,所以经过Get协议就能获取到所需的数据。
本小节继续介绍获取用户详细信息的操做,这个操做也是经过GET协议就能够完成的。
这个API的调用定义以下所示:
http请求方式: GET
https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
经过传入一个OpenId,咱们就能很好获取到用户的相关信息了。
前面小节咱们已经定义了它的接口,说明了传入及返回值,根据定义,它的实现函数以下所示。
///<summary>
/// 获取用户基本信息
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="openId">普通用户的标识,对当前公众号惟一</param>
///<param name="lang">返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语</param>
public UserJson GetUserDetail(string accessToken, string openId, Language lang = Language.zh_CN)
{
string url = string.Format("https://api.weixin.qq.com/cgi-bin/user/info?access_token={0}&openid={1}&lang={2}",
accessToken, openId, lang.ToString());
UserJson result = JsonHelper<UserJson>.ConvertJson(url);
return result;
}
最后,咱们结合获取用户列表和获取用户详细信息的两个API,咱们看看调用的代码(测试代码)。
privatevoid btnGetUsers_Click(object sender, EventArgs e)
{
IUserApi userBLL = new UserApi();
List<string> userList = userBLL.GetUserList(token);
foreach (string openId in userList)
{
UserJson userInfo = userBLL.GetUserDetail(token, openId);
if (userInfo != null)
{
string tips = string.Format("{0}:{1}", userInfo.nickname, userInfo.openid);
Console.WriteLine(tips);
}
}
}
在上个月的对C#开发微信门户及应用作了介绍,写过了几篇的随笔进行分享,因为时间关系,间隔了一段时间没有继续写这个系列的博客了,并非对这个 方面中止了研究,而是继续深刻探索这方面的技术,为了更好的应用起来,专心作好底层的技术开发。本篇继续上一篇的介绍,主要介绍分组管理方面的开发应用, 这篇的内容和上一篇,做为一个完整的用户信息和分组信息管理的组合。
用户分组的引入,主要是方便管理关注者列表,以及方便向不一样的组别发送消息的操做的,一个公众帐号,最多支持建立500个分组。
用户分组管理,包含下面几个方面的内容:
1 建立分组
2 查询全部分组
3 查询用户所在分组
4 修改分组名
5 移动用户分组
微信对于建立分组的定义以下所示。
http请求方式: POST(请使用https协议)
https://api.weixin.qq.com/cgi-bin/groups/create?access_token=ACCESS_TOKEN
POST数据格式:json
POST数据例子:{"group":{"name":"test"}}
正常返回的结果以下所示。
{
"group": {
"id": 107,
"name": "test"
}
}
其余接口,也是相似的方式,经过POST一些参数进去URL里面,获取返回的Json数据。
前面随笔定义了GroupJson的实体类信息以下所示。
/// <summary>
/// 分组信息
/// </summary>
public class GroupJson : BaseJsonResult
{
/// <summary>
/// 分组id,由微信分配
/// </summary>
public int id { get; set; }
/// <summary>
/// 分组名字,UTF8编码
/// </summary>
public string name { get; set; }
}
根据以上几个接口的定义,我定义了几个接口,并把它们概括到用户管理的API接口里面。
///<summary>
/// 查询全部分组
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<returns></returns>
List<GroupJson> GetGroupList(string accessToken);
///<summary>
/// 建立分组
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="name">分组名称</param>
///<returns></returns>
GroupJson CreateGroup(string accessToken, string name);
///<summary>
/// 查询用户所在分组
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="openid">用户的OpenID</param>
///<returns></returns>
int GetUserGroupId(string accessToken, string openid);
///<summary>
/// 修改分组名
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="id">分组id,由微信分配</param>
///<param name="name">分组名字(30个字符之内)</param>
///<returns></returns>
CommonResult UpdateGroupName(string accessToken, int id, string name);
///<summary>
/// 移动用户分组
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="openid">用户的OpenID</param>
///<param name="to_groupid">分组id</param>
///<returns></returns>
CommonResult MoveUserToGroup(string accessToken, string openid, int to_groupid);
2.1 建立用户分组
为了解析如何实现建立用户分组的POST数据操做,咱们来一步步了解建立用户的具体过程。
首先须要建立一个动态定义的实体类信息,它包含几个须要说起的属性,以下所示。
string url = string.Format("https://api.weixin.qq.com/cgi-bin/groups/create?access_token={0}", accessToken);
var data = new
{
group = new
{
name = name
}
};
string postData = data.ToJson();
其中咱们把对象转换为合适的Json数据操做,放到了扩展方法ToJson里面了,这个主要就是方便把动态定义的实体类转换Json内容,主要就是调用Json.NET的序列号操做。
///<summary>
/// 把对象为json字符串
///</summary>
///<param name="obj">待序列号对象</param>
///<returns></returns>
publicstaticstring ToJson(thisobject obj)
{
return JsonConvert.SerializeObject(obj, Formatting.Indented);
}
准备好Post的数据后,咱们就进一步看看获取数据并转换为合适格式的操做代码。
GroupJson group = null;
CreateGroupResult result = JsonHelper<CreateGroupResult>.ConvertJson(url, postData);
if (result != null)
{
group = result.group;
}
其中POST数据并转换为合适格式实体类的操做,放在了ConvertJson方法里面,这个方法的定义以下所示,里面的HttpHelper是我公用类库的辅助类,主要就是调用底层的httpWebRequest对象方法,进行数据的提交,并获取返回结果。
///<summary>
/// 转换Json字符串到具体的对象
///</summary>
///<param name="url">返回Json数据的连接地址</param>
///<param name="postData">POST提交的数据</param>
///<returns></returns>
publicstatic T ConvertJson(string url, string postData)
{
HttpHelper helper = new HttpHelper();
string content = helper.GetHtml(url, postData, true);
VerifyErrorCode(content);
T result = JsonConvert.DeserializeObject<T>(content);
return result;
}
这样,完整的建立用户分组的操做函数以下所示。
///<summary>
/// 建立分组
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="name">分组名称</param>
///<returns></returns>
public GroupJson CreateGroup(string accessToken, string name)
{
string url = string.Format("https://api.weixin.qq.com/cgi-bin/groups/create?access_token={0}", accessToken);
var data = new
{
group = new
{
name = name
}
};
string postData = data.ToJson();
GroupJson group = null;
CreateGroupResult result = JsonHelper<CreateGroupResult>.ConvertJson(url, postData);
if (result != null)
{
group = result.group;
}
return group;
}
2.2 查询全部分组
查询全部分组,能够把服务器上的分组所有获取下来,也就是每一个分组的ID和名称。
///<summary>
/// 查询全部分组
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<returns></returns>
public List<GroupJson> GetGroupList(string accessToken)
{
string url = string.Format("https://api.weixin.qq.com/cgi-bin/groups/get?access_token={0}", accessToken);
List<GroupJson> list = new List<GroupJson>();
GroupListJsonResult result = JsonHelper<GroupListJsonResult>.ConvertJson(url);
if (result != null&& result.groups != null)
{
list.AddRange(result.groups);
}
return list;
}
2.3 查询用户所在分组
每一个用户都属于一个分组,默认在 未分组 这个分组里面,咱们能够经过API获取用户的分组信息,也就是获取所在用户分组的ID。
///<summary>
/// 查询用户所在分组
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="openid">用户的OpenID</param>
///<returns></returns>
publicint GetUserGroupId(string accessToken, string openid)
{
string url = string.Format("https://api.weixin.qq.com/cgi-bin/groups/getid?access_token={0}", accessToken);
var data = new
{
openid = openid
};
string postData = data.ToJson();
int groupId = -1;
GroupIdJsonResult result = JsonHelper<GroupIdJsonResult>.ConvertJson(url, postData);
if (result != null)
{
groupId = result.groupid;
}
return groupId;
}
2.4 修改分组名称
也能够在实际中,调整用户所在的分组,操做代码以下。
///<summary>
/// 修改分组名
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="id">分组id,由微信分配</param>
///<param name="name">分组名字(30个字符之内)</param>
///<returns></returns>
public CommonResult UpdateGroupName(string accessToken, int id, string name)
{
string url = string.Format("https://api.weixin.qq.com/cgi-bin/groups/update?access_token={0}", accessToken);
var data = new
{
group = new
{
id = id,
name = name
}
};
string postData = data.ToJson();
return Helper.GetExecuteResult(url, postData);
}
这里的返回值CommonResult是,一个实体类,包含了bool的成功与否的标志,以及String类型的错误信息(若是有的话)。
对于这个GetExecuteResult函数体,里面主要就是提交数据,而后获取结果,并根据结果进行处理的函数。
///<summary>
/// 通用的操做结果
///</summary>
///<param name="url">网页地址</param>
///<param name="postData">提交的数据内容</param>
///<returns></returns>
publicstatic CommonResult GetExecuteResult(string url, string postData = null)
{
CommonResult success = new CommonResult();
try
{
ErrorJsonResult result;
if (postData != null)
{
result = JsonHelper<ErrorJsonResult>.ConvertJson(url, postData);
}
else
{
result = JsonHelper<ErrorJsonResult>.ConvertJson(url);
}
if (result != null)
{
success.Success = (result.errcode == ReturnCode.请求成功);
success.ErrorMessage = result.errmsg;
}
}
catch (WeixinException ex)
{
success.ErrorMessage = ex.Message;
}
return success;
}
}
上面红色部分的意思,就是转换为实体类的时候,若是错误是微信里面定义的,那么记录错误信息,其余异常我不处理(也就是抛出去)。
2.5 移动用户到新的分组
移动用户到新的分组的操做和上面小节的差很少,具体看代码。
///<summary>
/// 移动用户分组
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="openid">用户的OpenID</param>
///<param name="to_groupid">分组id</param>
///<returns></returns>
public CommonResult MoveUserToGroup(string accessToken, string openid, int to_groupid)
{
string url = string.Format("https://api.weixin.qq.com/cgi-bin/groups/members/update?access_token={0}", accessToken);
var data = new
{
openid = openid,
to_groupid = to_groupid
};
string postData = data.ToJson();
return Helper.GetExecuteResult(url, postData);
}
上面小节,定义并实现了用户分组的各种接口,全部的用户相关的都已经毫无保留贴出代码,它的调用操做以下代码所示(测试代码)。
privatevoid btnGetGroupList_Click(object sender, EventArgs e)
{
IUserApi userBLL = new UserApi();
List<GroupJson> list = userBLL.GetGroupList(token);
foreach (GroupJson info in list)
{
string tips = string.Format("{0}:{1}", info.name, info.id);
Console.WriteLine(tips);
}
}
privatevoid btnFindUserGroup_Click(object sender, EventArgs e)
{
IUserApi userBLL = new UserApi();
int groupId = userBLL.GetUserGroupId(token, openId);
string tips = string.Format("GroupId:{0}", groupId);
Console.WriteLine(tips);
}
privatevoid btnCreateGroup_Click(object sender, EventArgs e)
{
IUserApi userBLL = new UserApi();
GroupJson info = userBLL.CreateGroup(token, "建立测试分组");
if (info != null)
{
string tips = string.Format("GroupId:{0} GroupName:{1}", info.id, info.name);
Console.WriteLine(tips);
string newName = "建立测试修改";
CommonResult result = userBLL.UpdateGroupName(token, info.id, newName);
Console.WriteLine("修改分组名称:" + (result.Success ? "成功" : "失败:" + result.ErrorMessage));
}
}
privatevoid btnUpdateGroup_Click(object sender, EventArgs e)
{
int groupId = 111;
string newName = "建立测试修改";
IUserApi userBLL = new UserApi();
CommonResult result = userBLL.UpdateGroupName(token, groupId, newName);
Console.WriteLine("修改分组名称:" + (result.Success ? "成功" : "失败:" + result.ErrorMessage));
}
privatevoid btnMoveToGroup_Click(object sender, EventArgs e)
{
int togroup_id = 111;//输入分组ID
if (togroup_id >0)
{
IUserApi userBLL = new UserApi();
CommonResult result = userBLL.MoveUserToGroup(token, openId, togroup_id);
Console.WriteLine("移动用户分组名称:" + (result.Success ? "成功" : "失败:" + result.ErrorMessage));
}
}
了解了上面的代码和调用规则,咱们就能经过API进行用户分组信息的管理了。经过在应用程序中集成相关的接口代码,咱们就可以很好的控制咱们的关注用户列表和用户分组信息。从而为咱们下一步用户的信息推送打好基础。
前面几篇继续了我本身对于C#开发微信门户及应用的技术探索和相关的经验总结,继续探索微信API并分享相关的技术,一方面是为了和你们对这方面进行互动沟通,另外一方面也是专心作好微信应用的底层技术开发,把基础模块夯实,在将来的应用中派上用途。本随笔继续介绍微信门户菜单的管理操做。
微信门户的菜单,通常服务号和订阅号均可以拥有这个模块的开发,可是订阅号好像须要认证后才能拥有,而服务号则不须要认证就能够拥有 了。这个菜单能够有编辑模式和开发模式,编辑模式主要就是在微信门户的平台上,对菜单进行编辑;而开发模式,就是用户能够经过调用微信的API对菜单进行 定制开发,经过POST数据到微信服务器,从而生成对应的菜单内容。本文主要介绍基于开发模式的菜单管理操做。
自定义菜单可以帮助公众号丰富界面,让用户更好更快地理解公众号的功能。目前自定义菜单最多包括3个一级菜单,每一个一级菜单最多包含5个二级菜单。一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。目前自定义菜单接口可实现两种类型按钮,以下:
click:
用户点击click类型按钮后,微信服务器会经过消息接口推送消息类型为event 的结构给开发者(参考消息接口指南),而且带上按钮中开发者填写的key值,开发者能够经过自定义的key值与用户进行交互;
view:
用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的url值 (即网页连接),达到打开网页的目的,建议与网页受权获取用户基本信息接口结合,得到用户的登入我的信息。
菜单提交的数据,自己是一个Json的数据字符串,它的官方例子数据以下所示。
{
"button":[
{
"type":"click",
"name":"今日歌曲",
"key":"V1001_TODAY_MUSIC"
},
{
"type":"click",
"name":"歌手简介",
"key":"V1001_TODAY_SINGER"
},
{
"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"
}]
}]
}
从上面咱们能够看到,菜单不一样的type类型,有不一样的字段内容,如type为view的有url属性,而type为click的,则有key属性。而菜单能够有子菜单sub_button属性,总得来讲,为了构造好对应的菜单实体类信息,不是一下就能分析的出来。
我看过一些微信接口的开发代码,把菜单的分为了好多个实体类,指定了继承关系,而后分别对他们进行属性的配置,大概的关系以下所示。
这种多层关系的继承方式能解决问题,不过我以为并非优雅的解决方案。其实结合Json.NET自身的Attribute属性配置,能够指定那些为空的内容在序列号为Json字符串的时候,不显示出来的。
[JsonProperty( NullValueHandling = NullValueHandling.Ignore)]
有了这个属性,咱们就能够统必定义菜单的实体类信息更多的属性了,能够把View类型和Click类型的菜单属性的url和key合并在一块儿。
///<summary>
/// 菜单基本信息
///</summary>
publicclass MenuInfo
{
///<summary>
/// 按钮描述,既按钮名字,不超过16个字节,子菜单不超过40个字节
///</summary>
publicstring name { get; set; }
///<summary>
/// 按钮类型(click或view)
///</summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
publicstring type { get; set; }
///<summary>
/// 按钮KEY值,用于消息接口(event类型)推送,不超过128字节
///</summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
publicstring key { get; set; }
///<summary>
/// 网页连接,用户点击按钮可打开连接,不超过256字节
///</summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
publicstring url { get; set; }
///<summary>
/// 子按钮数组,按钮个数应为2~5个
///</summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public List<MenuInfo> sub_button { get; set; }
.......
可是,这么多信息,不一样的类型我须要指定不一样的属性类型,那不是挺麻烦,万一我在View类型的菜单里面,把key属性设置了,那怎么办?
解决方法就是咱们定义几个构造函数,分别用来构造不一样的菜单信息,以下所示是对菜单不一样的类型,赋值给不一样的属性的构造函数。
///<summary>
/// 参数化构造函数
///</summary>
///<param name="name">按钮名称</param>
///<param name="buttonType">菜单按钮类型</param>
///<param name="value">按钮的键值(Click),或者链接URL(View)</param>
public MenuInfo(string name, ButtonType buttonType, string value)
{
this.name = name;
this.type = buttonType.ToString();
if (buttonType == ButtonType.click)
{
this.key = value;
}
elseif(buttonType == ButtonType.view)
{
this.url = value;
}
}
好了,还有另一个问题,子菜单也就是属性sub_button是无关紧要的东西,有的话,须要指定Name属性,并添加它的sub_button集合对象就能够了,那么咱们在增长一个构造子菜单的对象信息的构造函数。
///<summary>
/// 参数化构造函数,用于构造子菜单
///</summary>
///<param name="name">按钮名称</param>
///<param name="sub_button">子菜单集合</param>
public MenuInfo(string name, IEnumerable<MenuInfo> sub_button)
{
this.name = name;
this.sub_button = new List<MenuInfo>();
this.sub_button.AddRange(sub_button);
}
因为只指定Name和sub_button的属性内容,其余内容为null的话,天然构造出来的Json就没有包含它们,很是完美!
为了获取菜单的信息,咱们还须要定义两个实体对象,以下所示。
///<summary>
/// 菜单的Json字符串对象
///</summary>
publicclass MenuJson
{
public List<MenuInfo> button { get; set; }
public MenuJson()
{
button = new List<MenuInfo>();
}
}
///<summary>
/// 菜单列表的Json对象
///</summary>
publicclass MenuListJson
{
public MenuJson menu { get; set; }
}
咱们从微信的定义里面,能够看到,咱们经过API能够获取菜单信息、建立菜单、删除菜单,那么咱们来定义它们的接口以下。
///<summary>
/// 菜单的相关操做
///</summary>
publicinterface IMenuApi
{
///<summary>
/// 获取菜单数据
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<returns></returns>
MenuJson GetMenu(string accessToken);
///<summary>
/// 建立菜单
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="menuJson">菜单对象</param>
///<returns></returns>
CommonResult CreateMenu(string accessToken, MenuJson menuJson);
///<summary>
/// 删除菜单
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<returns></returns>
CommonResult DeleteMenu(string accessToken);
}
具体的获取菜单信息的实现以下。
///<summary>
/// 获取菜单数据
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<returns></returns>
public MenuJson GetMenu(string accessToken)
{
MenuJson menu = null;
var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/get?access_token={0}", accessToken);
MenuListJson list = JsonHelper<MenuListJson>.ConvertJson(url);
if (list != null)
{
menu = list.menu;
}
return menu;
}
这里就是把返回的Json数据,统一转换为咱们须要的实体信息了,一步到位。
调用代码以下所示。
privatevoid btnGetMenuJson_Click(object sender, EventArgs e)
{
IMenuApi menuBLL = new MenuApi();
MenuJson menu = menuBLL.GetMenu(token);
if (menu != null)
{
Console.WriteLine(menu.ToJson());
}
}
建立和删除菜单对象的操做实现以下所示。
///<summary>
/// 建立菜单
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="menuJson">菜单对象</param>
///<returns></returns>
public CommonResult CreateMenu(string accessToken, MenuJson menuJson)
{
var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}", accessToken);
string postData = menuJson.ToJson();
return Helper.GetExecuteResult(url, postData);
}
///<summary>
/// 删除菜单
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<returns></returns>
public CommonResult DeleteMenu(string accessToken)
{
var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={0}", accessToken);
return Helper.GetExecuteResult(url);
}
看到这里,有些人可能会问,实体类你简化了,那么建立菜单是否是挺麻烦的,特别是构造对应的信息应该如何操做呢?前面不是介绍了不一样的构造函数了吗,经过他们简单就搞定了,不用记下太多的实体类及它们的继承关系来处理菜单信息。
privatevoid btnCreateMenu_Click(object sender, EventArgs e)
{
MenuInfo productInfo = new MenuInfo("软件产品", new MenuInfo[] {
new MenuInfo("病人资料管理系统", ButtonType.click, "patient"),
new MenuInfo("客户关系管理系统", ButtonType.click, "crm"),
new MenuInfo("酒店管理系统", ButtonType.click, "hotel"),
new MenuInfo("送水管理系统", ButtonType.click, "water")
});
MenuInfo frameworkInfo = new MenuInfo("框架产品", new MenuInfo[] {
new MenuInfo("Win开发框架", ButtonType.click, "win"),
new MenuInfo("WCF开发框架", ButtonType.click, "wcf"),
new MenuInfo("混合式框架", ButtonType.click, "mix"),
new MenuInfo("Web开发框架", ButtonType.click, "web"),
new MenuInfo("代码生成工具", ButtonType.click, "database2sharp")
});
MenuInfo relatedInfo = new MenuInfo("相关连接", new MenuInfo[] {
new MenuInfo("公司介绍", ButtonType.click, "Event_Company"),
new MenuInfo("官方网站", ButtonType.view, "http://www.iqidi.com"),
new MenuInfo("提点建议", ButtonType.click, "Event_Suggestion"),
new MenuInfo("联系客服", ButtonType.click, "Event_Contact"),
new MenuInfo("发邮件", ButtonType.view, "http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=S31yfX15fn8LOjplKCQm")
});
MenuJson menuJson = new MenuJson();
menuJson.button.AddRange(new MenuInfo[] { productInfo, frameworkInfo, relatedInfo });
//Console.WriteLine(menuJson.ToJson());
if (MessageUtil.ShowYesNoAndWarning("您确认要建立菜单吗") == System.Windows.Forms.DialogResult.Yes)
{
IMenuApi menuBLL = new MenuApi();
CommonResult result = menuBLL.CreateMenu(token, menuJson);
Console.WriteLine("建立菜单:" + (result.Success ? "成功" : "失败:" + result.ErrorMessage));
}
}
这个就是我微信门户里面的菜单操做了,具体效果能够关注个人微信门户:广州爱奇迪,也能够扫描下面二维码进行关注了解。
菜单的效果以下:
最近一直在弄微信的集成功能开发,发现微信给认证帐户开通了一个多客服的功能,对于客户的咨询,能够切换至客服处理的方式,并且能够添加多个客服进 行处理,这个在客户咨询比较多的时候,是一个不错的营销功能。微信多客服的功能,可以在很大程度上利用客服员工资源,及时迅速对客户咨询信息进行处理,为 企业带来更多的机会和市场。
默认这个多客服的功能,须要在微信公众平台中的服务中心进行主动开通,默认是不开通的,为了体验这个功能,我这里把多客服功能进行开通。
微信的多客服功能,对于客服的响应操做,既能够在电脑的客户端上进行操做,也能够在微信多客服助手进行信息处理,二者都能对客户的信息进行回应、结束会话等操做。
开通微信多客服功能后,就须要添加一些处理客户信息的客服工号了。
多客服帐号采用“工号@微信号”的形式进行登陆,请您在登陆窗口依照下图形式输入账号信息。
在电脑客户端上使用
在手机客户端上进行多客服的使用,就是关注一个帐号,信息经过转发到这里进行处理。关注公众号”多客服助手“就搞定了。
经过上面两种途径,可以很好处理客户的相关信息,其实也就是相似电话坐席的方式,让不一样的客服员工,对来访的客户进行处理。
在微信的多客服开发介绍中,内容介绍的比较少,以下所示。
在新的微信协议中,开发模式也能够接入客服系统。 开发者若是须要使用客服系统,须要在接收到用户发送的消息时,返回一个MsgType为transfer_customer_service的消息,微信 服务器在收到这条消息时,会把用户此次发送的和之后一段时间内发送的消息转发客服系统。返回的消息举例以下。
<xml>
<ToUserName><![CDATA[touser]]></ToUserName>
<FromUserName><![CDATA[fromuser]]></FromUserName>
<CreateTime>1399197672</CreateTime>
<MsgType><![CDATA[transfer_customer_service]]></MsgType>
</xml>
而在开发的时候,咱们通常把它封装为一个实体类信息,以下所示。主要就是指定消息类型,和翻转传入传出对象就能够了。
///<summary>
/// 客服消息
///</summary>
[System.Xml.Serialization.XmlRoot(ElementName = "xml")]
publicclass ResponseCustomer : BaseMessage
{
public ResponseCustomer()
{
this.MsgType = ResponseMsgType.transfer_customer_service.ToString().ToLower();
}
public ResponseCustomer(BaseMessage info) : this()
{
this.FromUserName = info.ToUserName;
this.ToUserName = info.FromUserName;
}
}
而后调用处理的时候,代码以下所示。
ResponseCustomer customInfo = new ResponseCustomer(info);
xml = customInfo.ToXml();
如我在客户应答处理里面,客户回应0,我就切换进入客服模式,这样客户后续全部的输入内容,均不会触发微信门户里面的解析,而转发到客服模式,让客服的工号能够和客户进行交谈了。
//处理 0 指令, 人工客服
if (string.IsNullOrEmpty(xml) && eventKey.Trim() == "0")
{
xml = base.DealEvent(eventInfo, "event_customservice");
}
而在DealEvent里面,根据这个条件进行处理就能够了。
//人工客服
if (eventKey == "event_customservice")
{
ResponseCustomer customInfo = new ResponseCustomer(info);
xml = customInfo.ToXml();
}
经过使用多客服的客户端,这样处理消息交互起来很是方便,能得到客户的对话信息了,在电脑客户端上,看到的界面以下所示。
手机上的谈话截图以下所示。
这样就可以经过多途径,及时响应客户的信息了。
若是感兴趣或者体验相关的客服应答功能,能够关注个人微信了解下。具体效果能够关注个人微信门户:广州爱奇迪,也能够扫描下面二维码进行关注了解。
最近对微信接口进行深刻的研究,经过把底层接口一步步进行封装后,逐步升级到自动化配置、自动化应答,以及后台处理界面的优化和完善上,力求搭建一个较为完善、适用的微信门户应用管理系统。
微信门户应用管理系统,采用基于MVC+EasyUI的路线,因为多数域名服务器上都只能支持.NET4.0,因此以MVC3,C#4.0做为开发基础,基本上可以部署在任何.NET服务器上。
在微信门户系统里面,实现下面这些功能操做:
1)实现菜单的动态配置及更新到服务器上;
2)动态定义事件和响应消息,实现对不一样行业,不一样需求的菜单动做响应;
3)动态的应答指令配置处理,实现整套应答链的消息处理;
4)获取订阅用户和用户分组信息,并能够实现用户分组信息的维护等操做;
5)管理并更新多媒体文件、图文消息等内容,方便为客户推送消息作准备。
6)使用向选定订阅用户或者分组进行消息的群发功能。
在系统中管理菜单,并经过把菜单提交到服务器上,实现菜单的动态配置和生成,可以为咱们系统适应各类的须要,实现灵活的处理。
微信菜单的添加界面以下所示。
微信菜单的修改界面以下所示
微信菜单定义是存储在数据库里面,若是须要提交到微信服务器上并生效,则须要调用微信API接口进行处理,我在页面的Controller控制器里增长一个提交到服务器的处理方法。
在微信服务帐号的门户上,菜单的表现效果以下所示。
对于动态生成的菜单,大多数状况下是用做Click的方式,也就是须要定义每一个菜单的事件响应操做,咱们使用微信的话,能够了解到,微信的处理事件,通常能够响应用户文本消息、图片消息、图文消息等内容,常规下,通常使用文本消息或者图文消息居多。
为了进一步实现响应内容的重用,咱们把菜单的事件定义和内容定义进行分开管理,事件定义能够使用多个文本消息,也能够使用多个图文消息进行组合,这样能够实现更加灵活的使用环境。
添加事件定义以下所示
事件的响应内容编码,能够选择输入或者从“编辑”按钮中选择,当选择“编辑”按钮进行选择的时候,系统弹出一个对话框供用户对事件的响应内容编码选择。
完成选择后,回到原来的新增界面,将会看到返回的记录就是咱们选择的记录。
微信事件的编辑界面以下所示,相似新增界面的内容。
上面说到,菜单的事件经过关联事件编码进行处理,而事件自己能够组合多个消息内容,所以消息内容是响应客户操做的最小单元,它们能够是一条文本消息、图文消息,也能够是多条消息的组合(同类型的话)。
为了方便管理,我把消息分为了图文、指令、文本类型,若是须要,还能够根据须要把它细化为其余类型的消息。
消息内容的添加界面以下所示。
文本消息的手机上界面效果以下所示。
这里无论是文本消息仍是图文消息,咱们统一以图文消息的定义来定义消息,若是是文本消息,咱们只须要获取描述内容做为消息的主体便可。
图文消息的编辑界面以下所示,主要就是填写完整的内容和图片,以及页面详细的连接便可。
上面的这个客户关系管理系统的消息,在手机上显示的界面效果以下所示,单击连接,能够切换到消息跳转连接地址的。
应答指令的维护,有点相似于事件的管理,主要就是定义一些用到的指令,方便构建应答系统的响应链,从而实现一步步的操做指令。
在后台设置好应答指令后,系统就能根据应答指令链进行处理了。首先咱们须要提供一个进入应答链的提示界面,以下所示。
但咱们在菜单选择应答系统后,系统返回一个文本提示界面,以下所示。
这个界面里面提示了一些按键,包括几个固定的按键和一些业务按键,输入简单的1~6能够对选择进行响应。
咱们看到上面的界面,输入指令1后,系统进入下一层的应答指令,而后又列出几个可供输入的按键和内容提示。
当咱们继续输入业务按键1后,响应的是一个图文消息,也是关于按键的详细说明。
这个时候,咱们也还能够输入*号按键,返回上一级菜单的。
输入0则转入了客服对话模式,后续您发的任何消息,将会转发到多客服系统里面了。
当用户发送消息后,客服助手就能及时收到消息并处理和客户的应答了。
为了更有效管理订阅用户以及分组信息,咱们能够从微信服务器上获取相关的信息,供咱们了解关注的用户信息,也能够为后续的群发消息作准备。
订阅用户的管理以下所示,默承认以经过用户的地区进行查看,地区根据:国家-省份-城市这样的级别进行展开。单击同步数据,能够把服务器上的用户数据下载到本地进行更新或者写入。
订阅用户,还能够根据分组进行查看
双击能够查看订阅用户信息,查看订阅用户的详细信息界面以下所示。
建立分组的界面以下所示。
编辑分组信息界面以下所示。
当对分组进行编辑保存后,系统会记住那些修改过的,同步的时候,把本地新增的内容,在服务器上建立分组;把修改的的分组名称,在服务器上进行修改,而后进行同步列表处理。
多媒体管理是指把本地文件上传到微信服务器上进行保存,方便信息的发送等操做。微信要求,某些信息,必须是先上传到服务器上,而后才能使用它的媒体ID进行发送的。
文件成功上传到服务器后,在列表里面的“文件上传标识,就是一串BASE64的编码数据,同时有一个上传的时间戳(由于微信服务器只保留了3天的媒体数据,超过时限的数据会被自动删除。
同时,在列表的上面,有两个重要的功能:上传选定的记录,从新上传过时的记录。方便咱们对本身多媒体文件的从新更新操做。
添加界面操做以下所示,其中引入了附件上传的控件进行文件的操做,很是方便。同时上传成功的文件,会在列表中列出。
多媒体文件能够是下面几种方式:图片、语音、视频、缩略图。
保存后的数据记录,文件上传标识和时间戳都是空的,咱们若是要使用,必须把他们上传到微信的服务器上,而后根据它的MediaId进行信息的发送,上传选定的记录操做界面以下所示。
多媒体文件顺利上传后,记录的信息以下所示。
图文消息分为单图文消息和多图文消息两种,单图文消息以下所示。
多图文消息以下所示:
和多媒体数据管理同样,图文消息也是经过一样的方式进行管理,先上传到服务器,而后在进行消息的发送操做,多媒体消息同样有时间方面的限制要求,具体在咱们的微信门户平台里面管理界面以下所示。
添加图文消息界面以下所示,保存后,能够在编辑界面中的“其余图文列表”里面,继续添加多图文的消息内容。
在添加界面中,选择图文消息的缩略图,都是经过选定指定的,已经上传到服务器上图片或者缩略图资源才能够的。
添加后的多图文列表,能够进行查看管理。
保存记录后,而后继续上传,上传后的记录界面以下所示,成功后返回一个上传后的服务器标识和时间戳,不然提示错误。
为了方便记录客户的输入和发送信息,咱们在微信门户管理平台里面记录用户的输入数据,具体会话消息管理界面以下所示。
咱们能够双击最近48小时内的任何一条记录,能够给关注的客户进行消息的发送操做,若是消息发送成功,用户在手机的微信帐号里面就能收到相关的发送消息了。
为了对客户进行相应的营销操做,有时候咱们须要对指定的群主或者人员进行消息的群发,让客户常常性的了解咱们产品的信息和活动。
因为群发消息,除了文本消息,能够直接编辑发送外,其余数据,必需要求是上传到服务器的多媒体文件或者图文消息内容,所以前面的多媒体管理和图文消 息管理,就是主要为了群发消息的目的引入的。有了上面的多媒体和多图文信息,咱们从平台里面选择记录便可进行发送,从而省却麻烦的连带工做,实现高效的信 息群发操做。
群发的消息,能够按群发分组进行查看,也能够按照消息类型进行查看,使得咱们管理起来根据方便。
添加图文消息,能够选择文本消息、图文消息、图片消息等内容,根据不一样的内容,界面提供不一样的选择操做。
消息的群发类型分为两种,一种是根据分组,那么从平台里面选择对应的分组便可;一种是根据用户的OpenID进行发送,提供给用户输入。主要的操做界面以下所示。
微信公众号(包括服务号和订阅号)均可以对菜单进行自定义设置,咱们为了方便管理,通常先把菜单数据在本地管理维护,须要更新的时候,把它们更新到 微信服务器上就能够了。本文基于这个方式,介绍个人微信门户平台管理系统中菜单提交到微信服务器上的操做。微信门户应用管理系统,采用基于 MVC+EasyUI的路线,因为多数域名服务器上都只能支持.NET4.0,因此以MVC3,C#4.0做为开发基础,基本上可以部署在任何.NET服 务器上。
微信公众号的菜单咱们能够经过网站进行本地的管理,维护好它们之间的层级关系,因为微信对自定义的菜单要求比较严格,如下是微信对自定义菜单的要求:
目前自定义菜单最多包括3个一级菜单,每一个一级菜单最多包含5个二级菜单。一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。
所以咱们本身根据约定,不要越界便可,不然提交菜单到服务器,可能会返回一些错误,这些细节,咱们在建立本地菜单管理的时候,注意一下就能够了。我在早期的一篇文章也介绍了自定义菜单的一些内容,须要能够进行回顾一下《C#开发微信门户及应用(6)--微信门户菜单的管理操做》,本篇主要是介绍在个人平台管理系统里面,调用前面介绍的菜单接口API,实现菜单提交到服务器的操做。
根据微信的自定义菜单要求,我在管理系统里面,对微信的菜单几个基础性的界面设计以下。
主菜单管理界面以下所示。
添加菜单的界面设计以下所示
微信菜单的修改界面以下所示
微信菜单定义是存储在数据库里面,若是须要提交到微信服务器上并生效,则须要调用微信API接口进行处理,我在页面的Controller控制器里增长一个提交到服务器的处理方法。
上面几个界面,主要就是根据微信菜单的属性,对菜单进行维护管理,咱们最终的目的是把它们放到服务器上去,供咱们处理客户的相关事件操做的。
提交菜单的操做,咱们在MVC的View页面里面,使用JQuery的Ajax提交便可(前提是咱们在控制器里面添加相应的处理,后面介绍),界面脚本代码以下所示。
//绑定提交按钮的的点击事件
function BindSubmitEvent() {
$("#btnSubmit").click(function () {
$.messager.confirm("提交菜单确认", "您确认须要提交菜单到微信服务器吗?", function (action) {
if (action) {
//提交数据
$.ajax({
url: '/Menu/UpdateWeixinMenu',
type: 'post',
dataType: 'json',
success: function (data) {
if (data.Success) {
$.messager.alert("提示", "提交微信菜单成功");
}
else {
$.messager.alert("提示", "提交微信菜单失败:" + data.ErrorMessage);
}
},
data: ''
});
}
});
});
}
上面红色的代码,就是咱们在MVC的控制器里面定义的方法,咱们只须要经过POST方法,对控制器方法调用,就能实现菜单提交到微信服务器上,至于具体里面的细节,咱们能够把它挪到控制器或者更底层进行处理就是了,页面不须要涉及太多的逻辑就是了。
上面那个Menu控制器的UpdateWeixinMenu的方法代码以下所示(主要就是根据我前面介绍过的开发模型进行处理就是了)。
/// <summary>
///更新微信菜单
/// </summary>
/// <returns></returns>
public ActionResult UpdateWeixinMenu()
{
string token = base.GetAccessToken();
MenuListJson menuJson = GetWeixinMenu();
IMenuApi menuApi = new MenuApi();
CommonResult result = menuApi.CreateMenu(token, menuJson);
return ToJsonContent(result);
}
上面的几个方法这里逐一介绍一下。GetAccessToken主要就是得到当前操做的访问令牌,这里的操做能够用缓存进行缓存,不然频繁的获取AccessToken,达到天天指定的次数后,当天就不能再用了。
GetWeixinMenu方法,主要就是为了方便,对获取构造微信的自定义菜单数据进行了一个函数封装,具体代码以下所示。
///<summary>
/// 生成微信菜单的Json数据
///</summary>
///<returns></returns>
private MenuListJson GetWeixinMenu()
{
MenuListJson menuJson = new MenuListJson();
List<MenuNodeInfo> menuList = BLLFactory<Menu>.Instance.GetTree();
foreach (MenuNodeInfo info in menuList)
{
ButtonType type = (info.Type == "click") ? ButtonType.click : ButtonType.view;
string value = (type == ButtonType.click) ? info.Key : info.Url;
MenuJson weiInfo = new MenuJson(info.Name, type, value);
AddSubMenuButton(weiInfo, info.Children);
menuJson.button.Add(weiInfo);
}
return menuJson;
}
privatevoid AddSubMenuButton(MenuJson menu, List<MenuNodeInfo> menuList)
{
if (menuList.Count >0)
{
menu.sub_button = new List<MenuJson>();
}
foreach (MenuNodeInfo info in menuList)
{
ButtonType type = (info.Type == "click") ? ButtonType.click : ButtonType.view;
string value = (type == ButtonType.click) ? info.Key : info.Url;
MenuJson weiInfo = new MenuJson(info.Name, type, value);
menu.sub_button.Add(weiInfo);
AddSubMenuButton(weiInfo, info.Children);
}
}
上面的代码,就是把本地存储的MenuNodeInfo数据,经过递归遍历的方 式,转换为微信的自定义菜单实体MenuJson,这样咱们调用API就很是方便了,这个函数主要负责构造对应的实体信息就是了。至于调用微信API提交 菜单的事情,仍是让API本身亲自处理为好,他们的代码以下所示(也就是上面函数的部分代码)。
IMenuApi menuApi = new MenuApi();
CommonResult result = menuApi.CreateMenu(token, menuJson);
return ToJsonContent(result);
最终的结果是返回一个通用的结果CommonResult,这个结果对象,很是方便脚本的处理,若是有错误,则提示错误,不然也方便判断布尔值,也就是上面的页面代码脚本。
success: function (data) {
if (data.Success) {
$.messager.alert("提示", "提交微信菜单成功");
}
else {
$.messager.alert("提示", "提交微信菜单失败:" + data.ErrorMessage);
}
},
经过以上几部分的代码,咱们就能够实现前台MVC的视图界面,调用后台封装好的微信API,实现菜单的提交处理了。
若是感兴趣或者体验相关的客服应答功能,能够关注个人微信了解下。具体效果能够关注个人微信门户:广州爱奇迪,也能够扫描下面二维码进行关注了解。
在前面几篇文章中,逐步从原有微信的API封装的基础上过渡到微信应用平台管理系统里面,逐步介绍管理系统中的微信数据的界面设计,以及相关的处理 操做过程的逻辑和代码,但愿从更高一个层次,向你们介绍微信的应用开发过程。本篇主要介绍在管理系统中,如何实现微信用户分组信息的同步操做。
其实微信可以风风火火的缘由,主要就是由于有用户信息,因此同步并管理好微信帐号的关注用户数据是很是重要的。有了微信用户的数据,你 能够和你任何应用系统对接,实现系统-手机客户端的数据整合,还能够对用户进行营销管理,如发送用户感兴趣的产品消息、服务消息等,可以很好扩大企业的影 响力和市场行为。
在较早以前的一篇随笔《C#开发微信门户及应用(5)--用户分组信息管理》, 我曾经介绍了微信分组的各类底层的API封装操做,里面主要就是对微信提供API的.NET高级分组,对全部的信息交换,经过实体性进行数据交换,使得我 们调用API来处理微信的各类事务更加方便,从而为微信应用平台的管理奠基基础。其中这篇文章介绍了全部微信分组管理的API封装过程,用户分组管理,包 含下面几个方面的内容:
1)建立分组
2) 查询全部分组
3) 查询用户所在分组
4) 修改分组名
5) 移动用户分组
针对以上微信分组的操做,咱们能够在微信的应用管理系统里面,设计一个模块,用来管理微信的分组数据,在这个模块里面,能够建立分组,修改分组,查 看分组等基础操做,还能够实现同步微信分组的操做,同步操做,主要就是把新增的分组信息添加到微信里面,修改的分组也在微信中实现修改功能,删除目前微信 不支持,因此不用管了。最后,咱们能够在此从微信服务器上,把修改后的数据同步下来,同步的时候为了不对咱们提交不成功的数据,咱们须要对修改过的记录 作好标识,这个就是我对整个同步操做的逻辑处理了。
在管理系统里面,对微信分组的列表管理界面设计以下所示。
建立分组的时候,咱们只须要添加一个分组名称就能够了,界面设计也简单,可是咱们把建立的ID统一设计为-1,做为未同步的新增标识。
编辑分组信息界面以下所示。当对分组进行编辑保存后,系统会记住那些修改过的分组就是了。
为了更好实现分组同步的管理,我把分组的操做代码,封装在一个MVC的控制器的方法里面,页面代码经过Ajax调用就能够实现同步操做了,同步成功,或者失败,都会提示用户,让咱们对其结果进行了解。
同步的时候,把本地新增的内容,在服务器上建立分组;把修改的的分组名称,在服务器上进行修改,而后进行同步列表处理,同步操做前,列表界面可能以下所示,有新增记录ID=-1的,也有修改后,记录修改标志的。
用户分组的同步按钮操做,是调用一个脚本代码就能够了,具体代码以下所示。
//绑定提交按钮的的点击事件
function BindSyncDataEvent() {
$("#btnSyncData").click(function () {
$.messager.confirm("提交确认", "您确认须要和微信服务器同步分组信息吗?", function (action) {
if (action) {
//提交数据
$("#loading").show();
$.ajax({
url: '/Group/SyncGroup',
type: 'post',
dataType: 'json',
success: function (data) {
if (data.Success) {
$("#grid").datagrid("reload");
$.messager.alert("提示", "同步成功");
}
else {
$.messager.alert("提示", "同步失败:" + data.ErrorMessage);
}
},
data: ''
});
$("#loading").fadeOut(500);
}
});
});
}
其中上面红色部分就是经过Jquery调用的MVC的控制器方法,具体函数代码以下所示。
///<summary>
/// 同步服务器的分组信息
///</summary>
///<returns></returns>
public ActionResult SyncGroup()
{
string accessToken = GetAccessToken();
CommonResult result = BLLFactory<Group>.Instance.SyncGroup(accessToken);
return ToJsonContent(result);
}
从上面,咱们没有看到太多的逻辑,为了方便我对他们进行了进一步的封装,把它放到了业务逻辑层进行处理了。具体咱们看看它的代码逻辑吧,这里为了全部的数据库操做更加快捷和完整,使用了事务的操做,我把相关的代码贴出来,方便你们了解逻辑。
///<summary>
/// 同步服务器的分组信息
///</summary>
///<returns></returns>
public CommonResult SyncGroup(string accessToken)
{
CommonResult result = new CommonResult();
try
{
IUserApi api = new UserApi();
using (DbTransaction trans = baseDal.CreateTransaction())
{
//先把本地标志groupId = -1未上传的记录上传到服务器,而后进行本地更新
string condition = string.Format("GroupID = '-1' ");
List<GroupInfo> unSubmitList = base.Find(condition);
foreach (GroupInfo info in unSubmitList)
{
GroupJson groupJson = api.CreateGroup(accessToken, info.Name);
if (groupJson != null)
{
info.GroupID = groupJson.id;
baseDal.Update(info, info.ID, trans);
}
}
//把标志为修改状态的记录,在服务器上修改
condition = string.Format("GroupID >=0 and Modified =1 ");
List<GroupInfo> unModifyList = base.Find(condition);
foreach (GroupInfo info in unModifyList)
{
CommonResult modifyed = api.UpdateGroupName(accessToken, info.GroupID, info.Name);
if (modifyed != null&& modifyed.Success)
{
info.Modified = 0;//重置标志
baseDal.Update(info, info.ID, trans);
}
}
//删除具备删除标志的分组
//condition = string.Format("GroupID >=100 and Deleted=1 ");
//List<GroupInfo> unDeletedList = base.Find(condition);
//foreach (GroupInfo info in unDeletedList)
//{
// CommonResult deleted = api.DeleteGroup(accessToken, info.GroupID, info.Name);
// if (deleted != null && deleted.Success)
// {
// baseDal.Delete(info.ID, trans);
// }
//}
List<GroupJson> list = api.GetGroupList(accessToken);
foreach (GroupJson info in list)
{
UpdateGroup(info, trans);
}
try
{
trans.Commit();
result.Success = true;
}
catch
{
trans.Rollback();
throw;
}
}
}
catch (Exception ex)
{
result.ErrorMessage = ex.Message;
}
return result;
}
在Jquery同步的时候,咱们为了不等待时间太久而没法判断程序是否正常在工做,最好增长一个忙碌的提示操做,由于咱们使用了Ajax调用,因此咱们能够统一设置Ajax的忙碌和完成状态,具体设置代码以下所示。
//用来统一请求忙碌显示的设置
$.ajaxSetup({
beforeSend: function () {
$("#loading").show();
},
complete: function () {
$("#loading").hide();
}
});
若是感兴趣或者体验相关的微信功能,能够关注个人微信了解下。具体效果能够关注个人微信门户:广州爱奇迪,也能够扫描下面二维码进行关注了解。
在前面一系列文章中,咱们能够看到微信自定义菜单的重要性,能够说微信公众号帐号中,菜单是用户的第一印象,咱们要规划好这些菜单的内容,布局等信 息。根据微信菜单的定义,咱们能够看到,通常菜单主要分为两种,一种是普通的Url菜单(类型为View的菜单),一种是事件菜单(类型为Click的菜 单),通常状况下,微信的Url菜单,是没法得到用户的任何信息的,但微信用户信息很是重要,所以也提供了另一种方式(相似重定向的方式)来给咱们使 用,本篇主要介绍这种从新定向的方式菜单的使用,以使咱们可以尽量和用户进行交互。
微信对自定义菜单的要求:目前自定义菜单最多包括3个一级菜单,每一个一级菜单最多包含5个二级菜单。一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。
根据菜单的分类,咱们能够把它经过图形进行分类展现:
我对各类微信公众号进行了解,发现多数帐号采用的都是普通的View类型的菜单连接方式,经过它们连接到本身的微网站上,但也有一些作的好的,如省 立中山图书馆,就能经过重定向的方式,提供一个绑定图书馆用户和微信OpenID的入口,绑定后,用户就能够查看借阅的书籍,而后能够经过一键续借功能实 现图书的快速续借功能。
对于这种重定向类型的Url菜单事件,微信的说明以下:
若是用户在微信中(Web微信除外)访问公众号的第三方网页,公众号开发者能够经过此接口获取当前用户基本信息(包括昵称、性别、城市、国家)。利用用户信息,能够实现体验优化、用户来源统计、账号绑定、用户身份鉴权等功能。请 注意,“获取用户基本信息接口是在用户和公众号产生消息交互时,才能根据用户OpenID获取用户基本信息,而网页受权的方式获取用户基本信息,则无需消 息交互,只是用户进入到公众号的网页,就可弹出请求用户受权的界面,用户受权后,就可得到其基本信息(此过程甚至不须要用户已经关注公众号。)”
上面说了,重定向类型的菜单分为了两种,其实他们也仅仅是参数Scope类型的不一样,其余部分也仍是同样的。
为了展现,咱们在假设用户单击菜单的时候,切换到http://www.iqidi.com/testwx.ashx这个页面,并带过来当前用户的OpenID等参数信息
对于scope=snsapi_base方式的连接以下:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx3d81fc2886d86526&redirect_uri=http%3A%2F%2Fwww.iqidi.com%2Ftestwx.ashx&response_type=code&scope=snsapi_base&state=123#wechat_redirect
而对于scope=snsapi_userinfo方式的连接以下:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx3d81fc2886d86526&redirect_uri=http%3A%2F%2Fwww.iqidi.com%2Ftestwx.ashx&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect
不过他们给手机客户端的体验是不一样的,第一种能够平滑切换,可是第二种会弹出一个对话框供用户确认才能继续。
为了演示上面两种获取数据的不一样,我把他们传过来的code的值,用户换取OpenID后进行用户信息的解析,他们二者的结果都是同样了。具体测试界面以下所示。
其中TestWX.ashx的页面后台代码以下所示:
///<summary>
/// TestWX 的摘要说明
///</summary>
publicclass TestWX : IHttpHandler
{
string appId = ""; //换成你的信息
string appSecret = ""; //换成你的信息
publicvoid ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
string content = "";
if (context.Request != null&& context.Request.Url != null)
{
NameValueCollection list = HttpUtility.ParseQueryString(context.Request.Url.Query);
foreach (string key in list.AllKeys)
{
content += string.Format("{0}:{1} \r\n", key, list[key]);
}
}
string code = context.Request.QueryString["code"] ?? "";
if (!string.IsNullOrEmpty(code))
{
IBasicApi api = new BasicApi();
try
{
AppConfig config = new AppConfig();
appId = config.AppConfigGet("AppId");//从配置中获取微信程序ID
appSecret = config.AppConfigGet("AppSecret");//从配置中获取微信程序秘钥
AccessTokenResult result = api.GetAccessToken(appId, appSecret, code);
if (result != null)
{
content += string.Format("openid:{0}\r\n", result.openid);
string token = api.GetAccessToken(appId, appSecret);
IUserApi userApi = new UserApi();
UserJson userDetail = userApi.GetUserDetail(token, result.openid);
if (userDetail != null)
{
content += string.Format("nickname:{0} sex:{1}\r\n", userDetail.nickname, userDetail.sex);
content += string.Format("Location:{0} {1} {2} {3}\r\n", userDetail.country, userDetail.province, userDetail.city, userDetail.language);
content += string.Format("HeadUrl:{0} \r\n", userDetail.headimgurl);
content += string.Format("subscribe:{0},{1}\r\n", (userDetail.subscribe == 1) ? "已订阅" : "未订阅", userDetail.subscribe_time.GetDateTime());
}
}
}
catch { }
}
context.Response.Write(content);
}
在上面的代码中,我主要分为几步,一个是打印当前用户重定向过来的连接的参数信息,代码以下。
NameValueCollection list = HttpUtility.ParseQueryString(context.Request.Url.Query);
foreach (string key in list.AllKeys)
{
content += string.Format("{0}:{1} \r\n", key, list[key]);
}
而后获取到Code参数后,经过API接口,获取AccessTokenResult的数据,这里面有用户的OpenID
AccessTokenResult result = api.GetAccessToken(appId, appSecret, code);
当正常调用后,咱们把用户标识的OpenID进一步进行解析,调用API获取用户的详细信息,具体代码以下所示。
UserJson userDetail = userApi.GetUserDetail(token, result.openid);
当咱们把用户的相关信息获取到了,就能够作各类用户信息的展现了,以下代码所示。
if (userDetail != null)
{
content += string.Format("nickname:{0} sex:{1}\r\n", userDetail.nickname, userDetail.sex);
content += string.Format("Location:{0} {1} {2} {3}\r\n", userDetail.country, userDetail.province, userDetail.city, userDetail.language);
content += string.Format("HeadUrl:{0} \r\n", userDetail.headimgurl);
content += string.Format("subscribe:{0},{1}\r\n", (userDetail.subscribe == 1) ? "已订阅" : "未订阅", userDetail.subscribe_time.GetDateTime());
}
这种菜单就是须要指定域名,在微信后台中进行设置,重定向的连接必须属于这个域名之中,不然不会转到你但愿的连接。
这个方式,让咱们的微信应用程序后台能够得到用户的标识、用户详细信息等,咱们就能够用来绑定和用户相关的业务信息了,如上面提到的图书馆借阅信 息,送水客户的信息,客户的积分信息,或者能够和后台帐号进行关联实现更加复杂的应用等。用户的身份信息如此重要,若是结合到咱们的CRM系统、业务管理 系统,就能够发挥用户信息应用的做用了。
以上就是我对这个类型菜单连接的应用了解,具体还须要进一步深化其应用,但愿和你们共同探讨这方面的应用场景。
咱们知道,微信最开始就是作语音聊天而使得其更加流行的,所以语音的识别处理天然也就成为微信交流的一个重要途径,微信的开发接口,也提供了对语音 的消息请求处理。本文主要介绍如何利用语音的识别,对C#开发的微信门户应用的整个事件链的处理操做,使得在咱们的微信帐号里面,更加方便和多元化对用户 的输入进行处理。
微信的API这么定义语音的识别的:开通语音识别功能,用户每次发送语音给公众号时,微信会在推送的语音消息XML数据包中,增长一个Recongnition字段。
语音的消息格式以下所示。
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1357290913</CreateTime>
<MsgType><![CDATA[voice]]></MsgType>
<MediaId><![CDATA[media_id]]></MediaId>
<Format><![CDATA[Format]]></Format>
<MsgId>1234567890123456</MsgId>
</xml>
参数 |
描述 |
ToUserName |
开发者微信号 |
FromUserName |
发送方账号(一个OpenID) |
CreateTime |
消息建立时间 (整型) |
MsgType |
语音为voice |
MediaId |
语音消息媒体id,能够调用多媒体文件下载接口拉取数据。 |
Format |
语音格式,如amr,speex等 |
MsgID |
消息id,64位整型 |
根据以上微信接口的定义,咱们能够定义一个实体类来对消息的传递进行处理,以下所示。
///<summary>
/// 接收的语音消息
///</summary>
[System.Xml.Serialization.XmlRoot(ElementName = "xml")]
publicclass RequestVoice : BaseMessage
{
public RequestVoice()
{
this.MsgType = RequestMsgType.Voice.ToString().ToLower();
}
///<summary>
/// 语音格式,如amr,speex等
///</summary>
publicstring Format { get; set; }
///<summary>
/// 语音消息媒体id,能够调用多媒体文件下载接口拉取数据。
///</summary>
publicstring MediaId { get; set; }
///<summary>
/// 消息ID
///</summary>
public Int64 MsgId { get; set; }
///<summary>
/// 语音识别结果,UTF8编码
///</summary>
publicstringRecognition { get; set; }
}
咱们看到,这里咱们最感兴趣的是语音的识别结果,也就是Recognition的字段,这个就是微信服务器自动根据用户的语音转换过来的内容,我测试过,识别率仍是很是高的。
这个实体类,在整个微信应用的消息传递中的关系以下所示:
明确了上面的语音对象实体,咱们就能够看看它们之间是如何处理的。
微信消息的处理逻辑以下图所示。
其中咱们来看看语音的处理操做,个人代码处理逻辑以下所示。
///<summary>
/// 对语音请求信息进行处理
///</summary>
///<param name="info">语音请求信息实体</param>
///<returns></returns>
publicstring HandleVoice(Entity.RequestVoice info)
{
string xml = "";
// 开通语音识别功能,用户每次发送语音给公众号时,
// 微信会在推送的语音消息XML数据包中,增长一个Recongnition字段。
if (!string.IsNullOrEmpty(info.Recognition))
{
TextDispatch dispatch = new TextDispatch();
xml = dispatch.HandleVoiceText(info, info.Recognition);
}
else
{
xml = "";
}
return xml;
}
在这里,我先看看,是否得到了微信的语音识别结果,若是得到,那么这个时候,就是和处理用户文本输入的操做差很少了,所以把它转给TextDispatch的处理类进行处理。
其中这里面的处理逻辑以下所示。
首先我根据识别结果,寻找是否用户读出了微信门户的菜单名称,若是根据语音结果找到对应的菜单记录,那么咱们执行菜单事件(若是是URL的View 类型菜单,咱们没办法重定向到指定的连接,所以给出一个连接文本提示,给用户单击进入;若是没有找到菜单记录,那么咱们就把语音识别结果做为通常的事件进 行处理,若是事件逻辑没有处理,那么咱们最后给出一个默认的语音应答提示结果就能够了。
具体的处理代码以下所示。
///<summary>
/// 若是用户用语音读出菜单的内容,那么咱们应该先根据菜单对应的事件触发,最后再交给普通事件处理
///</summary>
///<param name="info"></param>
///<returns></returns>
publicstring HandleVoiceText(BaseMessage info, string voiceText)
{
string xml = "";
MenuInfo menuInfo = BLLFactory<Menu>.Instance.FindByName(voiceText);
if (menuInfo != null)
{
#region 若是找到菜单对象的处理
if (menuInfo.Type == "click")
{
//模拟单击事件
RequestEventClick eventInfo = new RequestEventClick();
eventInfo.CreateTime = info.CreateTime;
eventInfo.EventKey = menuInfo.Key;
eventInfo.FromUserName = info.FromUserName;
eventInfo.ToUserName = info.ToUserName;
xml = base.DealEvent(eventInfo, eventInfo.EventKey);
}
else
{
//因为没法自动切换到链接,
//转换为链接文本供用户进入
string content = string.Format("请单击连接进入<a href=\"{0}\">{1}</a> ", menuInfo.Url, menuInfo.Name);
ResponseText textInfo = new ResponseText(info);
textInfo.Content = content;
xml = textInfo.ToXml();
}
#endregion
}
else
{
//交给事件机制处理
if (string.IsNullOrEmpty(xml))
{
xml = HandleText(info, voiceText);
}
}
//最后若是没有处理到,那么提示用户的语音内容
if (string.IsNullOrEmpty(xml))
{
ResponseText textInfo = new ResponseText(info);
textInfo.Content = string.Format("很是抱歉,您输入的语音内容没有找到对应的处理方式。您的语音内容为:{0}", voiceText);
xml = textInfo.ToXml();
}
return xml;
}
微信门户测试界面效果以下所示。
为了方便对客户会话的记录,个人微信门户后台,会记录用户的语音输入内容,以下所示。
固然,微信后台的管理界面,也可以查到相应的语音记录,界面以下所示。
以上就是我对微信语音的消息定义和事件处理的逻辑,其实语音是一个重要的输入,若是正确的识别内容,比手工输入的效果更好,给用户提供另一种高效的输入和事件处理操做。
这样的处理模式,可以使得咱们整个微信门户框架,无论是对于用户的语音输入,仍是文本输入,仍是菜单事件的处理,均可以融为一体,实现更加完美的衔接。
本文继续上一篇《C#开发微信门户及应用(12)-使用语音处理》, 继续介绍微信的相关应用。咱们知道,地理位置信息能够用来作不少相关的应用,除了咱们能够知道用户所在的位置,还能够关联出一些地理位置的应用,如天气, 热映影片,附近景点,附近影院,交通事件等等,反正全部和地理位置相关的信息,咱们均可以根据须要作一些扩展应用。本文主要介绍利用地理位置信息,如何构 建使用这些应用的操做。
在使用前,咱们先来看看微信的接口,为咱们定义了那些关于与地理位置的信息。其实地理位置的信息,微信分为了两个方面,一个是接收用户的地理位置请求,一个是用户容许上报地理位置操做,定时发送的地理位置信息。
本文主要介绍基于第一种,用户上报地理位置后,如何处理的相关应用。
地理位置的上报操做,就是在输入的地方,选择+号进行添加地理位置,而后选择当前或者指定的地理位置地图,具体操做以下所示。
地理位置消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1351776360</CreateTime>
<MsgType><![CDATA[location]]></MsgType>
<Location_X>23.134521</Location_X>
<Location_Y>113.358803</Location_Y>
<Scale>20</Scale>
<Label><![CDATA[位置信息]]></Label>
<MsgId>1234567890123456</MsgId>
</xml>
参数 |
描述 |
ToUserName |
开发者微信号 |
FromUserName |
发送方账号(一个OpenID) |
CreateTime |
消息建立时间 (整型) |
MsgType |
location |
Location_X |
地理位置维度 |
Location_Y |
地理位置经度 |
Scale |
地图缩放大小 |
Label |
地理位置信息 |
MsgId |
消息id,64位整型 |
有了上面的地理位置信息,咱们在程序里面,须要在消息传递过来的时候,定义一个实体类信息,承载相关的地理位置信息,方便咱们进一步的处理操做。
///<summary>
/// 接收的地理位置消息
///</summary>
[System.Xml.Serialization.XmlRoot(ElementName = "xml")]
publicclass RequestLocation : BaseMessage
{
public RequestLocation()
{
this.MsgType = RequestMsgType.Location.ToString().ToLower();
}
///<summary>
/// 消息ID
///</summary>
public Int64 MsgId { get; set; }
///<summary>
/// 地理位置维度
///</summary>
publicdecimal Location_X { get; set; }
///<summary>
/// 地理位置经度
///</summary>
publicdecimal Location_Y { get; set; }
///<summary>
/// 地图缩放大小
///</summary>
publicint Scale { get; set; }
///<summary>
/// 地理位置信息
///</summary>
publicstring Label { get; set; }
}
有了这些信息,咱们在信息传递的时候,就能很好获得用户的相关数据了。
若是仅仅为了返回给用户,告诉用户目前的地理位置信息,能够用下面的操做就能够了。
///<summary>
/// 对地理位置请求信息进行处理
///</summary>
///<param name="info">地理位置请求信息实体</param>
///<returns></returns>
publicstring HandleLocation(Entity.RequestLocation info)
{
string xml = "";
ResponseText txtinfo = new ResponseText(info);
txtinfo.Content = string.Format("您发送的地理位置是:{0}", info.Label);
xml = txtinfo.ToXml();
return xml;
}
不过上面的信息,显然不符合咱们扩展应用的要求,所以咱们进一步进行完善里面对地理位置信息处理的操做。咱们进一步把关于地理位置的操做,放到事件处理模块里面进行处理,处理代码以下所示。
///<summary>
/// 对地理位置请求信息进行处理
///</summary>
///<param name="info">地理位置请求信息实体</param>
///<returns></returns>
publicstring HandleLocation(Entity.RequestLocation info)
{
string xml = "";
EventDispatch dispatch = new EventDispatch();
xml = dispatch.DealLocation(info, info.Label, info.Location_Y, info.Location_X);
return xml;
}
在处理的时候,咱们须要先保存用户的地理位置信息,把它存储到用户的上下文记录里面。这样咱们在处理指令的时候,把它获取到,而后传递给相关的方法就能够实现地理位置的扩展应用了。
//保存经纬度
string location = string.Format("{0},{1}", lat, lon);
bool result = BLLFactory<UserSet>.Instance.UpdateUserInput(info.FromUserName, location);
首先对用户地理位置的请求,我根据数据库配置给出了一个用户选择的指令提示,以下所示。
为了对地理位置请求的处理,我定义了一个用于处理这个操做的指令操做
这样整个地理位置的指令操做,就在应答链里面进行很好的跳转管理了。那么为了实现天气、放映影片、附近影院、旅游线路、交通事件等方面的扩展应用,咱们应该如何操做呢?
咱们知道,百度或者腾讯都提供了一些开放平台,给咱们进行各类方式的使用。那么咱们这里以使用百度LBS平台应用来构建一些模块。
这上面都有不少相关的接口供使用,咱们能够根据其提供的数据格式进行封装,而后进行调用处理就能够了。
刚才说了,我配置了一些指令,用来构建相关的应用,指令的最后是一些事件代码的定义,咱们对这些末端的事件代码进行处理,就能够给用户返回相关的信息了,整体的操做代码以下所示。
///<summary>
/// 其余插件操做,如天气,景点、电影影讯、交通等
///</summary>
///<param name="info">基础消息</param>
///<param name="eventKey">事件标识</param>
///<returns></returns>
publicstring DealPlugin(BaseMessage info, string eventKey)
{
//LogTextHelper.Info(eventKey);
string userInput = BLLFactory<UserSet>.Instance.GetUserInput(info.FromUserName);
string xml = "";
switch (eventKey)
{
case"event-void-wether":
xml = new WeatherPlugin().Response(info, userInput);
break;
case"event-void-movie":
xml = new MoviePlugin().Response(info, userInput);
break;
case"event-void-cinema":
xml = new CinemaPlugin().Response(info, userInput);
break;
case"event-void-travel":
xml = new TravelPlugin().Response(info, userInput);
break;
case"event-void-traffic":
xml = new TrafficEventPlugin().Response(info, userInput);
break;
default:
break;
}
return xml;
}
这里以天气为例,说明该如何调用百度的接口的,首先咱们封装一下相关的接口调用。
///<summary>
/// 根据参数调用百度接口,获取相关的结果数据
///</summary>
///<param name="location">地理位置</param>
///<param name="ak">API调用键</param>
///<returns></returns>
public BaiduWeatherResult Execute(string location, string ak)
{
location = HttpUtility.UrlEncode(location);
var url = string.Format("http://api.map.baidu.com/telematics/v3/weather?location={0}&output=json&ak={1}", location, ak);
BaiduWeatherResult result = BaiduJsonHelper<BaiduWeatherResult>.ConvertJson(url);
return result;
}
其中的BaiduWeatherResult 是我根据调用返回的Json结果,构建的一个实体类,用来存储返回的内容。具体代码以下所示。
///<summary>
/// 天气请求结果Json对象
///</summary>
publicclass BaiduWeatherResult : BaiduResult
{
///<summary>
/// 天气预报信息
///</summary>
public List<BaiduWeatherData> results = new List<BaiduWeatherData>();
}
///<summary>
/// 城市的天气信息
///</summary>
publicclass BaiduWeatherData
{
///<summary>
/// 当前城市
///</summary>
publicstring currentCity { get; set; }
///<summary>
/// 天气预报信息
///</summary>
public List<BaiduWeatherJson> weather_data = new List<BaiduWeatherJson>();
}
///<summary>
/// 天气预报的单条记录Json信息
///</summary>
publicclass BaiduWeatherJson
{
///<summary>
/// 天气预报时间
///</summary>
publicstring date { get; set; }
///<summary>
/// 白天的天气预报图片url
///</summary>
publicstring dayPictureUrl { get; set; }
///<summary>
/// 晚上的天气预报图片url
///</summary>
publicstring nightPictureUrl { get; set; }
///<summary>
/// 天气情况
///</summary>
publicstring weather { get; set; }
///<summary>
/// 风力
///</summary>
publicstring wind { get; set; }
///<summary>
/// 温度
///</summary>
publicstring temperature { get; set; }
}
为了构建返回给客户的图文数据,咱们须要构建一个News对象,而后生成XML数据返回给服务器进行处理便可。
///<summary>
/// 响应用户请求,并返回相应的XML数据
///</summary>
///<param name="info">微信基础信息</param>
///<param name="location">地理位置:经纬度坐标或者地名</param>
///<returns></returns>
publicstring Response(BaseMessage info, string location)
{
string xml = "";
//"广州" 或者 "116.305145,39.982368"
if (!string.IsNullOrEmpty(location))
{
BaiduWeatherResult result = Execute(location, baiduAK);
if (result != null&& result.results.Count >0)
{
BaiduWeatherData data = result.results[0];
if (data != null)
{
ArticleEntity first = new ArticleEntity();
first.Title = string.Format("{0} 天气预报", data.currentCity);
ResponseNews news = new ResponseNews(info);
news.Articles.Add(first);
int i = 0;
foreach (BaiduWeatherJson json in data.weather_data)
{
ArticleEntity article = new ArticleEntity();
article.Title = string.Format("{0}\n{1} {2} {3}", json.date, json.weather, json.wind, json.temperature);
if (i++ == 0)
{
article.PicUrl = IsDayTime() ? json.dayPictureUrl : json.nightPictureUrl;
}
else
{
article.PicUrl = json.dayPictureUrl;
}
news.Articles.Add(article);
}
xml = news.ToXml();
}
}
}
return xml;
}
这样就很好实现了总体的功能了,具体界面功能能够访问个人微信(广州爱奇迪)进行了解,下面是功能截图供参考。
我曾经在系列文章中的《C#开发微信门户及应用(11)--微信菜单的多种表现方式介绍》 中介绍了微信菜单里面的重定向操做,经过这个重定向操做,咱们能够获取一个code值,而后获取用户的openID,进而就能获取到更多的用户信息,这个 在会员信息的场景里面用的不少,本篇介绍在网站中迅速配置这样的菜单连接,并介绍如何在后台获取相关的用户信息,实现页面数据个性化的展示操做。
咱们知道,微信的自定义菜单分为两大类,分别对应Click类型和View类型的,而重定向属于View类型的一种,以下所示。
微信重定向的菜单,就是经过传入一个地址参数,让微信服务器进行跳转,它的主要规则以下所示。
对于scope=snsapi_base方式的连接以下:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx3d81fc2886d86526&redirect_uri=http%3A%2F%2Fwww.iqidi.com%2Ftestwx.ashx&response_type=code&scope=snsapi_base&state=123#wechat_redirect
而对于scope=snsapi_userinfo方式的连接以下:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx3d81fc2886d86526&redirect_uri=http%3A%2F%2Fwww.iqidi.com%2Ftestwx.ashx&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect
这两个菜单连接主要就是对咱们给定的连接地址进行UrlEncode处理,而后把它赋值给参数redirect_uri实现的。
因为连接地址比较长,若是每次须要在配置菜单的时候,都复制过来修改,很是不方便,咱们能够在自定义菜单的配置界面里面,增长一个按钮功能,对内容进行处理,以便实现咱们须要的地址转换,个人门户应用平台对自定义菜单的操做就是基于这个思路实现。
默认咱们只须要填写一个须要重定向的url地址就能够了,以下所示。
若是须要配置成重定向的菜单连接地址,那么调用【转换重定向菜单】按钮操做,使用脚本函数进行转换就能够了,转换后的结果以下所示。
原来就是利用后台的javascript实现参数的URL转码,还须要获取后台的AppId,这样才能构形成完整的地址链接。
前面说了,第一是须要实现URL转码,第二是获取后台的AppId,而后生成一个完整的URL就能够了。为了不你们的重复研究,我把这部分代码贴出来一块儿学习下。
在使用前,咱们还须要注意一个问题,就是重定向到指定页面后,这个页面会带有一个code的参数,这个参数很是重要,咱们须要获取出来,固然也是经过javascript来获取对应的code参数了。
这个逻辑能够用一个脚本函数来实现,以下所示
function getUrlVars(){
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for(var i = 0; i < hashes.length; i++)
{
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
定义了这个函数后,咱们在重定向的页面里面,能够获取code参数的操做以下所示。
var code = getUrlVars()["code"];
先放下这些,咱们先来讨论如何把连接地址转换为须要的连接地址操做。
咱们为了实现连接地址的互相转换(为了方便),咱们能够判断连接地址是否含有qq的域名就能够了。
if (url.indexOf("https://open.weixin.qq.com/connect/oauth2/authorize?") == 0) {
var redirect_uri = getUrlVars(url)["redirect_uri"];
if (redirect_uri != "") {
var newUrl = decodeURIComponent(redirect_uri);
$("#" + ctrlName).val(newUrl);
}
}
而若是是咱们输入的正常连接,那么就应该把它转换为重定向的连接地址,以下所示。
else {
var newUrl = encodeURIComponent(url);
var reNewUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=@ViewBag.appid&redirect_uri=" + newUrl + "&response_type=code&scope=snsapi_base&state=123#wechat_redirect";
$("#" + ctrlName).val(reNewUrl);
}
其中重定向连接须要带有一个当前微信开发用户的appId,这个不是固定的,是不一样的开发人员都不同的东西,这里使用了MVC的动态对象进行绑定:@ViewBag.appid。
在对应的MenuController控制器里面,给它赋值就能够了。
///<summary>
/// 默认的视图控制方法
///</summary>
///<returns></returns>
publicoverride ActionResult Index()
{
ViewBag.appid = GetAppId();
return View();
}
这样配置后的重定向菜单地址列表就以下所示了,咱们打开对应的记录详细页面,能够经过页面里面的功能按钮,随时对重定向菜单的地址进行转换,方便了解详细的连接内容。
配置了上面的连接地址后,咱们须要在网站里面增长这样的一个页面进行处理用户的信息,通常状况下,咱们多是为了方便用户查看本身的微信基础信息, 也为了给用户绑定用户我的数据使用的用途的,如用户能够绑定手机、Email邮箱等操做,还能够绑定和业务系统相关的用户名。这样用户就能够快速注册会员 或者和后台的系统进行关联了。
我设计的两个用户信息展现界面以下所示。
这两个界面主要使用了Jquery Mobile的相关内容,对界面进行了处理,整个模块结合了短信验证码的方式,对用户的手机进行验证处理,这样可以更高效的实现信息准确的绑定操做,当 然,还能够结合外部系统,绑定用户的帐号密码,这样用户能够在微信进入微网站平台进行购物、数据维护、业务管理等操做了,其实一旦绑定外部系统的ID,也 就是提供了一个快速进行外部系统的入口了。
具体的内容在下一篇继续介绍了。
前面介绍了不少篇关于使用C#开发微信门户及应用的文章,基本上把当时微信能作的接口都封装差很少了,微信框架也积累了很多模块和用户,最近发现微 信公众平台增长了很多内容,特别是在自定义菜单里面增长了扫一扫、发图片、发地理位置功能,这几个功能模块很重要,想一想之前想在微信公众号里面增长一个扫 描二维码的功能,都作不了,如今能够了,还能够拍照上传等功能,本文主要介绍基于我前面的框架系列文章,进一步介绍如何集成和使用这些新增功能。
用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操做后显示扫描结果(若是是URL,将进入URL),且会将扫码的结果传给开发者,开发者能够下发消息。
用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操做后,将扫码的结果传给开发者,同时收起扫一扫工具,而后弹出“消息接收中”提示框,随后可能会收到开发者下发的消息。
用户点击按钮后,微信客户端将调起系统相机,完成拍照操做后,将拍摄的相片发送给开发者,并推送事件给开发者,同时收起系统相机,随后可能会收到开发者下发的消息。
用户点击按钮后,微信客户端将弹出选择器供用户选择“拍照”或者“从手机相册选择”。用户选择后即走其余两种流程。
用户点击按钮后,微信客户端将调起微信相册,完成选择操做后,将选择的相片发送给开发者的服务器,并推送事件给开发者,同时收起相册,随后可能会收到开发者下发的消息。
用户点击按钮后,微信客户端将调起地理位置选择工具,完成选择操做后,将选择的地理位置发送给开发者的服务器,同时收起位置选择工具,随后可能会收到开发者下发的消息。
但请注意,以上新增能力,均仅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用户,旧版本微信用户点击后将没有回应,开发者也不能正常接收到事件推送。
微信不只增长了这些功能模块的支持,还考虑到咱们开发人员的方便,增长了一个叫作“menutest"的公众号,方便咱们测试。咱们在公众号搜索“menutest",而后关注它便可进行测试几个新增功能了。
“menutest"的公众号名称是”自定义菜单拓展测试“,我关注它并进行了测试,二维码、图片、地理位置都很OK,自己可以响应这些事件,而且图片、地理位置自身还能出现一个对应的事件,以下所示。
图片发送能够分为拍照、拍照和相册、微信相册三类,感受后面两个有点相似,但有这些功能都很不错的。
前面说了,微信提供这些功能,能够在菜单里面进行集成,也就是菜单的类型由原来CLICK/VIEW两种,变为如今8种类型,增长2个扫码操做、3种图片操做、1种地理位置操做。
所以把菜单的枚举类型扩展一下,以下所示。
///<summary>
/// 菜单按钮类型
///</summary>
publicenum ButtonType
{
///<summary>
/// 点击
///</summary>
click,
///<summary>
/// Url
///</summary>
view,
///<summary>
/// 扫码推事件的事件推送
///</summary>
scancode_push,
///<summary>
/// 扫码推事件且弹出“消息接收中”提示框的事件推送
///</summary>
scancode_waitmsg,
///<summary>
/// 弹出系统拍照发图的事件推送
///</summary>
pic_sysphoto,
///<summary>
/// 弹出拍照或者相册发图的事件推送
///</summary>
pic_photo_or_album,
///<summary>
/// 弹出微信相册发图器的事件推送
///</summary>
pic_weixin,
///<summary>
/// 弹出地理位置选择器的事件推送
///</summary>
location_select
}
而后在Winform里面调用建立菜单操做代码以下所示:
privatevoid btnCreateMenu_Click(object sender, EventArgs e)
{
MenuJson productInfo = new MenuJson("新功能测试", new MenuJson[] {
new MenuJson("扫码推事件", ButtonType.scancode_push, "scancode_push")
,new MenuJson("系统拍照发图", ButtonType.pic_sysphoto, "pic_sysphoto")
, new MenuJson("拍照相册发图", ButtonType.pic_photo_or_album, "pic_photo_or_album")
, new MenuJson("微信相册发图", ButtonType.pic_weixin, "pic_weixin")
, new MenuJson("地理位置选择", ButtonType.location_select, "location_select")
});
MenuJson frameworkInfo = new MenuJson("框架产品", new MenuJson[] {
new MenuJson("Win开发框架", ButtonType.click, "win"),
new MenuJson("WCF开发框架", ButtonType.click, "wcf"),
new MenuJson("混合式框架", ButtonType.click, "mix"),
new MenuJson("Web开发框架", ButtonType.click, "web")
,new MenuJson("代码生成工具", ButtonType.click, "database2sharp")
});
MenuJson relatedInfo = new MenuJson("相关连接", new MenuJson[] {
new MenuJson("公司介绍", ButtonType.click, "event_company"),
new MenuJson("官方网站", ButtonType.view, "http://www.iqidi.com"),
new MenuJson("联系咱们", ButtonType.click, "event_contact"),
new MenuJson("应答系统", ButtonType.click, "set-1"),
new MenuJson("人工客服", ButtonType.click, "event_customservice")
});
MenuListJson menuJson = new MenuListJson();
menuJson.button.AddRange(new MenuJson[] { productInfo, frameworkInfo, relatedInfo });
if (MessageUtil.ShowYesNoAndWarning("您确认要建立菜单吗") == System.Windows.Forms.DialogResult.Yes)
{
IMenuApi menuBLL = new MenuApi();
CommonResult result = menuBLL.CreateMenu(token, menuJson);
Console.WriteLine("建立菜单:" + (result.Success ? "成功" : "失败:" + result.ErrorMessage));
}
}
固然,通常状况下咱们都是在Web后台系统进行的,维护菜单都是在本身微信平台上进行菜单管理,而后一次性提交到微信服务器便可。
而在Web后台,只须要把数据库的数据变化为Json数据提交便可,操做和上面的相似。
///<summary>
///更新微信菜单
///</summary>
///<returns></returns>
public ActionResult UpdateWeixinMenu()
{
string token = base.GetAccessToken();
MenuListJson menuJson = GetWeixinMenu();
IMenuApi menuApi = new MenuApi();
CommonResult result = menuApi.CreateMenu(token, menuJson);
return ToJsonContent(result);
}
前面讲了,有了最新的功能,咱们就能够实现扫一扫功能,从而能够扫描条形码,二维码的功能。有了条形码、二维码的快速和识别,咱们就能开发一些如条码查询、商品处理等功能了。
这里咱们介绍如何在个人微信开发框架里面整合这个扫一扫的功能处理操做。
前面已经增长了一些新功能的测试菜单,咱们要作的就是响应这些事件处理,而后对他们进行应答处理就能够了。
下面是根据事件进行的一些API跳转处理,咱们同时定义了几个相关的实体类用来处理他们的信息,如RequestEventScancodePush、RequestEventScancodeWaitmsg、RequestEventPicSysphoto等等。
RequestEventScancodeWaitmsg实体类的代码以下所示,其余的相似处理。
///<summary>
/// 扫码推事件且弹出“消息接收中”提示框的事件推送
///</summary>
[System.Xml.Serialization.XmlRoot(ElementName = "xml")]
publicclass RequestEventScancodeWaitmsg : BaseEvent
{
public RequestEventScancodeWaitmsg()
{
this.MsgType = RequestMsgType.Event.ToString().ToLower();
this.Event = RequestEvent.scancode_waitmsg.ToString();
this.ScanCodeInfo = new ScanCodeInfo();
}
///<summary>
/// 事件KEY值,由开发者在建立菜单时设定
///</summary>
publicstring EventKey { get; set; }
///<summary>
/// 扫描信息
///</summary>
public ScanCodeInfo ScanCodeInfo { get; set; }
}
而根据实体类强类型的处理接口流转操做以下所示。
case RequestEvent.scancode_push:
{
//扫码推事件的事件推送
RequestEventScancodePush info = XmlConvertor.XmlToObject(postStr, typeof(RequestEventScancodePush)) as RequestEventScancodePush;
if (info != null)
{
responseContent = actionBLL.HandleEventScancodePush(info);
}
}
break;
case RequestEvent.scancode_waitmsg:
{
//扫码推事件且弹出“消息接收中”提示框的事件推送
RequestEventScancodeWaitmsg info = XmlConvertor.XmlToObject(postStr, typeof(RequestEventScancodeWaitmsg)) as RequestEventScancodeWaitmsg;
if (info != null)
{
responseContent = actionBLL.HandleEventScancodeWaitmsg(info);
}
}
break;
case RequestEvent.pic_sysphoto:
{
//弹出系统拍照发图的事件推送
RequestEventPicSysphoto info = XmlConvertor.XmlToObject(postStr, typeof(RequestEventPicSysphoto)) as RequestEventPicSysphoto;
if (info != null)
{
responseContent = actionBLL.HandleEventPicSysphoto(info);
}
}
break;
..................
处理扫描结果并返回的最终代码以下所示。
///<summary>
/// 扫码推事件且弹出“消息接收中”提示框的事件推送的处理
///</summary>
///<param name="info">扫描信息</param>
///<returns></returns>
publicstring HandleEventScancodeWaitmsg(RequestEventScancodeWaitmsg info)
{
ResponseText response = new ResponseText(info);
response.Content = string.Format("您的信息为:{0},能够结合后台进行数据查询。", info.ScanCodeInfo.ScanResult);
return response.ToXml();
}
最后咱们测试扫描一个条形码,能够看到返回的结果界面操做以下所示。
前面介绍了一些新菜单功能模块的集成,我我的对这种扫一扫菜单功能很是赞扬,这也是微信逐步整合更多硬件资源和接口处理的趋向,不过在集成使用的时 候,发现公众号偶尔出现闪退的状况,还有就是这些新功能虽而后台可以实现数据的处理和接收,可是有一些不能返回应答消息,很郁闷。也许随着版本研发的加 快,这些功能很快获得完善和解决。
另外微信开放平台也投入使用了,好些认证也是300元一年,不过暂时没有其应用的场景,我只是用到了它来获取微信帐号的unionid的功能,其余功能慢慢了解吧。
还有就是微信的企业号也已经出来了,并且我也已经申请认证经过,它的开发用户的API也有很多,有空继续研究并整合到微信开发框架里面吧。
在本系列随笔的前面,主要就是介绍微信公众号的门户应用开发,最近把整个微信框架进行了扩展补充,增长了最新的企业号的API封装和开发,后续主要介绍如何利用C#进行微信企业号的开发工做,本篇做为微信企业号的开发的起步篇,介绍微信企业号的配置和使用。
企业号是继公众号、订阅号的另一种微信类型,它主要是面对企业的。企业号是微信为企业客户提供的移动应用入口。能够帮助企业创建员工、上下游供应 链与企业 IT 系统间的链接。利用 企业号 ,企业或第三方合做伙伴能够帮助企业快速、低成本的实现高质量的移动轻应用,实现生产、管理、协做、运营的 移动化 。
我的以为企业号最大的亮点是能够不限数量的消息发送,也就是能够在企业员工之间畅通交流。相对于公众号和订阅号,发送消息的谨慎程度,微信企业号可 谓给人眼前一亮的感受。不过微信企业号是须要内部创建好通信录,关注者须要匹配通信录的微信号、邮箱、电话号码任一个经过才能够关注,也就是能够防止其余 外来人员的自由关注了,另外若是为了安全考虑,还能够设置二次验证,也就是一个审核过程。
企业号的认证和公众号同样,须要提供相关的企业资质文件,而且认证每一年都要收取费用,不然可能有人员和功能的一些限制。以为微信真是想着方法赚钱, 目前已有的收费模式有,订阅号、公众号、企业号、开放平台,好像都有认证收费的了,并且微信小店也还须要收2万的押金,一切都是钱呀。
好了,其余很少说,微信的注册地址是:https://qy.weixin.qq.com,一个邮箱不能同时注册微信公众号和微信企业号。
对于企业开通企业号并开始使用须要四步
1) 企业到微信官网( http://qy.weixin.qq.com )申请开通;
2) 开通后,企业在企业号管理后台导入成员,发布二维码;
3) 企业调用企业号 api 与企业自有系统对接开发;
4) 员工关注,收到微信信息,在微信中与企业交互
注册好企业号,就能够经过微信扫一扫,扫描企业二维码进行登陆了,扫描的时候,须要微信进行确认,才能够继续输入密码进行登陆,操做界面以下所示(左边是手机截图,右边是网页截图)。
登陆后咱们就能够看到对应的电脑端的管理界面了。
若是开发过微信公众号,那么咱们就知道,若是须要在微信服务器和网站服务器之间创建链接关系,实现消息的转发和处理,那么就应该设置一个回调模式,须要配置好相关的参数。而后在本身 网站服务器里面创建一个处理微信服务器消息的入口。
进入配置后,咱们须要修改相关的URL、Token、EncodingAESKey等参数,主要是URL,这个就是和公众号的入口处理同样的,须要咱们发布到网站服务器上的处理入口。
Token和AESKey能够根据提示动态生成一个便可,AESKey好像必须是23位的,因此这个通常是让它本身生成的,这个主要用来加密解密使用的。
URL、Token、EncodingAESKey三个参数说明。
1)URL是企业应用接收企业号推送请求的访问协议和地址,支持http或https协议。
2)Token可由企业任意填写,用于生成签名。
3)EncodingAESKey用于消息体的加密,是AES密钥的Base64编码。
验证URL、Token以及加密的详细处理请参考后续 “接收消息时的加解密处理” 的部分。
我公司的企业号配置后的界面以下所示。
这个URL里面指向的页面功能,须要对数据进行解析并返回给微信服务器,所以咱们须要在服务器上预先部署好这个处理功能入口。
除了上面的几个函数,还有一个CorpID的参数须要使用,咱们能够在后台主界面-设置里面查看到。
而后咱们为了方便网站后台使用,咱们和公众号的配置同样,把它放到了Web.Config里面,以下所示。
前面介绍了几个配置项,须要在回调页面里面使用的,本小节继续介绍如何实现企业号信息的回发,使之经过回调测试的操做。
因为回调测试的数据是经过Get方式发送的,所以咱们的处理逻辑代码以下所示,和公众号的相似处理,只是实现部分不太同样而已。
///<summary>
/// 企业号回调信息接口。统一接收并处理信息的入口。
///</summary>
publicclass corpapi : IHttpHandler
{
///<summary>
/// 处理企业号的信息
///</summary>
///<param name="context"></param>
publicvoid ProcessRequest(HttpContext context)
{
string postString = string.Empty;
if (HttpContext.Current.Request.HttpMethod.ToUpper() == "POST")
{
using (Stream stream = HttpContext.Current.Request.InputStream)
{
Byte[] postBytes = new Byte[stream.Length];
stream.Read(postBytes, 0, (Int32)stream.Length);
postString = Encoding.UTF8.GetString(postBytes);
}
if (!string.IsNullOrEmpty(postString))
{
Execute(postString);
}
}
else
{
Auth();
}
}
///<summary>
/// 成为开发者的第一步,验证并相应服务器的数据
///</summary>
privatevoid Auth()
{
#region 获取关键参数
string token = ConfigurationManager.AppSettings["CorpToken"];//从配置文件获取Token
if (string.IsNullOrEmpty(token))
{
LogTextHelper.Error(string.Format("CorpToken 配置项没有配置!"));
}
string encodingAESKey = ConfigurationManager.AppSettings["EncodingAESKey"];//从配置文件获取EncodingAESKey
if (string.IsNullOrEmpty(encodingAESKey))
{
LogTextHelper.Error(string.Format("EncodingAESKey 配置项没有配置!"));
}
string corpId = ConfigurationManager.AppSettings["CorpId"];//从配置文件获取corpId
if (string.IsNullOrEmpty(corpId))
{
LogTextHelper.Error(string.Format("CorpId 配置项没有配置!"));
}
#endregion
string echoString = HttpContext.Current.Request.QueryString["echoStr"];
string signature = HttpContext.Current.Request.QueryString["msg_signature"];//企业号的 msg_signature
string timestamp = HttpContext.Current.Request.QueryString["timestamp"];
string nonce = HttpContext.Current.Request.QueryString["nonce"];
string decryptEchoString = "";
if (new CorpBasicApi().CheckSignature(token, signature, timestamp, nonce, corpId, encodingAESKey, echoString, ref decryptEchoString))
{
if (!string.IsNullOrEmpty(decryptEchoString))
{
HttpContext.Current.Response.Write(decryptEchoString);
HttpContext.Current.Response.End();
}
}
}
具体的处理代码以下所示,里面的一个加解密处理的类是微信企业号附录里面提供的,我使用了C#版本的SDK而已。
///<summary>
/// 企业号基础操做API实现
///</summary>
publicclass CorpBasicApi : ICorpBasicApi
{
///<summary>
/// 验证企业号签名
///</summary>
///<param name="token">企业号配置的Token</param>
///<param name="signature">签名内容</param>
///<param name="timestamp">时间戳</param>
///<param name="nonce">nonce参数</param>
///<param name="corpId">企业号ID标识</param>
///<param name="encodingAESKey">加密键</param>
///<param name="echostr">内容字符串</param>
///<param name="retEchostr">返回的字符串</param>
///<returns></returns>
publicbool CheckSignature(string token, string signature, string timestamp, string nonce, string corpId, string encodingAESKey, string echostr, refstring retEchostr)
{
WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(token, encodingAESKey, corpId);
int result = wxcpt.VerifyURL(signature, timestamp, nonce, echostr, ref retEchostr);
if (result != 0)
{
LogTextHelper.Error("ERR: VerifyURL fail, ret: " + result);
returnfalse;
}
returntrue;
//ret==0表示验证成功,retEchostr参数表示明文,用户须要将retEchostr做为get请求的返回参数,返回给企业号。
// HttpUtils.SetResponse(retEchostr);
}
前面一篇随笔企业号的一些基础信息,以及介绍如何配置企业号的回调方式实现和企业号服务器进行沟通的桥梁。本篇主要仍是继续介绍企业号的开发工做的开展,介绍微信企业号通信录管理开发功能,介绍其中组织机构里面如何获取和管理部门的信息等内容。
首先咱们能够在企业号的管理后台里面建立一个组织机构,里面建立一些部门和人员列表,方便咱们开发和使用。
例如建立一个广州爱奇迪的根结构,而后在其中在建立一些组织机构,以下图所示。
而后给组织结构根节点“广州爱奇迪”增长一个管理员权限,之后再开发接口里面也就能够使用这个管理员所属的权限Secret值进行调用了。
CorpID是企业号的标识,每一个企业号拥有一个惟一的CorpID;Secret是管理组凭证密钥。
系统管理员可经过管理端的权限管理功能建立管理组,分配管理组对应用、通信录、接口的访问权限。完成后,管理组便可得到惟一的secret。系统管理员可经过权限管理查看全部管理组的secret,其余管理员可经过设置中的开发者凭据查看。
个人企业号的建立者和“广州爱奇迪”组织结构的管理员是不一样的,因为Secret是管理组凭证密钥,所以管理者负责不一样的组织机构管理的话,本身的管理Secret值可能就不一样了。若是咱们须要调用接口,就须要用到这个属于本身权限级别的Secret值,以下图所示。
若是不是企业号的建立者,那么可能不能修改里面的一些权限分配,只能查看。
和公众号同样,咱们调用企业号API的第一步也是须要先获取访问的票据AccessToken。这个票据是全局性的,有必定的时效和频率控制,所以须要适当的进行缓存,不能每次调用都去刷新获取。
企业号获取访问票据的主要的逻辑代码以下所示,其主要的就是须要使用管理者的Secret值去获取对应的口令,这样它就可以知道管理的是那个组织结构的了。
///<summary>
/// 获取每次操做微信API的Token访问令牌
///</summary>
///<param name="corpid">企业Id</param>
///<param name="corpsecret">管理组的凭证密钥</param>
///<returns></returns>
publicstring GetAccessTokenNoCache(string corpid, string corpsecret)
{
var url = string.Format("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={0}&corpsecret={1}",
corpid, corpsecret);
HttpHelper helper = new HttpHelper();
string result = helper.GetHtml(url);
string regex = "\"access_token\":\"(?<token>.*?)\"";
string token = CRegex.GetText(result, regex, "token");
return token;
}
微信企业号的说明以下所示:
当企业应用调用企业号接口时,企业号后台为根据这次访问的AccessToken,校验访问的合法性以及所对应的管理组的管理权限以返回相应的结果。
注:你应该审慎配置管理组的权限,够用即好,权限过大会增长误操做可能性及信息安全隐患。
AccessToken是企业号的全局惟一票据,调用接口时需携带AccessToken。AccessToken须要用CorpID和Secret来换取,不一样的Secret会返回不一样的AccessToken。正 常状况下AccessToken有效期为7200秒,有效期内重复获取返回相同结果,并自动续期。因为获取access_token的api调用次数很是 有限,建议企业全局存储与更新access_token,频繁刷新access_token会致使api调用受限,影响自身业务。
有了第一节里面的访问票据,咱们就能够利用API来作不少事情了,包括组织结构的获取、建立、删除等等功能。
建立部门的官方接口定义以下所示。
Https请求方式: POST
https://qyapi.weixin.qq.com/cgi-bin/department/create?access_token=ACCESS_TOKEN
请求包结构体为:
{
"name": "邮箱产品组",
"parentid": "1"
}
参数 |
必须 |
说明 |
access_token |
是 |
调用接口凭证 |
name |
是 |
部门名称。长度限制为1~64个字符 |
parentid |
是 |
父亲部门id。根部门id为1 |
{
"errcode": 0,
"errmsg": "created",
"id": 2
}
根据上面的一些相似的接口定义说明,咱们先来定义下组织机构部门数据的维护接口,而后在逐步实现和调用。
#region 部门管理
///<summary>
/// 建立部门。
/// 管理员须拥有“操做通信录”的接口权限,以及父部门的管理权限。
///</summary>
CorpDeptCreateJson CreateDept(string accessToken, string name, string parentId);
///<summary>
/// 更新部门。
/// 管理员须拥有“操做通信录”的接口权限,以及该部门的管理权限。
///</summary>
CommonResult DeleteDept(string accessToken, int id);
///<summary>
/// 删除部门.
/// 管理员须拥有“操做通信录”的接口权限,以及该部门的管理权限。
///</summary>
CorpDeptListJson ListDept(string accessToken);
///<summary>
/// 获取部门列表.
/// 管理员须拥有’获取部门列表’的接口权限,以及对部门的查看权限。
///</summary>
CommonResult UpdateDept(string accessToken, int id, string name);
#endregion
如建立部门的接口实现以下所示,主要就是构建URL和POST的数据包,而后统一调用并获取返回数据,转换为具体的Json对象实体便可。其余接口的实现方式相似,不在赘述。
///<summary>
/// 建立部门。
/// 管理员须拥有“操做通信录”的接口权限,以及父部门的管理权限。
///</summary>
public CorpDeptCreateJson CreateDept(string accessToken, string name, string parentId)
{
string urlFormat = "https://qyapi.weixin.qq.com/cgi-bin/department/create?access_token={0}";
var data = new
{
name = name,
parentId = parentId
};
var url = string.Format(urlFormat, accessToken);
var postData = data.ToJson();
CorpDeptCreateJson result = CorpJsonHelper<CorpDeptCreateJson>.ConvertJson(url, postData);
return result;
}
CorpDeptCreateJson 对象实体类的定义以下所示,咱们主要是根据返回结果进行定义的。
///<summary>
/// 建立部门的返回结果
///</summary>
publicclass CorpDeptCreateJson : BaseJsonResult
{
///<summary>
/// 返回的错误消息
///</summary>
public CorpReturnCode errcode { get; set; }
///<summary>
/// 对返回码的文本描述内容
///</summary>
publicstring errmsg { get; set; }
///<summary>
/// 建立的部门id。
///</summary>
publicint id { get; set; }
}
上面小节介绍了如何封装部门管理的API,那么咱们封装好了对应的接口和接口实现,怎么样在实际环境里面进行调用处理的呢,为了方便我建立一个小的Winform程序来测试对应API的功能,以下所示。
下面咱们来介绍一下调用的代码和效果展现。
privatevoid btnCreateDeleteDept_Click(object sender, EventArgs e)
{
ICorpAddressBookApi bll = new CorpAddressBookApi();
string name = "测试部门";
CorpDeptCreateJson json = bll.CreateDept(token, name, "2");
if (json != null)
{
Console.WriteLine("建立了部门:{0}, ID:{1}", name, json.id);
//更新部门信息
name = "测试部门修更名称";
CommonResult result = bll.UpdateDept(token, json.id, name);
if(result != null)
{
Console.WriteLine("修改部门名称:{0} {1}", (result.Success ? "成功" : "失败"), result.ErrorMessage);
}
//删除部门
result = bll.DeleteDept(token, json.id);
if (result != null)
{
Console.WriteLine("删除部门名称:{0} {1}", (result.Success ? "成功" : "失败"), result.ErrorMessage);
}
}
}
///<summary>
/// 获取部门列表
///</summary>
privatevoid btnListDept_Click(object sender, EventArgs e)
{
ICorpAddressBookApi bll = new CorpAddressBookApi();
CorpDeptListJson list = bll.ListDept(token);
foreach (CorpDeptJson info in list.department)
{
string tips = string.Format("{0}:{1}", info.name, info.id);
Console.WriteLine(tips);
}
}
在上篇随笔《C#开发微信门户及应用(17)-微信企业号的通信录管理开发之部门管理》介绍了通信录的部门的相关操做管理,通信录管理包括部门管理、成员管理、标签管理三个部分,本篇主要介绍成员的管理操做,包括建立、删除、更新、获取、获取部门成员几个操做要点。
为了方便,咱们能够建立一个部门组织结构,这是开发的前提,由于咱们通信录管理,也是基于一个组织机构下的,如上篇介绍的组织结构层次同样。我这里建立一个广州爱奇迪的根结构,而后在其中在建立一些组织机构,以下图所示。
在后台能够经过功能操做添加人员,本篇主要介绍如何调用微信企业号API进行人员管理的操做。
建立人员的API定义以下所示。
Https请求方式: POST
https://qyapi.weixin.qq.com/cgi-bin/user/create?access_token=ACCESS_TOKEN
请求包结构体为:
{
"userid": "zhangsan",
"name": "张三",
"department": [1, 2],
"position": "产品经理",
"mobile": "15913215421",
"gender": 1,
"tel": "62394",
"email": "zhangsan@gzdev.com",
"weixinid": "zhangsan4dev"
}
参数 |
必须 |
说明 |
access_token |
是 |
调用接口凭证 |
userid |
是 |
员工UserID。对应管理端的账号,企业内必须惟一。长度为1~64个字符 |
name |
是 |
成员名称。长度为1~64个字符 |
department |
否 |
成员所属部门id列表。注意,每一个部门的直属员工上限为1000个 |
position |
否 |
职位信息。长度为0~64个字符 |
mobile |
否 |
手机号码。企业内必须惟一,mobile/weixinid/email三者不能同时为空 |
gender |
否 |
性别。gender=0表示男,=1表示女。默认gender=0 |
tel |
否 |
办公电话。长度为0~64个字符 |
|
否 |
邮箱。长度为0~64个字符。企业内必须惟一 |
weixinid |
否 |
微信号。企业内必须惟一 |
管理员须拥有“操做通信录”的接口权限,以及指定部门的管理权限。
{
"errcode": 0,
"errmsg": "created"
}
咱们在C#里面,须要定义对应给的接口,而后根据须要构造对应的传递实体信息。
这里我把人员管理的接口所有定义好,接口定义以下所示。
#region 部门成员管理
///<summary>
/// 建立成员
///</summary>
CommonResult CreateUser(string accessToken, CorpUserJson user);
///<summary>
/// 更新成员
///</summary>
CommonResult UpdateUser(string accessToken, CorpUserUpdateJson user);
///<summary>
/// 删除成员
///</summary>
CommonResult DeleteUser(string accessToken, string userid);
///<summary>
/// 根据成员id获取成员信息
///</summary>
CorpUserGetJson GetUser(string accessToken, string userid);
///<summary>
/// 获取部门成员
///</summary>
CorpUserListJson GetDeptUser(string accessToken, int department_id, int fetch_child = 0, int status = 0);
#endregion
而后根据信息定义,建立一个承载人员信息的CorpUserJson实体对象,建立人员的实现操做代码以下所示。
///<summary>
/// 建立成员
///</summary>
public CommonResult CreateUser(string accessToken, CorpUserJson user)
{
string urlFormat = "https://qyapi.weixin.qq.com/cgi-bin/user/create?access_token={0}";
var data = new
{
userid = user.userid,
name = user.name,
department = user.department,
position = user.position,
mobile = user.mobile,
gender = user.gender,
tel = user.tel,
email = user.email,
weixinid = user.weixinid
};
var url = string.Format(urlFormat, accessToken);
var postData = data.ToJson();
return Helper.GetCorpExecuteResult(url, postData);
}
成员的数据更新和建立操做相似,它的企业号定义以下所示。
Https请求方式: POST
https://qyapi.weixin.qq.com/cgi-bin/user/update?access_token=ACCESS_TOKEN
请求包示例以下(若是非必须的字段未指定,则不更新该字段以前的设置值):
{
"userid": "zhangsan",
"name": "李四",
"department": [1],
"position": "后台工程师",
"mobile": "15913215421",
"gender": 1,
"tel": "62394",
"email": "zhangsan@gzdev.com",
"weixinid": "lisifordev",
"enable": 1
}
因为它的操做数据相似,所以它的实现代码也差很少,以下所示就是。
///<summary>
/// 更新成员
///</summary>
public CommonResult UpdateUser(string accessToken, CorpUserUpdateJson user)
{
string urlFormat = "https://qyapi.weixin.qq.com/cgi-bin/user/update?access_token={0}";
//string postData = user.ToJson();
var data = new
{
userid = user.userid,
name = user.name,
department = user.department,
position = user.position,
mobile = user.mobile,
gender = user.gender,
tel = user.tel,
email = user.email,
weixinid = user.weixinid,
enable = user.enable
};
var url = string.Format(urlFormat, accessToken);
var postData = data.ToJson();
return Helper.GetCorpExecuteResult(url, postData);
}
这些操做和上面的相似,不在赘述,主要就是根据须要定义他们对应的返回数据信息,而后解析Json数据便可转换为对应的实体。
Https请求方式: GET
https://qyapi.weixin.qq.com/cgi-bin/user/delete?access_token=ACCESS_TOKEN&userid=lisi
参数 |
必须 |
说明 |
access_token |
是 |
调用接口凭证 |
userid |
是 |
员工UserID。对应管理端的账号 |
{
"errcode": 0,
"errmsg": "deleted"
}
Https请求方式: GET
https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&userid=lisi
参数 |
必须 |
说明 |
access_token |
是 |
调用接口凭证 |
userid |
是 |
员工UserID |
{
"errcode": 0,
"errmsg": "ok",
"userid": "zhangsan",
"name": "李四",
"department": [1, 2],
"position": "后台工程师",
"mobile": "15913215421",
"gender": 1,
"tel": "62394",
"email": "zhangsan@gzdev.com",
"weixinid": "lisifordev",
"avatar": "http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3WJ6DSZUfiakYe37PKnQhBIeOQBO4czqrnZDS79FH5Wm5m4X69TBicnHFlhiafvDwklOpZeXYQQ2icg/0",
"status": 1
}
Https请求方式: GET
参数 |
必须 |
说明 |
access_token |
是 |
调用接口凭证 |
department_id |
是 |
获取的部门id |
fetch_child |
否 |
1/0:是否递归获取子部门下面的成员 |
status |
否 |
0获取所有员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 |
管理员须拥有’获取部门成员’的接口权限,以及指定部门的查看权限。
{
"errcode": 0,
"errmsg": "ok",
"userlist": [
{
"userid": "zhangsan",
"name": "李四"
}
]
}
这个返回值咱们定义一个实体对象用来存储数据便可。
///<summary>
/// 获取部门成员返回的数据
///</summary>
publicclass CorpUserListJson : BaseJsonResult
{
public CorpUserListJson()
{
this.userlist = new List<CorpUserSimpleJson>();
}
///<summary>
/// 返回的错误消息
///</summary>
public CorpReturnCode errcode { get; set; }
///<summary>
/// 对返回码的文本描述内容
///</summary>
publicstring errmsg { get; set; }
///<summary>
/// 成员列表
///</summary>
public List<CorpUserSimpleJson> userlist { get; set; }
}
上面介绍了一些企业号的接口定义和我对API的C#封装接口和部分实现代码,实现了功能后,咱们就能够在代码中对它进行测试,确信是否正常使用。
///<summary>
/// 人员管理综合性操做(建立、修改、获取信息、删除)
///</summary>
///<param name="sender"></param>
///<param name="e"></param>
privatevoid btnCorpUser_Click(object sender, EventArgs e)
{
CorpUserJson user = new CorpUserJson();
user.userid = "test";
user.name ="测试用户";
user.department = new List<int>(){2};
user.email = "test@163.com";
ICorpAddressBookApi bll = new CorpAddressBookApi();
CommonResult result = bll.CreateUser(token, user);
if (result != null)
{
Console.WriteLine("建立成员:{0} {1} {2}", user.name, (result.Success ? "成功" : "失败"), result.ErrorMessage);
string name = "修改测试";
user.name = name;
CorpUserUpdateJson userUpdate = new CorpUserUpdateJson(user);
result = bll.UpdateUser(token, userUpdate);
if (result != null)
{
Console.WriteLine("修更名称:{0} {1} {2}", name, (result.Success ? "成功" : "失败"), result.ErrorMessage);
}
CorpUserGetJson userGet = bll.GetUser(token, user.userid);
if (userGet != null)
{
Console.WriteLine("成员名称:{0} ({1} {2})", userGet.name, user.userid, user.email);
}
result = bll.DeleteUser(token, user.userid);
if (result != null)
{
Console.WriteLine("删除成员:{0} {1} {2}", name, (result.Success ? "成功" : "失败"), result.ErrorMessage);
}
}
}
获取部门人员的操做代码以下所示。
///<summary>
/// 获取部门人员
///</summary>
privatevoid btnCorpUserList_Click(object sender, EventArgs e)
{
int deptId = 1;
ICorpAddressBookApi bll = new CorpAddressBookApi();
CorpUserListJson result = bll.GetDeptUser(token, deptId);
if (result != null)
{
foreach(CorpUserSimpleJson item in result.userlist)
{
Console.WriteLine("成员名称:{0} {1}", item.name, item.userid);
}
}
}
人员的管理,相对来讲比较简单,主要是在必定的部门下建立人员,而后也能够给标签增长相应的人员,基本上就是这些了,不过必定须要确保有相应的权限进行操做。
咱们知道,企业号主要是面向企业需求而生的,所以内部消息的交流显得很是重要,并且发送、回复消息数量应该很可观,对于大企业尤为如此,所以能够 结合企业号实现内部消息的交流。企业号具备关注安全、消息无限制等特色,很适合企业内部的环境。本文主要介绍如何利用企业号实现文本、图片、文件、语音、 视频、图文消息等消息的发送操做。
对于企业号,有如下一些特色:
1)关注更安全
–只有企业通信录的成员才能关注企业号,分级管理员、保密消息等各类特性确保企业内部信息的安全。
企业能够设置自行验证关注者身份,进行二次安全验证,保证企业信息使用和传递安全。
若员工离职,企业管理员可在通信录中删除该成员,该成员即自动取消关注企业号,同时微信中的企业号历史记录也会被清除。
2)应用可配置
–企业可自行在企业号中可配置多个服务号,能够链接不一样的企业应用系统,只有受权的企业成员才能使用相应的服务号。
3)消息无限制
–发送消息无限制,并提供完善的的管理接口及微信原生能力,以适应企业复杂、个性化的应用场景。
企业能够主动发消息给员工,消息量不受限制。
4)使用更便捷
–企业号在微信中有统一的消息入口,用户能够更方便地管理企业号消息。微信通信录也能够直接访问企业号中的应用。
目前企业号的内容能够用下面的分层图来展现,分别包含素材管理、被动响应消息、通信录管理、自定义菜单等内容,详细能够看下面图示。
企业号和公众号同样,能够分为消息处理和事件处理,下面是他们两种类型的处理操做,也就发送的消息有文本消息、图片消息、文件消息、视频消息、语音消息、地理文字消息、图文和多媒体消息等。
事件处理主要就是关注、取消关注事件,以及菜单click类型和view类型两种操做,还有就是地理位置上报事件等。
两种类型的处理图以下所示。
在企业的管理后台,和公众号同样,能够看到对应信息交流记录,包括文字、图片、地理位置等等,以下所示。
因为消息分为几种类型,包括文本(Text)、图片(Image)、文件(File)、语音(Voice)、视频(Video)、图文消息等(News)、MpNews等。
所以咱们须要分别对它们进行必定的定义和封装处理,以下是它们的信息对象设计图。
企业号发送消息的官方定义以下:
企业能够主动发消息给员工,消息量不受限制。
调用接口时,使用Https协议、JSON数据包格式,数据包不需作加密处理。
目前支持文本、图片、语音、视频、文件、图文等消息类型。除了news类型,其它类型的消息可在发送时加上保密选项,保密消息会被打上水印,而且只有接收者才能阅读。
咱们以发送的文本消息为例进行说明,它的定义以下所示。
{
"touser": "UserID1|UserID2|UserID3",
"toparty": " PartyID1 | PartyID2 ",
"totag": " TagID1 | TagID2 ",
"msgtype": "text",
"agentid": "1",
"text": {
"content": "Holiday Request For Pony(http://xxxxx)"
},
"safe":"0"
}
参数 |
必须 |
说明 |
touser |
否 |
UserID列表(消息接收者,多个接收者用‘|’分隔)。特殊状况:指定为@all,则向关注该企业应用的所有成员发送 |
toparty |
否 |
PartyID列表,多个接受者用‘|’分隔。当touser为@all时忽略本参数 |
totag |
否 |
TagID列表,多个接受者用‘|’分隔。当touser为@all时忽略本参数 |
msgtype |
是 |
消息类型,此时固定为:text |
agentid |
是 |
企业应用的id,整型。可在应用的设置页面查看 |
content |
是 |
消息内容 |
safe |
否 |
表示是不是保密消息,0表示否,1表示是,默认0 |
其中每种消息都会包含如下消息所示,也就是它们共同的属性:
touser": "UserID1|UserID2|UserID3",
"toparty": " PartyID1 | PartyID2 ",
"totag": " TagID1 | TagID2 ",
"msgtype": "text",
"agentid": "1",
所以咱们能够定义一个基类用来方便承载这些共同的信息。
///<summary>
/// 企业号发送消息的基础消息内容
///</summary>
publicclass CorpSendBase
{
///<summary>
/// UserID列表(消息接收者,多个接收者用‘|’分隔)。特殊状况:指定为@all,则向关注该企业应用的所有成员发送
///</summary>
publicstring touser { get; set; }
///<summary>
/// PartyID列表,多个接受者用‘|’分隔。当touser为@all时忽略本参数
///</summary>
publicstring toparty { get; set; }
///<summary>
/// TagID列表,多个接受者用‘|’分隔。当touser为@all时忽略本参数
///</summary>
publicstring totag { get; set; }
///<summary>
/// 消息类型
///</summary>
publicstring msgtype { get; set; }
///<summary>
/// 企业应用的id,整型。可在应用的设置页面查看
///</summary>
publicstring agentid { get; set; }
///<summary>
/// 表示是不是保密消息,0表示否,1表示是,默认0
///</summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
publicstring safe { get; set; }
}
而后其余消息逐一继承这个基类便可,以下所示。
最终会构成下面这个继承关系图。
定义好相关的发送对象后,咱们就能够定义它的统一发送接口了,以下所示。
///<summary>
/// 企业号消息管理接口定义
///</summary>
publicinterface ICorpMessageApi
{
///<summary>
/// 发送消息。
/// 须要管理员对应用有使用权限,对收件人touser、toparty、totag有查看权限,不然本次调用失败。
///</summary>
///<param name="accessToken"></param>
///<returns></returns>
CommonResult SendMessage(string accessToken, CorpSendBase data);
}
最终,文本等类型的消息会根据接口定义进行实现,实现代码以下所示。注意,发送过程不须要调用加密类进行加密。
///<summary>
/// 企业号消息管理实现类
///</summary>
publicclass CorpMessageApi : ICorpMessageApi
{
///<summary>
/// 发送消息。
/// 须要管理员对应用有使用权限,对收件人touser、toparty、totag有查看权限,不然本次调用失败。
///</summary>
///<param name="accessToken"></param>
///<returns></returns>
public CommonResult SendMessage(string accessToken, CorpSendBase data)
{
CommonResult result = new CommonResult();
string urlFormat = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={0}";
var url = string.Format(urlFormat, accessToken);
var postData = data.ToJson();
//数据不用加密发送
CorpSendResult sendResult = CorpJsonHelper<CorpSendResult>.ConvertJson(url, postData);
if (sendResult != null)
{
result.Success = (sendResult.errcode == CorpReturnCode.请求成功);
result.ErrorMessage = string.Format("invaliduser:{0},invalidparty:{1},invalidtag:{2}",
sendResult.invaliduser, sendResult.invalidparty, sendResult.invalidtag);
}
return result;
}
}
定义好相应的发送对象后,咱们就能够进行统一的消息发送操做,包括文本、图片、文件、语音等等类型的消息,注意有些消息是须要上传到服务器上,而后在根据mediaId进行发送出去的。
发送文本和图片的操做代码以下所示。
privatevoid btnSendText_Click(object sender, EventArgs e)
{
//发送文本内容
ICorpMessageApi bll = new CorpMessageApi();
CorpSendText text = new CorpSendText("API 中文测试(http://www.iqidi.com)");
text.touser = "wuhuacong";
text.toparty = "4";//部门ID
text.totag = "0";
text.safe = "0";
text.agentid = "0";
CommonResult result = bll.SendMessage(token, text);
if (result != null)
{
Console.WriteLine("发送消息:{0} {1} {2}", text.text.content, (result.Success ? "成功" : "失败"), result.ErrorMessage);
}
}
privatevoid btnSendImage_Click(object sender, EventArgs e)
{
btnUpload_Click(sender, e);
if (!string.IsNullOrEmpty(image_mediaId))
{
//发送图片内容
ICorpMessageApi bll = new CorpMessageApi();
CorpSendImage image = new CorpSendImage(image_mediaId);
CommonResult result = bll.SendMessage(token, image);
if (result != null)
{
Console.WriteLine("发送图片消息:{0} {1} {2}", image_mediaId, (result.Success ? "成功" : "失败"), result.ErrorMessage);
}
}
}
最后在微信企业号上截图效果以下所示,包括了文本测试、文件测试、图文测试、语音测试均正常。
前面几篇陆续介绍了不少微信企业号的相关操做,企业号和公众号同样均可以自定义菜单,所以他们也能够经过API进行菜单的建立、获取列表、删除的操做,所以本篇继续探讨这个主体,介绍企业号的菜单管理操做。
菜单在不少状况下,可以给咱们提供一个快速入口,也能够用来获取用户信息的主要入口,经过OAuth2验证接口,以及自定义的重定向菜单,咱们就能够获取对应的用户ID,而后进一步获取到用户的相关数据,能够显示给客户。
菜单的事件处理以下所示,包括了单击和跳转两个操做,将来企业号可能会增长一些和公众号同样的扫码操做,拍照操做等功能的,目前只有两个。
官方的菜单定义接口包含了下面三种操做,菜单建立、列表获取和菜单删除,这点和公众号操做几乎同样了。
咱们定义菜单,包括定义它的一些属性,包含有name, type, key,url,以及一个指向自身引用的子菜单引用,所以菜单就能够循环构造多个层次,虽然严格意义上来说,企业号的菜单和公众号菜单同样,一级三个,二级最多五个,并且没有三级菜单了。
实体类的UML图示以下所示。
菜单管理的建立操做,官方定义以下所示。
Https请求方式: POST
https://qyapi.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN&agentid=1
请求包以下:
{
"button":[
{
"type":"click",
"name":"今日歌曲",
"key":"V1001_TODAY_MUSIC"
},
{
"name":"菜单",
"sub_button":[
{
"type":"view",
"name":"搜索",
"url":"http://www.soso.com/"
},
{
"type":"click",
"name":"赞一下咱们",
"key":"V1001_GOOD"
}
]
}
]
}
参数 |
必须 |
说明 |
access_token |
是 |
调用接口凭证 |
agentid |
是 |
企业应用的id,整型。可在应用的设置页面查看 |
button |
是 |
一级菜单数组,个数应为1~3个 |
sub_button |
否 |
二级菜单数组,个数应为1~5个 |
type |
是 |
菜单的响应动做类型,目前有click、view两种类型 |
name |
是 |
菜单标题,不超过16个字节,子菜单不超过40个字节 |
key |
click类型必须 |
菜单KEY值,用于消息接口推送,不超过128字节 |
url |
view类型必须 |
网页连接,员工点击菜单可打开连接,不超过256字节 |
管理员须拥有应用的管理权限,而且应用必须设置在回调模式。
返回结果
{
"errcode":0,
"errmsg":"ok"
}
根据上面官方的定义语义,咱们菜单管理的C#管理接口定义以下所示。
///<summary>
/// 企业号菜单管理接口定义
///</summary>
publicinterface ICorpMenuApi
{
///<summary>
/// 获取菜单数据
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<returns></returns>
MenuListJson GetMenu(string accessToken, string agentid);
///<summary>
/// 建立菜单
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="menuJson">菜单对象</param>
///<returns></returns>
CommonResult CreateMenu(string accessToken, MenuListJson menuJson, string agentid);
///<summary>
/// 删除菜单
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<returns></returns>
CommonResult DeleteMenu(string accessToken, string agentid);
}
咱们以建立菜单的实现为例来介绍微信企业号菜单的操做,其余的操做相似处理,都是返回一个公共的消息类,方便处理和读取,代码以下所示。
///<summary>
/// 建立菜单
///</summary>
///<param name="accessToken">调用接口凭证</param>
///<param name="menuJson">菜单对象</param>
///<returns></returns>
public CommonResult CreateMenu(string accessToken, MenuListJson menuJson, string agentid)
{
var url = string.Format("https://qyapi.weixin.qq.com/cgi-bin/menu/create?access_token={0}&agentid={1}", accessToken, agentid);
string postData = menuJson.ToJson();
return Helper.GetCorpExecuteResult(url, postData);
}
调用的代码和效果图以下所示。
privatevoid btnMenuCreate_Click(object sender, EventArgs e)
{
MenuJson productInfo = new MenuJson("产品介绍", new MenuJson[] {
new MenuJson("软件产品介绍", ButtonType.click, "event-software")
, new MenuJson("框架源码产品", ButtonType.click, "event-source")
, new MenuJson("软件定制开发", ButtonType.click, "event-develop")
});
MenuJson frameworkInfo = new MenuJson("框架产品", new MenuJson[] {
new MenuJson("Win开发框架", ButtonType.click, "win"),
new MenuJson("WCF开发框架", ButtonType.click, "wcf"),
new MenuJson("混合式框架", ButtonType.click, "mix"),
new MenuJson("Web开发框架", ButtonType.click, "web")
,new MenuJson("代码生成工具", ButtonType.click, "database2sharp")
});
MenuJson relatedInfo = new MenuJson("相关连接", new MenuJson[] {
new MenuJson("公司介绍", ButtonType.click, "event_company"),
new MenuJson("官方网站", ButtonType.view, "http://www.iqidi.com"),
new MenuJson("联系咱们", ButtonType.click, "event_contact"),
new MenuJson("应答系统", ButtonType.click, "set-1"),
new MenuJson("发邮件", ButtonType.view, "http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=S31yfX15fn8LOjplKCQm")
});
MenuListJson menuJson = new MenuListJson();
menuJson.button.AddRange(new MenuJson[] { productInfo, frameworkInfo, relatedInfo });
//Console.WriteLine(menuJson.ToJson());
if (MessageUtil.ShowYesNoAndWarning("您确认要建立菜单吗") == System.Windows.Forms.DialogResult.Yes)
{
ICorpMenuApi bll = new CorpMenuApi();
CommonResult result = bll.CreateMenu(token, menuJson, agentid);
Console.WriteLine("建立菜单:" + (result.Success ? "成功" : "失败:" + result.ErrorMessage));
}
}
privatevoid btnMenuGet_Click(object sender, EventArgs e)
{
ICorpMenuApi bll = new CorpMenuApi();
MenuListJson menu = bll.GetMenu(token, agentid);
if (menu != null)
{
Console.WriteLine(menu.ToJson());
}
}
调用代码的测试输出以下所示。