2023年2月

在前面几篇文章中,逐步从原有微信的API封装的基础上过渡到微信应用平台管理系统里面,逐步介绍管理系统中的微信数据的界面设计,以及相关的处理操作过程的逻辑和代码,希望从更高一个层次,向大家介绍微信的应用开发过程。本篇主要介绍在管理系统中,如何实现微信用户分组信息的同步操作。

其实微信能够风风火火的原因,主要就是因为有用户信息,所以同步并管理好微信账号的关注用户数据是非常重要的。有了微信用户的数据,你可以和你任何应用系统对接,实现系统-手机客户端的数据整合,还可以对用户进行营销管理,如发送用户感兴趣的产品消息、服务消息等,能够很好扩大企业的影响力和市场行为。

在较早之前的一篇随笔《
C#开发微信门户及应用(5)--用户分组信息管理
》,我曾经介绍了微信分组的各种底层的API封装操作,里面主要就是对微信提供API的.NET高级分组,对所有的信息交换,通过实体性进行数据交换,使得我们调用API来处理微信的各种事务更加方便,从而为微信应用平台的管理奠定基础。其中这篇文章介绍了所有微信分组管理的API封装过程,用户分组管理,包含下面几个方面的内容:

1)创建分组
2) 查询所有分组
3) 查询用户所在分组
4) 修改分组名
5) 移动用户分组

1、用户分组,在管理系统中的界面设计

针对以上微信分组的操作,我们可以在微信的应用管理系统里面,设计一个模块,用来管理微信的分组数据,在这个模块里面,可以创建分组,修改分组,查看分组等基础操作,还可以实现同步微信分组的操作,同步操作,主要就是把新增的分组信息添加到微信里面,修改的分组也在微信中实现修改功能,删除目前微信不支持,所以不用管了。最后,我们可以在此从微信服务器上,把修改后的数据同步下来,同步的时候为了避免对我们提交不成功的数据,我们需要对修改过的记录做好标识,这个就是我对整个同步操作的逻辑处理了。

在管理系统里面,对微信分组的列表管理界面设计如下所示。

创建分组的时候,我们只需要添加一个分组名称就可以了,界面设计也简单,但是我们把创建的ID统一设计为-1,作为未同步的新增标识。

编辑分组信息界面如下所示。
当对分组进行编辑保存后,系统会记住那些修改过的分组就是了。

2、分组同步操作代码展示

为了更好实现分组同步的管理,我把分组的操作代码,封装在一个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>
        publicActionResult SyncGroup()
{
string accessToken =GetAccessToken();
CommonResult result
= BLLFactory<Group>.Instance.SyncGroup(accessToken);returnToJsonContent(result);
}

从上面,我们没有看到太多的逻辑,为了方便我对他们进行了进一步的封装,把它放到了业务逻辑层进行处理了。具体我们看看它的代码逻辑吧,这里为了所有的数据库操作更加快捷和完整,使用了事务的操作,我把相关的代码贴出来,方便大家了解逻辑。

        /// <summary>
        ///同步服务器的分组信息/// </summary>
        /// <returns></returns>
        public CommonResult SyncGroup(stringaccessToken)
{
CommonResult result
= newCommonResult();try{
IUserApi api
= newUserApi();using (DbTransaction trans =baseDal.CreateTransaction())
{
//先把本地标志groupId = -1未上传的记录上传到服务器,然后进行本地更新 string condition = string.Format("GroupID = '-1'");
List
<GroupInfo> unSubmitList = base.Find(condition);foreach (GroupInfo info inunSubmitList)
{
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 inunModifyList)
{
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 inlist)
{
UpdateGroup(info, trans);
}
try{
trans.Commit();
result.Success
= true;
}
catch{
trans.Rollback();
throw;
}
}
}
catch(Exception ex)
{
result.ErrorMessage
=ex.Message;
}
returnresult;
}

在Jquery同步的时候,我们为了避免等待时间过久而无法判断程序是否正常在工作,最好增加一个忙碌的提示操作,因为我们使用了Ajax调用,所以我们可以统一设置Ajax的忙碌和完成状态,具体设置代码如下所示。

//用来统一请求忙碌显示的设置
$.ajaxSetup({
beforeSend: function () {
$("#loading").show();
},
complete: function () {
$("#loading").hide();
}
});

