您的位置:首页 > 产品设计 > UI/UE

用c#开发微信 (13) 微统计 - 阅读分享统计系统 3 UI设计及后台处理

2015-06-05 09:03 555 查看
 

微信平台自带的统计功能太简单,有时我们需要统计有哪些微信个人用户阅读、分享了微信公众号的手机网页,以及微信个人用户访问手机网页的来源:朋友圈分享访问、好友分享消息访问等。本系统实现了手机网页阅读、分享与来源统计及手机网页在朋友圈的传播路径分析。

本系统使用最传统的三层架构。本文是微统计的第三篇,主要介绍如下内容:

 

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带上,为了方便识别访问者和分享者。

 

 

所有界面如下:





未完待续!!!

 

 

[b]用c#开发微信 系列汇总[/b]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: