2023年2月

我们在做开发的时候,很多时候需要和Json数据格式打交道,如Web开发里面,很多时候,数据通过Json进行传递到页面上,然后在进行处理的。而使用Json的时候,我们很多时候会涉及到几个序列化对象的使用:
DataContractJsonSerializer,
JavaScriptSerializer


Json.NET
。大多数人都会选择性能以及通用性较好Json.NET,这个不是微软的类库,但是一个开源的世界级的Json操作类库,从下面的性能对比就可以看到它的其中之一的性能优点。

Json.NET能很好序列化或者反序列化.NET的各种类型数据,而且它的另一个优点,是可以配置Attribute属性,指定输出的属性的名称或者是否输出,这点我非常喜欢。

JSON(JavaScript Object Notation,JavaScript对象表示法)是一种轻量级的数据交换格式。
JSON是“名值对”的集合。结构由大括号'{}',中括号'[]',逗号',',冒号':',双引号'“”'组成,包含的数据类型有Object,Number,Boolean,String,Array, NULL等。

1、在Web中使用Json数据

在我的基于MVC+EasyUI的Web开发框架中,Web界面层大量使用了Ajax方式获取所需数据,然后绑定到树列表控件或者其他界面控件里面,在前面的一些Web框架随笔系列里面,我介绍过很多Json格式的操作。

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

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

如在MVC的视图里面,通过Web请求的Json数据后初始化树控件的代码如下所示

    //初始化组织机构列表
    functioninitDeptTreeview() {
$(
"#loading").show();

$(
'#treeDept').tree({
url:
'/User/GetMyDeptTreeJson?userId=@Session["UserId"]',
onClick:
function(node) {
loadDataByOu(node.id);
}
});

$(
"#loading").fadeOut(500);
}

或者用户角色的初始化界面代码

                $('#lbxRoles').empty();
$.getJSON(
"/Role/GetRolesByUser?r=" + Math.random() + "&userid=" + info.ID, function(json) {
$.each(json,
function(i, item) {
$(
'#lbxRoles').append('<option value="' + item.ID + '">' + item.Name + '</option>');
});
});

前面说过,Json.NET具有属性配置功能,可以指定某个属性是否输出,或者输出的名称转义等。
默认情况下,Json.Net序列化后结果中的字段名称和类中属性的名称一致,如果想自定义序列化后的字段名称,可以使用JsonProperty。

我们知道,EasyUI的Tree控件里面,它的数据格式,除了一个id和text是必须的之外,它的很多属性是可选的,也就是说在Json里面,可以不输出某个属性的内容。这个通过配置

[JsonProperty( NullValueHandling = NullValueHandling.Ignore)]

就可以忽略某个属性的输出了,如果这个属性的值为null的话。

树控件的数据还有一个checked属性, 如果没有转义功能,我们需要指定属性为checked, 而checked是C#里面的保留关键字,不能使用,那么就没办法了。而Json.NET提供了转义功能的配置Attribute,很好解决问题,如下所示。

        [JsonProperty(PropertyName = "checked", NullValueHandling =NullValueHandling.Ignore)]public bool? Checked { get; set; }

因此整个EasyUI的Tree数据对象信息,在C#里面可以定义为如下所示(这里可以忽略DataContract、DataMember的定义)。

    /// <summary>
    ///定义EasyUI树的相关数据,方便控制器生成Json数据进行传递/// </summary>