如果感兴趣或者体验相关的微信功能,可以关注我的微信了解下。具体效果可以关注我的微信门户:广州爱奇迪,也可以扫描下面二维码进行关注了解。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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


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

在前面一系列文章中,我们可以看到微信自定义菜单的重要性,可以说微信公众号账号中,菜单是用户的第一印象,我们要规划好这些菜单的内容,布局等信息。根据微信菜单的定义,我们可以看到,一般菜单主要分为两种,一种是普通的Url菜单(类型为View的菜单),一种是事件菜单(类型为Click的菜单),一般情况下,微信的Url菜单,是无法获得用户的任何信息的,但微信用户信息非常重要,因此也提供了另外一种方式(类似重定向的方式)来给我们使用,本篇主要介绍这种重新定向的方式菜单的使用,以使我们能够尽可能和用户进行交互。

1、微信自定义菜单的分类

微信对自定义菜单的要求:
目前自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。

根据菜单的分类,我们可以把它通过图形进行分类展示:

我对各种微信公众号进行了解,发现多数账号采用的都是普通的View类型的菜单链接方式,通过它们链接到自己的微网站上,但也有一些做的好的,如省立中山图书馆,就能通过重定向的方式,提供一个绑定图书馆用户和微信OpenID的入口,绑定后,用户就可以查看借阅的书籍,然后可以通过一键续借功能实现图书的快速续借功能。

对于这种重定向类型的Url菜单事件,微信的说明如下:

如果用户在微信中(Web微信除外)访问公众号的第三方网页,公众号开发者可以通过此接口获取当前用户基本信息(包括昵称、性别、城市、国家)。利用用户信息,可以实现体验优化、用户来源统计、帐号绑定、用户身份鉴权等功能。
请注意,“获取用户基本信息接口是在用户和公众号产生消息交互时,才能根据用户OpenID获取用户基本信息,而网页授权的方式获取用户基本信息,则无需消息交互,只是用户进入到公众号的网页,就可弹出请求用户授权的界面,用户授权后,就可获得其基本信息(此过程甚至不需要用户已经关注公众号。)”

2、重定向类型菜单的URL

上面说了,重定向类型的菜单分为了两种,其实他们也仅仅是参数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>
    public classTestWX : IHttpHandler
{
string appId = ""; //换成你的信息 string appSecret = ""; //换成你的信息 public voidProcessRequest(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 inlist.AllKeys)
{
content
+= string.Format("{0}:{1} \r\n", key, list[key]);
}
}
string code = context.Request.QueryString["code"] ?? "";if (!string.IsNullOrEmpty(code))
{
IBasicApi api
= newBasicApi();try{
AppConfig config
= newAppConfig();
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
= newUserApi();
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 inlist.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());
}

3、重定向链接菜单的用途

这种菜单就是需要指定域名,在微信后台中进行设置,重定向的链接必须属于这个域名之中,否则不会转到你希望的链接。

这个方式,让我们的微信应用程序后台可以获得用户的标识、用户详细信息等,我们就可以用来绑定和用户相关的业务信息了,如上面提到的图书馆借阅信息,送水客户的信息,客户的积分信息,或者可以和后台账号进行关联实现更加复杂的应用等。用户的身份信息如此重要,如果结合到我们的CRM系统、业务管理系统,就可以发挥用户信息应用的作用了。

以上就是我对这个类型菜单链接的应用了解,具体还需要进一步深化其应用,希望和大家共同探讨这方面的应用场景。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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


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

在我们做各种应用的时候,我们可能都会使用到图表统计,以前接触过一些不同的图表控件,在无意中发现了图表控件Highcharts,其强大的功能和丰富的互动效果,令人难以忘怀。本篇主要介绍在Web开发中使用图表控件Highcharts,以及对其进行统一汉化等操作,让我们的程序功能更加丰富,内容更加美观。

1、Highcharts基础介绍

Highcharts是一个非常流行,界面美观的纯Javascript图表库。它主要包括两个部分:Highcharts和Highstock。Highcharts可以为您的网站或Web应用程序提供直观,互动式的图表。目前支持线,样条,面积,areaspline,柱形图,条形图,饼图和散点图类型。Highstock可以为您方便地建立股票或一般的时间轴图表。它包括先进的导航选项,预设的日期范围,日期选择器,滚动和平移等等。

HIghChartS官网:
http://www.highcharts.com/

