diff --git a/Bootstrap.Admin/Controllers/AdminController.cs b/Bootstrap.Admin/Controllers/AdminController.cs index a4ea5266..3d864ccd 100644 --- a/Bootstrap.Admin/Controllers/AdminController.cs +++ b/Bootstrap.Admin/Controllers/AdminController.cs @@ -52,6 +52,12 @@ namespace Bootstrap.Admin.Controllers /// public ActionResult Logs() => View(new NavigatorBarModel(this)); + /// + /// + /// + /// + public ActionResult Logins() => View(new NavigatorBarModel(this)); + /// /// /// diff --git a/Bootstrap.Admin/Controllers/Api/LoginController.cs b/Bootstrap.Admin/Controllers/Api/LoginController.cs index 425a276b..592163b2 100644 --- a/Bootstrap.Admin/Controllers/Api/LoginController.cs +++ b/Bootstrap.Admin/Controllers/Api/LoginController.cs @@ -3,6 +3,8 @@ using Bootstrap.Security; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using System.Linq; namespace Bootstrap.Admin.Controllers.Api { @@ -16,6 +18,13 @@ namespace Bootstrap.Admin.Controllers.Api [ApiController] public class LoginController : ControllerBase { + /// + /// 获得登录历史记录 + /// + /// + [HttpGet] + public IEnumerable Get() => LoginHelper.Retrieves(); + /// /// /// diff --git a/Bootstrap.Admin/Views/Admin/Logins.cshtml b/Bootstrap.Admin/Views/Admin/Logins.cshtml new file mode 100644 index 00000000..3caa048f --- /dev/null +++ b/Bootstrap.Admin/Views/Admin/Logins.cshtml @@ -0,0 +1,29 @@ +@model NavigatorBarModel +@{ + ViewBag.Title = "登录日志"; +} +@section css { + + + + + + +} +@section javascript { + + + + + + + + + +} +
+
在线用户
+
+
+
+
\ No newline at end of file diff --git a/Bootstrap.Admin/Views/Admin/Logs.cshtml b/Bootstrap.Admin/Views/Admin/Logs.cshtml index a93083ea..ca009ff5 100644 --- a/Bootstrap.Admin/Views/Admin/Logs.cshtml +++ b/Bootstrap.Admin/Views/Admin/Logs.cshtml @@ -1,6 +1,6 @@ @model NavigatorBarModel @{ - ViewBag.Title = "系统日志"; + ViewBag.Title = "操作日志"; } @section css { diff --git a/Bootstrap.Admin/appsettings.json b/Bootstrap.Admin/appsettings.json index 15dbe2ae..276bcdbd 100644 --- a/Bootstrap.Admin/appsettings.json +++ b/Bootstrap.Admin/appsettings.json @@ -65,6 +65,7 @@ "KeepExceptionsPeriod": 12, "KeepLogsPeriod": 12, "CookieExpiresDays": 7, + "KeepLoginLogsPeriod": 7, "LongbowCache": { "Enabled": true, "CorsItems": [ diff --git a/Bootstrap.Admin/wwwroot/js/logins.js b/Bootstrap.Admin/wwwroot/js/logins.js new file mode 100644 index 00000000..5bb832eb --- /dev/null +++ b/Bootstrap.Admin/wwwroot/js/logins.js @@ -0,0 +1,31 @@ +// 登录日志 +$(function () { + var apiUrl = "api/Login"; + var $table = $('table').smartTable({ + url: apiUrl, + method: "get", + sidePagination: "client", + showToggle: false, + showRefresh: false, + showColumns: false, + columns: [ + { + title: "序号", formatter: function (value, row, index) { + var options = $table.bootstrapTable('getOptions'); + return options.pageSize * (options.pageNumber - 1) + index + 1; + } + }, + { title: "登陆名称", field: "UserName" }, + { title: "登录时间", field: "LoginTime" }, + { title: "主机", field: "Ip" }, + { title: "登录地点", field: "City" }, + { title: "浏览器", field: "Browser" }, + { title: "操作系统", field: "OS" }, + { title: "登录结果", field: "Result" } + ] + }); + + $('#refreshUsers').tooltip().on('click', function () { + $table.bootstrapTable('refresh'); + }); +}); \ No newline at end of file diff --git a/Bootstrap.DataAccess.MongoDB/LoginUser.cs b/Bootstrap.DataAccess.MongoDB/LoginUser.cs index 80859b21..5d46434f 100644 --- a/Bootstrap.DataAccess.MongoDB/LoginUser.cs +++ b/Bootstrap.DataAccess.MongoDB/LoginUser.cs @@ -1,4 +1,7 @@ -namespace Bootstrap.DataAccess.MongoDB +using MongoDB.Driver; +using System.Collections.Generic; + +namespace Bootstrap.DataAccess.MongoDB { /// /// @@ -15,5 +18,11 @@ DbManager.LoginUsers.InsertOne(user); return true; } + + /// + /// + /// + /// + public override IEnumerable Retrieves() => DbManager.LoginUsers.Find(FilterDefinition.Empty).SortByDescending(user => user.LoginTime).ToList(); } } diff --git a/Bootstrap.DataAccess/Helper/LoginHelper.cs b/Bootstrap.DataAccess/Helper/LoginHelper.cs index 31b9e057..20844f2a 100644 --- a/Bootstrap.DataAccess/Helper/LoginHelper.cs +++ b/Bootstrap.DataAccess/Helper/LoginHelper.cs @@ -1,4 +1,6 @@ -using Longbow.Data; +using Longbow.Cache; +using Longbow.Data; +using System.Collections.Generic; namespace Bootstrap.DataAccess { @@ -7,11 +9,26 @@ namespace Bootstrap.DataAccess /// public static class LoginHelper { + /// + /// + /// + public const string RetrieveLoginLogsDataKey = "LoginHelper-Retrieves"; + /// /// /// /// /// - public static bool Log(LoginUser user) => DbContextManager.Create().Log(user); + public static bool Log(LoginUser user) + { + var ret = DbContextManager.Create().Log(user); + if (ret) CacheManager.Clear(RetrieveLoginLogsDataKey); + return ret; + } + + /// + /// 查询一个月内所有登录信息 + /// + public static IEnumerable Retrieves() => CacheManager.GetOrAdd(RetrieveLoginLogsDataKey, key => DbContextManager.Create().Retrieves()); } } diff --git a/Bootstrap.DataAccess/LoginUser.cs b/Bootstrap.DataAccess/LoginUser.cs index 44f159a2..0dc50135 100644 --- a/Bootstrap.DataAccess/LoginUser.cs +++ b/Bootstrap.DataAccess/LoginUser.cs @@ -1,5 +1,8 @@ -using PetaPoco; +using Longbow; +using Longbow.Configuration; +using PetaPoco; using System; +using System.Collections.Generic; namespace Bootstrap.DataAccess { @@ -60,5 +63,16 @@ namespace Bootstrap.DataAccess db.Save(user); return true; } + + /// + /// + /// + protected const string KeepLoginLogsPeriodKey = "KeepLoginLogsPeriod"; + + /// + /// + /// + /// + public virtual IEnumerable Retrieves() => DbManager.Create().Fetch("Where LoginTime > @0 Order by LoginTime desc", DateTime.Today.AddDays(0 - LgbConvert.ReadValue(ConfigurationManager.AppSettings[KeepLoginLogsPeriodKey], 7))); } } diff --git a/DatabaseScripts/InitData.sql b/DatabaseScripts/InitData.sql index 68719da3..9062d4fe 100644 --- a/DatabaseScripts/InitData.sql +++ b/DatabaseScripts/InitData.sql @@ -45,7 +45,9 @@ INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [C INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (10, 0, N'站内消息', 100, N'fa fa-envelope', N'~/Admin/Messages', N'0') INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (11, 0, N'任务管理', 110, N'fa fa fa-tasks', N'~/Admin/Tasks', N'0') INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (12, 0, N'通知管理', 120, N'fa fa-bell', N'~/Admin/Notifications', N'0') -INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (13, 0, N'系统日志', 130, N'fa fa-gears', N'~/Admin/Logs', N'0') +INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (13, 0, N'日志管理', 130, N'fa fa-gears', N'#', N'0') +INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (20, 13, N'操作日志', 10, N'fa fa-edit', N'~/Admin/Logs', N'0') +INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (21, 13, N'登录日志', 20, N'fa fa-user-circle-o', N'~/Admin/Logins', N'0') INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (14, 0, N'在线用户', 140, N'fa fa-users', N'~/Admin/Online', N'0') INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (15, 0, N'程序异常', 150, N'fa fa-cubes', N'~/Admin/Exceptions', N'0') INSERT [dbo].[Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (16, 0, N'工具集合', 160, N'fa fa-gavel', N'#', N'0') diff --git a/DatabaseScripts/MongoDB/BootstrapAdmin.Navigations.json b/DatabaseScripts/MongoDB/BootstrapAdmin.Navigations.json index e00a5823..0fd52445 100644 --- a/DatabaseScripts/MongoDB/BootstrapAdmin.Navigations.json +++ b/DatabaseScripts/MongoDB/BootstrapAdmin.Navigations.json @@ -146,15 +146,39 @@ { "_id": ObjectId("5bd7b8445fa31256f77e4b9c"), "ParentId": "0", - "Name": "系统日志", + "Name": "日志管理", "Order": NumberInt(130), "Icon": "fa fa-gears", + "Url": "#", + "Category": "0", + "Target": "_self", + "IsResource": NumberInt(0), + "Application": "0" + }, + { + "_id": ObjectId("5bd9b3d868aa001661776f57"), + "ParentId": "5bd7b8445fa31256f77e4b9c", + "Name": "操作日志", + "Order": NumberInt(10), + "Icon": "fa fa-edit", "Url": "~/Admin/Logs", "Category": "0", "Target": "_self", "IsResource": NumberInt(0), "Application": "0" }, + { + "_id": ObjectId("5bd9b3d868aa001661776f58"), + "ParentId": "5bd7b8445fa31256f77e4b9c", + "Name": "登录日志", + "Order": NumberInt(20), + "Icon": "fa fa-user-circle-o", + "Url": "~/Admin/Logins", + "Category": "0", + "Target": "_self", + "IsResource": NumberInt(0), + "Application": "0" + }, { "_id": ObjectId("5bd7b8445fa31256f77e4b89"), "ParentId": "0", diff --git a/DatabaseScripts/MySQL/initData.sql b/DatabaseScripts/MySQL/initData.sql index d02e41e7..f06c3b2d 100644 --- a/DatabaseScripts/MySQL/initData.sql +++ b/DatabaseScripts/MySQL/initData.sql @@ -42,7 +42,9 @@ INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUE INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (10, 0, '站内消息', 100, 'fa fa-envelope', '~/Admin/Messages', '0'); INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (11, 0, '任务管理', 110, 'fa fa fa-tasks', '~/Admin/Tasks', '0'); INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (12, 0, '通知管理', 120, 'fa fa-bell', '~/Admin/Notifications', '0'); -INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (13, 0, '系统日志', 130, 'fa fa-gears', '~/Admin/Logs', '0'); +INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (13, 0, '系统日志', 130, 'fa fa-gears', '#', '0'); +INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (20, 13, '操作日志', 10, 'fa fa-edit', '~/Admin/Logs', '0'); +INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (21, 13, '登录日志', 20, 'fa fa-user-circle-o', '~/Admin/Logins', '0'); INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (14, 0, '在线用户', 140, 'fa fa-users', '~/Admin/Online', '0'); INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (15, 0, '程序异常', 150, 'fa fa-cubes', '~/Admin/Exceptions', '0'); INSERT INTO Navigations (ID, ParentId, Name, `Order`, Icon, Url, Category) VALUES (16, 0, '工具集合', 160, 'fa fa-gavel', '#', '0'); diff --git a/DatabaseScripts/Postgresql/initData.sql b/DatabaseScripts/Postgresql/initData.sql index abbcc8ab..e9cd5335 100644 --- a/DatabaseScripts/Postgresql/initData.sql +++ b/DatabaseScripts/Postgresql/initData.sql @@ -42,7 +42,9 @@ INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0 INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '站内消息', 100, 'fa fa-envelope', '~/Admin/Messages', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '任务管理', 110, 'fa fa fa-tasks', '~/Admin/Tasks', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '通知管理', 120, 'fa fa-bell', '~/Admin/Notifications', '0'); -INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '系统日志', 130, 'fa fa-gears', '~/Admin/Logs', '0'); +INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '系统日志', 130, 'fa fa-gears', '#', '0'); +INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (currval('navigations_id_seq') - 1, 0, '操作日志', 10, 'fa fa-edit', '~/Admin/Logs', '0'); +INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (currval('navigations_id_seq') - 2, 0, '登录日志', 20, 'fa fa-user-circle-o', '~/Admin/Logins', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '在线用户', 140, 'fa fa-users', '~/Admin/Online', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '程序异常', 150, 'fa fa-cubes', '~/Admin/Exceptions', '0'); INSERT INTO Navigations (ParentId, Name, "order", Icon, Url, Category) VALUES (0, '工具集合', 160, 'fa fa-gavel', '#', '0'); diff --git a/DatabaseScripts/SQLite/InitData.sql b/DatabaseScripts/SQLite/InitData.sql index fadaf135..b0137ba0 100644 --- a/DatabaseScripts/SQLite/InitData.sql +++ b/DatabaseScripts/SQLite/InitData.sql @@ -39,7 +39,9 @@ INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Ca INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (10, 0, '站内消息', 100, 'fa fa-envelope', '~/Admin/Messages', '0'); INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (11, 0, '任务管理', 110, 'fa fa fa-tasks', '~/Admin/Tasks', '0'); INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (12, 0, '通知管理', 120, 'fa fa-bell', '~/Admin/Notifications', '0'); -INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (13, 0, '系统日志', 130, 'fa fa-gears', '~/Admin/Logs', '0'); +INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (13, 0, '系统日志', 130, 'fa fa-gears', '#', '0'); +INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (20, 13, '操作日志', 10, 'fa fa-edit', '~/Admin/Logs', '0'); +INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (21, 13, '登录日志', 20, 'fa fa-user-circle-o', '~/Admin/Logins', '0'); INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (14, 0, '在线用户', 140, 'fa fa-users', '~/Admin/Online', '0'); INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (15, 0, '程序异常', 150, 'fa fa-cubes', '~/Admin/Exceptions', '0'); INSERT INTO [Navigations] ([ID], [ParentId], [Name], [Order], [Icon], [Url], [Category]) VALUES (16, 0, '工具集合', 160, 'fa fa-gavel', '#', '0'); diff --git a/UnitTest/Bootstrap.Admin/Api/LoginTest.cs b/UnitTest/Bootstrap.Admin/Api/LoginTest.cs index 7c275c2a..6e18b793 100644 --- a/UnitTest/Bootstrap.Admin/Api/LoginTest.cs +++ b/UnitTest/Bootstrap.Admin/Api/LoginTest.cs @@ -1,4 +1,6 @@ -using System.Net.Http; +using Bootstrap.DataAccess; +using System.Collections.Generic; +using System.Net.Http; using Xunit; namespace Bootstrap.Admin.Api @@ -7,6 +9,13 @@ namespace Bootstrap.Admin.Api { public LoginTest(BAWebHost factory) : base(factory, "api/Login") { } + [Fact] + public async void Login_Get() + { + var users = await Client.GetAsJsonAsync>(); + Assert.NotEmpty(users); + } + [Fact] public async void Login_Ok() { diff --git a/UnitTest/Bootstrap.Admin/Controllers/AdminTest.cs b/UnitTest/Bootstrap.Admin/Controllers/AdminTest.cs index 80acdc5b..9a58e1cb 100644 --- a/UnitTest/Bootstrap.Admin/Controllers/AdminTest.cs +++ b/UnitTest/Bootstrap.Admin/Controllers/AdminTest.cs @@ -14,7 +14,8 @@ namespace Bootstrap.Admin.Controllers [InlineData("Dicts", "字典表维护")] [InlineData("Roles", "角色管理")] [InlineData("Menus", "菜单管理")] - [InlineData("Logs", "系统日志")] + [InlineData("Logs", "操作日志")] + [InlineData("Logins", "登录日志")] [InlineData("FAIcon", "图标集")] [InlineData("IconView", "图标分类")] [InlineData("Settings", "网站设置")]