[DataContract]
[Serializable]
public classEasyTreeData
{
/// <summary> ///ID/// </summary> [DataMember]public string id { get; set; }/// <summary> ///节点名称/// </summary> [DataMember]public string text { get; set; }/// <summary> ///是否展开/// </summary> [JsonProperty(NullValueHandling =NullValueHandling.Ignore)]
[DataMember]
public string state { get; set; }/// <summary> ///图标样式/// </summary> [DataMember]
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string iconCls { get; set; }
[JsonProperty(PropertyName= "checked", NullValueHandling =NullValueHandling.Ignore)]
[DataMember(Name
="checked")]public bool? Checked { get; set; }/// <summary> ///子节点集合/// </summary> [DataMember]public List<EasyTreeData> children { get; set; }

使用上的实体类,并使用Json.NET来序列化我们的数据,我们可能得到下面的Json数据。

[
{
id:
"-1",
text:
"",
state:
"open",
checked: true,
children: [ ]
},
{
id:
"6",
text:
"总经办",
state:
"open",
iconCls:
"icon-group",
children: [ ]
}
]

我们从上面的Json数据可以看到,Checked属性顺利转换为checked的名称属性,标记为[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] ,而值为null的属性,将不会出现在Json的字符串里面了。

这个在某种情形下,正好就是我们所需要的。

2、在微信接口开发中使用Json

微信API的接口,大量使用了Json数据,不仅很多返回的数据是使用Json表达,而且其Post的数据,也多数使用Json数据格式,如在我的前两篇随笔《
C#开发微信门户及应用(5)--用户分组信息管理
》 、《
C#开发微信门户及应用(4)--关注用户列表及详细信息管理

里面介绍的内容,微信的很多接口都使用了Json数据。

如返回关注者列表的Json数据,就是如下所示。

{"total":2,"count":2,"data":{"openid":["","OPENID1","OPENID2"]},"next_openid":"NEXT_OPENID"}

创建用户分组,返回的数据格式如下,同样也是Json数据。

{"group": {"id": 107,"name": "test"}
}

通过下面基于Json.NET的Json数据的转换,就可以顺利从Json字符串转换为相应的实体对象了。

    /// <summary>
    /// Json字符串操作辅助类
    /// </summary>
    public class JsonHelper<T> where T : class, new()
{
/// <summary> /// 检查返回的记录,如果返回没有错误,或者结果提示成功,则不抛出异常 /// </summary> /// <param name="content">返回的结果</param> /// <returns></returns> private static bool 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);
throw new WeixinException(error);//抛出错误 }
}
return true;
}
/// <summary> /// 转换Json字符串到具体的对象 /// </summary> /// <param name="url">返回Json数据的链接地址</param> /// <returns></returns> public static T ConvertJson(string url)
{
HttpHelper helper
= newHttpHelper();
string content
=helper.GetHtml(url);
VerifyErrorCode(content);

T result
= JsonConvert.DeserializeObject<T>(content);returnresult;
}
}

而如果要把对象转换为Json字符串格式的数据,那么代码也很简单。

JsonConvert.SerializeObject(obj, Formatting.Indented);

前面几篇继续了我自己对于C#开发微信门户及应用的技术探索和相关的经验总结,继续探索微信API并分享相关的技术,一方面是为了和大家对这方面进行互动沟通
,另一方面也是专心做好微信应用的底层技术开发,把基础模块夯实,在未来的应用中派上用途。本随笔继续介绍微信门户菜单的管理操作。

1、菜单的基础信息

微信门户的菜单,一般服务号和订阅号都可以拥有这个模块的开发,但是订阅号好像需要认证后才能拥有,而服务号则不需要认证就可以拥有了。这个菜单可以有编辑模式和开发模式,编辑模式主要就是在微信门户的平台上,对菜单进行编辑;而开发模式,就是用户可以通过调用微信的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属性,总得来说,为了构造好对应的菜单实体类信息,不是一下就能分析的出来。

2、菜单的实体类定义

我看过一些微信接口的开发代码,把菜单的分为了好多个实体类,指定了继承关系,然后分别对他们进行属性的配置,大概的关系如下所示。

这种多层关系的继承方式能解决问题,不过我觉得并不是优雅的解决方案。其实结合Json.NET自身的Attribute属性配置,可以指定那些为空的内容在序列号为Json字符串的时候,不显示出来的。

[JsonProperty( NullValueHandling = NullValueHandling.Ignore)]