HighCharts Demo:
http://www.highcharts.com/demo/

Highcharts支持曲线图、饼图、柱状图、仪表图、散点图等等几十种图形,界面展示效果非常丰富,3D效果也很好看。列出几个供参考下吧



Highcharts使用jQuery等Javascript框架来处理基本的Javascript任务。因此,在使用Highcharts之前,需要在页面头部引用这些脚本文件。如果你使用jQuery作为基本框架,那么你需要在页面头部同时引用jQuery和Hightcharts两个文件就可以了。

由于我在Web开发框架中,主要采用了MVC+EasyUI的方式,因包含的文件如下所示。

@*添加Jquery,EasyUI和easyUI的语言包的JS文件*@<scripttype="text/javascript"src="~/Content/JqueryEasyUI/jquery.min.js"></script>
    <scripttype="text/javascript"src="~/Content/JqueryEasyUI/jquery.easyui.min.js"></script>
    <scripttype="text/javascript"src="~/Content/JqueryEasyUI/locale/easyui-lang-zh_CN.js"></script>@*图表JS文件应用*@<scriptsrc="~/Content/JQueryTools/Highcharts/js/highcharts.js"></script>

但是为了更好的展示效果,我们一般添加一个图标预定义的样式进去,同时添加导出功能的脚本,如下所示。

@*图表JS文件应用*@<scriptsrc="~/Content/JQueryTools/Highcharts/js/highcharts.js"></script>
    <scriptsrc="~/Content/JQueryTools/Highcharts/js/modules/exporting.js"></script>
    <scriptsrc="~/Content/JQueryTools/Highcharts/js/themes/grid.js"></script>

当然,如果我们散点图、3D图形等内容,还需要引入一些额外的js文件的

    <scriptsrc="~/Content/JQueryTools/Highcharts/js/highcharts-more.js"></script>
    <scriptsrc="~/Content/JQueryTools/Highcharts/js/highcharts-3d.js"></script>

2、图表的生成操作

前面说了,这个图表控件主要就是使用Jquery和Javascript来实现,我们来分析下一个饼图的Demo代码。

$(function() {
$(
'#container').highcharts({
chart: {
plotBackgroundColor:
null,
plotBorderWidth:
null,
plotShadow:
false},
title: {
text:
'Browser market shares at a specific website, 2014'},
tooltip: {
pointFormat:
'{series.name}: <b>{point.percentage:.1f}%</b>'},
plotOptions: {
pie: {
allowPointSelect:
true,
cursor:
'pointer',
dataLabels: {
enabled:
true,
format:
'<b>{point.name}</b>: {point.percentage:.1f} %',
style: {
color: (Highcharts.theme
&& Highcharts.theme.contrastTextColor) || 'black'}
}
}
},
series: [{
type:
'pie',
name:
'Browser share',
data: [
[
'Firefox', 45.0],
[
'IE', 26.8],
{
name:
'Chrome',
y:
12.8,
sliced:
true,
selected:
true},
[
'Safari', 8.5],
[
'Opera', 6.2],
[
'Others', 0.7]
]
}]
});
});

上面的脚本主要就是根据series属性里面的数据进行生成饼图的,那么我们实际开发的时候,数据肯定不是固定的,一般我们是通过动态方式赋值的。

如我一般倾向于使用Jquery的Ajax方式,调用后台获得数据,然后进行绑定的。那么这种情况下,如何操作脚本了呢,我们来分析看看。

