用c#开发微信 (13) 微统计 - 阅读分享统计系统 3 UI设计及后台处理
生活随笔
收集整理的這篇文章主要介紹了
用c#开发微信 (13) 微统计 - 阅读分享统计系统 3 UI设计及后台处理
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
?
微信平臺自帶的統計功能太簡單,有時我們需要統計有哪些微信個人用戶閱讀、分享了微信公眾號的手機網頁,以及微信個人用戶訪問手機網頁的來源:朋友圈分享訪問、好友分享消息訪問等。本系統實現了手機網頁閱讀、分享與來源統計及手機網頁在朋友圈的傳播路徑分析。
本系統使用最傳統的三層架構。本文是微統計的第三篇,主要介紹如下內容:
?
1. 為頁面HighCharts畫圖控件提供數據
2. 接收分享記錄信息并保存到數據庫
3. 訪問記錄統計圖
4. 閱讀統計界面
5. 處理文字請求
前端開發框架使用Bootstrap,沒有注明前臺的頁面表示前臺不用顯示任何內容
1. 為頁面HighCharts畫圖控件提供數據 Data.aspx
public partial class Data : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string result = ""; string typeStr = System.Web.HttpContext.Current.Request.QueryString["type"]; if (!string.IsNullOrEmpty(typeStr)) { switch (typeStr) { case "navChart": //頁面訪問圖 result = JsonConvert.SerializeObject(GetPageNavStatistics()); break; case "shareChart": //頁面分享圖 result = JsonConvert.SerializeObject(GetPageShareStatistics()); break; } } //將HighCharts繪圖所需的數據返回給頁面 HttpResponse response = System.Web.HttpContext.Current.Response; response.ContentType = "application/json"; response.Write(result); response.End(); } ? /// <summary> /// 獲取頁面訪問統計信息 /// </summary> /// <returns></returns> private ChartData GetPageNavStatistics() { //取過去兩天的數據進行統計 DateTime startTime = DateTime.Now.AddDays(-3); DateTime endTime = DateTime.Now.AddDays(1); List<PageNavEntity> temp = new PageNavBll().GetPageNavList(); List<decimal> statistics = new List<decimal>(); //HighCharts時間軸的起始時間 ChartData chartData = new ChartData { StartYear = startTime.Year, StartDay = startTime.Day, StartMonth = startTime.Month }; //生成按小時統計的數據 while (startTime < endTime) { statistics.Add(temp.FindAll(e => e.VisitTime >= startTime && e.VisitTime < startTime.AddHours(1)).Count()); startTime = startTime.AddHours(1); } chartData.Statistics = statistics.ToArray(); return chartData; } ? /// <summary> /// 獲取頁面分享統計信息 /// </summary> /// <returns></returns> private ChartData GetPageShareStatistics() { //取過去兩天的數據進行統計 DateTime startTime = DateTime.Now.AddDays(-3); DateTime endTime = DateTime.Now.AddDays(1); List<PageShareEntity> temp = new PageShareBll().GetPageShareList(); List<decimal> statistics = new List<decimal>(); //HighCharts時間軸的起始時間 ChartData chartData = new ChartData { StartYear = startTime.Year, StartDay = startTime.Day, StartMonth = startTime.Month }; //生成按小時統計的數據 while (startTime < endTime) { statistics.Add(temp.FindAll(e => e.ShareTime >= startTime && e.ShareTime < startTime.AddHours(1)).Count()); startTime = startTime.AddHours(1); } chartData.Statistics = statistics.ToArray(); return chartData; } }2. 接收分享記錄信息并保存到數據庫 Share.aspx
public partial class Share : System.Web.UI.Page { ILog m_Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); ? protected void Page_Load(object sender, EventArgs e) { string typeStr = Request.QueryString["type"]; ? m_Log.Info("share type: " + typeStr); m_Log.Info("share url: " + Request["url"]); ? if (!string.IsNullOrEmpty(typeStr)) { //識別分享類型 ShareType type = ShareType.Unknown; switch (typeStr) { case "timeline": type = ShareType.Timeline; break; case "friend": type = ShareType.Friend; break; } //構造分享記錄 var pageShare = new PageShareEntity() { Id = Guid.NewGuid(), Url = GetOrigenalUrl(Request["url"]), ParentShareOpenId = Request["s"], ShareOpenId = Request["u"], From = type, ShareTime = DateTime.Now }; //保存分享記錄 bool insertShare = new PageShareBll().InsertPageShare(pageShare); ? m_Log.Info("insert share: " + insertShare.ToString()); } } ? /// <summary> /// 獲取不含統計相關參數的頁面地址 /// </summary> /// <param name="url">網址</param> /// <returns>不含統計相關參數的頁面地址</returns> private string GetOrigenalUrl(string url) { url = System.Web.HttpUtility.UrlDecode(url); Uri uri = new Uri(url); StringBuilder urlBuilder = new StringBuilder(); //獲取不含QueryString的URL urlBuilder.Append("http://") .Append(uri.Host) .Append(uri.AbsolutePath) .Append("?"); //構造移除統計相關參數的Query Dictionary<string, string> queryString = uri.Query.Replace("?", "").Split('&').Where(p => !string.IsNullOrEmpty(p)).ToDictionary(p => p.Split('=')[0], p => p.Split('=')[1].Split('#')[0]); foreach (var key in queryString.Keys) { if (key != "s" && key != "u" && key != "from" && key != "code" && key != "state") { urlBuilder.Append(key).Append("=").Append(queryString[key]).Append("&"); } } return urlBuilder.ToString(); } }當發送朋友或朋友圈時保存分享數據。
?
3. 訪問記錄統計圖 StatisticsPage.aspx
前臺:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="StatisticsPage.aspx.cs" Inherits="Statistics.StatisticsPage" %> ? <!DOCTYPE html> ? <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>統計</title> <%-- Bootstrap --%> <link href="Css/bootstrap.min.css" rel="stylesheet" type="text/css" /> <script src="Scripts/bootstrap.min.js" type="text/javascript"></script> <script src="Scripts/jquery-1.9.1.min.js" type="text/javascript"></script> <%-- HighCharts用于圖表顯示 --%> <link href="Css/highcharts/charts.css" rel="stylesheet" type="text/css" /> <script src="Scripts/HighCharts/highcharts.js" type="text/javascript"></script> <script src="Scripts/HighCharts/highcharts-more.js" type="text/javascript"></script> <script src="Scripts/HighCharts/publiclinecharts.js" type="text/javascript"></script> </head> <body> <div class="container"> <div class="row-fluid"> <div class="span12"> <h3>訪問記錄</h3> <%-- 訪問記錄統計圖 --%> <div class="box"> <div class="box-content"> <div class="row" style="margin-top: 30px; "> <div class="area"> <div id="page-nav-chart"> </div> </div> </div> </div> </div> <%-- 訪問記錄列表 --%> <div class="maincontentinner1" > <div id="Div12" class="dataTables_wrapper"> <table id="page-nav-table" class="table table-bordered responsive dataTable"> <%-- 訪問記錄列表列名 --%> <thead> <tr> <th>頁面地址 </th> <th>訪問來源 </th> <th>訪問者openid </th> <th>分享自openid </th> <th>訪問時間 </th> </tr> </thead> <tbody id="page-nav-table-body"> <%-- 一行一行生成訪問記錄列表 --%> <% foreach (Statistics.ViewEntity.PageNavEntity entity in (ViewState["NavList"] as List<Statistics.ViewEntity.PageNavEntity>)) { %> <tr class="gradeX odd"> <td> <%= entity.Url%> </td> <td class=" "> <%= entity.From.ToString()%> </td> <td class=" "> <%= entity.NavOpenId%> </td> <td class=" "> <%= entity.ShareOpenId%> </td> <td class=" "> <%= entity.VisitTime.ToString()%> </td> </tr> <% } %> </tbody> </table> </div> </div> </div> </div> </div> <script> //圖表參數 var pageNavChartOpts = { getStatisticsUrl: 'Data.aspx?type=navChart', //讀取數據的訪問地址 titletext: "", ytext: "", startyear: 0, startmonth: 0, startday: 0, lineinterval: 3600 * 1000, //豎線以1小時為間隔顯示 pointInterval: 3600 * 1000,//點以1小時為間隔顯示 countArray: [], formid: "page-nav-chart", //圖表容器ID seriesname: "訪問次數", unit: "次" }; jQuery(function () { //使用HighCharts繪制圖表 highcharts.extFunction.PreDrawMethod = function (repJson) { pageNavChartOpts.startyear = repJson.StartYear; pageNavChartOpts.startmonth = repJson.StartMonth; pageNavChartOpts.startday = repJson.StartDay; highcharts.displayMode = repJson.DisplayMode; pageNavChartOpts.lineinterval = repJson.LineInterval; pageNavChartOpts.pointInterval = repJson.PointInterval; }; highcharts.init(pageNavChartOpts); }); </script> </body> </html>用HighCharts來圖表顯示數據。
?
后臺:
protected void Page_Load(object sender, EventArgs e) { //傳遞給頁面顯示的記錄列表 ViewState["NavList"] = new PageNavBll().GetPageNavList(); ViewState["ShareList"] = new PageShareBll().GetPageShareList(); }通過業務邏輯層獲取數據。
?
4. 閱讀統計界面 WeixinPageIndex.aspx
前臺:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WeixinPageIndex.aspx.cs" Inherits="Statistics.WeixinPageIndex" %> ? <!DOCTYPE html> ? <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> <script src="Scripts/jquery-1.9.1.min.js"></script> <script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> </head> <body> <form id="form1" type="post" runat="server"> <div> <%-- 所有的跳轉頁面,加上訪問者與分享者的OpenId --%> <a href="WeixinPageSubPage.aspx?u=<%= ViewState["navOpenId"] as string %>&s=<%= ViewState["shareOpenId"] as string %>">WeixinPageSubPage</a> ? </div> </form> </body> </html> <script> var url = location.href; alert(url); wx.config({ debug: true, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會打印。 appId: '<%= appID %>', // 必填,公眾號的唯一標識 timestamp: '<%= timestamp %>', // 必填,生成簽名的時間戳 nonceStr: '<%= nonceStr %>', // 必填,生成簽名的隨機串 signature: '<%= signature %>',// 必填,簽名,見附錄1 // 必填,需要使用的JS接口列表,所有JS接口列表見附錄2 jsApiList: [ 'onMenuShareAppMessage' ] }); ? friendcallback = function (res) { var shareUrl = "Share.aspx?type=friend&url=" + encodeURIComponent(url) + "&u=" + "<%= ViewState["navOpenId"] as string %>" + "&s=" + "<%= ViewState["shareOpenId"] as string %>"; ? //AJAX請求 $.ajax({ type: "get", url: shareUrl, beforeSend: function () { }, success: function () { }, complete: function () { }, error: function () { } }); }; ? wx.ready(function () { wx.onMenuShareAppMessage({ title: '用c#開發微信 系列匯總', desc: '網上開發微信開發的教程很多,但c#相對較少。這里列出了我所有c#開發微信的文章,方便自己隨時查閱。如果可能,我盡量附上源碼,這樣就可以直接發布運行看效果,更好地理解原理。', link: url, imgUrl: 'http://demo.open.weixin.qq.com/jssdk/images/p2166127561.jpg', trigger: function (res) { }, success: function (res) { friendcallback(res); }, cancel: function (res) { }, fail: function (res) { alert(JSON.stringify(res)); } }); wx.onMenuShareTimeline({ title: '用c#開發微信 系列匯總', desc: '網上開發微信開發的教程很多,但c#相對較少。這里列出了我所有c#開發微信的文章,方便自己隨時查閱。如果可能,我盡量附上源碼,這樣就可以直接發布運行看效果,更好地理解原理。', link: url, imgUrl: 'http://demo.open.weixin.qq.com/jssdk/images/p2166127561.jpg', trigger: function (res) { }, success: function (res) { friendcallback(res); }, cancel: function (res) { }, fail: function (res) { alert(JSON.stringify(res)); } }); }); ? </script>利用JS-SDK來獲取分享者,并通過Share頁面來保存分享數據。
?
?
后臺:
public partial class WeixinPageIndex : System.Web.UI.Page { static ILog m_Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); ? public string timestamp = string.Empty; public string nonceStr = string.Empty; public string signature = string.Empty; ? /// <summary> /// 從微信公眾平臺獲取的開發者憑據 /// </summary> public readonly string appID = ConfigurationManager.AppSettings["appID"]; /// <summary> /// 從微信公眾平臺獲取的開發者憑據 /// </summary> readonly string appSecret = ConfigurationManager.AppSettings["appSecret"]; ? protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { #region 1. Get wx.config ? string ticket = string.Empty; timestamp = JSSDKHelper.GetTimestamp(); nonceStr = JSSDKHelper.GetNoncestr(); JSSDKHelper jssdkhelper = new JSSDKHelper(); ? try { ticket = JsApiTicketContainer.TryGetTicket(appID, appSecret); signature = jssdkhelper.GetSignature(ticket, nonceStr, timestamp, Request.Url.AbsoluteUri.ToString()); } catch (ErrorJsonResultException ex) { m_Log.Error("errorcode:" + ex.JsonResult.errcode.ToString() + " errmsg: " + ex.JsonResult.errmsg, ex); } ? #endregion ? NameValueCollection parameters = System.Web.HttpContext.Current.Request.Params; ? m_Log.Info("URL: " + Request.Url.ToString()); ? //取得鏈接中的分享者OpenId string shareOpenId = parameters["s"]; //獲取訪問者的OpenId string navOpenId = GetNavOpenId(); ? if (navOpenId != null) { NavStatistics(navOpenId, shareOpenId); //傳遞給頁面的訪問者OpenId ViewState["navOpenId"] = navOpenId; //傳遞給頁面的分享者OpenId ViewState["shareOpenId"] = shareOpenId; } ? m_Log.Info("timestamp: " + timestamp + " nocestr: " + nonceStr + " singnature: " + signature); m_Log.Info("nav open id: " + navOpenId + " share open id: " + shareOpenId); } } ? /// <summary> /// 記錄頁面訪問 /// </summary> /// <param name="navOpenId">訪問者微信openid</param> /// <param name="shareOpenId">當訪問來源為朋友圈時的分享者微信openid</param> private void NavStatistics(string navOpenId, string shareOpenId) { //獲取訪問來源 NavFrom fromType = GetNavFromType(); //構造訪問記錄 var pageNav = new PageNavEntity() { Id = Guid.NewGuid(), Url = GetOrigenalUrl(), NavOpenId = navOpenId, ShareOpenId = navOpenId == shareOpenId ? "" : shareOpenId, From = fromType, VisitTime = DateTime.Now }; //訪問記錄寫入數據庫 new PageNavBll().InsertPageNav(pageNav); } ? /// <summary> /// 判斷頁面訪問來源類型 /// </summary> /// <returns></returns> private static NavFrom GetNavFromType() { //網址中的參數集合 NameValueCollection parameters = System.Web.HttpContext.Current.Request.Params; string fromStr = parameters["from"]; //發送給朋友、分享到朋友圈的鏈接會含有from參數 ? m_Log.Info("from: " + fromStr); ? NavFrom fromType; if (!Enum.TryParse<NavFrom>(fromStr, true, out fromType)) //通過判斷from參數,識別頁面訪問是來自于發送給朋友的鏈接還是分享到朋友圈的鏈接 { //獲取HTTP訪問頭中的User-Agent參數的值 string agent = System.Web.HttpContext.Current.Request.Headers["User-Agent"]; if (agent.Contains(NavFrom.MicroMessenger.ToString())) //判斷頁面是否是在微信內置瀏覽器中打開 fromType = NavFrom.MicroMessenger; else fromType = NavFrom.Other; } return fromType; } ? /// <summary> /// 獲取不含統計相關參數的頁面地址 /// </summary> /// <returns>不含統計相關參數的頁面地址</returns> private string GetOrigenalUrl() { StringBuilder urlBuilder = new StringBuilder(); //獲取不含QueryString的URL urlBuilder.Append("http://") .Append(System.Web.HttpContext.Current.Request.Url.Host) .Append(System.Web.HttpContext.Current.Request.Url.AbsolutePath) .Append("?"); //構造移除統計相關參數的Query foreach (var key in System.Web.HttpContext.Current.Request.QueryString.AllKeys) { if (key != "s" && key != "u" && key != "from" && key != "code" && key != "state") { urlBuilder.Append(key).Append("=").Append(System.Web.HttpContext.Current.Request.QueryString[key]).Append("&"); } } return urlBuilder.ToString(); } ? /// <summary> /// 獲取訪問者openId /// </summary> private string GetNavOpenId() { NameValueCollection parameters = System.Web.HttpContext.Current.Request.Params; //獲取鏈接中的openId string navOpenId = parameters["u"]; #region 如果是從微信瀏覽器瀏覽,獲取真實的微信OpenId if (!string.IsNullOrEmpty(appID) && !string.IsNullOrEmpty(appSecret)) { string accessSource = System.Web.HttpContext.Current.Request.ServerVariables["HTTP_USER_AGENT"]; ? if (accessSource.Contains("MicroMessenger")) //如果是從微信打開頁面 { string[] cookieKeys = new[] { CookieHelper.COOKIE_NAME }; Dictionary<string, string> realIdCookie = CookieHelper.GetLoginCookies(cookieKeys); //獲取保存在Cookie中的OpenId //如果Cookie中不存在OpenId,或者鏈接中的openId與Cookie中的OpenId不一致,鏈接中的openId為分享者的OpenId,需要獲取當前用戶的真實OpenId if (NeedGetReadOpenId(parameters, realIdCookie)) { if (parameters["code"] == null) { // 先去獲取code,并記錄分享者 string snsapi_baseUrl = GoCodeUrl(navOpenId); if (!string.IsNullOrEmpty(snsapi_baseUrl)) { CookieHelper.CleanLoginCookie(cookieKeys); //跳轉到微信網頁授權頁面 System.Web.HttpContext.Current.Response.Redirect(snsapi_baseUrl, true); System.Web.HttpContext.Current.Response.End(); return null; } } else { m_Log.Info("code: " + parameters["code"].ToString()); ? OAuthAccessTokenResult tokenResult = GetRealOpenId(parameters["code"].ToString()); if (null != tokenResult && !string.IsNullOrEmpty(tokenResult.openid)) { m_Log.Info("tokenResult.openid: " + tokenResult.openid); ? navOpenId = tokenResult.openid; // 獲取到的當前訪問者的OpenId保存到cookie里 CookieHelper.CleanLoginCookie(cookieKeys); realIdCookie[CookieHelper.COOKIE_NAME] = tokenResult.openid; CookieHelper.WriteLoginCookies(realIdCookie, DateTime.MinValue); } } } } } #endregion return navOpenId; } ? /// <summary> /// 如果Cookie中存在OpenId且鏈接中的openId與Cookie中的OpenId一致 /// 則不需要調用網頁授權接口,鏈接中的openId即為當前訪問者的真實OpenId /// </summary> /// <param name="parameters"></param> /// <param name="realIdCookie"></param> /// <returns></returns> private bool NeedGetReadOpenId(NameValueCollection parameters, Dictionary<string, string> realIdCookie) { string referer = System.Web.HttpContext.Current.Request.ServerVariables["HTTP_REFERER"]; string openId = null; if (realIdCookie != null) { if (realIdCookie.ContainsKey(CookieHelper.COOKIE_NAME)) { openId = realIdCookie[CookieHelper.COOKIE_NAME]; } } ? m_Log.Info("NeedGetReadOpenId openid: " + openId + " referer: " + referer + " u: " + parameters["u"].ToString()); ? if (!string.IsNullOrEmpty(referer) && openId == parameters["u"].ToString()) return false; else return true; } ? /// <summary> /// 網頁授權接口第一步 /// 跳轉到獲取code的url /// </summary> /// <param name="shareOpenId">當訪問來源為朋友圈時的分享者微信openid</param> private string GoCodeUrl(string shareOpenId) { string url = System.Web.HttpContext.Current.Request.Url.AbsoluteUri + "&s=" + shareOpenId; //添加分享者OpenId return OAuthApi.GetAuthorizeUrl(appID, url, "STATE", OAuthScope.snsapi_base); } ? /// <summary> /// 網頁授權接口第二步 /// 解析code并獲取當前訪問者真正的openId /// </summary> /// <param name="parameters">url參數</param> /// <returns>真正的openId</returns> private OAuthAccessTokenResult GetRealOpenId(string code) { OAuthAccessTokenResult result = new OAuthAccessTokenResult(); try { result = OAuthApi.GetAccessToken(appID, appSecret, code); } catch (Exception ex) { m_Log.Error(ex.Message, ex); } return result; } }保存訪問者記錄,識別訪問者和分享者。
?
5. 處理文字請求
在CustomMessageHandler里處理文字請求,詳細的使用方法可參考《用c#開發微信(2)掃描二維碼,用戶授權后獲取用戶基本信息 (源碼下載)》:
public override IResponseMessageBase OnTextRequest(RequestMessageText requestMessage) { var responseMessage = CreateResponseMessage<ResponseMessageNews>(); responseMessage.Articles.Add(new Article() { Title = "首頁", Description = "點擊進入首頁", PicUrl = "", Url = System.Configuration.ConfigurationManager.AppSettings["site"] + "/WeixinPageIndex.aspx?u=" + requestMessage.FromUserName }); return responseMessage; }這里把訪問者的OpenId帶上,為了方便識別訪問者和分享者。
?
?
所有界面如下:
未完待續!!!
?
?
用c#開發微信 系列匯總
總結
以上是生活随笔為你收集整理的用c#开发微信 (13) 微统计 - 阅读分享统计系统 3 UI设计及后台处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: struts2原理分析之反射技术动态获取
- 下一篇: Linux中THIS_MODULE宏定义