有了这个属性,我们就可以统一定义菜单的实体类信息更多的属性了,可以把View类型和Click类型的菜单属性的url和key合并在一起。

    /// <summary>
    ///菜单基本信息/// </summary>
    public classMenuInfo
{
/// <summary> ///按钮描述,既按钮名字,不超过16个字节,子菜单不超过40个字节/// </summary> public string name { get; set; }/// <summary> ///按钮类型(click或view)/// </summary> [JsonProperty(NullValueHandling =NullValueHandling.Ignore)]public string type { get; set; }/// <summary> ///按钮KEY值,用于消息接口(event类型)推送,不超过128字节/// </summary> [JsonProperty(NullValueHandling =NullValueHandling.Ignore)]public string key { get; set; }/// <summary> ///网页链接,用户点击按钮可打开链接,不超过256字节/// </summary> [JsonProperty(NullValueHandling =NullValueHandling.Ignore)]public string url { get; set; }/// <summary> ///子按钮数组,按钮个数应为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, stringvalue)
{
this.name =name;this.type =buttonType.ToString();if (buttonType ==ButtonType.click)
{
this.key =value;
}
else if(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>
    public classMenuJson
{
public List<MenuInfo> button { get; set; }publicMenuJson()
{
button
= new List<MenuInfo>();
}
}
/// <summary> ///菜单列表的Json对象/// </summary> public classMenuListJson
{
public MenuJson menu { get; set; }
}

3、菜单管理操作的接口实现

我们从微信的定义里面,可以看到,我们通过API可以获取菜单信息、创建菜单、删除菜单,那么我们来定义它们的接口如下。

    /// <summary>
    ///菜单的相关操作/// </summary>
    public interfaceIMenuApi
{
/// <summary> ///获取菜单数据/// </summary> /// <param name="accessToken">调用接口凭证</param> /// <returns></returns> MenuJson GetMenu(stringaccessToken);/// <summary> ///创建菜单/// </summary> /// <param name="accessToken">调用接口凭证</param> /// <param name="menuJson">菜单对象</param> /// <returns></returns> CommonResult CreateMenu(stringaccessToken, MenuJson menuJson);/// <summary> ///删除菜单/// </summary> /// <param name="accessToken">调用接口凭证</param> /// <returns></returns> CommonResult DeleteMenu(stringaccessToken);
}

具体的获取菜单信息的实现如下。

        /// <summary>
        ///获取菜单数据/// </summary>
        /// <param name="accessToken">调用接口凭证</param>
        /// <returns></returns>
        public MenuJson GetMenu(stringaccessToken)
{
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;
}
returnmenu;
}

这里就是把返回的Json数据,统一转换为我们需要的实体信息了,一步到位。

调用代码如下所示。

        private void btnGetMenuJson_Click(objectsender, EventArgs e)
{
IMenuApi menuBLL
= newMenuApi();
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(stringaccessToken, MenuJson menuJson)
{
var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}", accessToken);string postData =menuJson.ToJson();returnHelper.GetExecuteResult(url, postData);
}
/// <summary> ///删除菜单/// </summary> /// <param name="accessToken">调用接口凭证</param> /// <returns></returns> public CommonResult DeleteMenu(stringaccessToken)
{
var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={0}", accessToken);returnHelper.GetExecuteResult(url);
}

看到这里,有些人可能会问,实体类你简化了,那么创建菜单是不是挺麻烦的,特别是构造对应的信息应该如何操作呢?前面不是介绍了不同的构造函数了吗,通过他们简单就搞定了,不用记下太多的实体类及它们的继承关系来处理菜单信息。

        private void btnCreateMenu_Click(objectsender, EventArgs e)
{
MenuInfo productInfo
= new MenuInfo("软件产品", newMenuInfo[] {new MenuInfo("病人资料管理系统", ButtonType.click, "patient"),new MenuInfo("客户关系管理系统", ButtonType.click, "crm"),new MenuInfo("酒店管理系统", ButtonType.click, "hotel"),new MenuInfo("送水管理系统", ButtonType.click, "water")
});

MenuInfo frameworkInfo
= new MenuInfo("框架产品", newMenuInfo[] {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("相关链接", newMenuInfo[] {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
= newMenuJson();
menuJson.button.AddRange(
newMenuInfo[] { productInfo, frameworkInfo, relatedInfo });//Console.WriteLine(menuJson.ToJson()); if (MessageUtil.ShowYesNoAndWarning("您确认要创建菜单吗") ==System.Windows.Forms.DialogResult.Yes)
{
IMenuApi menuBLL
= newMenuApi();
CommonResult result
=menuBLL.CreateMenu(token, menuJson);
Console.WriteLine(
"创建菜单:" + (result.Success ? "成功" : "失败:" +result.ErrorMessage));
}
}

这个就是我微信门户里面的菜单操作了,具体效果可以关注我的微信门户:广州爱奇迪,也可以扫描下面二维码进行关注了解。

菜单的效果如下:

如果对这个系列感兴趣,可以关注我的其他文章,系列随笔如下所示:

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)--开始使用微信接口

最近一直在弄微信的集成功能开发,发现微信给认证账户开通了一个多客服的功能,对于客户的咨询,可以切换至客服处理的方式,而且可以添加多个客服进行处理,这个在客户咨询比较多的时候,是一个不错的营销功能。微信多客服的功能,能够在很大程度上利用客服员工资源,及时迅速对客户咨询信息进行处理,为企业带来更多的机会和市场。

默认这个多客服的功能,需要在微信公众平台中的服务中心进行主动开通,默认是不开通的,为了体验这个功能,我这里把多客服功能进行开通。

1、多客服准备工作

微信的多客服功能,对于客服的响应操作,既可以在电脑的客户端上进行操作,也可以在微信多客服助手进行信息处理,两者都能对客户的信息进行回应、结束会话等操作。

开通微信多客服功能后,就需要添加一些处理客户信息的客服工号了。

多客服账号采用“
工号@微信号
”的形式进行登录,请您在登录窗口
依照下图形式输入帐号信息。

2、使用多客服客户端或助手操作

在电脑客户端上使用

在手机客户端上进行多客服的使用,就是关注一个账号,信息通过转发到这里进行处理。关注公众号”
多客服助手
“就搞定了

通过上面两种途径,能够很好处理客户的相关信息,其实也就是类似电话坐席的方式,让不同的客服员工,对来访的客户进行处理。

3、微信多客服的开发使用

在微信的多客服开发介绍中,内容介绍的比较少,如下所示。

在新的微信协议中,开发模式也可以接入客服系统。 开发者如果需要使用客服系统,需要在接收到用户发送的消息时,返回一个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")]public classResponseCustomer : BaseMessage
{
publicResponseCustomer()
{
this.MsgType =ResponseMsgType.transfer_customer_service.ToString().ToLower();
}
public ResponseCustomer(BaseMessage info) : this()
{
this.FromUserName =info.ToUserName;this.ToUserName =info.FromUserName;
}
}

然后调用处理的时候,代码如下所示。

 ResponseCustomer customInfo = newResponseCustomer(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
= newResponseCustomer(info);
xml
=customInfo.ToXml();
}

通过使用多客服的客户端,这样处理消息交互起来非常方便,能获得客户的对话信息了,在电脑客户端上,看到的界面如下所示。

手机上的谈话截图如下所示。


这样就能够通过多途径,及时响应客户的信息了。

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

如果对这个系列感兴趣,可以关注我的其他文章,系列随笔如下所示:

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)--开始使用微信接口

最近对微信接口进行深入的研究,通过把底层接口一步步进行封装后,逐步升级到自动化配置、自动化应答,以及后台处理界面的优化和完善上,力求搭建一个较为完善、适用的微信门户应用管理系统。

微信门户应用管理系统,采用基于MVC+EasyUI的路线,由于多数域名服务器上都只能支持.NET4.0,所以以MVC3,C#4.0作为开发基础,基本上能够部署在任何.NET服务器上。

在微信门户系统里面,实现下面这些功能操作:

1)实现菜单的动态配置及更新到服务器上;

2)动态定义事件和响应消息,实现对不同行业,不同需求的菜单动作响应;

3)动态的应答指令配置处理,实现整套应答链的消息处理;

4)获取订阅用户和用户分组信息,并可以实现用户分组信息的维护等操作;