首先我们先在脚本函数里面,初始化一个chart对象,并把其中涉series数据data设置为空就是了。

            var chart1 = newHighcharts.Chart({
chart: {
renderTo:
"container1",
plotBackgroundColor:
null,
plotBorderWidth:
null,
plotShadow:
false,
},
title: {
text:
'集团分子公司人员组成'},
tooltip: {
pointFormat:
'{series.name}: <b>{point.y}</b>'},
plotOptions: {
pie: {
allowPointSelect:
true,
cursor:
'pointer',
dataLabels: {
enabled:
true,
format:
'<b>{point.name}</b>: {point.percentage:.1f} %',
style: {
color: (Highcharts.theme
&& Highcharts.theme.contrastTextColor) || 'black'}

},
//showInLegend: true }
},
series: [{
type:
'pie',
name: '人员数量'
,
data: []
}]

});

第二步是通过Ajax调用后台连接获得数据,然后绑定到具体的属性上就可以了,具体代码如下所示。

            //通过Ajax获取图表1数据
            $.ajaxSettings.async = false;var data1 =[];
$.getJSON(
"/User/GetCompanyUserCountJson", function(dict) {for (var key indict) {if(dict.hasOwnProperty(key)) {
data1.push([key, dict[key]]);
}
};
chart1.series[
0].setData(data1);
});

而图表的HTML代码则是如下所示,主要就是新增一个div,id为container1,用来放置图表就是了。

                             <divclass="box">
                                 <divclass="box-title">
                                     <divstyle="float: left">
                                         <imgsrc="~/Content/JqueryEasyUI/themes/icons/customed/user.png"alt=""width="20"height="20" />图表1</div>
                                     <divstyle="float: right; padding-right: 10px;">更多</div>
                                 </div>
                                 <divclass="box-content"style="height: 310px">
                                     <divid="container1"style="height: 300px;max-width:500px"></div>
                                 </div>
                             </div>

完成以上的代码,我们运行就可以看到下面的图形了,这样看起来是不是比较酷一些呢。

3、图表的导出功能及菜单汉化

从上面的图表里面看到,每个图表的右上角,都有一个菜单的功能,里面有一些功能,如打印图片、导出图片等操作,具体菜单的表现如下所示。

但是上面的菜单式我经过了汉化处理的, 默认的显示效果是英文的,如下所示。

显然英文的菜单,不是我们希望的,我们需要汉化一下才更好,那么如何汉化上面的通用菜单呢,需要每个模块都重复一样的汉化吗,当然不需要了。我们可以把它放到全局设置里面。

前面我们介绍了,为了使得图表展示更好的效果,我们包含了一个grid.js的图表样式,其实里面也还有其他样式可以使用的,不过我觉得还是grid.js的样式最佳,如下所示。

那么既然使用了这个样式设置,我们把全局的一些设置,如汉化的操作,也放到这里就可以了。

我们在这个文件的底部,增加一个SetOption的操作代码就可以,这些汉化的菜单,由于我使用了最新版本,有些参数是和旧版本不一致的,所以冲着这个辛苦劲,应该推荐鼓励下哦。呵呵

设置汉化的代码。如下所示。

//Apply the theme
var highchartsOptions =Highcharts.setOptions(Highcharts.theme);//汉化图表菜单
Highcharts.setOptions({
lang: {
contextButtonTitle:
"图表菜单",
printChart:
"打印图片",
downloadJPEG:
"下载JPEG 图片",
downloadPDF:
"下载PDF文档",
downloadPNG:
"下载PNG 图片",
downloadSVG:
"下载SVG 矢量图",
exportButtonTitle:
"导出图片"}
});

基于MVC4+EasyUI的Web开发框架的系列文章:

基于MVC4+EasyUI的Web开发框架形成之旅--总体介绍


基于MVC4+EasyUI的Web开发框架形成之旅--MVC控制器的设计

基于MVC4+EasyUI的Web开发框架形成之旅--界面控件的使用

基于MVC4+EasyUI的Web开发框架形成之旅--附件上传组件uploadify的使用

基于MVC4+EasyUI的Web开发框架形成之旅--框架总体界面介绍

基于MVC4+EasyUI的Web开发框架形成之旅--基类控制器CRUD的操作

基于MVC4+EasyUI的Web开发框架形成之旅--权限控制

基于MVC4+EasyUI的Web开发框架经验总结(1)-利用jQuery Tags Input 插件显示选择记录

基于MVC4+EasyUI的Web开发框架经验总结(2)- 使用EasyUI的树控件构建Web界面

基于MVC4+EasyUI的Web开发框架经验总结(3)- 使用Json实体类构建菜单数据

基于MVC4+EasyUI的Web开发框架经验总结(4)--使用图表控件Highcharts

基于MVC4+EasyUI的Web开发框架经验总结(5)--使用HTML编辑控件CKEditor和CKFinder

基于MVC4+EasyUI的Web开发框架经验总结(6)--在页面中应用下拉列表的处理

基于MVC4+EasyUI的Web开发框架经验总结(7)--实现省份、城市、行政区三者联动

基于MVC4+EasyUI的Web开发框架经验总结(8)--实现Office文档的预览

基于MVC4+EasyUI的Web开发框架经验总结(9)--在Datagrid里面实现外键字段的转义操作

基于MVC4+EasyUI的Web开发框架经验总结(10)--在Web界面上实现数据的导入和导出

基于MVC4+EasyUI的Web开发框架经验总结(11)--使用Bundles处理简化页面代码

基于MVC4+EasyUI的Web开发框架经验总结(12)--利用Jquery处理数据交互的几种方式

基于MVC4+EasyUI的Web开发框架经验总结(13)--DataGrid控件实现自动适应宽带高度

基于MVC4+EasyUI的Web开发框架经验总结(14)--自动生成图标样式文件和图标的选择操作

我们知道,微信最开始就是做语音聊天而使得其更加流行的,因此语音的识别处理自然也就成为微信交流的一个重要途径,微信的开发接口,也提供了对语音的消息请求处理。本文主要介绍如何利用语音的识别,对C#开发的微信门户应用的整个事件链的处理操作,使得在我们的微信账号里面,更加方便和多元化对用户的输入进行处理。

1、微信语音接口的定义0

微信的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")]public classRequestVoice : BaseMessage
{
publicRequestVoice()
{
this.MsgType =RequestMsgType.Voice.ToString().ToLower();
}
/// <summary> ///语音格式,如amr,speex等/// </summary> public string Format { get; set; }/// <summary> ///语音消息媒体id,可以调用多媒体文件下载接口拉取数据。/// </summary> public string MediaId { get; set; }/// <summary> ///消息ID/// </summary> public Int64 MsgId { get; set; }/// <summary> ///语音识别结果,UTF8编码/// </summary> public string Recognition{ get; set; }

}

我们看到,这里我们最感兴趣的是语音的识别结果,也就是Recognition的字段,这个就是微信服务器自动根据用户的语音转换过来的内容,我测试过,识别率还是非常高的。

这个实体类,在整个微信应用的消息传递中的关系如下所示:

2、语音的处理操作

明确了上面的语音对象实体,我们就可以看看它们之间是如何处理的。

微信消息的处理逻辑如下图所示。

其中我们来看看语音的处理操作,我的代码处理逻辑如下所示。

        /// <summary>
        ///对语音请求信息进行处理/// </summary>
        /// <param name="info">语音请求信息实体</param>
        /// <returns></returns>
        public stringHandleVoice(Entity.RequestVoice info)
{
string xml = "";//开通语音识别功能,用户每次发送语音给公众号时,//微信会在推送的语音消息XML数据包中,增加一个Recongnition字段。 if (!string.IsNullOrEmpty(info.Recognition))
{
TextDispatch dispatch= newTextDispatch();
xml
=dispatch.HandleVoiceText(info, info.Recognition);
}
else{
xml
= "";
}
returnxml;
}

在这里,我先看看,是否获得了微信的语音识别结果,如果获得,那么这个时候,就是和处理用户文本输入的操作差不多了,因此把它转给TextDispatch的处理类进行处理。

其中这里面的处理逻辑如下所示。

首先我根据识别结果,寻找是否用户读出了微信门户的菜单名称,如果根据语音结果找到对应的菜单记录,那么我们执行菜单事件(如果是URL的View类型菜单,我们没办法重定向到指定的链接,因此给出一个链接文本提示,给用户单击进入;如果没有找到菜单记录,那么我们就把语音识别结果作为一般的事件进行处理,如果事件逻辑没有处理,那么我们最后给出一个默认的语音应答提示结果就可以了。

具体的处理代码如下所示。

        /// <summary>
        ///如果用户用语音读出菜单的内容,那么我们应该先根据菜单对应的事件触发,最后再交给普通事件处理/// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        public string HandleVoiceText(BaseMessage info, stringvoiceText)
{
string xml = "";
MenuInfo menuInfo
= BLLFactory<Menu>.Instance.FindByName(voiceText);if (menuInfo != null)
{
#region 如果找到菜单对象的处理 if (menuInfo.Type == "click")
{
//模拟单击事件 RequestEventClick eventInfo = newRequestEventClick();
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
= newResponseText(info);
textInfo.Content
=content;

xml
=textInfo.ToXml();
}
#endregion}else{//交给事件机制处理 if (string.IsNullOrEmpty(xml))
{
xml
=HandleText(info, voiceText);
}
}
//最后如果没有处理到,那么提示用户的语音内容 if (string.IsNullOrEmpty(xml))
{
ResponseText textInfo
= newResponseText(info);
textInfo.Content
= string.Format("非常抱歉,您输入的语音内容没有找到对应的处理方式。您的语音内容为:{0}", voiceText);
xml
=textInfo.ToXml();
}
returnxml;
}

微信门户测试界面效果如下所示。


为了方便对客户会话的记录,我的微信门户后台,会记录用户的语音输入内容,如下所示。

当然,微信后台的管理界面,也能够查到相应的语音记录,界面如下所示。

以上就是我对微信语音的消息定义和事件处理的逻辑,其实语音是一个重要的输入,如果正确的识别内容,比手工输入的效果更好,给用户提供另外一种高效的输入和事件处理操作。

这样的处理模式,能够使得我们整个微信门户框架,不管是对于用户的语音输入,还是文本输入,还是菜单事件的处理,都可以融为一体,实现更加完美的衔接。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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


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

本文继续上一篇《
C#开发微信门户及应用(12)-使用语音处理
》,继续介绍微信的相关应用。我们知道,地理位置信息可以用来做很多相关的应用,除了我们可以知道用户所在的位置,还可以关联出一些地理位置的应用,如天气,热映影片,附近景点,附近影院,交通事件等等,反正所有和地理位置相关的信息,我们都可以根据需要做一些扩展应用。本文主要介绍利用地理位置信息,如何构建使用这些应用的操作。

1、微信的地理位置信息

在使用前,我们先来看看微信的接口,为我们定义了那些关于与地理位置的信息。其实地理位置的信息,微信分为了两个方面,一个是接收用户的地理位置请求,一个是用户允许上报地理位置操作,定时发送的地理位置信息。

本文主要介绍基于第一种,用户上报地理位置后,如何处理的相关应用。

地理位置的上报操作,就是在输入的地方,选择+号进行添加地理位置,然后选择当前或者指定的地理位置地图,具体操作如下所示。


地理位置消息

<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")]public classRequestLocation : BaseMessage
{
publicRequestLocation()
{
this.MsgType =RequestMsgType.Location.ToString().ToLower();
}
/// <summary> ///消息ID/// </summary> public Int64 MsgId { get; set; }/// <summary> ///地理位置维度/// </summary> public decimal Location_X { get; set; }/// <summary> ///地理位置经度/// </summary> public decimal Location_Y { get; set; }/// <summary> ///地图缩放大小/// </summary> public int Scale { get; set; }/// <summary> ///地理位置信息/// </summary> public string Label { get; set; }

}

有了这些信息,我们在信息传递的时候,就能很好得到用户的相关数据了。

如果仅仅为了返回给用户,告诉用户目前的地理位置信息,可以用下面的操作就可以了。

        /// <summary>
        ///对地理位置请求信息进行处理/// </summary>
        /// <param name="info">地理位置请求信息实体</param>
        /// <returns></returns>
        public stringHandleLocation(Entity.RequestLocation info)
{
string xml = "";

ResponseText txtinfo
= newResponseText(info);
txtinfo.Content
= string.Format("您发送的地理位置是:{0}", info.Label);
xml
=txtinfo.ToXml();returnxml;
}

2、地址位置的应用处理

不过上面的信息,显然不符合我们扩展应用的要求,因此我们进一步进行完善里面对地理位置信息处理的操作。我们进一步把关于地理位置的操作,放到事件处理模块里面进行处理,处理代码如下所示。

        /// <summary>
        ///对地理位置请求信息进行处理/// </summary>
        /// <param name="info">地理位置请求信息实体</param>
        /// <returns></returns>
        public stringHandleLocation(Entity.RequestLocation info)
{
string xml = "";
EventDispatch dispatch
= newEventDispatch();
xml
=dispatch.DealLocation(info, info.Label, info.Location_Y, info.Location_X);returnxml;
}

在处理的时候,我们需要先保存用户的地理位置信息,把它存储到用户的上下文记录里面。这样我们在处理指令的时候,把它获取到,然后传递给相关的方法就可以实现地理位置的扩展应用了。

            //保存经纬度
            string location = string.Format("{0},{1}", lat, lon);bool result = BLLFactory<UserSet>.Instance.UpdateUserInput(info.FromUserName, location);

首先对用户地理位置的请求,我根据数据库配置给出了一个用户选择的指令提示,如下所示。

为了对地理位置请求的处理,我定义了一个用于处理这个操作的指令操作

这样整个地理位置的指令操作,就在应答链里面进行很好的跳转管理了。那么为了实现天气、放映影片、附近影院、旅游线路、交通事件等方面的扩展应用,我们应该如何操作呢?

3、地址位置应用扩展

我们知道,百度或者腾讯都提供了一些开放平台,给我们进行各种方式的使用。那么我们这里以使用百度LBS平台应用来构建一些模块。

这上面都有很多相关的接口供使用,我们可以根据其提供的数据格式进行封装,然后进行调用处理就可以了。

刚才说了,我配置了一些指令,用来构建相关的应用,指令的最后是一些事件代码的定义,我们对这些末端的事件代码进行处理,就可以给用户返回相关的信息了,总体的操作代码如下所示。

        /// <summary>
        ///其他插件操作,如天气,景点、电影影讯、交通等/// </summary>
        /// <param name="info">基础消息</param>
        /// <param name="eventKey">事件标识</param>
        /// <returns></returns>
        public string DealPlugin(BaseMessage info, stringeventKey)
{
//LogTextHelper.Info(eventKey); string userInput = BLLFactory<UserSet>.Instance.GetUserInput(info.FromUserName);string xml = "";switch(eventKey)
{
case "event-void-wether":
xml
= newWeatherPlugin().Response(info, userInput);break;case "event-void-movie":
xml
= newMoviePlugin().Response(info, userInput);break;case "event-void-cinema":
xml
= newCinemaPlugin().Response(info, userInput);break;case "event-void-travel":
xml
= newTravelPlugin().Response(info, userInput);break;case "event-void-traffic":
xml
= newTrafficEventPlugin().Response(info, userInput);break;default:break;
}
returnxml;
}

这里以天气为例,说明该如何调用百度的接口的,首先我们封装一下相关的接口调用。

        /// <summary>
        ///根据参数调用百度接口,获取相关的结果数据/// </summary>
        /// <param name="location">地理位置</param>
        /// <param name="ak">API调用键</param>
        /// <returns></returns>
        public BaiduWeatherResult Execute(string location, stringak)
{
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);returnresult;
}

其中的BaiduWeatherResult 是我根据调用返回的Json结果,构建的一个实体类,用来存储返回的内容。具体代码如下所示。

    /// <summary>
    ///天气请求结果Json对象/// </summary>
    public classBaiduWeatherResult : BaiduResult
{
/// <summary> ///天气预报信息/// </summary> public List<BaiduWeatherData> results = new List<BaiduWeatherData>();
}
/// <summary> ///城市的天气信息/// </summary> public classBaiduWeatherData
{
/// <summary> ///当前城市/// </summary> public string currentCity { get; set; }/// <summary> ///天气预报信息/// </summary> public List<BaiduWeatherJson> weather_data = new List<BaiduWeatherJson>();
}
/// <summary> ///天气预报的单条记录Json信息/// </summary> public classBaiduWeatherJson
{
/// <summary> ///天气预报时间/// </summary> public string date { get; set; }/// <summary> ///白天的天气预报图片url/// </summary> public string dayPictureUrl { get; set; }/// <summary> ///晚上的天气预报图片url/// </summary> public string nightPictureUrl { get; set; }/// <summary> ///天气状况/// </summary> public string weather { get; set; }/// <summary> ///风力/// </summary> public string wind { get; set; }/// <summary> ///温度/// </summary> public string temperature { get; set; }
}

为了构建返回给客户的图文数据,我们需要构建一个News对象,然后生成XML数据返回给服务器进行处理即可。

        /// <summary>
        ///响应用户请求,并返回相应的XML数据/// </summary>
        /// <param name="info">微信基础信息</param>
        /// <param name="location">地理位置:经纬度坐标或者地名</param>
        /// <returns></returns>
        public string Response(BaseMessage info, stringlocation)
{
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
= newArticleEntity();
first.Title
= string.Format("{0} 天气预报", data.currentCity);

ResponseNews news
= newResponseNews(info);
news.Articles.Add(first);
int i = 0;foreach (BaiduWeatherJson json indata.weather_data)
{
ArticleEntity article
= newArticleEntity();
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();
}
}
}
returnxml;
}

这样就很好实现了整体的功能了,具体界面功能可以访问我的微信(广州爱奇迪)进行了解,下面是功能截图供参考。



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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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


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