Browse Source

文章访问记录图表

懒得勤快 3 years ago
parent
commit
7dcb2085ed

+ 19 - 0
src/Masuit.MyBlogs.Core/Controllers/PostController.cs

@@ -1116,6 +1116,25 @@ namespace Masuit.MyBlogs.Core.Controllers
             return Ok(list);
             return Ok(list);
         }
         }
 
 
+        /// <summary>
+        /// 文章访问记录图表
+        /// </summary>
+        /// <param name="id"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        [HttpGet("/post/records-chart"), MyAuthorize]
+        [ProducesResponseType((int)HttpStatusCode.OK)]
+        public async Task<IActionResult> PostVisitRecordChart(CancellationToken cancellationToken)
+        {
+            var list = await PostVisitRecordService.GetAll().Select(e => new { e.Time.Date, e.IP }).GroupBy(t => t.Date).Select(g => new
+            {
+                Date = g.Key,
+                Count = g.Count(),
+                UV = g.Select(e => e.IP).Distinct().Count()
+            }).OrderBy(a => a.Date).ToListAsync(cancellationToken);
+            return Ok(list);
+        }
+
         /// <summary>
         /// <summary>
         /// 文章访问记录分析
         /// 文章访问记录分析
         /// </summary>
         /// </summary>

+ 1 - 1
src/Masuit.MyBlogs.Core/Masuit.MyBlogs.Core.csproj

@@ -49,7 +49,7 @@
         <PackageReference Include="Collections.Pooled" Version="1.0.82" />
         <PackageReference Include="Collections.Pooled" Version="1.0.82" />
         <PackageReference Include="CSRedisCore" Version="3.8.2" />
         <PackageReference Include="CSRedisCore" Version="3.8.2" />
         <PackageReference Include="EFCoreSecondLevelCacheInterceptor" Version="3.5.1" />
         <PackageReference Include="EFCoreSecondLevelCacheInterceptor" Version="3.5.1" />
-        <PackageReference Include="Hangfire" Version="1.7.29" />
+        <PackageReference Include="Hangfire" Version="1.7.30" />
         <PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" />
         <PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" />
         <PackageReference Include="htmldiff.net-core" Version="1.3.6" />
         <PackageReference Include="htmldiff.net-core" Version="1.3.6" />
         <PackageReference Include="IP2Region" Version="1.2.0" />
         <PackageReference Include="IP2Region" Version="1.2.0" />

+ 2 - 2
src/Masuit.MyBlogs.Core/Views/Msg/Index_Admin.cshtml

@@ -109,7 +109,7 @@
                                     <header class="panel-heading">${startfloor--}# ${rows[i].IsMaster ? `<i class="icon icon-user"></i>` : ""}${rows[i].NickName}${rows[i].IsMaster ? `(管理员)` : ""} | ${rows[i].PostDate}
                                     <header class="panel-heading">${startfloor--}# ${rows[i].IsMaster ? `<i class="icon icon-user"></i>` : ""}${rows[i].NickName}${rows[i].IsMaster ? `(管理员)` : ""} | ${rows[i].PostDate}
                                         <span class="pull-right" style="font-size: 10px;">${rows[i].Status == 4 ? `<a class="label label-success" onclick="pass(${rows[i].Id})">通过</a> |` : ""} <a class="label label-danger" onclick="del(${rows[i].Id})">删除</a><span class="hidden-sm hidden-xs"> | ${GetOperatingSystem(rows[i].OperatingSystem) + " | " + GetBrowser(rows[i].Browser)}</span></span>
                                         <span class="pull-right" style="font-size: 10px;">${rows[i].Status == 4 ? `<a class="label label-success" onclick="pass(${rows[i].Id})">通过</a> |` : ""} <a class="label label-danger" onclick="del(${rows[i].Id})">删除</a><span class="hidden-sm hidden-xs"> | ${GetOperatingSystem(rows[i].OperatingSystem) + " | " + GetBrowser(rows[i].Browser)}</span></span>
                                     </header>
                                     </header>
-                                    <div class="panel-body">
+                                    <div class="panel-body line-height24">
                                         ${rows[i].Content} <a class="label label-info" href="?uid=${rows[i].Id}"><i class="icon-comment"></i></a>
                                         ${rows[i].Content} <a class="label label-info" href="?uid=${rows[i].Id}"><i class="icon-comment"></i></a>
                                         <div class="margin-top10"></div>
                                         <div class="margin-top10"></div>
                                         <div class="pull-left">
                                         <div class="pull-left">
@@ -147,7 +147,7 @@
                             ${depth}-${floor++}# ${item.IsMaster ? `<i class="icon icon-user"></i>` : ""}${item.NickName}${item.IsMaster ? `(管理员)` : ""} | ${item.PostDate}
                             ${depth}-${floor++}# ${item.IsMaster ? `<i class="icon icon-user"></i>` : ""}${item.NickName}${item.IsMaster ? `(管理员)` : ""} | ${item.PostDate}
                             <span class="pull-right hidden-sm hidden-xs" style="font-size: 10px;">${item.Status == 4 ? `<a class="label label-success" onclick="pass(${item.Id})">通过</a> |` : ""} <a class="label label-danger" onclick="del(${item.Id})">删除</a> | ${GetOperatingSystem(item.OperatingSystem) + " | " + GetBrowser(item.Browser)}</span>
                             <span class="pull-right hidden-sm hidden-xs" style="font-size: 10px;">${item.Status == 4 ? `<a class="label label-success" onclick="pass(${item.Id})">通过</a> |` : ""} <a class="label label-danger" onclick="del(${item.Id})">删除</a> | ${GetOperatingSystem(item.OperatingSystem) + " | " + GetBrowser(item.Browser)}</span>
                         </div>
                         </div>
-                        <div class="panel-body">
+                        <div class="panel-body line-height24">
                             ${item.Content}
                             ${item.Content}
                             <a class="label label-${color}" href="?uid=${item.Id}"><i class="icon-comment"></i></a>
                             <a class="label label-${color}" href="?uid=${item.Id}"><i class="icon-comment"></i></a>
                             <div class="margin-top10"></div>
                             <div class="margin-top10"></div>

+ 2 - 2
src/Masuit.MyBlogs.Core/Views/Post/Details_Admin.cshtml

@@ -411,7 +411,7 @@
 									<header class="panel-heading ${rows[i].IsMaster ? "text-red" : ""} ${rows[i].IsAuthor ? "text-bold" : ""}">${startfloor--}# ${rows[i].IsMaster ? `<i class="icon icon-user"></i>` : ""}${rows[i].NickName}${rows[i].IsMaster ? `(管理员)` : ""} | ${rows[i].CommentDate}
 									<header class="panel-heading ${rows[i].IsMaster ? "text-red" : ""} ${rows[i].IsAuthor ? "text-bold" : ""}">${startfloor--}# ${rows[i].IsMaster ? `<i class="icon icon-user"></i>` : ""}${rows[i].NickName}${rows[i].IsMaster ? `(管理员)` : ""} | ${rows[i].CommentDate}
 										<span class="pull-right" style="font-size: 10px;">${rows[i].Status == 4 ? `<a class="label label-success" onclick="pass(${rows[i].Id})">通过</a> |` : ""} <a class="label label-danger" onclick="del(${rows[i].Id})">删除</a> | ${GetOperatingSystem(rows[i].OperatingSystem) + " | " + GetBrowser(rows[i].Browser)}</span>
 										<span class="pull-right" style="font-size: 10px;">${rows[i].Status == 4 ? `<a class="label label-success" onclick="pass(${rows[i].Id})">通过</a> |` : ""} <a class="label label-danger" onclick="del(${rows[i].Id})">删除</a> | ${GetOperatingSystem(rows[i].OperatingSystem) + " | " + GetBrowser(rows[i].Browser)}</span>
 									</header>
 									</header>
-									<div class="panel-body">
+									<div class="panel-body line-height24">
 										${rows[i].Content}
 										${rows[i].Content}
 										<span class="cmvote label label-info" data-id="${rows[i].Id}"><i class="icon-thumbsup"></i>(<span class="count">${rows[i].VoteCount}</span>)</span>
 										<span class="cmvote label label-info" data-id="${rows[i].Id}"><i class="icon-thumbsup"></i>(<span class="count">${rows[i].VoteCount}</span>)</span>
 										<a class="label label-info" href="?uid=${rows[i].Id}"><i class="icon-comment"></i></a><div class="margin-top10"></div>
 										<a class="label label-info" href="?uid=${rows[i].Id}"><i class="icon-comment"></i></a><div class="margin-top10"></div>
@@ -450,7 +450,7 @@
 								${depth}-${floor++}# ${item.IsMaster ? `<i class="icon icon-user"></i>` : ""}${item.NickName}${item.IsMaster ? `(管理员)` : ""} | ${item.CommentDate}
 								${depth}-${floor++}# ${item.IsMaster ? `<i class="icon icon-user"></i>` : ""}${item.NickName}${item.IsMaster ? `(管理员)` : ""} | ${item.CommentDate}
 								<span class="pull-right" style="font-size: 10px;">${item.Status == 4 ? `<a class="label label-success" onclick="pass(${item.Id})">通过</a> |` : ""} <a class="label label-danger" onclick="del(${item.Id})">删除</a> | ${GetOperatingSystem(item.OperatingSystem) + " | " + GetBrowser(item.Browser)}</span>
 								<span class="pull-right" style="font-size: 10px;">${item.Status == 4 ? `<a class="label label-success" onclick="pass(${item.Id})">通过</a> |` : ""} <a class="label label-danger" onclick="del(${item.Id})">删除</a> | ${GetOperatingSystem(item.OperatingSystem) + " | " + GetBrowser(item.Browser)}</span>
 							</div>
 							</div>
-							<div class="panel-body">
+							<div class="panel-body line-height24">
 								${item.Content}
 								${item.Content}
 								<span class="cmvote label label-${color}" data-id="${item.Id}"><i class="icon-thumbsup"></i>(<span class="count">${item.VoteCount}</span>)</span>
 								<span class="cmvote label label-${color}" data-id="${item.Id}"><i class="icon-thumbsup"></i>(<span class="count">${item.VoteCount}</span>)</span>
 								<a class="label label-${color}" href="?uid=${item.Id}"><i class="icon-comment"></i></a>
 								<a class="label label-${color}" href="?uid=${item.Id}"><i class="icon-comment"></i></a>

+ 2 - 2
src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/article.js

