C#開發微信門戶及應用教程
C#開發微信門戶及應用(1)--開始使用微信接口... 6
1、微信賬號... 6
2、微信菜單定義... 7
3、接入微信的鏈接處理... 8
4、使用開發方式創建菜單... 14
5、我創建的菜單案例... 17
C#開發微信門戶及應用(2)--微信消息的處理和應答... 18
1、微信的消息應答交互... 18
2、微信的管理接口... 25
C#開發微信門戶及應用(3)--文本消息和圖文消息的應答... 29
1、實體信息關系及定義... 30
2、消息的回復處理... 37
C#開發微信門戶及應用(4)--關注用戶列表及詳細信息管理... 41
1、關注用戶列表及用戶分組信息... 41
2、獲取AIP調用者的的Token.. 47
3、獲取關注用戶列表... 50
4、獲取用戶詳細信息... 59
C#開發微信門戶及應用(5)--用戶分組信息管理... 62
1、用戶分組管理內容... 62
2、用戶分組管理接口的實現... 67
3、用戶分組接口的調用... 79
C#開發微信門戶及應用(6)--微信門戶菜單的管理操作... 82
1、菜單的基礎信息... 82
2、菜單的實體類定義... 85
3、菜單管理操作的接口實現... 91
C#開發微信門戶及應用(7)-微信多客服功能及開發集成... 100
1、多客服准備工作... 101
2、使用多客服客戶端或助手操作... 102
3、微信多客服的開發使用... 103
C#開發微信門戶及應用(8)-微信門戶應用管理系統功能介紹... 108
1、微信菜單管理... 109
2、菜單事件的處理... 112
3、微信消息內容管理... 116
4、應答指令的維護... 121
5、訂閱用戶管理... 129
6、用戶分組管理... 134
7、多媒體管理... 136
8、圖文消息處理... 139
9、會話消息管理... 145
10、群發消息管理... 147
C#開發微信門戶及應用(9)-微信門戶菜單管理及提交到微信服務器... 149
1、微信菜單的要求及相關界面設計... 150
2、提交菜單到微信服務器的操作... 153
C#開發微信門戶及應用(10)--在管理系統中同步微信用戶分組信息... 160
1、用戶分組,在管理系統中的界面設計... 161
2、分組同步操作代碼展示... 163
C#開發微信門戶及應用(11)--微信菜單的多種表現方式介紹... 172
1、微信自定義菜單的分類... 172
2、重定向類型菜單的URL. 174
3、重定向鏈接菜單的用途... 182
C#開發微信門戶及應用(12)-使用語音處理... 182
1、微信語音接口的定義0. 183
2、語音的處理操作... 186
C#開發微信門戶及應用(13)-使用地理位置擴展相關應用... 197
1、微信的地理位置信息... 198
2、地址位置的應用處理... 205
3、地址位置應用擴展... 208
C#開發微信門戶及應用(14)-在微信菜單中采用重定向獲取用戶數據... 223
1、微信重定向菜單的配置... 224
2、腳本轉換操作的實現代碼... 227
3、重定向頁面的設計及處理... 230
C#開發微信門戶及應用(15)-微信菜單增加掃一掃、發圖片、發地理位置功能... 233
1、微信幾個功能的官方介紹... 234
2、微信新菜單功能的測試公眾號... 236
3、改進菜單對象和提交菜單... 238
4、微信掃一掃功能集成... 245
5、新菜單功能測試發現的問題... 250
C#開發微信門戶及應用(16)-微信企業號的配置和使用... 251
1、微信企業號的注冊和登陸... 251
2、設置開發回調模式... 256
3、實現回調頁面的功能開發... 259
C#開發微信門戶及應用(17)-微信企業號的通訊錄管理開發之部門管理... 266
1、企業組織的創建和配置... 266
2、API訪問的全局唯一票據AccessToken的獲取... 270
2、通訊錄管理之部門信息的維護... 272
3、部門管理的API調用... 278
C#開發微信門戶及應用(18)-微信企業號的通訊錄管理開發之成員管理... 281
1、成員的創建操作... 281
2、成員的更新操作... 287
3、成員的刪除、成員的獲取、部門成員的獲取操作... 290
7、綜合例子調用代碼... 295
C#開發微信門戶及應用(19)-微信企業號的消息發送(文本、圖片、文件、語音、視頻、圖文消息等) 299
1、企業號特點... 299
2、企業號的管理接口內容... 300
3、企業號消息和事件的處理... 302
4、企業號消息管理... 304
5、消息接口的定義和實現... 310
6、消息的發送操作和實際效果... 313
C#開發微信門戶及應用(20)-微信企業號的菜單管理... 317
1、菜單的總體介紹... 318
2、菜單的實體類定義和接口定義處理... 319
3、企業號菜單管理接口的調用和處理效果... 324
C#開發微信門戶及應用(1)--開始使用微信接口
微信應用如火如荼,很多公司都希望搭上信息快車,這個是一個商機,也是一個技術的方向,因此,有空研究下、學習下微信的相關開發,也就 成為日常計划的重要事情之一了。本系列文章希望從一個循序漸進的角度上,全面介紹微信的相關開發過程和相關經驗總結,希望給大家了解一下相關的開發歷程。 本隨筆主要針對微信開發過程的前期准備和一些初始的工作的介紹。
在寫下本文的之前一周時間里,我主要就是參考一些介紹文章以及微信公眾平台的相關接口說明,並結合C#的代碼開發,整理了自己公司的門 戶界面,實現了微信工作號的初步用戶交互和信息展示工作,隨着工作的進一步開展,越來越多的功能可能加入,並希望從應用角度上擴展微信的接口,從而實現我 對微信接口的技術探秘和了解過程。
1、微信賬號
要開發使用微信的平台API,就需要到微信的公眾平台(https://mp.weixin.qq.com/)去注冊,擁有一個服務號或者訂閱號,服務號主要面對企業和組織,訂閱號主要面向組織和個人,他們之間有一定的差異,根據不同的需要自己申請對應的賬號即可。
為了使用一些高級的接口,你可能需要擁有服務號和高級的認證。賬號注冊過程,需要下載一個申請表格,打印並蓋公章,另外還需要申請人拿着身份證拍照(有點怪異,呵呵),然后上傳到服務器進行審核,一般很快就能獲取批復。
我以公司名義申請了服務號,賬號注冊后,會在主界面上顯示你的相關信息,另外給你申請一個二維碼的東西,掃描二維碼即可進入公司的微信關注確認對話框,非常方便。如下就是我申請后的公司賬號二維碼,可以直接使用掃描。
2、微信菜單定義
微信有兩種方式的菜單定義,一種是編輯模式,一種是開發模式,兩者互斥,也就是說,一旦我們采用了開發模式,就不能使用編輯模式了,反過來也一樣。編輯下的菜單,其實也是可以管理的,但是微信不支持,覺得很不爽。
一般情況下,如果我們剛剛申請了微信號碼,可以使用編輯菜單測試一下,根據說明編輯一些菜單試試。雖然微信說24小時內更新,不過一般很快,最快可能一兩分鍾就更新了,感覺還是不錯的。
使用開發者模式,你需要根據微信的要求,在服務器上放置一個頁面鏈接,使用C#開發的,可以采用***.ashx的命名方式,使用Asp.NET的一般處理程序即可,不需要使用普通的頁面。
使用開發模式的菜單,也就是可以調用微信API進行菜單創建的工作,對於調用微信的API(微信有很多API可以調用),我們需要知道,有幾個參數的重要性,所以在開發模式打開的時候,會給你列出這些參數,如下所示。
3、接入微信的鏈接處理
上面說了,你申請開發模式對菜單或者對其他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;
}
}
4、使用開發方式創建菜單
一旦你順利通過微信的認證,那么它就讓你以開發方式調用它的API,並且可以隨意創建你的菜單了。
創建菜單的方式,你可以通過下面的位置進入到他的API處理界面里面。
進入后,你會發現微信把很多消息的處理,分門別類放到不同的分類里面了。
其實我們現在初步要做的就是如何看看,使用代碼方式調用創建菜單,進入菜單的API調試界面里面。
你會發現里面還需要輸入一個Access_Token的東西,這個是一個會話身份認證,因此你還需要到接口里面去找這個如何創建的。下面圖中的兩個紅色部分,就是我們開始的時候,微信提示我們“開發者憑據”的兩個關鍵參數。
弄完這些,你就可以根據獲得的Access_Token進行菜單的創建工作了,根據菜單的定義,它分為幾類,可以分為URL方式(View),事件方式(Click)。
click:用戶點擊click類型按鈕后,微信服務器會通過消息接口推送消息類型為event 的結構給開發者(參考消息接口指南),並且帶上按鈕中開發者填寫的key值,開發者可以通過自定義的key值與用戶進行交互;
view:用戶點擊view類型按鈕后,微信客戶端將會打開開發者在按鈕中填寫的url值 (即網頁鏈接),達到打開網頁的目的,建議與網頁授權獲取用戶基本信息接口結合,獲得用戶的登入個人信息。
5、我創建的菜單案例
在隨筆的開始,我公布了一個二維碼,一旦使用微信掃一掃,進行關注服務號后,那么就可以看到我自己創建的菜單了。主菜單一般最多三列,每個主菜單還可以有子菜單,他們的文字都有所限制的。
我們來看看我公司的微信門戶菜單,看起來是不是很酷呢。
C#開發微信門戶及應用(2)--微信消息的處理和應答
微信應用如火如荼,很多公司都希望搭上信息快車,這個是一個商機,也是一個技術的方向,因此,有空研究下、學習下微信的相關開發,也就 成為計划的安排事情之一了。本系列文章希望從一個循序漸進的角度上,全面介紹微信的相關開發過程和相關經驗總結,希望給大家了解一下相關的開發歷程。本篇 隨筆主要基於上一篇《C#開發微信門戶及應用(1)--開始使用微信接口》的基礎上進行深入的介紹,介紹微信消息的處理和應答的過程。
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);
..............................
}
從上面的代碼可以看出,不同的消息,到處理函數這里,就以不同的消息實體類的方式傳遞過來了(注意:實體類是我根據程序開發需要自己定義的,非微信本身的實體類),這樣非常方便我們處理操作,否則每次需要解析不同的消息內容,很容易出現問題,這樣強類型的數據類型,提高了我們開發微信應用的強壯型和高效性。這些實體類的對象有一定的繼承關系的,他們的繼承關系如下所示。
2、微信的管理接口
上面的消息分類是微信服務器向開發者服務器發送的消息請求操作,還有一種消息,是我們開發者服務器向微信服務器進行的消息請求或者響應,這種這里暫且稱之為微信的管理接口,它表明了我們可以通過這些接口進行相關的消息回復或者數據管理操作。它的分類圖如下所示。
微信的回復消息處理,它也和上面小節的信息一樣,它也是繼承自BaseMessage實體類的(同樣,下圖的實體類及其繼承關系也是自定義的,方便程序開發),它的關系如下所示
回復的消息,一般用的最多的是文本消息和圖文消息。
文本消息的效果如下所示。
圖文消息,可以增加圖片,還可以增加詳細的鏈接頁面,是非常好看的一種效果,對於一些內容比較多,希望展現更好效果的,一般采用這種,效果如下所示。
C#開發微信門戶及應用(3)--文本消息和圖文消息的應答
微信應用如火如荼,很多公司都希望搭上信息快車,這個是一個商機,也是一個技術的方向,因此,有空研究下、學習下微信的相關開發,也就 成為計划的安排事情之一了。本系列文章希望從一個循序漸進的角度上,全面介紹微信的相關開發過程和相關經驗總結,希望給大家了解一下相關的開發歷程。
在前面兩篇兩篇隨筆《C#開發微信門戶及應用(1)--開始使用微信接口》和《C#開發微信門戶及應用(2)--微信消息的處理和應答》里面,大致介紹了我微信應用的框架構建,本隨筆繼續介紹這一主題,介紹消息應答里面的文本應答和圖文應答的過程。
我們知道,給手機用戶發送響應消息,它可以分為好多種方式,如回復文本消息、回復圖片消息、回復語音消息、回復視頻消息、回復音樂消息、回復圖文消息等,如下所示。
而其中圖片、視頻、語音這三種方式,是需要開通微信認證才可以向用戶發送存在微信服務器上的媒體信息,一般沒有認證的公眾號或者服務號,是不能發送這幾種內容的。
1、實體信息關系及定義
在上一篇微信開發的隨筆中,我展示了對接收消息和回復消息的應用實體類,這些實體類是我根據需要,根據開發需要,在應用層面對它們進行了封裝,如回復的消息關系如下所示。
消息基類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; }
}
而其中的圖文列表集合中的對象,它也是一個實體類型,包含了一些圖文的鏈接,標題等信息,不在贅述。
2、消息的回復處理
如對於文本消息,我們可以用以下的方式進行處理。
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#開發微信門戶及應用(4)--關注用戶列表及詳細信息管理
在上個月的對C#開發微信門戶及應用做了介紹,寫過了幾篇的隨筆進行分享,由於時間關系,間隔了一段時間沒有繼續寫這個系列的博客了,並不是對這個方面停止了研究,而是繼續深入探索這方面的技術,為了更好的應用起來,專心做好底層的技術開發。
微信的很重要的一個特點就是能夠利用其平台龐大的用戶群體,因此很容易整合在CRM(客戶關系管理)系統里面,服務號和訂閱好都能夠向關注者推送相 關的產品消息,還能和48小時內響應消息和事件的活躍用戶進行交互對話,因此用戶信息是微信API里面非常重要的一環,本隨筆主要介紹獲取關注用戶、查看 用戶信息、分組管理等方面的開發應用。
1、關注用戶列表及用戶分組信息
在微信的管理平台上,我們可以看到自己賬號的關注者用戶,以及用戶分組信息,如下所示。
上面的管理界面,能看到關注者用戶的基礎信息,但是使用微信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、46、64、96、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; }
}
2、獲取AIP調用者的的Token
在做微信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秒過期,因此在這個時間段里面,我們盡可能使用緩存來記錄它的值,如果超過了這個時間,我們調用這個方法的時候,它會自動重新獲取一個新的值給我們了。
3、獲取關注用戶列表
獲取關注用戶列表,一次拉取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)
4、獲取用戶詳細信息
上面的獲取列表操作,相對比較簡單,而且不用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#開發微信門戶及應用(5)--用戶分組信息管理
在上個月的對C#開發微信門戶及應用做了介紹,寫過了幾篇的隨筆進行分享,由於時間關系,間隔了一段時間沒有繼續寫這個系列的博客了,並不是對這個 方面停止了研究,而是繼續深入探索這方面的技術,為了更好的應用起來,專心做好底層的技術開發。本篇繼續上一篇的介紹,主要介紹分組管理方面的開發應用, 這篇的內容和上一篇,作為一個完整的用戶信息和分組信息管理的組合。
1、用戶分組管理內容
用戶分組的引入,主要是方便管理關注者列表,以及方便向不同的組別發送消息的操作的,一個公眾賬號,最多支持創建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、用戶分組管理接口的實現
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);
}
3、用戶分組接口的調用
上面小節,定義並實現了用戶分組的各類接口,所有的用戶相關的都已經毫無保留貼出代碼,它的調用操作如下代碼所示(測試代碼)。
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#開發微信門戶及應用(6)--微信門戶菜單的管理操作
前面幾篇繼續了我自己對於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>
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; }
}
3、菜單管理操作的接口實現
我們從微信的定義里面,可以看到,我們通過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));
}
}
這個就是我微信門戶里面的菜單操作了,具體效果可以關注我的微信門戶:廣州愛奇迪,也可以掃描下面二維碼進行關注了解。
菜單的效果如下:
C#開發微信門戶及應用(7)-微信多客服功能及開發集成
最近一直在弄微信的集成功能開發,發現微信給認證賬戶開通了一個多客服的功能,對於客戶的咨詢,可以切換至客服處理的方式,而且可以添加多個客服進 行處理,這個在客戶咨詢比較多的時候,是一個不錯的營銷功能。微信多客服的功能,能夠在很大程度上利用客服員工資源,及時迅速對客戶咨詢信息進行處理,為 企業帶來更多的機會和市場。
默認這個多客服的功能,需要在微信公眾平台中的服務中心進行主動開通,默認是不開通的,為了體驗這個功能,我這里把多客服功能進行開通。
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")]
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();
}
通過使用多客服的客戶端,這樣處理消息交互起來非常方便,能獲得客戶的對話信息了,在電腦客戶端上,看到的界面如下所示。
手機上的談話截圖如下所示。
這樣就能夠通過多途徑,及時響應客戶的信息了。
如果感興趣或者體驗相關的客服應答功能,可以關注我的微信了解下。具體效果可以關注我的微信門戶:廣州愛奇迪,也可以掃描下面二維碼進行關注了解。
C#開發微信門戶及應用(8)-微信門戶應用管理系統功能介紹
最近對微信接口進行深入的研究,通過把底層接口一步步進行封裝后,逐步升級到自動化配置、自動化應答,以及后台處理界面的優化和完善上,力求搭建一個較為完善、適用的微信門戶應用管理系統。
微信門戶應用管理系統,采用基於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、訂閱用戶管理
為了更有效管理訂閱用戶以及分組信息,我們可以從微信服務器上獲取相關的信息,供我們了解關注的用戶信息,也可以為后續的群發消息做准備。
訂閱用戶的管理如下所示,默認可以通過用戶的地區進行查看,地區根據:國家-省份-城市這樣的級別進行展開。單擊同步數據,可以把服務器上的用戶數據下載到本地進行更新或者寫入。
訂閱用戶,還可以根據分組進行查看
雙擊可以查看訂閱用戶信息,查看訂閱用戶的詳細信息界面如下所示。
6、用戶分組管理
創建分組的界面如下所示。
編輯分組信息界面如下所示。
當對分組進行編輯保存后,系統會記住那些修改過的,同步的時候,把本地新增的內容,在服務器上創建分組;把修改的的分組名稱,在服務器上進行修改,然后進行同步列表處理。
7、多媒體管理
多媒體管理是指把本地文件上傳到微信服務器上進行保存,方便信息的發送等操作。微信要求,某些信息,必須是先上傳到服務器上,然后才能使用它的媒體ID進行發送的。
文件成功上傳到服務器后,在列表里面的“文件上傳標識,就是一串BASE64的編碼數據,同時有一個上傳的時間戳(因為微信服務器只保留了3天的媒體數據,超過期限的數據會被自動刪除。
同時,在列表的上面,有兩個重要的功能:上傳選定的記錄,重新上傳過期的記錄。方便我們對自己多媒體文件的重新更新操作。
添加界面操作如下所示,其中引入了附件上傳的控件進行文件的操作,非常方便。同時上傳成功的文件,會在列表中列出。
多媒體文件可以是下面幾種方式:圖片、語音、視頻、縮略圖。
保存后的數據記錄,文件上傳標識和時間戳都是空的,我們如果要使用,必須把他們上傳到微信的服務器上,然后根據它的MediaId進行信息的發送,上傳選定的記錄操作界面如下所示。
多媒體文件順利上傳后,記錄的信息如下所示。
8、圖文消息處理
圖文消息分為單圖文消息和多圖文消息兩種,單圖文消息如下所示。
多圖文消息如下所示:
和多媒體數據管理一樣,圖文消息也是通過同樣的方式進行管理,先上傳到服務器,然后在進行消息的發送操作,多媒體消息一樣有時間方面的限制要求,具體在我們的微信門戶平台里面管理界面如下所示。
添加圖文消息界面如下所示,保存后,可以在編輯界面中的“其他圖文列表”里面,繼續添加多圖文的消息內容。
在添加界面中,選擇圖文消息的縮略圖,都是通過選定指定的,已經上傳到服務器上圖片或者縮略圖資源才可以的。
添加后的多圖文列表,可以進行查看管理。
保存記錄后,然后繼續上傳,上傳后的記錄界面如下所示,成功后返回一個上傳后的服務器標識和時間戳,否則提示錯誤。
9、會話消息管理
為了方便記錄客戶的輸入和發送信息,我們在微信門戶管理平台里面記錄用戶的輸入數據,具體會話消息管理界面如下所示。
我們可以雙擊最近48小時內的任何一條記錄,可以給關注的客戶進行消息的發送操作,如果消息發送成功,用戶在手機的微信賬號里面就能收到相關的發送消息了。
10、群發消息管理
為了對客戶進行相應的營銷操作,有時候我們需要對指定的群主或者人員進行消息的群發,讓客戶經常性的了解我們產品的信息和活動。
由於群發消息,除了文本消息,可以直接編輯發送外,其他數據,必須要求是上傳到服務器的多媒體文件或者圖文消息內容,因此前面的多媒體管理和圖文消 息管理,就是主要為了群發消息的目的引入的。有了上面的多媒體和多圖文信息,我們從平台里面選擇記錄即可進行發送,從而省卻麻煩的連帶工作,實現高效的信 息群發操作。
群發的消息,可以按群發分組進行查看,也可以按照消息類型進行查看,使得我們管理起來根據方便。
添加圖文消息,可以選擇文本消息、圖文消息、圖片消息等內容,根據不同的內容,界面提供不同的選擇操作。
消息的群發類型分為兩種,一種是根據分組,那么從平台里面選擇對應的分組即可;一種是根據用戶的OpenID進行發送,提供給用戶輸入。主要的操作界面如下所示。
C#開發微信門戶及應用(9)-微信門戶菜單管理及提交到微信服務器
微信公眾號(包括服務號和訂閱號)都可以對菜單進行自定義設置,我們為了方便管理,一般先把菜單數據在本地管理維護,需要更新的時候,把它們更新到 微信服務器上就可以了。本文基於這個方式,介紹我的微信門戶平台管理系統中菜單提交到微信服務器上的操作。微信門戶應用管理系統,采用基於 MVC+EasyUI的路線,由於多數域名服務器上都只能支持.NET4.0,所以以MVC3,C#4.0作為開發基礎,基本上能夠部署在任何.NET服 務器上。
1、微信菜單的要求及相關界面設計
微信公眾號的菜單我們可以通過網站進行本地的管理,維護好它們之間的層級關系,由於微信對自定義的菜單要求比較嚴格,以下是微信對自定義菜單的要求:
目前自定義菜單最多包括3個一級菜單,每個一級菜單最多包含5個二級菜單。一級菜單最多4個漢字,二級菜單最多7個漢字,多出來的部分將會以“...”代替。
因此我們自己根據約定,不要越界即可,否則提交菜單到服務器,可能會返回一些錯誤,這些細節,我們在創建本地菜單管理的時候,注意一下就可以了。我在早期的一篇文章也介紹了自定義菜單的一些內容,需要可以進行回顧一下《C#開發微信門戶及應用(6)--微信門戶菜單的管理操作》,本篇主要是介紹在我的平台管理系統里面,調用前面介紹的菜單接口API,實現菜單提交到服務器的操作。
根據微信的自定義菜單要求,我在管理系統里面,對微信的菜單幾個基礎性的界面設計如下。
主菜單管理界面如下所示。
添加菜單的界面設計如下所示
微信菜單的修改界面如下所示
微信菜單定義是存儲在數據庫里面,如果需要提交到微信服務器上並生效,則需要調用微信API接口進行處理,我在頁面的Controller控制器里增加一個提交到服務器的處理方法。
2、提交菜單到微信服務器的操作
上面幾個界面,主要就是根據微信菜單的屬性,對菜單進行維護管理,我們最終的目的是把它們放到服務器上去,供我們處理客戶的相關事件操作的。
提交菜單的操作,我們在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,實現菜單的提交處理了。
如果感興趣或者體驗相關的客服應答功能,可以關注我的微信了解下。具體效果可以關注我的微信門戶:廣州愛奇迪,也可以掃描下面二維碼進行關注了解。
C#開發微信門戶及應用(10)--在管理系統中同步微信用戶分組信息
在前面幾篇文章中,逐步從原有微信的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>
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();
}
});
如果感興趣或者體驗相關的微信功能,可以關注我的微信了解下。具體效果可以關注我的微信門戶:廣州愛奇迪,也可以掃描下面二維碼進行關注了解。
C#開發微信門戶及應用(11)--微信菜單的多種表現方式介紹
在前面一系列文章中,我們可以看到微信自定義菜單的重要性,可以說微信公眾號賬號中,菜單是用戶的第一印象,我們要規划好這些菜單的內容,布局等信 息。根據微信菜單的定義,我們可以看到,一般菜單主要分為兩種,一種是普通的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>
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());
}
3、重定向鏈接菜單的用途
這種菜單就是需要指定域名,在微信后台中進行設置,重定向的鏈接必須屬於這個域名之中,否則不會轉到你希望的鏈接。
這個方式,讓我們的微信應用程序后台可以獲得用戶的標識、用戶詳細信息等,我們就可以用來綁定和用戶相關的業務信息了,如上面提到的圖書館借閱信 息,送水客戶的信息,客戶的積分信息,或者可以和后台賬號進行關聯實現更加復雜的應用等。用戶的身份信息如此重要,如果結合到我們的CRM系統、業務管理 系統,就可以發揮用戶信息應用的作用了。
以上就是我對這個類型菜單鏈接的應用了解,具體還需要進一步深化其應用,希望和大家共同探討這方面的應用場景。
C#開發微信門戶及應用(12)-使用語音處理
我們知道,微信最開始就是做語音聊天而使得其更加流行的,因此語音的識別處理自然也就成為微信交流的一個重要途徑,微信的開發接口,也提供了對語音 的消息請求處理。本文主要介紹如何利用語音的識別,對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")]
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的字段,這個就是微信服務器自動根據用戶的語音轉換過來的內容,我測試過,識別率還是非常高的。
這個實體類,在整個微信應用的消息傳遞中的關系如下所示:
2、語音的處理操作
明確了上面的語音對象實體,我們就可以看看它們之間是如何處理的。
微信消息的處理邏輯如下圖所示。
其中我們來看看語音的處理操作,我的代碼處理邏輯如下所示。
///<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#開發微信門戶及應用(13)-使用地理位置擴展相關應用
本文繼續上一篇《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")]
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;
}
2、地址位置的應用處理
不過上面的信息,顯然不符合我們擴展應用的要求,因此我們進一步進行完善里面對地理位置信息處理的操作。我們進一步把關於地理位置的操作,放到事件處理模塊里面進行處理,處理代碼如下所示。
///<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);
首先對用戶地理位置的請求,我根據數據庫配置給出了一個用戶選擇的指令提示,如下所示。
為了對地理位置請求的處理,我定義了一個用於處理這個操作的指令操作
這樣整個地理位置的指令操作,就在應答鏈里面進行很好的跳轉管理了。那么為了實現天氣、放映影片、附近影院、旅游線路、交通事件等方面的擴展應用,我們應該如何操作呢?
3、地址位置應用擴展
我們知道,百度或者騰訊都提供了一些開放平台,給我們進行各種方式的使用。那么我們這里以使用百度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#開發微信門戶及應用(14)-在微信菜單中采用重定向獲取用戶數據
我曾經在系列文章中的《C#開發微信門戶及應用(11)--微信菜單的多種表現方式介紹》 中介紹了微信菜單里面的重定向操作,通過這個重定向操作,我們可以獲取一個code值,然后獲取用戶的openID,進而就能獲取到更多的用戶信息,這個 在會員信息的場景里面用的很多,本篇介紹在網站中迅速配置這樣的菜單鏈接,並介紹如何在后台獲取相關的用戶信息,實現頁面數據個性化的展現操作。
我們知道,微信的自定義菜單分為兩大類,分別對應Click類型和View類型的,而重定向屬於View類型的一種,如下所示。
1、微信重定向菜單的配置
微信重定向的菜單,就是通過傳入一個地址參數,讓微信服務器進行跳轉,它的主要規則如下所示。
對於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,這樣才能構造成完整的地址連接。
2、腳本轉換操作的實現代碼
前面說了,第一是需要實現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();
}
這樣配置后的重定向菜單地址列表就如下所示了,我們打開對應的記錄詳細頁面,可以通過頁面里面的功能按鈕,隨時對重定向菜單的地址進行轉換,方便了解詳細的鏈接內容。
3、重定向頁面的設計及處理
配置了上面的鏈接地址后,我們需要在網站里面增加這樣的一個頁面進行處理用戶的信息,一般情況下,我們可能是為了方便用戶查看自己的微信基礎信息, 也為了給用戶綁定用戶個人數據使用的用途的,如用戶可以綁定手機、Email郵箱等操作,還可以綁定和業務系統相關的用戶名。這樣用戶就可以快速注冊會員 或者和后台的系統進行關聯了。
我設計的兩個用戶信息展示界面如下所示。
這兩個界面主要使用了Jquery Mobile的相關內容,對界面進行了處理,整個模塊結合了短信驗證碼的方式,對用戶的手機進行驗證處理,這樣能夠更高效的實現信息准確的綁定操作,當 然,還可以結合外部系統,綁定用戶的賬號密碼,這樣用戶可以在微信進入微網站平台進行購物、數據維護、業務管理等操作了,其實一旦綁定外部系統的ID,也 就是提供了一個快速進行外部系統的入口了。
具體的內容在下一篇繼續介紹了。
C#開發微信門戶及應用(15)-微信菜單增加掃一掃、發圖片、發地理位置功能
前面介紹了很多篇關於使用C#開發微信門戶及應用的文章,基本上把當時微信能做的接口都封裝差不多了,微信框架也積累了不少模塊和用戶,最近發現微 信公眾平台增加了不少內容,特別是在自定義菜單里面增加了掃一掃、發圖片、發地理位置功能,這幾個功能模塊很重要,想想以前想在微信公眾號里面增加一個掃 描二維碼的功能,都做不了,現在可以了,還可以拍照上傳等功能,本文主要介紹基於我前面的框架系列文章,進一步介紹如何集成和使用這些新增功能。
1、微信幾個功能的官方介紹
1). 掃碼推送事件
用戶點擊按鈕后,微信客戶端將調起掃一掃工具,完成掃碼操作后顯示掃描結果(如果是URL,將進入URL),且會將掃碼的結果傳給開發者,開發者可以下發消息。
2). 掃碼推送事件,且彈出“消息接收中”提示框
用戶點擊按鈕后,微信客戶端將調起掃一掃工具,完成掃碼操作后,將掃碼的結果傳給開發者,同時收起掃一掃工具,然后彈出“消息接收中”提示框,隨后可能會收到開發者下發的消息。
3). 彈出系統拍照發圖
用戶點擊按鈕后,微信客戶端將調起系統相機,完成拍照操作后,將拍攝的相片發送給開發者,並推送事件給開發者,同時收起系統相機,隨后可能會收到開發者下發的消息。
4). 彈出拍照或者相冊發圖
用戶點擊按鈕后,微信客戶端將彈出選擇器供用戶選擇“拍照”或者“從手機相冊選擇”。用戶選擇后即走其他兩種流程。
5). 彈出微信相冊發圖器
用戶點擊按鈕后,微信客戶端將調起微信相冊,完成選擇操作后,將選擇的相片發送給開發者的服務器,並推送事件給開發者,同時收起相冊,隨后可能會收到開發者下發的消息。
6). 彈出地理位置選擇器
用戶點擊按鈕后,微信客戶端將調起地理位置選擇工具,完成選擇操作后,將選擇的地理位置發送給開發者的服務器,同時收起位置選擇工具,隨后可能會收到開發者下發的消息。
但請注意,以上新增能力,均僅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用戶,舊版本微信用戶點擊后將沒有回應,開發者也不能正常接收到事件推送。
2、微信新菜單功能的測試公眾號
微信不僅增加了這些功能模塊的支持,還考慮到我們開發人員的方便,增加了一個叫做“menutest"的公眾號,方便我們測試。我們在公眾號搜索“menutest",然后關注它即可進行測試幾個新增功能了。
“menutest"的公眾號名稱是”自定義菜單拓展測試“,我關注它並進行了測試,二維碼、圖片、地理位置都很OK,本身能夠響應這些事件,並且圖片、地理位置自身還能出現一個對應的事件,如下所示。
圖片發送可以分為拍照、拍照和相冊、微信相冊三類,感覺后面兩個有點類似,但有這些功能都很不錯的。
3、改進菜單對象和提交菜單
前面說了,微信提供這些功能,可以在菜單里面進行集成,也就是菜單的類型由原來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);
}
4、微信掃一掃功能集成
前面講了,有了最新的功能,我們就可以實現掃一掃功能,從而可以掃描條形碼,二維碼的功能。有了條形碼、二維碼的快速和識別,我們就能開發一些如條碼查詢、商品處理等功能了。
這里我們介紹如何在我的微信開發框架里面整合這個掃一掃的功能處理操作。
前面已經增加了一些新功能的測試菜單,我們要做的就是響應這些事件處理,然后對他們進行應答處理就可以了。
下面是根據事件進行的一些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();
}
最后我們測試掃描一個條形碼,可以看到返回的結果界面操作如下所示。
5、新菜單功能測試發現的問題
前面介紹了一些新菜單功能模塊的集成,我個人對這種掃一掃菜單功能非常贊賞,這也是微信逐步整合更多硬件資源和接口處理的趨向,不過在集成使用的時 候,發現公眾號偶爾出現閃退的情況,還有就是這些新功能雖然后台能夠實現數據的處理和接收,但是有一些不能返回應答消息,很郁悶。也許隨着版本研發的加 快,這些功能很快得到完善和解決。
另外微信開放平台也投入使用了,好些認證也是300元一年,不過暫時沒有其應用的場景,我只是用到了它來獲取微信賬號的unionid的功能,其他功能慢慢了解吧。
還有就是微信的企業號也已經出來了,而且我也已經申請認證通過,它的開發用戶的API也有不少,有空繼續研究並整合到微信開發框架里面吧。
C#開發微信門戶及應用(16)-微信企業號的配置和使用
在本系列隨筆的前面,主要就是介紹微信公眾號的門戶應用開發,最近把整個微信框架進行了擴展補充,增加了最新的企業號的API封裝和開發,后續主要介紹如何利用C#進行微信企業號的開發工作,本篇作為微信企業號的開發的起步篇,介紹微信企業號的配置和使用。
1、微信企業號的注冊和登陸
企業號是繼公眾號、訂閱號的另外一種微信類型,它主要是面對企業的。企業號是微信為企業客戶提供的移動應用入口。可以幫助企業建立員工、上下游供應 鏈與企業 IT 系統間的連接。利用 企業號 ,企業或第三方合作伙伴可以幫助企業快速、低成本的實現高質量的移動輕應用,實現生產、管理、協作、運營的 移動化 。
個人覺得企業號最大的亮點是可以不限數量的消息發送,也就是可以在企業員工之間暢通交流。相對於公眾號和訂閱號,發送消息的謹慎程度,微信企業號可 謂給人眼前一亮的感覺。不過微信企業號是需要內部建立好通訊錄,關注者需要匹配通訊錄的微信號、郵箱、電話號碼任一個通過才可以關注,也就是可以防止其他 外來人員的自由關注了,另外如果為了安全考慮,還可以設置二次驗證,也就是一個審核過程。
企業號的認證和公眾號一樣,需要提供相關的企業資質文件,並且認證每年都要收取費用,否則可能有人員和功能的一些限制。覺得微信真是想着方法賺錢, 目前已有的收費模式有,訂閱號、公眾號、企業號、開放平台,好像都有認證收費的了,而且微信小店也還需要收2萬的押金,一切都是錢呀。
好了,其他不多說,微信的注冊地址是:https://qy.weixin.qq.com,一個郵箱不能同時注冊微信公眾號和微信企業號。
對於企業開通企業號並開始使用需要四步
1) 企業到微信官網( http://qy.weixin.qq.com )申請開通;
2) 開通后,企業在企業號管理后台導入成員,發布二維碼;
3) 企業調用企業號 api 與企業自有系統對接開發;
4) 員工關注,收到微信信息,在微信中與企業交互
注冊好企業號,就可以通過微信掃一掃,掃描企業二維碼進行登錄了,掃描的時候,需要微信進行確認,才可以繼續輸入密碼進行登錄,操作界面如下所示(左邊是手機截圖,右邊是網頁截圖)。
登錄后我們就可以看到對應的電腦端的管理界面了。
2、設置開發回調模式
如果開發過微信公眾號,那么我們就知道,如果需要在微信服務器和網站服務器之間建立連接關系,實現消息的轉發和處理,那么就應該設置一個回調模式,需要配置好相關的參數。然后在自己 網站服務器里面建立一個處理微信服務器消息的入口。
進入配置后,我們需要修改相關的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里面,如下所示。
3、實現回調頁面的功能開發
前面介紹了幾個配置項,需要在回調頁面里面使用的,本小節繼續介紹如何實現企業號信息的回發,使之通過回調測試的操作。
由於回調測試的數據是通過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);
}
C#開發微信門戶及應用(17)-微信企業號的通訊錄管理開發之部門管理
前面一篇隨筆企業號的一些基礎信息,以及介紹如何配置企業號的回調方式實現和企業號服務器進行溝通的橋梁。本篇主要還是繼續介紹企業號的開發工作的開展,介紹微信企業號通訊錄管理開發功能,介紹其中組織機構里面如何獲取和管理部門的信息等內容。
1、企業組織的創建和配置
首先我們可以在企業號的管理后台里面創建一個組織機構,里面創建一些部門和人員列表,方便我們開發和使用。
例如創建一個廣州愛奇迪的根結構,然后在其中在創建一些組織機構,如下圖所示。
然后給組織結構根節點“廣州愛奇迪”增加一個管理員權限,以后再開發接口里面也就可以使用這個管理員所屬的權限Secret值進行調用了。
CorpID是企業號的標識,每個企業號擁有一個唯一的CorpID;Secret是管理組憑證密鑰。
系統管理員可通過管理端的權限管理功能創建管理組,分配管理組對應用、通訊錄、接口的訪問權限。完成后,管理組即可獲得唯一的secret。系統管理員可通過權限管理查看所有管理組的secret,其他管理員可通過設置中的開發者憑據查看。
我的企業號的創建者和“廣州愛奇迪”組織結構的管理員是不同的,由於Secret是管理組憑證密鑰,因此管理者負責不同的組織機構管理的話,自己的管理Secret值可能就不同了。如果我們需要調用接口,就需要用到這個屬於自己權限級別的Secret值,如下圖所示。
如果不是企業號的創建者,那么可能不能修改里面的一些權限分配,只能查看。
2、API訪問的全局唯一票據AccessToken的獲取
和公眾號一樣,我們調用企業號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調用受限,影響自身業務。
2、通訊錄管理之部門信息的維護
有了第一節里面的訪問票據,我們就可以利用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; }
}
3、部門管理的API調用
上面小節介紹了如何封裝部門管理的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#開發微信門戶及應用(18)-微信企業號的通訊錄管理開發之成員管理
在上篇隨筆《C#開發微信門戶及應用(17)-微信企業號的通訊錄管理開發之部門管理》介紹了通訊錄的部門的相關操作管理,通訊錄管理包括部門管理、成員管理、標簽管理三個部分,本篇主要介紹成員的管理操作,包括創建、刪除、更新、獲取、獲取部門成員幾個操作要點。
1、成員的創建操作
為了方便,我們可以創建一個部門組織結構,這是開發的前提,因為我們通訊錄管理,也是基於一個組織機構下的,如上篇介紹的組織結構層次一樣。我這里創建一個廣州愛奇迪的根結構,然后在其中在創建一些組織機構,如下圖所示。
在后台可以通過功能操作添加人員,本篇主要介紹如何調用微信企業號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);
}
2、成員的更新操作
成員的數據更新和創建操作類似,它的企業號定義如下所示。
- 請求說明
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);
}
3、成員的刪除、成員的獲取、部門成員的獲取操作
這些操作和上面的類似,不在贅述,主要就是根據需要定義他們對應的返回數據信息,然后解析Json數據即可轉換為對應的實體。
1)刪除人員的定義如下:
- 請求說明
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"
}
2)成員的獲取定義如下:
- 請求說明
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
}
3)部門成員的獲取定義如下:
- 請求說明
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; }
}
7、綜合例子調用代碼
上面介紹了一些企業號的接口定義和我對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);
}
}
}
人員的管理,相對來說比較簡單,主要是在一定的部門下創建人員,然后也可以給標簽增加相應的人員,基本上就是這些了,不過一定需要確保有相應的權限進行操作。
C#開發微信門戶及應用(19)-微信企業號的消息發送(文本、圖片、文件、語音、視頻、圖文消息等)
我們知道,企業號主要是面向企業需求而生的,因此內部消息的交流顯得非常重要,而且發送、回復消息數量應該很可觀,對於大企業尤其如此,因此可以 結合企業號實現內部消息的交流。企業號具有關注安全、消息無限制等特點,很適合企業內部的環境。本文主要介紹如何利用企業號實現文本、圖片、文件、語音、 視頻、圖文消息等消息的發送操作。
1、企業號特點
對於企業號,有以下一些特點:
1)關注更安全
–只有企業通訊錄的成員才能關注企業號,分級管理員、保密消息等各種特性確保企業內部信息的安全。
企業可以設置自行驗證關注者身份,進行二次安全驗證,保證企業信息使用和傳遞安全。
若員工離職,企業管理員可在通訊錄中刪除該成員,該成員即自動取消關注企業號,同時微信中的企業號歷史記錄也會被清除。
2)應用可配置
–企業可自行在企業號中可配置多個服務號,可以連接不同的企業應用系統,只有授權的企業成員才能使用相應的服務號。
3)消息無限制
–發送消息無限制,並提供完善的的管理接口及微信原生能力,以適應企業復雜、個性化的應用場景。
企業可以主動發消息給員工,消息量不受限制。
4)使用更便捷
–企業號在微信中有統一的消息入口,用戶可以更方便地管理企業號消息。微信通訊錄也可以直接訪問企業號中的應用。
2、企業號的管理接口內容
目前企業號的內容可以用下面的分層圖來展示,分別包含素材管理、被動響應消息、通訊錄管理、自定義菜單等內容,詳細可以看下面圖示。
3、企業號消息和事件的處理
企業號和公眾號一樣,可以分為消息處理和事件處理,下面是他們兩種類型的處理操作,也就發送的消息有文本消息、圖片消息、文件消息、視頻消息、語音消息、地理文字消息、圖文和多媒體消息等。
事件處理主要就是關注、取消關注事件,以及菜單click類型和view類型兩種操作,還有就是地理位置上報事件等。
兩種類型的處理圖如下所示。
4、企業號消息管理
在企業的管理后台,和公眾號一樣,可以看到對應信息交流記錄,包括文字、圖片、地理位置等等,如下所示。
由於消息分為幾種類型,包括文本(Text)、圖片(Image)、文件(File)、語音(Voice)、視頻(Video)、圖文消息等(News)、MpNews等。
因此我們需要分別對它們進行一定的定義和封裝處理,如下是它們的信息對象設計圖。
企業號發送消息的官方定義如下:
企業可以主動發消息給員工,消息量不受限制。
調用接口時,使用Https協議、JSON數據包格式,數據包不需做加密處理。
目前支持文本、圖片、語音、視頻、文件、圖文等消息類型。除了news類型,其它類型的消息可在發送時加上保密選項,保密消息會被打上水印,並且只有接收者才能閱讀。
我們以發送的文本消息為例進行說明,它的定義如下所示。
- text消息
{
"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; }
}
然后其他消息逐一繼承這個基類即可,如下所示。
最終會構成下面這個繼承關系圖。
5、消息接口的定義和實現
定義好相關的發送對象后,我們就可以定義它的統一發送接口了,如下所示。
///<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;
}
}
6、消息的發送操作和實際效果
定義好相應的發送對象后,我們就可以進行統一的消息發送操作,包括文本、圖片、文件、語音等等類型的消息,注意有些消息是需要上傳到服務器上,然后在根據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);
}
}
}
最后在微信企業號上截圖效果如下所示,包括了文本測試、文件測試、圖文測試、語音測試均正常。
C#開發微信門戶及應用(20)-微信企業號的菜單管理
前面幾篇陸續介紹了很多微信企業號的相關操作,企業號和公眾號一樣都可以自定義菜單,因此他們也可以通過API進行菜單的創建、獲取列表、刪除的操作,因此本篇繼續探討這個主體,介紹企業號的菜單管理操作。
菜單在很多情況下,能夠給我們提供一個快速入口,也可以用來獲取用戶信息的主要入口,通過OAuth2驗證接口,以及自定義的重定向菜單,我們就可以獲取對應的用戶ID,然后進一步獲取到用戶的相關數據,可以顯示給客戶。
1、菜單的總體介紹
菜單的事件處理如下所示,包括了單擊和跳轉兩個操作,未來企業號可能會增加一些和公眾號一樣的掃碼操作,拍照操作等功能的,目前只有兩個。
官方的菜單定義接口包含了下面三種操作,菜單創建、列表獲取和菜單刪除,這點和公眾號操作幾乎一樣了。
2、菜單的實體類定義和接口定義處理
我們定義菜單,包括定義它的一些屬性,包含有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);
}
3、企業號菜單管理接口的調用和處理效果
調用的代碼和效果圖如下所示。
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());
}
}
調用代碼的測試輸出如下所示。