5)管理并更新多媒体文件、图文消息等内容,方便为客户推送消息做准备。

6)使用向选定订阅用户或者分组进行消息的群发功能。

1、微信菜单管理

在系统中管理菜单,并通过把菜单提交到服务器上,实现菜单的动态配置和生成,能够为我们系统适应各种的需要,实现灵活的处理。

微信菜单的添加界面如下所示。

微信菜单的修改界面如下所示

微信菜单定义是存储在数据库里面,如果需要提交到微信服务器上并生效,则需要调用微信API接口进行处理,我在页面的Controller控制器里增加一个提交到服务器的处理方法。

在微信服务账号的门户上,菜单的表现效果如下所示。

2、菜单事件的处理

对于动态生成的菜单,大多数情况下是用作Click的方式,也就是需要定义每个菜单的事件响应操作,我们使用微信的话,可以了解到,微信的处理事件,一般可以响应用户文本消息、图片消息、图文消息等内容,常规下,一般使用文本消息或者图文消息居多。

为了进一步实现响应内容的重用,我们把菜单的事件定义和内容定义进行分开管理,事件定义可以使用多个文本消息,也可以使用多个图文消息进行组合,这样可以实现更加灵活的使用环境。

添加事件定义如下所示

事件的响应内容编码,可以选择输入或者从“编辑”按钮中选择,当选择“编辑”按钮进行选择的时候,系统弹出一个对话框供用户对事件的响应内容编码选择。