@@ -437,7 +437,7 @@ function loadParentComments(data) {
 								<header class="panel-heading ${rows[i].IsMaster ? "text-red" : ""} ${rows[i].IsAuthor ? "text-bold" : ""}">${startfloor--}# ${rows[i].IsMaster ? `<i class="icon icon-user"></i>` : ""}${rows[i].NickName}${rows[i].IsMaster ? `(管理员)` : ""} | ${rows[i].CommentDate}
 								<header class="panel-heading ${rows[i].IsMaster ? "text-red" : ""} ${rows[i].IsAuthor ? "text-bold" : ""}">${startfloor--}# ${rows[i].IsMaster ? `<i class="icon icon-user"></i>` : ""}${rows[i].NickName}${rows[i].IsMaster ? `(管理员)` : ""} | ${rows[i].CommentDate}
 									<span class="pull-right hidden-sm hidden-xs" style="font-size: 10px;">${GetOperatingSystem(rows[i].OperatingSystem) + " | " + GetBrowser(rows[i].Browser)}</span>
 									<span class="pull-right hidden-sm hidden-xs" style="font-size: 10px;">${GetOperatingSystem(rows[i].OperatingSystem) + " | " + GetBrowser(rows[i].Browser)}</span>
 								</header>
 								</header>
-								<div class="panel-body">
+								<div class="panel-body line-height24">
 									${rows[i].Content} 
 									${rows[i].Content} 
 									<span class="cmvote label label-info" data-id="${rows[i].Id}"><i class="icon-thumbsup"></i>(<span>${rows[i].VoteCount}</span>)</span>
 									<span class="cmvote label label-info" data-id="${rows[i].Id}"><i class="icon-thumbsup"></i>(<span>${rows[i].VoteCount}</span>)</span>
 									<a class="label label-info" href="?uid=${rows[i].Id}"><i class="icon-comment"></i></a>
 									<a class="label label-info" href="?uid=${rows[i].Id}"><i class="icon-comment"></i></a>
@@ -469,7 +469,7 @@ function loadComments(comments, depth = 0) {
 							${depth}-${floor++}# ${item.IsMaster ?`<i class="icon icon-user"></i>`:""}${item.NickName}${item.IsMaster ?`(管理员)`:""} | ${item.CommentDate}
 							${depth}-${floor++}# ${item.IsMaster ?`<i class="icon icon-user"></i>`:""}${item.NickName}${item.IsMaster ?`(管理员)`:""} | ${item.CommentDate}
 							<span class="pull-right hidden-sm hidden-xs" style="font-size: 10px;">${GetOperatingSystem(item.OperatingSystem) + " | " + GetBrowser(item.Browser)}</span>
 							<span class="pull-right hidden-sm hidden-xs" style="font-size: 10px;">${GetOperatingSystem(item.OperatingSystem) + " | " + GetBrowser(item.Browser)}</span>
 						</div>
 						</div>
-						<div class="panel-body">
+						<div class="panel-body line-height24">
 							${item.Content} 
 							${item.Content} 
 							<span class="cmvote label label-${color}" data-id="${item.Id}"><i class="icon-thumbsup"></i>(<span>${item.VoteCount}</span>)</span>
 							<span class="cmvote label label-${color}" data-id="${item.Id}"><i class="icon-thumbsup"></i>(<span>${item.VoteCount}</span>)</span>
 							<a class="label label-${color}" href="?uid=${item.Id}"><i class="icon-comment"></i></a>
 							<a class="label label-${color}" href="?uid=${item.Id}"><i class="icon-comment"></i></a>

+ 2 - 2
src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/leavemsg.js

@@ -272,7 +272,7 @@ function loadParentMsgs(data) {
 									<header class="panel-heading">${startfloor--}# ${rows[i].IsMaster? `<i class="icon icon-user"></i>` : ""}${rows[i].NickName}${rows  [i].IsMaster ? `(管理员)` : ""} | ${rows[i].PostDate}
 									<header class="panel-heading">${startfloor--}# ${rows[i].IsMaster? `<i class="icon icon-user"></i>` : ""}${rows[i].NickName}${rows  [i].IsMaster ? `(管理员)` : ""} | ${rows[i].PostDate}
 										<span class="pull-right hidden-sm hidden-xs" style="font-size: 10px;">${GetOperatingSystem(rows[i].OperatingSystem) + " | " + GetBrowser(rows[i].Browser)}</span>
 										<span class="pull-right hidden-sm hidden-xs" style="font-size: 10px;">${GetOperatingSystem(rows[i].OperatingSystem) + " | " + GetBrowser(rows[i].Browser)}</span>
 									</header>
 									</header>
-									<div class="panel-body">
+									<div class="panel-body line-height24">
 										${rows[i].Content}
 										${rows[i].Content}
 										<a class="label label-info" href="?uid=${rows[i].Id}"><i class="icon-comment"></i></a>
 										<a class="label label-info" href="?uid=${rows[i].Id}"><i class="icon-comment"></i></a>
 										${loadMsgs(rows[i].Children)}
 										${loadMsgs(rows[i].Children)}
@@ -303,7 +303,7 @@ function loadMsgs(msg, depth = 0) {
 							${depth}-${floor++}# ${item.IsMaster ? `<i class="icon icon-user"></i>` : ""}${item.NickName}${item.IsMaster ? `(管理员)` : ""} | ${item.PostDate}<span class="pull-right hidden-sm hidden-xs" style="font-size: 10px;">${GetOperatingSystem(item.OperatingSystem) + " | " + GetBrowser(item.Browser)}
 							${depth}-${floor++}# ${item.IsMaster ? `<i class="icon icon-user"></i>` : ""}${item.NickName}${item.IsMaster ? `(管理员)` : ""} | ${item.PostDate}<span class="pull-right hidden-sm hidden-xs" style="font-size: 10px;">${GetOperatingSystem(item.OperatingSystem) + " | " + GetBrowser(item.Browser)}
 							</span>
 							</span>
 						</div>
 						</div>
-						<div class="panel-body">
+						<div class="panel-body line-height24">
 							${item.Content}
 							${item.Content}
 							<a class="label label-${color}" href="?uid=${item.Id}"><i class="icon-comment"></i></a>
 							<a class="label label-${color}" href="?uid=${item.Id}"><i class="icon-comment"></i></a>
 							${loadMsgs(item.Children, depth)}
 							${loadMsgs(item.Children, depth)}

+ 1 - 0
src/Masuit.MyBlogs.Core/wwwroot/ng-views/app/route.config.js

@@ -26,6 +26,7 @@ myApp.config([
                             {
                             {
                                 files: [
                                 files: [
                                     "https://maplemei.gitee.io/xm-select/xm-select.js",
                                     "https://maplemei.gitee.io/xm-select/xm-select.js",
+                                    "https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js",
                                 ],
                                 ],
                                 cache: true
                                 cache: true
                             }, cpath + "/post.js"
                             }, cpath + "/post.js"

+ 83 - 0
src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/post.js

@@ -272,6 +272,7 @@
             });
             });
         });
         });
     }
     }
+    
     self.insight= function(row) {
     self.insight= function(row) {
         layer.full(layer.open({
         layer.full(layer.open({
           type: 2,
           type: 2,
@@ -281,6 +282,88 @@
           content: '/'+row.Id+'/insight'
           content: '/'+row.Id+'/insight'
         }));
         }));
     }
     }
+
+    window.fetch("/post/records-chart", {
+		credentials: 'include',
+		method: 'GET',
+		mode: 'cors'
+	}).then(function(response) {
+		return response.json();
+	}).then(function(res) {
+		var pv = [];
+		var uv = [];
+		for (let item of res) {
+			pv.push([Date.parse(item.Date), item.Count]);
+			uv.push([Date.parse(item.Date),item.UV]);
+		}
+		var chartDom = document.getElementById('chart');
+		var myChart = echarts.init(chartDom);
+		var option = {
+			tooltip: {
+				trigger: 'axis',
+				position: function(pt) {
+					return [pt[0], '10%'];
+				}
+			},
+			title: {
+				left: 'center',
+				text: '最近90天阅读量趋势,日均:' + (res.reduce((acr, cur) => acr + cur.Count, 0) / (new Date() - new Date(res[0].Date)) * (1000 * 60 * 60 * 24)).toFixed(2)
+			},
+			xAxis: {
+				type: 'time',
+				axisLabel: {
+					formatter:function (value){
+						var dt=new Date(value);
+						return dt.toLocaleDateString();
+					}
+				}
+			},
+			yAxis: {
+				type: 'value'
+			},
+			series: [
+				{
+					name: 'PV',
+					type: 'line',
+					smooth: true,
+					symbol: 'none',
+					areaStyle: {},
+					data: pv,
+					markPoint: {
+						data: [
+							{ type: 'max', name: '最大值' },
+							{ type: 'min', name: '最小值' }
+						]
+					},
+					markLine: {
+						data: [
+							{ type: 'average', name: '平均值' }
+						]
+					}
+				},
+				{
+					name: 'UV',
+					type: 'line',
+					smooth: true,
+					symbol: 'none',
+					areaStyle: {},
+					data: uv,
+					markPoint: {
+						data: [
+							{ type: 'max', name: '最大值' },
+							{ type: 'min', name: '最小值' }
+						]
+					},
+					markLine: {
+						data: [
+							{ type: 'average', name: '平均值' }
+						]
+					}
+				}
+			]
+		};
+		myChart.setOption(option);
+	});
 }]);
 }]);
 myApp.controller("writeblog", ["$scope", "$http", "$timeout","$location", function ($scope, $http, $timeout,$location) {
 myApp.controller("writeblog", ["$scope", "$http", "$timeout","$location", function ($scope, $http, $timeout,$location) {
     UEDITOR_CONFIG.initialFrameHeight=null;
     UEDITOR_CONFIG.initialFrameHeight=null;

+ 3 - 0
src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/post/postlist.html

@@ -210,4 +210,7 @@
             </tbody>
             </tbody>
         </table>
         </table>
     </div>
     </div>
+</div>
+<div class="row">
+    <div id="chart" style="height: 500px"></div>
 </div>
 </div>