完成选择后,回到原来的新增界面,将会看到返回的记录就是我们选择的记录。

微信事件的编辑界面如下所示,类似新增界面的内容。

3、微信消息内容管理

上面说到,菜单的事件通过关联事件编码进行处理,而事件本身可以组合多个消息内容,因此消息内容是响应客户操作的最小单元,它们可以是一条文本消息、图文消息,也可以是多条消息的组合(同类型的话)。

为了方便管理,我把消息分为了图文、指令、文本类型,如果需要,还可以根据需要把它细化为其他类型的消息。

消息内容的添加界面如下所示。

文本消息的手机上界面效果如下所示。

这里不管是文本消息还是图文消息,我们统一以图文消息的定义来定义消息,如果是文本消息,我们只需要获取描述内容作为消息的主体即可。

图文消息的编辑界面如下所示,主要就是填写完整的内容和图片,以及页面详细的链接即可。

上面的这个客户关系管理系统的消息,在手机上显示的界面效果如下所示,单击链接,可以切换到消息跳转链接地址的。

4、应答指令的维护

应答指令的维护,有点类似于事件的管理,主要就是定义一些用到的指令,方便构建应答系统的响应链,从而实现一步步的操作指令。

在后台设置好应答指令后,系统就能根据应答指令链进行处理了。首先我们需要提供一个进入应答链的提示界面,如下所示。

但我们在菜单选择应答系统后,系统返回一个文本提示界面,如下所示。

这个界面里面提示了一些按键,包括几个固定的按键和一些业务按键,输入简单的1~6可以对选择进行响应。

我们看到上面的界面,输入指令1后,系统进入下一层的应答指令,然后又列出几个可供输入的按键和内容提示。

当我们继续输入业务按键1后,响应的是一个图文消息,也是关于按键的详细说明。

这个时候,我们也还可以输入*号按键,返回上一级菜单的。

输入0则转入了客服对话模式,后续您发的任何消息,将会转发到多客服系统里面了。

当用户发送消息后,客服助手就能及时收到消息并处理和客户的应答了。

5、订阅用户管理

为了更有效管理订阅用户以及分组信息,我们可以从微信服务器上获取相关的信息,供我们了解关注的用户信息,也可以为后续的群发消息做准备。

订阅用户的管理如下所示,默认可以通过用户的地区进行查看,地区根据:国家-省份-城市这样的级别进行展开。单击同步数据,可以把服务器上的用户数据下载到本地进行更新或者写入。

订阅用户,还可以根据分组进行查看

双击可以查看订阅用户信息,查看订阅用户的详细信息界面如下所示。

7、
用户分组管理

创建分组的界面如下所示。

编辑分组信息界面如下所示。

当对分组进行编辑保存后,系统会记住那些修改过的,同步的时候,把本地新增的内容,在服务器上创建分组;把修改的的分组名称,在服务器上进行修改,然后进行同步列表处理。


8、多媒体管理

多媒体管理是指把本地文件上传到微信服务器上进行保存,方便信息的发送等操作。微信要求,某些信息,必须是先上传到服务器上,然后才能使用它的媒体ID进行发送的。

文件成功上传到服务器后,在列表里面的“文件上传标识,就是一串BASE64的编码数据,同时有一个上传的时间戳(因为微信服务器只保留了3天的媒体数据,超过期限的数据会被自动删除。

同时,在列表的上面,有两个重要的功能:上传选定的记录,重新上传过期的记录。方便我们对自己多媒体文件的重新更新操作。

添加界面操作如下所示,其中引入了附件上传的控件进行文件的操作,非常方便。同时上传成功的文件,会在列表中列出。

多媒体文件可以是下面几种方式:图片、语音、视频、缩略图。

保存后的数据记录,文件上传标识和时间戳都是空的,我们如果要使用,必须把他们上传到微信的服务器上,然后根据它的MediaId进行信息的发送,上传选定的记录操作界面如下所示。

多媒体文件顺利上传后,记录的信息如下所示。

9、图文消息处理

图文消息分为单图文消息和多图文消息两种,单图文消息如下所示。

多图文消息如下所示:

和多媒体数据管理一样,图文消息也是通过同样的方式进行管理,先上传到服务器,然后在进行消息的发送操作,多媒体消息一样有时间方面的限制要求,具体在我们的微信门户平台里面管理界面如下所示。

添加图文消息界面如下所示,保存后,可以在编辑界面中的“其他图文列表”里面,继续添加多图文的消息内容。

在添加界面中,选择图文消息的缩略图,都是通过选定指定的,已经上传到服务器上图片或者缩略图资源才可以的。

添加后的多图文列表,可以进行查看管理。

保存记录后,然后继续上传,上传后的记录界面如下所示,成功后返回一个上传后的服务器标识和时间戳,否则提示错误。

10、会话消息管理

为了方便记录客户的输入和发送信息,我们在微信门户管理平台里面记录用户的输入数据,具体会话消息管理界面如下所示。

我们可以双击最近48小时内的任何一条记录,可以给关注的客户进行消息的发送操作,如果消息发送成功,用户在手机的微信账号里面就能收到相关的发送消息了。

11、群发消息管理

为了对客户进行相应的营销操作,有时候我们需要对指定的群主或者人员进行消息的群发,让客户经常性的了解我们产品的信息和活动。

由于群发消息,除了文本消息,可以直接编辑发送外,其他数据,必须要求是上传到服务器的多媒体文件或者图文消息内容,因此前面的多媒体管理和图文消息管理,就是主要为了群发消息的目的引入的。有了上面的多媒体和多图文信息,我们从平台里面选择记录即可进行发送,从而省却麻烦的连带工作,实现高效的信息群发操作。

群发的消息,可以按群发分组进行查看,也可以按照消息类型进行查看,使得我们管理起来根据方便。

添加图文消息,可以选择文本消息、图文消息、图片消息等内容,根据不同的内容,界面提供不同的选择操作。

消息的群发类型分为两种,一种是根据分组,那么从平台里面选择对应的分组即可;一种是根据用户的OpenID进行发送,提供给用户输入。主要的操作界面如下所示。

如果对这个系列感兴趣,可以参考相关的进行学习。

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)--开始使用微信接口

微信公众号(包括服务号和订阅号)都可以对菜单进行自定义设置,我们为了方便管理,一般先把菜单数据在本地管理维护,需要更新的时候,把它们更新到微信服务器上就可以了。本文基于这个方式,介绍我的微信门户平台管理系统中菜单提交到微信服务器上的操作。微信门户应用管理系统,采用基于MVC+EasyUI的路线,由于多数域名服务器上都只能支持.NET4.0,所以以MVC3,C#4.0作为开发基础,基本上能够部署在任何.NET服务器上。

1、微信菜单的要求及相关界面设计

微信公众号的菜单我们可以通过网站进行本地的管理,维护好它们之间的层级关系,由于微信对自定义的菜单要求比较严格,以下是微信对自定义菜单的要求:

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

因此我们自己根据约定,不要越界即可,否则提交菜单到服务器,可能会返回一些错误,这些细节,我们在创建本地菜单管理的时候,注意一下就可以了。我在早期的一篇文章也介绍了自定义菜单的一些内容,需要可以进行回顾一下《
C#开发微信门户及应用(6)--微信门户菜单的管理操作
》,本篇主要是介绍在我的平台管理系统里面,调用前面介绍的菜单接口API,实现菜单提交到服务器的操作。

根据微信的自定义菜单要求,我在管理系统里面,对微信的菜单几个基础性的界面设计如下。

主菜单管理界面如下所示。

添加菜单的界面设计如下所示

微信菜单的修改界面如下所示

微信菜单定义是存储在数据库里面,如果需要提交到微信服务器上并生效,则需要调用微信API接口进行处理,我在页面的Controller控制器里增加一个提交到服务器的处理方法。

2、提交菜单到微信服务器的操作

上面几个界面,主要就是根据微信菜单的属性,对菜单进行维护管理,我们最终的目的是把它们放到服务器上去,供我们处理客户的相关事件操作的。

提交菜单的操作,我们在MVC的View页面里面,使用JQuery的Ajax提交即可(前提是我们在控制器里面添加相应的处理,后面介绍),界面脚本代码如下所示。

        //绑定提交按钮的的点击事件
        functionBindSubmitEvent() {
$(
"#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
= newMenuApi();
CommonResult result
=menuApi.CreateMenu(token, menuJson);returnToJsonContent(result);
}

上面的几个方法这里逐一介绍一下。GetAccessToken主要就是获得当前操作的访问令牌,这里的操作可以用缓存进行缓存,否则频繁的获取AccessToken,达到每天指定的次数后,当天就不能再用了。

GetWeixinMenu方法,主要就是为了方便,对获取构造微信的自定义菜单数据进行了一个函数封装,具体代码如下所示。

       /// <summary>
        ///生成微信菜单的Json数据/// </summary>
        /// <returns></returns>
        privateMenuListJson GetWeixinMenu()
{
MenuListJson menuJson
= newMenuListJson();

List
<MenuNodeInfo> menuList = BLLFactory<Menu>.Instance.GetTree();foreach (MenuNodeInfo info inmenuList)
{
ButtonType type
= (info.Type == "click") ?ButtonType.click : ButtonType.view;string value = (type == ButtonType.click) ?info.Key : info.Url;

MenuJson weiInfo
= newMenuJson(info.Name, type, value);
AddSubMenuButton(weiInfo, info.Children);

menuJson.button.Add(weiInfo);
}
returnmenuJson;
}

        private void AddSubMenuButton(MenuJson menu, List<MenuNodeInfo>menuList)
{
if (menuList.Count > 0)
{
menu.sub_button
= new List<MenuJson>();
}
foreach (MenuNodeInfo info inmenuList)
{
ButtonType type
= (info.Type == "click") ?ButtonType.click : ButtonType.view;string value = (type == ButtonType.click) ?info.Key : info.Url;

MenuJson weiInfo
= newMenuJson(info.Name, type, value);
menu.sub_button.Add(weiInfo);

AddSubMenuButton(weiInfo, info.Children);
}
}

上面的代码,就是把本地存储的MenuNodeInfo数据,通过递归遍历的方式,转换为微信的自定义菜单实体MenuJson,这样我们调用API就非常方便了,这个函数主要负责构造对应的实体信息就是了。至于调用微信API提交菜单的事情,还是让API自己亲自处理为好,他们的代码如下所示(也就是上面函数的部分代码)。

        IMenuApi menuApi = newMenuApi();
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,实现菜单的提交处理了。

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

如果对这个系列感兴趣,可以关注我的其他文章,系列随笔如下所示:

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)--开始使用微信接口