Pārlūkot izejas kodu

1.用户体验优化
2.清理代码,修正bug
3.评论留言优化
4.文章合并映射时不映射状态字段
5.缓存优化
6.文章合并自动审核

懒得勤快 6 gadi atpakaļ
vecāks
revīzija
29addabffc
98 mainītis faili ar 4696 papildinājumiem un 5433 dzēšanām
  1. 3409 3396
      database/mysql/myblogs.sql
  2. 0 0
      src/Masuit.MyBlogs.Core/App_Data/ban.txt
  3. 41 25
      src/Masuit.MyBlogs.Core/Common/CommonHelper.cs
  4. 0 118
      src/Masuit.MyBlogs.Core/Common/DocumentConvert.cs
  5. 6 4
      src/Masuit.MyBlogs.Core/Configs/MappingProfile.cs
  6. 24 31
      src/Masuit.MyBlogs.Core/Controllers/AdminController.cs
  7. 1 1
      src/Masuit.MyBlogs.Core/Controllers/BannerController.cs
  8. 29 32
      src/Masuit.MyBlogs.Core/Controllers/BaseController.cs
  9. 1 0
      src/Masuit.MyBlogs.Core/Controllers/CategoryController.cs
  10. 91 81
      src/Masuit.MyBlogs.Core/Controllers/CommentController.cs
  11. 104 3
      src/Masuit.MyBlogs.Core/Controllers/ErrorController.cs
  12. 31 47
      src/Masuit.MyBlogs.Core/Controllers/HomeController.cs
  13. 57 75
      src/Masuit.MyBlogs.Core/Controllers/LinksController.cs
  14. 1 2
      src/Masuit.MyBlogs.Core/Controllers/LoginController.cs
  15. 3 4
      src/Masuit.MyBlogs.Core/Controllers/MenuController.cs
  16. 14 13
      src/Masuit.MyBlogs.Core/Controllers/MergeController.cs
  17. 9 22
      src/Masuit.MyBlogs.Core/Controllers/MiscController.cs
  18. 65 66
      src/Masuit.MyBlogs.Core/Controllers/MsgController.cs
  19. 45 49
      src/Masuit.MyBlogs.Core/Controllers/NoticeController.cs
  20. 183 311
      src/Masuit.MyBlogs.Core/Controllers/PostController.cs
  21. 9 9
      src/Masuit.MyBlogs.Core/Controllers/SearchController.cs
  22. 6 13
      src/Masuit.MyBlogs.Core/Controllers/SeminarController.cs
  23. 20 19
      src/Masuit.MyBlogs.Core/Controllers/SubscribeController.cs
  24. 15 15
      src/Masuit.MyBlogs.Core/Controllers/SystemController.cs
  25. 47 46
      src/Masuit.MyBlogs.Core/Controllers/UploadController.cs
  26. 0 35
      src/Masuit.MyBlogs.Core/Extensions/AllowAccessFirewallAttribute.cs
  27. 27 25
      src/Masuit.MyBlogs.Core/Extensions/AuthorityAttribute.cs
  28. 0 89
      src/Masuit.MyBlogs.Core/Extensions/ExceptionMiddleware.cs
  29. 34 13
      src/Masuit.MyBlogs.Core/Extensions/FirewallAttribute.cs
  30. 0 85
      src/Masuit.MyBlogs.Core/Extensions/FirewallMiddleware.cs
  31. 37 30
      src/Masuit.MyBlogs.Core/Extensions/Hangfire/HangfireBackJob.cs
  32. 7 7
      src/Masuit.MyBlogs.Core/Extensions/MyExceptionFilter.cs
  33. 1 1
      src/Masuit.MyBlogs.Core/Extensions/UEditor/UeditorConfig.cs
  34. 1 1
      src/Masuit.MyBlogs.Core/Hubs/MyHub.cs
  35. 22 1
      src/Masuit.MyBlogs.Core/Infrastructure/Services/PostService.cs
  36. 31 7
      src/Masuit.MyBlogs.Core/Masuit.MyBlogs.Core.csproj
  37. 1 31
      src/Masuit.MyBlogs.Core/Models/DTO/CommentInputDto.cs
  38. 10 0
      src/Masuit.MyBlogs.Core/Models/DTO/CommentOutputDto.cs
  39. 0 20
      src/Masuit.MyBlogs.Core/Models/DTO/LeaveMessageInputDto.cs
  40. 10 0
      src/Masuit.MyBlogs.Core/Models/DTO/LeaveMessageOutputDto.cs
  41. 5 0
      src/Masuit.MyBlogs.Core/Models/Entity/Comment.cs
  42. 5 0
      src/Masuit.MyBlogs.Core/Models/Entity/LeaveMessage.cs
  43. 10 0
      src/Masuit.MyBlogs.Core/Models/ViewModel/CommentViewModel.cs
  44. 10 0
      src/Masuit.MyBlogs.Core/Models/ViewModel/LeaveMessageViewModel.cs
  45. 18 19
      src/Masuit.MyBlogs.Core/Startup.cs
  46. 2 2
      src/Masuit.MyBlogs.Core/Views/Error/AccessDeny.cshtml
  47. 1 0
      src/Masuit.MyBlogs.Core/Views/Msg/Index.cshtml
  48. 40 30
      src/Masuit.MyBlogs.Core/Views/Msg/Index_Admin.cshtml
  49. 11 7
      src/Masuit.MyBlogs.Core/Views/Post/Details.cshtml
  50. 14 8
      src/Masuit.MyBlogs.Core/Views/Post/Details_Admin.cshtml
  51. 0 85
      src/Masuit.MyBlogs.Core/Views/Post/Publish_Admin.cshtml
  52. 1 1
      src/Masuit.MyBlogs.Core/Views/Search/Search.cshtml
  53. 2 12
      src/Masuit.MyBlogs.Core/Views/Shared/_Layout.cshtml
  54. 6 0
      src/Masuit.MyBlogs.Core/bundleconfig.json
  55. 1 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/css/modules/code.css
  56. 1 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/css/modules/laydate/default/laydate.css
  57. 1 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/css/modules/layer/default/layer.css
  58. BIN
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/font/iconfont.eot
  59. 1 8
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/font/iconfont.svg
  60. BIN
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/font/iconfont.ttf
  61. BIN
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/font/iconfont.woff
  62. BIN
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/font/iconfont.woff2
  63. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/carousel.js
  64. 1 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/code.js
  65. 1 0
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/colorpicker.js
  66. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/element.js
  67. 1 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/flow.js
  68. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/form.js
  69. 1 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/jquery.js
  70. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/laydate.js
  71. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/layedit.js
  72. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/layer.js
  73. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/laypage.js
  74. 1 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/laytpl.js
  75. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/mobile.js
  76. 1 0
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/rate.js
  77. 1 0
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/slider.js
  78. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/table.js
  79. 1 0
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/transfer.js
  80. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/tree.js
  81. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/upload.js
  82. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/util.js
  83. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/layui.all.js
  84. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/layui.js
  85. 0 347
      src/Masuit.MyBlogs.Core/wwwroot/Assets/timeline.css
  86. 59 0
      src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/article.js
  87. 0 0
      src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/article.min.js
  88. 57 0
      src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/leavemsg.js
  89. 0 0
      src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/leavemsg.min.js
  90. 0 116
      src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/scripts.js
  91. 4 4
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/post.js
  92. 0 0
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/post.min.js
  93. 2 2
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/post/edit.html
  94. 2 2
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/post/writeblog.html
  95. 43 22
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/system/firewall.html
  96. 0 10
      src/Masuit.MyBlogs.Core/wwwroot/template/bugfeed.html
  97. 0 10
      src/Masuit.MyBlogs.Core/wwwroot/template/bugreport.html
  98. BIN
      src/packages/Aspose.Words.18.11.0.nupkg

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 3409 - 3396
database/mysql/myblogs.sql


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
src/Masuit.MyBlogs.Core/App_Data/ban.txt


+ 41 - 25
src/Masuit.MyBlogs.Core/Common/CommonHelper.cs

@@ -10,7 +10,8 @@ using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
-
+using System.Net;
+using System.Threading;
 #if !DEBUG
 using Masuit.MyBlogs.Core.Models.ViewModel;
 using Masuit.Tools.Models;
@@ -25,24 +26,32 @@ namespace Masuit.MyBlogs.Core.Common
     {
         static CommonHelper()
         {
-            BanRegex = File.ReadAllText(Path.Combine(AppContext.BaseDirectory + "App_Data", "ban.txt"));
-            ModRegex = File.ReadAllText(Path.Combine(AppContext.BaseDirectory + "App_Data", "mod.txt"));
-            DenyIP = File.ReadAllText(Path.Combine(AppContext.BaseDirectory + "App_Data", "denyip.txt"));
-            string[] lines = File.ReadAllLines(Path.Combine(AppContext.BaseDirectory + "App_Data", "DenyIPRange.txt"));
-            DenyIPRange = new Dictionary<string, string>();
-            foreach (string line in lines)
+            ThreadPool.QueueUserWorkItem(s =>
             {
-                try
-                {
-                    var strs = line.Split(' ');
-                    DenyIPRange[strs[0]] = strs[1];
-                }
-                catch (IndexOutOfRangeException)
+                while (true)
                 {
-                }
-            }
+                    BanRegex = File.ReadAllText(Path.Combine(AppContext.BaseDirectory + "App_Data", "ban.txt"));
+                    ModRegex = File.ReadAllText(Path.Combine(AppContext.BaseDirectory + "App_Data", "mod.txt"));
+                    DenyIP = File.ReadAllText(Path.Combine(AppContext.BaseDirectory + "App_Data", "denyip.txt"));
+                    string[] lines = File.ReadAllLines(Path.Combine(AppContext.BaseDirectory + "App_Data", "DenyIPRange.txt"));
+                    DenyIPRange = new Dictionary<string, string>();
+                    foreach (string line in lines)
+                    {
+                        try
+                        {
+                            var strs = line.Split(' ');
+                            DenyIPRange[strs[0]] = strs[1];
+                        }
+                        catch (IndexOutOfRangeException)
+                        {
+                        }
+                    }
 
-            IPWhiteList = File.ReadAllText(Path.Combine(AppContext.BaseDirectory + "App_Data", "whitelist.txt")).Split(',', ',').ToList();
+                    IPWhiteList = File.ReadAllText(Path.Combine(AppContext.BaseDirectory + "App_Data", "whitelist.txt")).Split(',', ',').ToList();
+                    Console.WriteLine("刷新公共数据...");
+                    Thread.Sleep(TimeSpan.FromMinutes(10));
+                }
+            });
         }
 
         /// <summary>
@@ -135,18 +144,25 @@ namespace Masuit.MyBlogs.Core.Common
             }
 
             bool denyed = DenyIP.Split(',').Contains(ip) || DenyIPRange.Any(kv => kv.Key.StartsWith(ip.Split('.')[0]) && ip.IpAddressInRange(kv.Key, kv.Value));
-            if (SystemSettings.GetOrAdd("EnableDenyArea", "false") == "true")
+            if (SystemSettings.GetOrAdd("EnableDenyArea", "false") == "false")
             {
-                using (DbSearcher searcher = new DbSearcher(Path.Combine(AppContext.BaseDirectory + "App_Data", "ip2region.db")))
-                {
-                    var pos = searcher.MemorySearch(ip).Region;
-                    string[] region = pos.Split("|");
-                    string[] denyAreas = SystemSettings.GetOrAdd("DenyArea", "").Split(',', ',');
-                    denyed = denyed || denyAreas.Intersect(region).Any() || pos.Contains(denyAreas);
-                }
+                return denyed;
             }
 
-            return denyed;
+            var pos = GetIPLocation(ip);
+            string[] region = pos.Split("|");
+            string[] denyAreas = SystemSettings.GetOrAdd("DenyArea", "").Split(',', ',');
+            return denyed || denyAreas.Intersect(region).Any() || pos.Contains(denyAreas);
+        }
+
+        public static string GetIPLocation(this IPAddress ip) => GetIPLocation(ip.MapToIPv4().ToString());
+
+        public static string GetIPLocation(this string ip)
+        {
+            using (var searcher = new DbSearcher(Path.Combine(AppContext.BaseDirectory + "App_Data", "ip2region.db")))
+            {
+                return searcher.MemorySearch(ip).Region;
+            }
         }
 
         /// <summary>

+ 0 - 118
src/Masuit.MyBlogs.Core/Common/DocumentConvert.cs

@@ -1,6 +1,5 @@
 using Aspose.Words;
 using System.IO;
-using System.Threading.Tasks;
 
 namespace Masuit.MyBlogs.Core.Common
 {
@@ -9,13 +8,6 @@ namespace Masuit.MyBlogs.Core.Common
     /// </summary>
     public static class DocumentConvert
     {
-        static DocumentConvert()
-        {
-            // AsposeLicense.ActivateMemoryPatching();
-        }
-
-        #region doc转html
-
         /// <summary>
         /// doc转html
         /// </summary>
@@ -27,115 +19,5 @@ namespace Masuit.MyBlogs.Core.Common
             Document doc = new Document(docPath);
             doc.Save(Path.Combine(htmlDir, index), SaveFormat.Html);
         }
-
-        /// <summary>
-        /// doc转html
-        /// </summary>
-        /// <param name="docPath">doc文件路径</param>
-        /// <param name="htmlDir">生成的html所在目录,由于生成html后会将图片都放到同级的目录下,所以用文件夹保存,默认的html文件名为index.html</param>
-        /// <param name="index">默认文档名为index.html</param>
-        public static void Doc2HtmlAsync(string docPath, string htmlDir, string index = "index.html")
-        {
-            Document doc = new Document(docPath);
-            Task.Run(() => doc.Save(Path.Combine(htmlDir, index), SaveFormat.Html));
-        }
-
-        #endregion doc转html
-
-        #region doc转pdf
-
-        /// <summary>
-        /// doc转pdf
-        /// </summary>
-        /// <param name="docPath">doc源文件</param>
-        /// <param name="pdfPath">目标pdf文件</param>
-        /// <param name="paperSize">纸张大小</param>
-        /// <param name="leftMargin">左边距</param>
-        /// <param name="rightMargin">右边距</param>
-        /// <param name="topMargin">上边距</param>
-        /// <param name="bottomMargin">下边距</param>
-        public static void Doc2Pdf(string docPath, string pdfPath, PaperSize paperSize = PaperSize.A4, double leftMargin = 20, double rightMargin = 20, double topMargin = 20, double bottomMargin = 20)
-        {
-            Document doc = new Document(docPath);
-            PageSetup pageSetup = new DocumentBuilder(doc).PageSetup;
-            pageSetup.PaperSize = paperSize;
-            pageSetup.LeftMargin = leftMargin;
-            pageSetup.RightMargin = rightMargin;
-            pageSetup.TopMargin = topMargin;
-            pageSetup.BottomMargin = bottomMargin;
-            doc.Save(pdfPath, SaveFormat.Pdf);
-        }
-
-        /// <summary>
-        /// doc转pdf
-        /// </summary>
-        /// <param name="docPath">doc源文件</param>
-        /// <param name="pdfPath">目标pdf文件</param>
-        /// <param name="paperSize">纸张大小</param>
-        /// <param name="leftMargin">左边距</param>
-        /// <param name="rightMargin">右边距</param>
-        /// <param name="topMargin">上边距</param>
-        /// <param name="bottomMargin">下边距</param>
-        public static void Doc2PdfAsync(string docPath, string pdfPath, PaperSize paperSize = PaperSize.A4, double leftMargin = 20, double rightMargin = 20, double topMargin = 20, double bottomMargin = 20)
-        {
-            Document doc = new Document(docPath);
-            PageSetup pageSetup = new DocumentBuilder(doc).PageSetup;
-            pageSetup.PaperSize = paperSize;
-            pageSetup.LeftMargin = leftMargin;
-            pageSetup.RightMargin = rightMargin;
-            pageSetup.TopMargin = topMargin;
-            pageSetup.BottomMargin = bottomMargin;
-            Task.Run(() => doc.Save(pdfPath, SaveFormat.Pdf));
-        }
-
-        #endregion doc转pdf
-
-        #region html转Word
-
-        /// <summary>
-        /// html转Word
-        /// </summary>
-        /// <param name="htmlPath">html源文件</param>
-        /// <param name="docPath">目标doc文件</param>
-        /// <param name="paperSize">纸张大小,默认A4纸</param>
-        /// <param name="leftMargin">左边距,默认10</param>
-        /// <param name="rightMargin">右边距,默认10</param>
-        /// <param name="topMargin">上边距,默认10</param>
-        /// <param name="bottomMargin">下边距,默认10</param>
-        public static void Html2Word(string htmlPath, string docPath, PaperSize paperSize = PaperSize.A4, double leftMargin = 20, double rightMargin = 20, double topMargin = 20, double bottomMargin = 20)
-        {
-            Document doc = new Document(htmlPath);
-            PageSetup pageSetup = new DocumentBuilder(doc).PageSetup;
-            pageSetup.PaperSize = paperSize;
-            pageSetup.LeftMargin = leftMargin;
-            pageSetup.RightMargin = rightMargin;
-            pageSetup.TopMargin = topMargin;
-            pageSetup.BottomMargin = bottomMargin;
-            doc.Save(docPath, SaveFormat.Doc);
-        }
-
-        /// <summary>
-        /// html转Word
-        /// </summary>
-        /// <param name="htmlPath">html源文件</param>
-        /// <param name="docPath">目标doc文件</param>
-        /// <param name="paperSize">纸张大小,默认A4纸</param>
-        /// <param name="leftMargin">左边距,默认10</param>
-        /// <param name="rightMargin">右边距,默认10</param>
-        /// <param name="topMargin">上边距,默认10</param>
-        /// <param name="bottomMargin">下边距,默认10</param>
-        public static void Html2WordAsync(string htmlPath, string docPath, PaperSize paperSize = PaperSize.A4, double leftMargin = 20, double rightMargin = 20, double topMargin = 20, double bottomMargin = 20)
-        {
-            Document doc = new Document(htmlPath);
-            PageSetup pageSetup = new DocumentBuilder(doc).PageSetup;
-            pageSetup.PaperSize = paperSize;
-            pageSetup.LeftMargin = leftMargin;
-            pageSetup.RightMargin = rightMargin;
-            pageSetup.TopMargin = topMargin;
-            pageSetup.BottomMargin = bottomMargin;
-            Task.Run(() => doc.Save(docPath, SaveFormat.Doc));
-        }
-
-        #endregion html转Word
     }
 }

+ 6 - 4
src/Masuit.MyBlogs.Core/Configs/MappingProfile.cs

@@ -3,6 +3,7 @@ using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.Entity;
 using Masuit.MyBlogs.Core.Models.Enum;
 using Masuit.MyBlogs.Core.Models.ViewModel;
+using Masuit.Tools.Systems;
 using System.Linq;
 
 namespace Masuit.MyBlogs.Core.Configs
@@ -22,12 +23,12 @@ namespace Masuit.MyBlogs.Core.Configs
             CreateMap<Category, CategoryOutputDto>().ForMember(c => c.TotalPostCount, e => e.MapFrom(c => c.Post.Count)).ForMember(c => c.PendedPostCount, e => e.MapFrom(c => c.Post.Count())).ReverseMap();
             CreateMap<CategoryInputDto, CategoryOutputDto>().ReverseMap();
 
-            CreateMap<Comment, CommentInputDto>().ReverseMap();
+            CreateMap<CommentInputDto, Comment>().ForMember(c => c.Status, e => e.MapFrom(c => Status.Pending)).ReverseMap();
             CreateMap<Comment, CommentOutputDto>().ReverseMap();
             CreateMap<CommentInputDto, CommentOutputDto>().ReverseMap();
             CreateMap<Comment, CommentViewModel>().ForMember(c => c.CommentDate, e => e.MapFrom(c => c.CommentDate.ToString("yyyy-MM-dd HH:mm:ss"))).ReverseMap();
 
-            CreateMap<LeaveMessage, LeaveMessageInputDto>().ReverseMap();
+            CreateMap<LeaveMessageInputDto, LeaveMessage>().ForMember(c => c.Status, e => e.MapFrom(c => Status.Pending)).ReverseMap();
             CreateMap<LeaveMessage, LeaveMessageOutputDto>().ReverseMap();
             CreateMap<LeaveMessageInputDto, LeaveMessageOutputDto>().ReverseMap();
             CreateMap<LeaveMessage, LeaveMessageViewModel>().ForMember(l => l.PostDate, e => e.MapFrom(l => l.PostDate.ToString("yyyy-MM-dd HH:mm:ss"))).ReverseMap();
@@ -57,6 +58,7 @@ namespace Masuit.MyBlogs.Core.Configs
             CreateMap<PostInputDto, PostOutputDto>().ReverseMap();
             CreateMap<PostHistoryVersion, PostOutputDto>().ForMember(p => p.CategoryName, e => e.MapFrom(p => p.Category.Name)).ReverseMap();
             CreateMap<Post, PostViewModel>().ForMember(p => p.CategoryName, e => e.MapFrom(p => p.Category.Name)).ForMember(p => p.PostDate, e => e.MapFrom(p => p.PostDate.ToString("yyyy-MM-dd HH:mm:ss"))).ForMember(p => p.ModifyDate, e => e.MapFrom(p => p.ModifyDate.ToString("yyyy-MM-dd HH:mm:ss"))).ReverseMap();
+            CreateMap<Post, PostDataModel>().ForMember(p => p.ModifyDate, e => e.MapFrom(p => p.ModifyDate.ToString("yyyy-MM-dd HH:mm"))).ForMember(p => p.PostDate, e => e.MapFrom(p => p.PostDate.ToString("yyyy-MM-dd HH:mm"))).ForMember(p => p.Status, e => e.MapFrom(p => p.Status.GetDisplay())).ForMember(p => p.ModifyCount, e => e.MapFrom(p => p.PostHistoryVersion.Count)).ForMember(p => p.ViewCount, e => e.MapFrom(p => p.TotalViewCount));
 
             CreateMap<SearchDetails, SearchDetailsInputDto>().ReverseMap();
             CreateMap<SearchDetails, SearchDetailsOutputDto>().ReverseMap();
@@ -76,10 +78,10 @@ namespace Masuit.MyBlogs.Core.Configs
 
             CreateMap<PostMergeRequestInputDtoBase, PostMergeRequest>().ForMember(p => p.Id, e => e.Ignore()).ForMember(p => p.MergeState, e => e.Ignore()).ReverseMap();
             CreateMap<PostMergeRequestInputDto, PostMergeRequest>().ForMember(p => p.Id, e => e.Ignore()).ForMember(p => p.MergeState, e => e.Ignore()).ReverseMap();
-            CreateMap<PostMergeRequestInputDto, Post>().ForMember(p => p.Id, e => e.Ignore()).ReverseMap();
+            CreateMap<PostMergeRequestInputDto, Post>().ForMember(p => p.Id, e => e.Ignore()).ForMember(p => p.Status, e => e.Ignore()).ReverseMap();
             CreateMap<PostMergeRequest, PostMergeRequestOutputDtoBase>().ForMember(p => p.PostTitle, e => e.MapFrom(r => r.Post.Title));
             CreateMap<PostMergeRequest, PostMergeRequestOutputDto>().ForMember(p => p.PostTitle, e => e.MapFrom(r => r.Post.Title));
-            CreateMap<PostMergeRequest, Post>().ForMember(p => p.Id, e => e.Ignore()).ReverseMap();
+            CreateMap<PostMergeRequest, Post>().ForMember(p => p.Id, e => e.Ignore()).ForMember(p => p.Status, e => e.Ignore()).ReverseMap();
             CreateMap<Post, PostMergeRequestOutputDto>().ReverseMap();
         }
     }

+ 24 - 31
src/Masuit.MyBlogs.Core/Controllers/AdminController.cs

@@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc.Filters;
 using System;
-using System.Collections.Generic;
+using System.Linq;
 
 namespace Masuit.MyBlogs.Core.Controllers
 {
@@ -64,46 +64,39 @@ namespace Masuit.MyBlogs.Core.Controllers
         public override void OnActionExecuting(ActionExecutingContext filterContext)
         {
             base.OnActionExecuting(filterContext);
-            if (filterContext.HttpContext.Request.Method.Equals("GET", StringComparison.InvariantCultureIgnoreCase)) //get方式的多半是页面
-            {
-                UserInfoOutputDto user = filterContext.HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo);
+            var user = filterContext.HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo);
 #if DEBUG
-                user = UserInfoService.GetByUsername("masuit").Mapper<UserInfoOutputDto>();
-                filterContext.HttpContext.Session.Set(SessionKey.UserInfo, user);
+            user = UserInfoService.GetByUsername("masuit").Mapper<UserInfoOutputDto>();
+            filterContext.HttpContext.Session.Set(SessionKey.UserInfo, user);
 #endif
-                if (user == null && Request.Cookies.Count > 2) //执行自动登录
+            if (user == null && Request.Cookies.Any(x => x.Key == "username" || x.Key == "password")) //执行自动登录
+            {
+                string name = Request.Cookies["username"];
+                string pwd = Request.Cookies["password"]?.DesDecrypt(AppConfig.BaiduAK);
+                var userInfo = UserInfoService.Login(name, pwd);
+                if (userInfo != null)
                 {
-                    string name = Request.Cookies["username"];
-                    string pwd = Request.Cookies["password"]?.DesDecrypt(AppConfig.BaiduAK);
-                    var userInfo = UserInfoService.Login(name, pwd);
-                    if (userInfo != null)
+                    Response.Cookies.Append("username", name, new CookieOptions
                     {
-                        Response.Cookies.Append("username", name, new CookieOptions
-                        {
-                            Expires = DateTime.Now.AddDays(7)
-                        });
-                        Response.Cookies.Append("password", Request.Cookies["password"], new CookieOptions
-                        {
-                            Expires = DateTime.Now.AddDays(7)
-                        });
-                        filterContext.HttpContext.Session.Set(SessionKey.UserInfo, userInfo);
-                    }
+                        Expires = DateTime.Now.AddDays(7)
+                    });
+                    Response.Cookies.Append("password", Request.Cookies["password"], new CookieOptions
+                    {
+                        Expires = DateTime.Now.AddDays(7)
+                    });
+                    filterContext.HttpContext.Session.Set(SessionKey.UserInfo, userInfo);
                 }
             }
-            else
+            if (ModelState.IsValid) return;
+            var errmsgs = ModelState.SelectMany(kv => kv.Value.Errors.Select(e => e.ErrorMessage)).ToList();
+            if (errmsgs.Any())
             {
-                if (ModelState.IsValid) return;
-                List<string> errmsgs = new List<string>();
-                ModelState.ForEach(kv => kv.Value.Errors.ForEach(error => errmsgs.Add(error.ErrorMessage)));
-                if (errmsgs.Count > 1)
+                for (var i = 0; i < errmsgs.Count; i++)
                 {
-                    for (var i = 0; i < errmsgs.Count; i++)
-                    {
-                        errmsgs[i] = i + 1 + ". " + errmsgs[i];
-                    }
+                    errmsgs[i] = i + 1 + ". " + errmsgs[i];
                 }
-                filterContext.Result = ResultData(null, false, "数据校验失败,错误信息:" + string.Join(" | ", errmsgs));
             }
+            filterContext.Result = ResultData(null, false, "数据校验失败,错误信息:" + string.Join(" | ", errmsgs));
         }
     }
 }

+ 1 - 1
src/Masuit.MyBlogs.Core/Controllers/BannerController.cs

@@ -34,7 +34,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [HttpPost]
         public async Task<IActionResult> Save(Banner banner)
         {
-            Banner entity = BannerService.GetById(banner.Id);
+            var entity = BannerService.GetById(banner.Id);
             if (entity != null)
             {
                 entity.Url = banner.Url;

+ 29 - 32
src/Masuit.MyBlogs.Core/Controllers/BaseController.cs

@@ -13,8 +13,8 @@ using Masuit.Tools.Security;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.EntityFrameworkCore.Internal;
 using System;
-using System.Collections.Generic;
 using System.Linq;
 using System.Net;
 
@@ -23,7 +23,7 @@ namespace Masuit.MyBlogs.Core.Controllers
     /// <summary>
     /// 基本父控制器
     /// </summary>
-    [ApiExplorerSettings(IgnoreApi = true), Firewall]
+    [ApiExplorerSettings(IgnoreApi = true), ServiceFilter(typeof(FirewallAttribute))]
     public class BaseController : Controller
     {
         /// <summary>
@@ -41,6 +41,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// </summary>
         public ILinksService LinksService { get; set; }
 
+        public UserInfoOutputDto CurrentUser => HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
+
         public IMapper Mapper { get; set; }
         public MapperConfiguration MapperConfig { get; set; }
 
@@ -83,50 +85,45 @@ namespace Masuit.MyBlogs.Core.Controllers
         {
             base.OnActionExecuting(filterContext);
             var user = filterContext.HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo);
+#if DEBUG
+            user = UserInfoService.GetByUsername("masuit").Mapper<UserInfoOutputDto>();
+            filterContext.HttpContext.Session.Set(SessionKey.UserInfo, user);
+#endif
             if (CommonHelper.SystemSettings.GetOrAdd("CloseSite", "false") == "true" && user?.IsAdmin != true)
             {
                 filterContext.Result = RedirectToAction("ComingSoon", "Error");
             }
 
-            if (filterContext.HttpContext.Request.Method.Equals("GET", StringComparison.InvariantCultureIgnoreCase)) //get方式的多半是页面
+            if (user == null && Request.Cookies.Any(x => x.Key == "username" || x.Key == "password")) //执行自动登录
             {
-#if DEBUG
-                user = UserInfoService.GetByUsername("masuit").Mapper<UserInfoOutputDto>();
-                filterContext.HttpContext.Session.Set(SessionKey.UserInfo, user);
-#endif
-                if (user == null && Request.Cookies.Count > 2) //执行自动登录
+                string name = Request.Cookies["username"];
+                string pwd = Request.Cookies["password"]?.DesDecrypt(AppConfig.BaiduAK);
+                var userInfo = UserInfoService.Login(name, pwd);
+                if (userInfo != null)
                 {
-                    string name = Request.Cookies["username"];
-                    string pwd = Request.Cookies["password"]?.DesDecrypt(AppConfig.BaiduAK);
-                    var userInfo = UserInfoService.Login(name, pwd);
-                    if (userInfo != null)
+                    Response.Cookies.Append("username", name, new CookieOptions
                     {
-                        Response.Cookies.Append("username", name, new CookieOptions
-                        {
-                            Expires = DateTime.Now.AddDays(7)
-                        });
-                        Response.Cookies.Append("password", Request.Cookies["password"], new CookieOptions
-                        {
-                            Expires = DateTime.Now.AddDays(7)
-                        });
-                        filterContext.HttpContext.Session.Set(SessionKey.UserInfo, userInfo);
-                    }
+                        Expires = DateTime.Now.AddDays(7)
+                    });
+                    Response.Cookies.Append("password", Request.Cookies["password"], new CookieOptions
+                    {
+                        Expires = DateTime.Now.AddDays(7)
+                    });
+                    filterContext.HttpContext.Session.Set(SessionKey.UserInfo, userInfo);
                 }
             }
-            else
+
+            if (ModelState.IsValid) return;
+            var errmsgs = ModelState.SelectMany(kv => kv.Value.Errors.Select(e => e.ErrorMessage)).ToList();
+            if (errmsgs.Any())
             {
-                if (ModelState.IsValid) return;
-                List<string> errmsgs = new List<string>();
-                ModelState.ForEach(kv => kv.Value.Errors.ForEach(error => errmsgs.Add(error.ErrorMessage)));
-                if (errmsgs.Count > 1)
+                for (var i = 0; i < errmsgs.Count; i++)
                 {
-                    for (var i = 0; i < errmsgs.Count; i++)
-                    {
-                        errmsgs[i] = i + 1 + ". " + errmsgs[i];
-                    }
+                    errmsgs[i] = i + 1 + ". " + errmsgs[i];
                 }
-                filterContext.Result = ResultData(errmsgs, false, "数据校验失败,错误信息:" + string.Join(" | ", errmsgs), true, HttpStatusCode.BadRequest);
             }
+
+            filterContext.Result = ResultData(errmsgs, false, "数据校验失败,错误信息:" + errmsgs.Join(" | "), user != null, HttpStatusCode.BadRequest);
         }
 
         /// <summary>在调用操作方法后调用。</summary>

+ 1 - 0
src/Masuit.MyBlogs.Core/Controllers/CategoryController.cs

@@ -59,6 +59,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             {
                 return ResultData(null, true, "分类添加成功!");
             }
+
             return ResultData(null, false, "分类添加失败!");
         }
 

+ 91 - 81
src/Masuit.MyBlogs.Core/Controllers/CommentController.cs

@@ -12,6 +12,7 @@ using Masuit.Tools.Html;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore.Internal;
 using Microsoft.Net.Http.Headers;
 using System;
 using System.Collections.Generic;
@@ -34,17 +35,17 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <summary>
         /// 发表评论
         /// </summary>
-        /// <param name="comment"></param>
+        /// <param name="dto"></param>
         /// <returns></returns>
         [HttpPost, ValidateAntiForgeryToken]
-        public ActionResult Put(CommentInputDto comment)
+        public ActionResult Put(CommentInputDto dto)
         {
-            if (Regex.Match(comment.Content, CommonHelper.BanRegex).Length > 0)
+            if (Regex.Match(dto.Content, CommonHelper.BanRegex).Length > 0)
             {
                 return ResultData(null, false, "您提交的内容包含敏感词,被禁止发表,请注意改善您的言辞!");
             }
 
-            Post post = PostService.GetById(comment.PostId);
+            Post post = PostService.GetById(dto.PostId);
             if (post is null)
             {
                 return ResultData(null, false, "评论失败,文章不存在!");
@@ -55,18 +56,20 @@ namespace Masuit.MyBlogs.Core.Controllers
                 return ResultData(null, false, "本文已禁用评论功能,不允许任何人回复!");
             }
 
-            comment.Content = comment.Content.Trim().Replace("<p><br></p>", string.Empty);
-            if (comment.Content.RemoveHtmlTag().Trim().Equals(HttpContext.Session.Get<string>("comment" + comment.PostId)))
+            dto.Content = dto.Content.Trim().Replace("<p><br></p>", string.Empty);
+            if (dto.Content.RemoveHtmlTag().Trim().Equals(HttpContext.Session.Get<string>("comment" + dto.PostId)))
             {
                 return ResultData(null, false, "您刚才已经在这篇文章发表过一次评论了,换一篇文章吧,或者换一下评论内容吧!");
             }
 
-            if (Regex.Match(comment.Content, CommonHelper.ModRegex).Length <= 0)
+            var comment = dto.Mapper<Comment>();
+            if (Regex.Match(dto.Content, CommonHelper.ModRegex).Length <= 0)
             {
                 comment.Status = Status.Pended;
             }
 
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo);
+            comment.CommentDate = DateTime.Now;
+            var user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo);
             if (user != null)
             {
                 comment.NickName = user.NickName;
@@ -78,64 +81,70 @@ namespace Masuit.MyBlogs.Core.Controllers
                     comment.IsMaster = true;
                 }
             }
-            comment.Content = comment.Content.HtmlSantinizerStandard().ClearImgAttributes();
-            comment.CommentDate = DateTime.Now;
-            comment.Browser = comment.Browser ?? Request.Headers[HeaderNames.UserAgent];
+            comment.Content = dto.Content.HtmlSantinizerStandard().ClearImgAttributes();
+            comment.Browser = dto.Browser ?? Request.Headers[HeaderNames.UserAgent];
             comment.IP = HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
-            Comment com = CommentService.AddEntitySaved(comment.Mapper<Comment>());
-            if (com != null)
+            comment.Location = comment.IP.GetIPLocation().Split("|").Where(s => !int.TryParse(s, out _)).ToHashSet().Join("|");
+            comment = CommentService.AddEntitySaved(comment);
+            if (comment == null)
             {
-                HttpContext.Session.Set("comment" + comment.PostId, comment.Content.RemoveHtmlTag().Trim());
-                var emails = new HashSet<string>();
-                var email = CommonHelper.SystemSettings["ReceiveEmail"]; //站长邮箱
-                emails.Add(email);
-                string content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/notify.html").Replace("{{title}}", post.Title).Replace("{{time}}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")).Replace("{{nickname}}", com.NickName).Replace("{{content}}", com.Content);
-                if (comment.Status == Status.Pended)
+                return ResultData(null, false, "评论失败");
+            }
+
+            HttpContext.Session.Set("comment" + comment.PostId, comment.Content.RemoveHtmlTag().Trim());
+            var emails = new HashSet<string>();
+            var email = CommonHelper.SystemSettings["ReceiveEmail"]; //站长邮箱
+            emails.Add(email);
+            var content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/notify.html")
+                .Replace("{{title}}", post.Title)
+                .Replace("{{time}}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
+                .Replace("{{nickname}}", comment.NickName)
+                .Replace("{{content}}", comment.Content);
+            if (comment.Status == Status.Pended)
+            {
+                if (!comment.IsMaster)
                 {
-                    if (!com.IsMaster)
+                    MessageService.AddEntitySaved(new InternalMessage()
                     {
-                        MessageService.AddEntitySaved(new InternalMessage()
-                        {
-                            Title = $"来自【{com.NickName}】的新文章评论",
-                            Content = com.Content,
-                            Link = Url.Action("Details", "Post", new { id = com.PostId, cid = com.Id }, Request.Scheme) + "#comment"
-                        });
-                    }
+                        Title = $"来自【{comment.NickName}】的新文章评论",
+                        Content = comment.Content,
+                        Link = Url.Action("Details", "Post", new { id = comment.PostId, cid = comment.Id }, Request.Scheme) + "#comment"
+                    });
+                }
 #if !DEBUG
-                    if (com.ParentId == 0)
-                    {
-                        emails.Add(post.Email);
-                        emails.Add(post.ModifierEmail);
-                        //新评论,只通知博主和楼主
-                        foreach (var s in emails)
-                        {
-                            BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Domain"] + "|博客文章新评论:", content.Replace("{{link}}", Url.Action("Details", "Post", new { id = com.PostId, cid = com.Id }, Request.Scheme) + "#comment"), s));
-                        }
-                    }
-                    else
+                if (comment.ParentId == 0)
+                {
+                    emails.Add(post.Email);
+                    emails.Add(post.ModifierEmail);
+                    //新评论,只通知博主和楼主
+                    foreach (var s in emails)
                     {
-                        emails.Add(post.Email);
-                        emails.Add(post.ModifierEmail);
-                        //通知博主和上层所有关联的评论访客
-                        var pid = CommentService.GetParentCommentIdByChildId(com.Id);
-                        CommentService.GetSelfAndAllChildrenCommentsByParentId(pid).Select(c => c.Email).ForEach(s => emails.Add(s));
-                        emails.Remove(com.Email);
-                        string link = Url.Action("Details", "Post", new { id = com.PostId, cid = com.Id }, Request.Scheme) + "#comment";
-                        foreach (var s in emails)
-                        {
-                            BackgroundJob.Enqueue(() => CommonHelper.SendMail($"{CommonHelper.SystemSettings["Domain"]}{CommonHelper.SystemSettings["Title"]}文章评论回复:", content.Replace("{{link}}", link), s));
-                        }
+                        BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Domain"] + "|博客文章新评论:", content.Replace("{{link}}", Url.Action("Details", "Post", new { id = comment.PostId, cid = comment.Id }, Request.Scheme) + "#comment"), s));
                     }
-#endif
-                    return ResultData(null, true, "评论发表成功,服务器正在后台处理中,这会有一定的延迟,稍后将显示到评论列表中");
                 }
-                foreach (var s in emails)
+                else
                 {
-                    BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Domain"] + "|博客文章新评论(待审核):", content.Replace("{{link}}", Url.Action("Details", "Post", new { id = com.PostId, cid = com.Id }, Request.Scheme) + "#comment") + "<p style='color:red;'>(待审核)</p>", s));
+                    //通知博主和上层所有关联的评论访客
+                    var pid = CommentService.GetParentCommentIdByChildId(comment.Id);
+                    emails.AddRange(CommentService.GetSelfAndAllChildrenCommentsByParentId(pid).Select(c => c.Email).ToArray());
+                    emails.AddRange(post.Email, post.ModifierEmail);
+                    emails.Remove(comment.Email);
+                    string link = Url.Action("Details", "Post", new { id = comment.PostId, cid = comment.Id }, Request.Scheme) + "#comment";
+                    foreach (var s in emails)
+                    {
+                        BackgroundJob.Enqueue(() => CommonHelper.SendMail($"{CommonHelper.SystemSettings["Domain"]}{CommonHelper.SystemSettings["Title"]}文章评论回复:", content.Replace("{{link}}", link), s));
+                    }
                 }
-                return ResultData(null, true, "评论成功,待站长审核通过以后将显示");
+#endif
+                return ResultData(null, true, "评论发表成功,服务器正在后台处理中,这会有一定的延迟,稍后将显示到评论列表中");
+            }
+
+            foreach (var s in emails)
+            {
+                BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Domain"] + "|博客文章新评论(待审核):", content.Replace("{{link}}", Url.Action("Details", "Post", new { id = comment.PostId, cid = comment.Id }, Request.Scheme) + "#comment") + "<p style='color:red;'>(待审核)</p>", s));
             }
-            return ResultData(null, false, "评论失败");
+
+            return ResultData(null, true, "评论成功,待站长审核通过以后将显示");
         }
 
         /// <summary>
@@ -151,15 +160,17 @@ namespace Masuit.MyBlogs.Core.Controllers
             {
                 return ResultData(null, false, "您刚才已经投过票了,感谢您的参与!");
             }
-            if (cm != null)
+
+            if (cm == null)
             {
-                cm.VoteCount++;
-                CommentService.UpdateEntity(cm);
-                HttpContext.Session.Set("cm" + id, id.GetBytes());
-                bool b = CommentService.SaveChanges() > 0;
-                return ResultData(null, b, b ? "投票成功" : "投票失败");
+                return ResultData(null, false, "非法操作");
             }
-            return ResultData(null, false, "非法操作");
+
+            cm.VoteCount++;
+            CommentService.UpdateEntity(cm);
+            HttpContext.Session.Set("cm" + id, id.GetBytes());
+            bool b = CommentService.SaveChanges() > 0;
+            return ResultData(null, b, b ? "投票成功" : "投票失败");
         }
 
         /// <summary>
@@ -173,12 +184,11 @@ namespace Masuit.MyBlogs.Core.Controllers
         [HttpPost]
         public ActionResult GetComments(int? id, int page = 1, int size = 5, int cid = 0)
         {
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
             int total; //总条数,用于前台分页
             if (cid != 0)
             {
                 int pid = CommentService.GetParentCommentIdByChildId(cid);
-                List<Comment> single = CommentService.GetSelfAndAllChildrenCommentsByParentId(pid).ToList();
+                var single = CommentService.GetSelfAndAllChildrenCommentsByParentId(pid).ToList();
                 if (single.Any())
                 {
                     total = 1;
@@ -192,14 +202,12 @@ namespace Masuit.MyBlogs.Core.Controllers
                     });
                 }
             }
-            IList<Comment> parent = CommentService.LoadPageEntities(page, size, out total, c => c.PostId == id && c.ParentId == 0 && (c.Status == Status.Pended || user.IsAdmin), c => c.CommentDate, false).ToList();
+            var parent = CommentService.LoadPageEntities(page, size, out total, c => c.PostId == id && c.ParentId == 0 && (c.Status == Status.Pended || CurrentUser.IsAdmin), c => c.CommentDate, false).ToList();
             if (!parent.Any())
             {
                 return ResultData(null, false, "没有评论");
             }
-            var list = new List<Comment>();
-            parent.ForEach(c => CommentService.GetSelfAndAllChildrenCommentsByParentId(c.Id).ForEach(result => list.Add(result)));
-            var qlist = list.Where(c => (c.Status == Status.Pended || user.IsAdmin));
+            parent = parent.SelectMany(c => CommentService.GetSelfAndAllChildrenCommentsByParentId(c.Id).Where(x => (x.Status == Status.Pended || CurrentUser.IsAdmin))).ToList();
             if (total > 0)
             {
                 return ResultData(new
@@ -208,7 +216,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                     parentTotal = total,
                     page,
                     size,
-                    rows = qlist.Mapper<IList<CommentViewModel>>()
+                    rows = parent.Mapper<IList<CommentViewModel>>()
                 });
             }
             return ResultData(null, false, "没有评论");
@@ -224,12 +232,11 @@ namespace Masuit.MyBlogs.Core.Controllers
         [HttpPost]
         public ActionResult GetPageComments(int page = 1, int size = 5, int cid = 0)
         {
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
             int total; //总条数,用于前台分页
             if (cid != 0)
             {
                 int pid = CommentService.GetParentCommentIdByChildId(cid);
-                List<Comment> single = CommentService.GetSelfAndAllChildrenCommentsByParentId(pid).ToList();
+                var single = CommentService.GetSelfAndAllChildrenCommentsByParentId(pid).ToList();
                 if (single.Any())
                 {
                     total = 1;
@@ -243,14 +250,14 @@ namespace Masuit.MyBlogs.Core.Controllers
                     });
                 }
             }
-            IList<Comment> parent = CommentService.LoadPageEntities(page, size, out total, c => c.ParentId == 0 && (c.Status == Status.Pended || user.IsAdmin), c => c.CommentDate, false).ToList();
+
+            var parent = CommentService.LoadPageEntities(page, size, out total, c => c.ParentId == 0 && (c.Status == Status.Pended || CurrentUser.IsAdmin), c => c.CommentDate, false).ToList();
             if (!parent.Any())
             {
                 return ResultData(null, false, "没有评论");
             }
-            var list = new List<Comment>();
-            parent.ForEach(c => CommentService.GetSelfAndAllChildrenCommentsByParentId(c.Id).ForEach(result => list.Add(result)));
-            var qlist = list.Where(c => (c.Status == Status.Pended || user.IsAdmin));
+
+            var qlist = parent.SelectMany(c => CommentService.GetSelfAndAllChildrenCommentsByParentId(c.Id)).Where(c => (c.Status == Status.Pended || CurrentUser.IsAdmin));
             if (total > 0)
             {
                 return ResultData(new
@@ -279,15 +286,18 @@ namespace Masuit.MyBlogs.Core.Controllers
             bool b = CommentService.UpdateEntitySaved(comment);
             var pid = comment.ParentId == 0 ? comment.Id : CommentService.GetParentCommentIdByChildId(id);
 #if !DEBUG
-            string content = System.IO.File.ReadAllText(Path.Combine(HostingEnvironment.WebRootPath, "template", "notify.html")).Replace("{{title}}", post.Title).Replace("{{time}}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")).Replace("{{nickname}}", comment.NickName).Replace("{{content}}", comment.Content);
-            var emails = CommentService.GetSelfAndAllChildrenCommentsByParentId(pid).Select(c => c.Email).Distinct().Except(new List<string> { comment.Email, CommonHelper.SystemSettings["ReceiveEmail"] }).ToList();
-            var emailSet = emails.Append(post.ModifierEmail).ToHashSet();
-            string link = Url.Action("Details", "Post", new
+            var content = System.IO.File.ReadAllText(Path.Combine(HostingEnvironment.WebRootPath, "template", "notify.html"))
+                .Replace("{{title}}", post.Title)
+                .Replace("{{time}}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
+                .Replace("{{nickname}}", comment.NickName)
+                .Replace("{{content}}", comment.Content);
+            var emails = CommentService.GetSelfAndAllChildrenCommentsByParentId(pid).Select(c => c.Email).Append(post.ModifierEmail).Except(new List<string> { comment.Email, CurrentUser.Email }).ToHashSet();
+            var link = Url.Action("Details", "Post", new
             {
                 id = comment.PostId,
                 cid = pid
             }, Request.Scheme) + "#comment";
-            foreach (var email in emailSet)
+            foreach (var email in emails)
             {
                 BackgroundJob.Enqueue(() => CommonHelper.SendMail($"{Request.Host}{CommonHelper.SystemSettings["Title"]}文章评论回复:", content.Replace("{{link}}", link), email));
             }
@@ -314,7 +324,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Authority]
         public ActionResult GetPendingComments(int page = 1, int size = 10)
         {
-            List<CommentOutputDto> list = CommentService.LoadPageEntities<DateTime, CommentOutputDto>(page, size, out int total, c => c.Status == Status.Pending, c => c.CommentDate, false).ToList();
+            var list = CommentService.LoadPageEntities<DateTime, CommentOutputDto>(page, size, out int total, c => c.Status == Status.Pending, c => c.CommentDate, false).ToList();
             var pageCount = Math.Ceiling(total * 1.0 / size).ToInt32();
             return PageResult(list, pageCount, total);
         }

+ 104 - 3
src/Masuit.MyBlogs.Core/Controllers/ErrorController.cs

@@ -1,4 +1,16 @@
-using Microsoft.AspNetCore.Mvc;
+using Hangfire;
+using Masuit.MyBlogs.Core.Common;
+using Masuit.MyBlogs.Core.Configs;
+using Masuit.MyBlogs.Core.Extensions;
+using Masuit.MyBlogs.Core.Infrastructure.Services;
+using Masuit.MyBlogs.Core.Models.Enum;
+using Masuit.Tools;
+using Masuit.Tools.Core.Net;
+using Masuit.Tools.Security;
+using Masuit.Tools.Systems;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using System;
 
 namespace Masuit.MyBlogs.Core.Controllers
 {
@@ -8,6 +20,8 @@ namespace Masuit.MyBlogs.Core.Controllers
     [ApiExplorerSettings(IgnoreApi = true)]
     public class ErrorController : Controller
     {
+        public BroadcastService BroadcastService { get; set; }
+
         /// <summary>
         /// 404
         /// </summary>
@@ -15,9 +29,9 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("error"), Route("{*url}", Order = 99999), ResponseCache(Duration = 36000)]
         public ActionResult Index()
         {
-            Response.StatusCode = 404;
             if (Request.Method.ToLower().Equals("get"))
             {
+                Response.StatusCode = 404;
                 return View();
             }
             return Json(new
@@ -35,9 +49,9 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("ServiceUnavailable"), ResponseCache(Duration = 36000)]
         public ActionResult ServiceUnavailable()
         {
-            Response.StatusCode = 503;
             if (Request.Method.ToLower().Equals("get"))
             {
+                Response.StatusCode = 503;
                 return View();
             }
             return Json(new
@@ -55,6 +69,11 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("AccessDeny"), ResponseCache(Duration = 360000)]
         public ActionResult AccessDeny()
         {
+            if (Request.Cookies["Email"].MDString3(AppConfig.BaiduAK).Equals(Request.Cookies["FullAccessToken"]))
+            {
+                return Redirect("/");
+            }
+
             Response.StatusCode = 403;
             return View();
         }
@@ -79,5 +98,87 @@ namespace Masuit.MyBlogs.Core.Controllers
         {
             return View();
         }
+
+        /// <summary>
+        /// 检查访问密码
+        /// </summary>
+        /// <param name="email"></param>
+        /// <param name="token"></param>
+        /// <returns></returns>
+        [HttpPost, ValidateAntiForgeryToken, AllowAccessFirewall, ResponseCache(Duration = 115, VaryByQueryKeys = new[] { "email", "token" })]
+        public ActionResult CheckViewToken(string email, string token)
+        {
+            if (string.IsNullOrEmpty(token))
+            {
+                return ResultData(null, false, "请输入访问密码!");
+            }
+
+            var s = RedisHelper.Get("token:" + email);
+            if (!token.Equals(s))
+            {
+                return ResultData(null, false, "访问密码不正确!");
+            }
+
+            HttpContext.Session.Set("FullAccessViewToken", token);
+            Response.Cookies.Append("Email", email, new CookieOptions
+            {
+                Expires = DateTime.Now.AddYears(1)
+            });
+            Response.Cookies.Append("FullAccessToken", email.MDString3(AppConfig.BaiduAK), new CookieOptions
+            {
+                Expires = DateTime.Now.AddYears(1)
+            });
+            return ResultData(null);
+
+        }
+
+        /// <summary>
+        /// 检查授权邮箱
+        /// </summary>
+        /// <param name="email"></param>
+        /// <returns></returns>
+        [HttpPost, ValidateAntiForgeryToken, AllowAccessFirewall, ResponseCache(Duration = 115, VaryByQueryKeys = new[] { "email" })]
+        public ActionResult GetViewToken(string email)
+        {
+            if (string.IsNullOrEmpty(email) || !email.MatchEmail())
+            {
+                return ResultData(null, false, "请输入正确的邮箱!");
+            }
+
+            if (RedisHelper.Exists("get:" + email))
+            {
+                RedisHelper.Expire("get:" + email, 120);
+                return ResultData(null, false, "发送频率限制,请在2分钟后重新尝试发送邮件!请检查你的邮件,若未收到,请检查你的邮箱地址或邮件垃圾箱!");
+            }
+
+            if (!BroadcastService.Any(b => b.Email.Equals(email) && b.SubscribeType == SubscribeType.ArticleToken))
+            {
+                return ResultData(null, false, "您目前没有权限访问这个链接,请联系站长开通访问权限!");
+            }
+
+            var token = SnowFlake.GetInstance().GetUniqueShortId(6);
+            RedisHelper.Set("token:" + email, token, 86400);
+            BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Domain"] + "博客访问验证码", $"{CommonHelper.SystemSettings["Domain"]}本次验证码是:<span style='color:red'>{token}</span>,有效期为24h,请按时使用!", email));
+            RedisHelper.Set("get:" + email, token, 120);
+            return ResultData(null);
+
+        }
+
+        /// <summary>
+        /// 响应数据
+        /// </summary>
+        /// <param name="data">数据</param>
+        /// <param name="success">响应状态</param>
+        /// <param name="message">响应消息</param>
+        /// <returns></returns>
+        public ActionResult ResultData(object data, bool success = true, string message = "")
+        {
+            return Ok(new
+            {
+                Success = success,
+                Message = message,
+                Data = data
+            });
+        }
     }
 }

+ 31 - 47
src/Masuit.MyBlogs.Core/Controllers/HomeController.cs

@@ -1,18 +1,19 @@
 using AutoMapper.QueryableExtensions;
 using EFSecondLevelCache.Core;
+using Masuit.MyBlogs.Core.Extensions;
 using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
 using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.Entity;
 using Masuit.MyBlogs.Core.Models.Enum;
 using Masuit.MyBlogs.Core.Models.ViewModel;
-using Masuit.Tools;
-using Masuit.Tools.Core.Net;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Net.Http.Headers;
 using System;
 using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
 using System.Linq;
 using System.Linq.Expressions;
+using System.Runtime.InteropServices;
 using System.Threading.Tasks;
 
 namespace Masuit.MyBlogs.Core.Controllers
@@ -50,17 +51,15 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <summary>
         /// 首页
         /// </summary>
-        /// <param name="orderBy"></param>
         /// <returns></returns>
-        [ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "orderBy" }, VaryByHeader = HeaderNames.Cookie)]
-        public ActionResult Index(OrderBy orderBy = OrderBy.ModifyDate)
+        [HttpGet, ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "orderBy" }, VaryByHeader = HeaderNames.Cookie)]
+        public ActionResult Index()
         {
-            ViewBag.Total = PostService.Count(p => p.Status == Status.Pended);
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
+            ViewBag.Total = PostService.Count(p => p.Status == Status.Pended || CurrentUser.IsAdmin);
             var banners = BannerService.GetAllFromL2CacheNoTracking(b => new Random().Next()).ToList();
             List<FastShare> fastShares = FastShareService.GetAllFromL2CacheNoTracking(s => s.Sort).ToList();
             ViewBag.FastShare = fastShares;
-            var viewModel = GetIndexPageViewModel(1, 15, orderBy, user);
+            var viewModel = GetIndexPageViewModel(1, 15, OrderBy.ModifyDate, CurrentUser);
             viewModel.Banner = banners;
             return View(viewModel);
         }
@@ -72,12 +71,11 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <param name="size"></param>
         /// <param name="orderBy"></param>
         /// <returns></returns>
-        [Route("p/{page:int?}/{size:int?}/{orderBy:int?}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "page", "size", "orderBy" }, VaryByHeader = HeaderNames.Cookie)]
-        public ActionResult Post(int page = 1, int size = 15, OrderBy orderBy = OrderBy.ModifyDate)
+        [Route("p"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "page", "size", "orderBy" }, VaryByHeader = HeaderNames.Cookie)]
+        public ActionResult Post([Optional]OrderBy? orderBy, [Range(1, int.MaxValue, ErrorMessage = "页码必须大于0")]int page = 1, [Range(1, int.MaxValue, ErrorMessage = "页大小必须大于0")]int size = 15)
         {
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
-            ViewBag.Total = PostService.LoadEntitiesFromL2Cache<PostOutputDto>(p => p.Status == Status.Pended || user.IsAdmin && !p.IsFixedTop).Count(p => !p.IsFixedTop);
-            var viewModel = GetIndexPageViewModel(page, size, orderBy, user);
+            ViewBag.Total = PostService.LoadEntitiesFromL2Cache<PostOutputDto>(p => p.Status == Status.Pended || CurrentUser.IsAdmin && !p.IsFixedTop).Count(p => !p.IsFixedTop);
+            var viewModel = GetIndexPageViewModel(page, size, orderBy, CurrentUser);
             return View(viewModel);
         }
 
@@ -90,11 +88,10 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <param name="orderBy"></param>
         /// <returns></returns>
         [Route("tag/{id}/{page:int?}/{size:int?}/{orderBy:int?}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "id", "page", "size", "orderBy" }, VaryByHeader = HeaderNames.Cookie)]
-        public ActionResult Tag(string id, int page = 1, int size = 15, OrderBy orderBy = OrderBy.ModifyDate)
+        public ActionResult Tag(string id, [Optional]OrderBy? orderBy, [Range(1, int.MaxValue, ErrorMessage = "页码必须大于0")]int page = 1, [Range(1, int.MaxValue, ErrorMessage = "页大小必须大于0")]int size = 15)
         {
             IList<PostOutputDto> posts;
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
-            var temp = PostService.LoadEntities<PostOutputDto>(p => p.Label.Contains(id) && (p.Status == Status.Pended || user.IsAdmin)).OrderByDescending(p => p.IsFixedTop);
+            var temp = PostService.LoadEntities<PostOutputDto>(p => p.Label.Contains(id) && (p.Status == Status.Pended || CurrentUser.IsAdmin)).OrderByDescending(p => p.IsFixedTop);
             switch (orderBy)
             {
                 case OrderBy.CommentCount:
@@ -116,7 +113,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                     posts = temp.ThenByDescending(p => p.ModifyDate).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
                     break;
             }
-            var viewModel = GetIndexPageViewModel(1, 1, orderBy, user);
+            var viewModel = GetIndexPageViewModel(1, 1, orderBy, CurrentUser);
             ViewBag.Total = temp.Count();
             ViewBag.Tag = id;
             viewModel.Posts = posts;
@@ -133,12 +130,10 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <returns></returns>
         [Route("cat/{id:int}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "id", "page", "size", "orderBy" }, VaryByHeader = HeaderNames.Cookie)]
         [Route("cat/{id:int}/{page:int?}/{size:int?}/{orderBy:int?}")]
-        public async Task<ActionResult> Category(int id, int page = 1, int size = 15, OrderBy orderBy = OrderBy.ModifyDate)
+        public async Task<ActionResult> Category(int id, [Optional]OrderBy? orderBy, [Range(1, int.MaxValue, ErrorMessage = "页码必须大于0")]int page = 1, [Range(1, int.MaxValue, ErrorMessage = "页大小必须大于0")]int size = 15)
         {
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
-            var cat = await CategoryService.GetByIdAsync(id);
-            if (cat is null) return RedirectToAction("Index", "Error");
-            var posts = PostService.LoadEntitiesNoTracking(p => p.CategoryId == cat.Id && (p.Status == Status.Pended || user.IsAdmin)).OrderByDescending(p => p.IsFixedTop);
+            var cat = await CategoryService.GetByIdAsync(id) ?? throw new NotFoundException("文章分类未找到");
+            var posts = PostService.LoadEntitiesNoTracking(p => p.CategoryId == cat.Id && (p.Status == Status.Pended || CurrentUser.IsAdmin)).OrderByDescending(p => p.IsFixedTop);
             ViewBag.Total = posts.Count();
             switch (orderBy)
             {
@@ -161,7 +156,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                     posts = posts.ThenByDescending(p => p.ModifyDate);
                     break;
             }
-            var viewModel = GetIndexPageViewModel(1, 1, orderBy, user);
+            var viewModel = GetIndexPageViewModel(1, 1, orderBy, CurrentUser);
             ViewBag.CategoryName = cat.Name;
             ViewBag.Desc = cat.Description;
             viewModel.Posts = posts.Skip(size * (page - 1)).Take(size).ProjectTo<PostOutputDto>(MapperConfig).Cacheable().ToList();
@@ -176,9 +171,9 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <param name="orderBy"></param>
         /// <param name="user"></param>
         /// <returns></returns>
-        private IndexPageViewModel GetIndexPageViewModel(int page, int size, OrderBy orderBy, UserInfoOutputDto user)
+        private IndexPageViewModel GetIndexPageViewModel(int page, int size, OrderBy? orderBy, UserInfoOutputDto user)
         {
-            IQueryable<PostOutputDto> postList = PostService.LoadEntities<PostOutputDto>(p => (p.Status == Status.Pended || user.IsAdmin)); //准备文章的查询
+            IQueryable<PostOutputDto> postsQuery = PostService.LoadEntities<PostOutputDto>(p => (p.Status == Status.Pended || user.IsAdmin)); //准备文章的查询
             var notices = NoticeService.LoadPageEntitiesFromL2Cache<DateTime, NoticeOutputDto>(1, 5, out int _, n => (n.Status == Status.Display || user.IsAdmin), n => n.ModifyDate, false).ToList(); //加载前5条公告
             var cats = CategoryService.LoadEntitiesFromL2Cache<string, CategoryOutputDto>(c => c.Status == Status.Available, c => c.Name).ToList(); //加载分类目录
             var hotSearches = RedisHelper.Get<List<KeywordsRankOutputDto>>("SearchRank:Week").Take(10).ToList(); //热词统计
@@ -192,21 +187,10 @@ namespace Masuit.MyBlogs.Core.Controllers
                     order = p => p.AverageViewCount;
                     break;
             }
-            var hot6Post = postList.OrderByDescending(order).Skip(0).Take(5).Cacheable().ToList(); //热门文章
-            var tags = new List<string>(); //标签云
-            var tagdic = new Dictionary<string, int>();
+            var hot6Post = postsQuery.OrderByDescending(order).Skip(0).Take(5).Cacheable().ToList(); //热门文章
             var newdic = new Dictionary<string, int>(); //标签云最终结果
-            postList.Select(p => p.Label).Cacheable().ToList().ForEach(m =>
-            {
-                if (!string.IsNullOrEmpty(m))
-                {
-                    tags.AddRange(m.Split(',', ','));
-                }
-            }); //统计标签
-            tags.GroupBy(s => s).ForEach(g =>
-            {
-                tagdic.Add(g.Key, g.Count());
-            }); //将标签分组
+            var tagdic = postsQuery.Where(p => !string.IsNullOrEmpty(p.Label)).Select(p => p.Label).Cacheable().SelectMany(s => s.Split(',', ',')).GroupBy(s => s).ToDictionary(g => g.Key, g => g.Count()); //统计标签
+
             if (tagdic.Any())
             {
                 int min = tagdic.Values.Min();
@@ -220,27 +204,27 @@ namespace Masuit.MyBlogs.Core.Controllers
             switch (orderBy) //文章排序
             {
                 case OrderBy.CommentCount:
-                    posts = postList.Where(p => !p.IsFixedTop).OrderByDescending(p => p.CommentCount).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
+                    posts = postsQuery.Where(p => !p.IsFixedTop).OrderByDescending(p => p.CommentCount).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
                     break;
                 case OrderBy.PostDate:
-                    posts = postList.Where(p => !p.IsFixedTop).OrderByDescending(p => p.PostDate).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
+                    posts = postsQuery.Where(p => !p.IsFixedTop).OrderByDescending(p => p.PostDate).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
                     break;
                 case OrderBy.ViewCount:
-                    posts = postList.Where(p => !p.IsFixedTop).OrderByDescending(p => p.TotalViewCount).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
+                    posts = postsQuery.Where(p => !p.IsFixedTop).OrderByDescending(p => p.TotalViewCount).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
                     break;
                 case OrderBy.VoteCount:
-                    posts = postList.Where(p => !p.IsFixedTop).OrderByDescending(p => p.VoteUpCount).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
+                    posts = postsQuery.Where(p => !p.IsFixedTop).OrderByDescending(p => p.VoteUpCount).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
                     break;
                 case OrderBy.AverageViewCount:
-                    posts = postList.Where(p => !p.IsFixedTop).OrderByDescending(p => p.AverageViewCount).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
+                    posts = postsQuery.Where(p => !p.IsFixedTop).OrderByDescending(p => p.AverageViewCount).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
                     break;
                 default:
-                    posts = postList.Where(p => !p.IsFixedTop).OrderByDescending(p => p.ModifyDate).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
+                    posts = postsQuery.Where(p => !p.IsFixedTop).OrderByDescending(p => p.ModifyDate).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
                     break;
             }
             if (page == 1)
             {
-                posts = postList.Where(p => p.IsFixedTop).OrderByDescending(p => p.ModifyDate).AsEnumerable().Union(posts).ToList();
+                posts = postsQuery.Where(p => p.IsFixedTop).OrderByDescending(p => p.ModifyDate).AsEnumerable().Union(posts).ToList();
             }
             return new IndexPageViewModel()
             {
@@ -250,7 +234,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 Posts = posts,
                 Tags = newdic,
                 Top6Post = hot6Post,
-                PostsQueryable = postList
+                PostsQueryable = postsQuery
             };
         }
     }

+ 57 - 75
src/Masuit.MyBlogs.Core/Controllers/LinksController.cs

@@ -3,9 +3,7 @@ using Masuit.MyBlogs.Core.Extensions;
 using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.Entity;
 using Masuit.MyBlogs.Core.Models.Enum;
-using Masuit.MyBlogs.Core.Models.ViewModel;
 using Masuit.Tools;
-using Masuit.Tools.Core.Net;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Net.Http.Headers;
 using System;
@@ -22,6 +20,9 @@ namespace Masuit.MyBlogs.Core.Controllers
     /// </summary>
     public class LinksController : BaseController
     {
+        public IHttpClientFactory HttpClientFactory { get; set; }
+        private HttpClient HttpClient => HttpClientFactory.CreateClient();
+
         /// <summary>
         /// 友情链接页
         /// </summary>
@@ -29,14 +30,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("links"), ResponseCache(Duration = 600, VaryByHeader = HeaderNames.Cookie)]
         public ActionResult Index()
         {
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo);
-            List<LinksOutputDto> list = LinksService.LoadEntities<object, LinksOutputDto>(l => l.Status == Status.Available, l => l.Recommend, false).ToList();
-            if (user != null && user.IsAdmin)
-            {
-                return View("Index_Admin", list);
-            }
-
-            return View(list);
+            var list = LinksService.LoadEntities<object, LinksOutputDto>(l => l.Status == Status.Available, l => l.Recommend, false).ToList();
+            return CurrentUser.IsAdmin ? View("Index_Admin", list) : View(list);
         }
 
         /// <summary>
@@ -57,52 +52,45 @@ namespace Masuit.MyBlogs.Core.Controllers
                 return ResultData(null, false, "添加失败!检测到您的网站已经是本站的友情链接了!");
             }
 
-            Uri uri = new Uri(links.Url);
-            using (HttpClient client = new HttpClient()
-            {
-                BaseAddress = uri,
-                Timeout = TimeSpan.FromSeconds(10)
-            })
+            HttpClient.DefaultRequestHeaders.UserAgent.Add(ProductInfoHeaderValue.Parse("Mozilla/5.0"));
+            HttpClient.DefaultRequestHeaders.Referrer = new Uri(Request.Scheme + "://" + Request.Host.ToString());
+            return await await HttpClient.GetAsync(links.Url).ContinueWith(async t =>
             {
-                client.DefaultRequestHeaders.UserAgent.Add(ProductInfoHeaderValue.Parse("Mozilla/5.0"));
-                client.DefaultRequestHeaders.Referrer = new Uri(Request.Scheme + "://" + Request.Host.ToString());
-                return await await client.GetAsync(uri.PathAndQuery).ContinueWith(async t =>
+                if (t.IsFaulted || t.IsCanceled)
                 {
-                    if (t.IsFaulted || t.IsCanceled)
-                    {
-                        return ResultData(null, false, "添加失败!检测到您的网站疑似挂了,或者连接到你网站的时候超时,请检查下!");
-                    }
+                    return ResultData(null, false, "添加失败!检测到您的网站疑似挂了,或者连接到你网站的时候超时,请检查下!");
+                }
 
-                    var res = await t;
-                    if (res.IsSuccessStatusCode)
-                    {
-                        var s = await res.Content.ReadAsStringAsync();
-                        if (s.Contains(CommonHelper.SystemSettings["Domain"]))
-                        {
-                            var entry = LinksService.GetFirstEntity(l => l.Url.Equals(links.Url));
-                            bool b;
-                            if (entry is null)
-                            {
-                                b = LinksService.AddEntitySaved(links) != null;
-                            }
-                            else
-                            {
-                                entry.Url = links.Url;
-                                entry.Except = links.Except;
-                                entry.Name = links.Name;
-                                entry.Recommend = links.Recommend;
-                                b = LinksService.UpdateEntitySaved(entry);
-                            }
+                var res = await t;
+                if (!res.IsSuccessStatusCode)
+                {
+                    return ResultData(null, false, "添加失败!检测到您的网站疑似挂了!返回状态码为:" + res.StatusCode);
+                }
 
-                            return ResultData(null, b, b ? "添加成功!这可能有一定的延迟,如果没有看到您的链接,请稍等几分钟后刷新页面即可,如有疑问,请联系站长。" : "添加失败!这可能是由于网站服务器内部发生了错误,如有疑问,请联系站长。");
-                        }
+                var s = await res.Content.ReadAsStringAsync();
+                if (!s.Contains(CommonHelper.SystemSettings["Domain"]))
+                {
+                    return ResultData(null, false, $"添加失败!检测到您的网站上未将本站设置成友情链接,请先将本站主域名:{CommonHelper.SystemSettings["Domain"]}在您的网站设置为友情链接,并且能够展示后,再次尝试添加即可!");
+                }
 
-                        return ResultData(null, false, $"添加失败!检测到您的网站上未将本站设置成友情链接,请先将本站主域名:{CommonHelper.SystemSettings["Domain"]}在您的网站设置为友情链接,并且能够展示后,再次尝试添加即可!");
-                    }
+                var entry = LinksService.GetFirstEntity(l => l.Url.Equals(links.Url));
+                bool b;
+                if (entry is null)
+                {
+                    b = LinksService.AddEntitySaved(links) != null;
+                }
+                else
+                {
+                    entry.Url = links.Url;
+                    entry.Except = links.Except;
+                    entry.Name = links.Name;
+                    entry.Recommend = links.Recommend;
+                    b = LinksService.UpdateEntitySaved(entry);
+                }
 
-                    return ResultData(null, false, "添加失败!检测到您的网站疑似挂了!返回状态码为:" + res.StatusCode);
-                });
-            }
+                return ResultData(null, b, b ? "添加成功!这可能有一定的延迟,如果没有看到您的链接,请稍等几分钟后刷新页面即可,如有疑问,请联系站长。" : "添加失败!这可能是由于网站服务器内部发生了错误,如有疑问,请联系站长。");
+
+            });
         }
 
         /// <summary>
@@ -139,36 +127,30 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Authority]
         public async Task<ActionResult> Check(string link)
         {
-            Uri uri = new Uri(link);
-            using (var client = new HttpClient()
-            {
-                BaseAddress = uri,
-                Timeout = TimeSpan.FromSeconds(10)
-            })
+            HttpClient.DefaultRequestHeaders.UserAgent.Add(ProductInfoHeaderValue.Parse("Mozilla/5.0"));
+            return await await HttpClient.GetAsync(link).ContinueWith(async t =>
             {
-                client.DefaultRequestHeaders.UserAgent.Add(ProductInfoHeaderValue.Parse("Mozilla/5.0"));
-                return await await client.GetAsync(uri.PathAndQuery).ContinueWith(async t =>
+                if (t.IsFaulted || t.IsCanceled)
+                {
+                    return ResultData(null, false, link + " 似乎挂了!");
+                }
+
+                var res = await t;
+                if (!res.IsSuccessStatusCode)
                 {
-                    if (t.IsFaulted || t.IsCanceled)
-                    {
-                        return ResultData(null, false, link + " 似乎挂了!");
-                    }
+                    return ResultData(null, false, link + " 对方网站返回错误的状态码!http响应码为:" + res.StatusCode);
+                }
 
-                    var res = await t;
-                    if (res.IsSuccessStatusCode)
-                    {
-                        var s = await res.Content.ReadAsStringAsync();
-                        if (s.Contains(CommonHelper.SystemSettings["Domain"]))
-                        {
-                            return ResultData(null, true, "友情链接正常!");
-                        }
+                var s = await res.Content.ReadAsStringAsync();
+                if (s.Contains(CommonHelper.SystemSettings["Domain"]))
+                {
+                    return ResultData(null, true, "友情链接正常!");
+                }
 
-                        return ResultData(null, false, link + " 对方似乎没有本站的友情链接!");
-                    }
+                return ResultData(null, false, link + " 对方似乎没有本站的友情链接!");
+
+            });
 
-                    return ResultData(null, false, link + " 对方网站返回错误的状态码!http响应码为:" + res.StatusCode);
-                });
-            }
         }
 
         /// <summary>

+ 1 - 2
src/Masuit.MyBlogs.Core/Controllers/LoginController.cs

@@ -2,7 +2,6 @@
 using Masuit.MyBlogs.Core.Models.DTO;
 using Microsoft.AspNetCore.Mvc;
 using System;
-using System.Collections.Generic;
 using System.Linq;
 
 namespace Masuit.MyBlogs.Core.Controllers
@@ -27,7 +26,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         public ActionResult GetRecentRecord(int id)
         {
             var time = DateTime.Now.AddMonths(-1);
-            List<LoginRecordOutputDto> list = LoginRecordService.LoadEntitiesFromL2Cache<DateTime, LoginRecordOutputDto>(r => r.UserInfoId == id && r.LoginTime >= time, r => r.LoginTime, false).ToList();
+            var list = LoginRecordService.LoadEntitiesFromL2Cache<DateTime, LoginRecordOutputDto>(r => r.UserInfoId == id && r.LoginTime >= time, r => r.LoginTime, false).ToList();
             return ResultData(list);
         }
     }

+ 3 - 4
src/Masuit.MyBlogs.Core/Controllers/MenuController.cs

@@ -1,5 +1,4 @@
-using AutoMapper;
-using Masuit.MyBlogs.Core.Common;
+using Masuit.MyBlogs.Core.Common;
 using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
 using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.Entity;
@@ -38,7 +37,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <returns></returns>
         public ActionResult GetMenuType()
         {
-            Array array = Enum.GetValues(typeof(MenuType));
+            var array = Enum.GetValues(typeof(MenuType));
             var list = new List<object>();
             foreach (Enum e in array)
             {
@@ -74,7 +73,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             {
                 model.Icon = null;
             }
-            Menu m = MenuService.GetById(model.Id);
+            var m = MenuService.GetById(model.Id);
             if (m == null)
             {
                 var menu = MenuService.AddEntitySaved(model.Mapper<Menu>());

+ 14 - 13
src/Masuit.MyBlogs.Core/Controllers/MergeController.cs

@@ -67,7 +67,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         {
             var newer = PostMergeRequestService.GetById(mid) ?? throw new NotFoundException("待合并文章未找到");
             var old = newer.Post;
-            HtmlDiff.HtmlDiff diffHelper = new HtmlDiff.HtmlDiff(old.Content, newer.Content);
+            var diffHelper = new HtmlDiff.HtmlDiff(old.Content, newer.Content);
             string diffOutput = diffHelper.Build();
             old.Content = Regex.Replace(Regex.Replace(diffOutput, "<ins.+?</ins>", string.Empty), @"<\w+></\w+>", string.Empty);
             newer.Content = Regex.Replace(Regex.Replace(diffOutput, "<del.+?</del>", string.Empty), @"<\w+></\w+>", string.Empty);
@@ -90,15 +90,16 @@ namespace Masuit.MyBlogs.Core.Controllers
             merge.Post.ModifyDate = DateTime.Now;
             merge.MergeState = MergeStatus.Merged;
             var b = PostMergeRequestService.UpdateEntitySaved(merge);
-            if (b)
+            if (!b)
             {
-                string link = Request.Scheme + "://" + Request.Host + "/" + merge.Post.Id;
-                string content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/merge-pass.html").Replace("{{link}}", link).Replace("{{title}}", merge.Post.Title);
-                BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Title"] + "博客你提交的修改已通过", content, merge.ModifierEmail));
-                return ResultData(null, true, "文章合并完成!");
+                return ResultData(null, false, "文章合并失败!");
             }
 
-            return ResultData(null, false, "文章合并失败!");
+            string link = Request.Scheme + "://" + Request.Host + "/" + merge.Post.Id;
+            string content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/merge-pass.html").Replace("{{link}}", link).Replace("{{title}}", merge.Post.Title);
+            BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Title"] + "博客你提交的修改已通过", content, merge.ModifierEmail));
+            return ResultData(null, true, "文章合并完成!");
+
         }
 
         /// <summary>
@@ -132,15 +133,15 @@ namespace Masuit.MyBlogs.Core.Controllers
             var merge = PostMergeRequestService.GetById(id) ?? throw new NotFoundException("待合并文章未找到");
             merge.MergeState = MergeStatus.Reject;
             var b = PostMergeRequestService.UpdateEntitySaved(merge);
-            if (b)
+            if (!b)
             {
-                string link = Request.Scheme + "://" + Request.Host + "/" + merge.Post.Id + "/merge/" + id;
-                string content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/merge-reject.html").Replace("{{link}}", link).Replace("{{title}}", merge.Post.Title).Replace("{{reason}}", reason);
-                BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Title"] + "博客你提交的修改已被拒绝", content, merge.ModifierEmail));
-                return ResultData(null, true, "合并已拒绝!");
+                return ResultData(null, false, "操作失败!");
             }
 
-            return ResultData(null, false, "操作失败!");
+            var link = Request.Scheme + "://" + Request.Host + "/" + merge.Post.Id + "/merge/" + id;
+            var content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/merge-reject.html").Replace("{{link}}", link).Replace("{{title}}", merge.Post.Title).Replace("{{reason}}", reason);
+            BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Title"] + "博客你提交的修改已被拒绝", content, merge.ModifierEmail));
+            return ResultData(null, true, "合并已拒绝!");
         }
     }
 }

+ 9 - 22
src/Masuit.MyBlogs.Core/Controllers/MiscController.cs

@@ -3,9 +3,7 @@ using Masuit.MyBlogs.Core.Extensions;
 using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
 using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.Entity;
-using Masuit.MyBlogs.Core.Models.ViewModel;
 using Masuit.Tools;
-using Masuit.Tools.Core.Net;
 using Masuit.Tools.Html;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Mvc;
@@ -53,11 +51,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("misc/{id:int}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "id" }, VaryByHeader = HeaderNames.Cookie)]
         public ActionResult Index(int id)
         {
-            Misc misc = MiscService.GetById(id);
-            if (misc is null)
-            {
-                return RedirectToAction("Index", "Error");
-            }
+            var misc = MiscService.GetById(id) ?? throw new NotFoundException("页面未找到");
             return View(misc);
         }
 
@@ -68,12 +62,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("donate")]
         public ActionResult Donate()
         {
-            var user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo);
-            if (user != null && user.IsAdmin)
-            {
-                return View("Donate_Admin");
-            }
-            return View();
+            return CurrentUser.IsAdmin ? View("Donate_Admin") : View();
         }
 
         /// <summary>
@@ -149,20 +138,18 @@ namespace Masuit.MyBlogs.Core.Controllers
                 return ResultData(null, false, "杂项页已经被删除!");
             }
 
-            var srcs = post.Content.MatchImgSrcs();
+            var srcs = post.Content.MatchImgSrcs().Where(s => s.StartsWith("/"));
             foreach (var path in srcs)
             {
-                if (path.StartsWith("/"))
+                try
+                {
+                    System.IO.File.Delete(Path.Combine(HostingEnvironment.WebRootPath + path));
+                }
+                catch (IOException)
                 {
-                    try
-                    {
-                        System.IO.File.Delete(Path.Combine(HostingEnvironment.WebRootPath + path));
-                    }
-                    catch (IOException)
-                    {
-                    }
                 }
             }
+
             bool b = MiscService.DeleteByIdSaved(id);
             return ResultData(null, b, b ? "删除成功" : "删除失败");
         }

+ 65 - 66
src/Masuit.MyBlogs.Core/Controllers/MsgController.cs

@@ -10,6 +10,7 @@ using Masuit.Tools.Core.Net;
 using Masuit.Tools.Html;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore.Internal;
 using Microsoft.Net.Http.Headers;
 using System;
 using System.Collections.Generic;
@@ -43,13 +44,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         [ResponseCache(Duration = 600, VaryByHeader = HeaderNames.Cookie), Route("msg")]
         public ActionResult Index()
         {
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
             ViewBag.TotalCount = LeaveMessageService.LoadEntitiesNoTracking(m => m.ParentId == 0 && m.Status == Status.Pended).Count();
-            if (user.IsAdmin)
-            {
-                return View("Index_Admin");
-            }
-            return View();
+            return CurrentUser.IsAdmin ? View("Index_Admin") : View();
         }
 
         /// <summary>
@@ -61,7 +57,6 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <returns></returns>
         public ActionResult GetMsgs(int page = 1, int size = 10, int cid = 0)
         {
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
             int total;
             if (cid != 0)
             {
@@ -80,14 +75,13 @@ namespace Masuit.MyBlogs.Core.Controllers
                     });
                 }
             }
-            IEnumerable<LeaveMessage> parent = LeaveMessageService.LoadPageEntitiesNoTracking(page, size, out total, m => m.ParentId == 0 && (m.Status == Status.Pended || user.IsAdmin), m => m.PostDate, false);
+            var parent = LeaveMessageService.LoadPageEntitiesNoTracking(page, size, out total, m => m.ParentId == 0 && (m.Status == Status.Pended || CurrentUser.IsAdmin), m => m.PostDate, false);
             if (!parent.Any())
             {
                 return ResultData(null, false, "没有留言");
             }
-            var list = new List<LeaveMessageViewModel>();
-            parent.ForEach(c => LeaveMessageService.GetSelfAndAllChildrenMessagesByParentId(c.Id).ForEach(result => list.Add(result.Mapper<LeaveMessageViewModel>())));
-            var qlist = list.Where(c => c.Status == Status.Pended || user.IsAdmin);
+
+            var qlist = parent.ToList().SelectMany(c => LeaveMessageService.GetSelfAndAllChildrenMessagesByParentId(c.Id)).Where(c => c.Status == Status.Pended || CurrentUser.IsAdmin);
             if (total > 0)
             {
                 return ResultData(new
@@ -105,27 +99,30 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <summary>
         /// 发表留言
         /// </summary>
-        /// <param name="msg"></param>
+        /// <param name="dto"></param>
         /// <returns></returns>
         [HttpPost, ValidateAntiForgeryToken]
-        public ActionResult Put(LeaveMessageInputDto msg)
+        public ActionResult Put(LeaveMessageInputDto dto)
         {
-            if (Regex.Match(msg.Content, CommonHelper.BanRegex).Length > 0)
+            if (Regex.Match(dto.Content, CommonHelper.BanRegex).Length > 0)
             {
                 return ResultData(null, false, "您提交的内容包含敏感词,被禁止发表,请注意改善您的言辞!");
             }
 
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo);
-            msg.Content = msg.Content.Trim().Replace("<p><br></p>", string.Empty);
-            if (msg.Content.RemoveHtmlTag().Trim().Equals(HttpContext.Session.Get<string>("msg")))
+            dto.Content = dto.Content.Trim().Replace("<p><br></p>", string.Empty);
+            if (dto.Content.RemoveHtmlTag().Trim().Equals(HttpContext.Session.Get<string>("msg")))
             {
                 return ResultData(null, false, "您刚才已经发表过一次留言了!");
             }
-            if (Regex.Match(msg.Content, CommonHelper.ModRegex).Length <= 0)
+
+            var msg = dto.Mapper<LeaveMessage>();
+            if (Regex.Match(dto.Content, CommonHelper.ModRegex).Length <= 0)
             {
                 msg.Status = Status.Pended;
             }
 
+            msg.PostDate = DateTime.Now;
+            var user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo);
             if (user != null)
             {
                 msg.NickName = user.NickName;
@@ -137,55 +134,57 @@ namespace Masuit.MyBlogs.Core.Controllers
                     msg.IsMaster = true;
                 }
             }
-            msg.PostDate = DateTime.Now;
-            msg.Content = msg.Content.HtmlSantinizerStandard().ClearImgAttributes();
-            msg.Browser = msg.Browser ?? Request.Headers[HeaderNames.UserAgent];
+
+            msg.Content = dto.Content.HtmlSantinizerStandard().ClearImgAttributes();
+            msg.Browser = dto.Browser ?? Request.Headers[HeaderNames.UserAgent];
             msg.IP = HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
-            LeaveMessage msg2 = LeaveMessageService.AddEntitySaved(msg.Mapper<LeaveMessage>());
-            if (msg2 != null)
+            msg.Location = msg.IP.GetIPLocation().Split("|").Where(s => !int.TryParse(s, out _)).ToHashSet().Join("|");
+            msg = LeaveMessageService.AddEntitySaved(msg);
+            if (msg == null)
+            {
+                return ResultData(null, false, "留言发表失败!");
+            }
+
+            HttpContext.Session.Set("msg", msg.Content.RemoveHtmlTag().Trim());
+            var email = CommonHelper.SystemSettings["ReceiveEmail"];
+            var content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/notify.html").Replace("{{title}}", "网站留言板").Replace("{{time}}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")).Replace("{{nickname}}", msg.NickName).Replace("{{content}}", msg.Content);
+            if (msg.Status == Status.Pended)
             {
-                HttpContext.Session.Set("msg", msg.Content.RemoveHtmlTag().Trim());
-                var email = CommonHelper.SystemSettings["ReceiveEmail"];
-                string content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/notify.html").Replace("{{title}}", "网站留言板").Replace("{{time}}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")).Replace("{{nickname}}", msg2.NickName).Replace("{{content}}", msg2.Content);
-                if (msg.Status == Status.Pended)
+                if (!msg.IsMaster)
                 {
-                    if (!msg2.IsMaster)
+                    MessageService.AddEntitySaved(new InternalMessage()
                     {
-                        MessageService.AddEntitySaved(new InternalMessage()
-                        {
-                            Title = $"来自【{msg2.NickName}】的新留言",
-                            Content = msg2.Content,
-                            Link = Url.Action("Index", "Msg", new { cid = msg2.Id }, Request.Scheme)
-                        });
-                    }
+                        Title = $"来自【{msg.NickName}】的新留言",
+                        Content = msg.Content,
+                        Link = Url.Action("Index", "Msg", new { cid = msg.Id }, Request.Scheme)
+                    });
+                }
 #if !DEBUG
-                    if (msg.ParentId == 0)
-                    {
-                        //新评论,只通知博主
-                        BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Domain"] + "|博客新留言:", content.Replace("{{link}}", Url.Action("Index", "Msg", new { cid = msg2.Id }, Request.Scheme)), email));
-                    }
-                    else
+                if (msg.ParentId == 0)
+                {
+                    //新评论,只通知博主
+                    BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Domain"] + "|博客新留言:", content.Replace("{{link}}", Url.Action("Index", "Msg", new { cid = msg.Id }, Request.Scheme)), email));
+                }
+                else
+                {
+                    //通知博主和上层所有关联的评论访客
+                    var pid = LeaveMessageService.GetParentMessageIdByChildId(msg.Id);
+                    var emails = LeaveMessageService.GetSelfAndAllChildrenMessagesByParentId(pid).Select(c => c.Email).Append(email).Except(new[] { msg.Email }).ToHashSet();
+                    string link = Url.Action("Index", "Msg", new { cid = msg.Id }, Request.Scheme);
+                    foreach (var s in emails)
                     {
-                        //通知博主和上层所有关联的评论访客
-                        var pid = LeaveMessageService.GetParentMessageIdByChildId(msg2.Id);
-                        var emails = LeaveMessageService.GetSelfAndAllChildrenMessagesByParentId(pid).Select(c => c.Email).ToList();
-                        emails.Add(email);
-                        string link = Url.Action("Index", "Msg", new { cid = msg2.Id }, Request.Scheme);
-                        foreach (var s in emails.Distinct().Except(new[] { msg2.Email }))
-                        {
-                            BackgroundJob.Enqueue(() => CommonHelper.SendMail($"{CommonHelper.SystemSettings["Domain"]}{CommonHelper.SystemSettings["Title"]} 留言回复:", content.Replace("{{link}}", link), s));
-                        }
+                        BackgroundJob.Enqueue(() => CommonHelper.SendMail($"{CommonHelper.SystemSettings["Domain"]}{CommonHelper.SystemSettings["Title"]} 留言回复:", content.Replace("{{link}}", link), s));
                     }
-#endif
-                    return ResultData(null, true, "留言发表成功,服务器正在后台处理中,这会有一定的延迟,稍后将会显示到列表中!");
                 }
-                BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Domain"] + "|博客新留言(待审核):", content.Replace("{{link}}", Url.Action("Index", "Msg", new
-                {
-                    cid = msg2.Id
-                }, Request.Scheme)) + "<p style='color:red;'>(待审核)</p>", email));
-                return ResultData(null, true, "留言发表成功,待站长审核通过以后将显示到列表中!");
+#endif
+                return ResultData(null, true, "留言发表成功,服务器正在后台处理中,这会有一定的延迟,稍后将会显示到列表中!");
             }
-            return ResultData(null, false, "留言发表失败!");
+
+            BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Domain"] + "|博客新留言(待审核):", content.Replace("{{link}}", Url.Action("Index", "Msg", new
+            {
+                cid = msg.Id
+            }, Request.Scheme)) + "<p style='color:red;'>(待审核)</p>", email));
+            return ResultData(null, true, "留言发表成功,待站长审核通过以后将显示到列表中!");
         }
 
         /// <summary>
@@ -201,12 +200,12 @@ namespace Masuit.MyBlogs.Core.Controllers
             bool b = LeaveMessageService.UpdateEntitySaved(msg);
 #if !DEBUG
             var pid = msg.ParentId == 0 ? msg.Id : LeaveMessageService.GetParentMessageIdByChildId(id);
-            string content = System.IO.File.ReadAllText(Path.Combine(HostingEnvironment.WebRootPath, "template", "notify.html")).Replace("{{time}}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")).Replace("{{nickname}}", msg.NickName).Replace("{{content}}", msg.Content);
-            var emails = LeaveMessageService.GetSelfAndAllChildrenMessagesByParentId(pid).Select(c => c.Email).Distinct().Except(new List<string>() { msg.Email }).ToList();
-            string link = Url.Action("Index", "Msg", new { cid = pid }, Request.Scheme);
+            var content = System.IO.File.ReadAllText(Path.Combine(HostingEnvironment.WebRootPath, "template", "notify.html")).Replace("{{time}}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")).Replace("{{nickname}}", msg.NickName).Replace("{{content}}", msg.Content);
+            var emails = LeaveMessageService.GetSelfAndAllChildrenMessagesByParentId(pid).Select(c => c.Email).Except(new List<string> { msg.Email, CurrentUser.Email }).ToHashSet();
+            var link = Url.Action("Index", "Msg", new { cid = pid }, Request.Scheme);
             foreach (var s in emails)
             {
-                BackgroundJob.Enqueue(() => CommonHelper.SendMail($"{Request.Host}{CommonHelper.SystemSettings["Title"]} 留言回复:", content.Replace("{{link}}", link), string.Join(",", s)));
+                BackgroundJob.Enqueue(() => CommonHelper.SendMail($"{Request.Host}{CommonHelper.SystemSettings["Title"]} 留言回复:", content.Replace("{{link}}", link), s));
             }
 #endif
             return ResultData(null, b, b ? "审核通过!" : "审核失败!");
@@ -231,7 +230,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Authority]
         public ActionResult GetPendingMsgs(int page = 1, int size = 10)
         {
-            List<LeaveMessageOutputDto> list = LeaveMessageService.LoadPageEntities<DateTime, LeaveMessageOutputDto>(page, size, out int total, m => m.Status == Status.Pending, l => l.PostDate, false).ToList();
+            var list = LeaveMessageService.LoadPageEntities<DateTime, LeaveMessageOutputDto>(page, size, out int total, m => m.Status == Status.Pending, l => l.PostDate, false).ToList();
             var pageCount = Math.Ceiling(total * 1.0 / size).ToInt32();
             return PageResult(list, pageCount, total);
         }
@@ -246,7 +245,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Authority]
         public ActionResult Read(int id)
         {
-            InternalMessage msg = MessageService.GetById(id);
+            var msg = MessageService.GetById(id);
             msg.Read = true;
             MessageService.UpdateEntitySaved(msg);
             return Content("ok");
@@ -260,7 +259,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Authority]
         public ActionResult Unread(int id)
         {
-            InternalMessage msg = MessageService.GetById(id);
+            var msg = MessageService.GetById(id);
             msg.Read = false;
             MessageService.UpdateEntitySaved(msg);
             return Content("ok");
@@ -322,7 +321,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Authority]
         public ActionResult MarkRead(int id)
         {
-            List<InternalMessage> msgs = MessageService.LoadEntities(m => m.Id <= id).ToList();
+            var msgs = MessageService.LoadEntities(m => m.Id <= id).ToList();
             foreach (var t in msgs)
             {
                 t.Read = true;

+ 45 - 49
src/Masuit.MyBlogs.Core/Controllers/NoticeController.cs

@@ -4,7 +4,6 @@ using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
 using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.Entity;
 using Masuit.MyBlogs.Core.Models.Enum;
-using Masuit.MyBlogs.Core.Models.ViewModel;
 using Masuit.Tools;
 using Masuit.Tools.Core.Net;
 using Masuit.Tools.Html;
@@ -53,27 +52,25 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("notice"), ResponseCache(Duration = 60, VaryByQueryKeys = new[] { "page", "size", "id" }, VaryByHeader = HeaderNames.Cookie)]
         public ActionResult Index(int page = 1, int size = 10, int id = 0)
         {
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo);
-            List<NoticeOutputDto> list;
-            int total;
-            if (user != null && user.IsAdmin)
+            var list = NoticeService.LoadPageEntities<DateTime, NoticeOutputDto>(page, size, out var total, n => n.Status == Status.Display, n => n.ModifyDate, false).ToList();
+            ViewBag.Total = total;
+            if (!CurrentUser.IsAdmin)
+            {
+                return View(list);
+            }
+
+            if (id == 0)
             {
-                if (id != 0)
-                {
-                    Notice notice = NoticeService.GetById(id);
-                    ViewBag.Total = 1;
-                    return View("Index_Admin", new List<NoticeOutputDto>
-                    {
-                        notice.MapTo<NoticeOutputDto>()
-                    });
-                }
-                list = NoticeService.LoadPageEntities<DateTime, NoticeOutputDto>(page, size, out total, n => n.Status == Status.Display, n => n.ModifyDate, false).ToList();
-                ViewBag.Total = total;
                 return View("Index_Admin", list);
             }
-            list = NoticeService.LoadPageEntities<DateTime, NoticeOutputDto>(page, size, out total, n => n.Status == Status.Display, n => n.ModifyDate, false).ToList();
-            ViewBag.Total = total;
-            return View(list);
+
+            var notice = NoticeService.GetById(id);
+            ViewBag.Total = 1;
+            return View("Index_Admin", new List<NoticeOutputDto>
+            {
+                notice.MapTo<NoticeOutputDto>()
+            });
+
         }
 
         /// <summary>
@@ -84,12 +81,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("n/{id:int}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "id" }, VaryByHeader = HeaderNames.Cookie)]
         public ActionResult Details(int id)
         {
-            Notice notice = NoticeService.GetById(id);
-            if (notice != null)
-            {
-                return View(notice);
-            }
-            return RedirectToAction("Index");
+            var notice = NoticeService.GetById(id) ?? throw new NotFoundException("页面未找到");
+            return View(notice);
         }
 
         /// <summary>
@@ -123,20 +116,18 @@ namespace Masuit.MyBlogs.Core.Controllers
                 return ResultData(null, false, "公告已经被删除!");
             }
 
-            var srcs = post.Content.MatchImgSrcs();
+            var srcs = post.Content.MatchImgSrcs().Where(s => s.StartsWith("/"));
             foreach (var path in srcs)
             {
-                if (path.StartsWith("/"))
+                try
+                {
+                    System.IO.File.Delete(HostingEnvironment.WebRootPath + path);
+                }
+                catch
                 {
-                    try
-                    {
-                        System.IO.File.Delete(HostingEnvironment.WebRootPath + path);
-                    }
-                    catch
-                    {
-                    }
                 }
             }
+
             bool b = NoticeService.DeleteByIdSaved(id);
             return ResultData(null, b, b ? "删除成功" : "删除失败");
         }
@@ -149,7 +140,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Authority]
         public async Task<ActionResult> Edit(Notice notice)
         {
-            Notice entity = NoticeService.GetById(notice.Id);
+            var entity = NoticeService.GetById(notice.Id);
             entity.ModifyDate = DateTime.Now;
             entity.Title = notice.Title;
             entity.Content = await _imagebedClient.ReplaceImgSrc(notice.Content.ClearImgAttributes());
@@ -165,7 +156,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <returns></returns>
         public ActionResult GetPageData(int page = 1, int size = 10)
         {
-            List<Notice> list = NoticeService.LoadPageEntitiesNoTracking(page, size, out int total, n => true, n => n.ModifyDate, false).ToList();
+            var list = NoticeService.LoadPageEntitiesNoTracking(page, size, out int total, n => true, n => n.ModifyDate, false).ToList();
             var pageCount = Math.Ceiling(total * 1.0 / size).ToInt32();
             return PageResult(list, pageCount, total);
         }
@@ -177,13 +168,15 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <returns></returns>
         public ActionResult Get(int id)
         {
-            Notice notice = NoticeService.GetById(id);
-            if (HttpContext.Session.Get("notice" + id) is null)
+            var notice = NoticeService.GetById(id);
+            if (HttpContext.Session.Get("notice" + id) != null)
             {
-                notice.ViewCount++;
-                NoticeService.UpdateEntitySaved(notice);
-                HttpContext.Session.Set("notice" + id, id.GetBytes());
+                return ResultData(notice.MapTo<NoticeOutputDto>());
             }
+
+            notice.ViewCount += 1;
+            NoticeService.UpdateEntitySaved(notice);
+            HttpContext.Session.Set("notice" + id, id.GetBytes());
             return ResultData(notice.MapTo<NoticeOutputDto>());
         }
 
@@ -195,17 +188,20 @@ namespace Masuit.MyBlogs.Core.Controllers
         public ActionResult Last()
         {
             var notice = NoticeService.GetFirstEntity(n => n.Status == Status.Display, n => n.ModifyDate, false);
-            if (notice != null)
+            if (notice == null)
+            {
+                return ResultData(null, false);
+            }
+
+            if (HttpContext.Session.Get("notice" + notice.Id) != null)
             {
-                if (HttpContext.Session.Get("notice" + notice.Id) is null)
-                {
-                    notice.ViewCount++;
-                    NoticeService.UpdateEntitySaved(notice);
-                    HttpContext.Session.Set("notice" + notice.Id, notice.Id.GetBytes());
-                }
                 return ResultData(notice.Mapper<NoticeOutputDto>());
             }
-            return ResultData(null, false);
+
+            notice.ViewCount += 1;
+            NoticeService.UpdateEntitySaved(notice);
+            HttpContext.Session.Set("notice" + notice.Id, notice.Id.GetBytes());
+            return ResultData(notice.Mapper<NoticeOutputDto>());
         }
     }
 }

+ 183 - 311
src/Masuit.MyBlogs.Core/Controllers/PostController.cs

@@ -1,6 +1,8 @@
-using EFSecondLevelCache.Core;
+using AutoMapper.QueryableExtensions;
+using EFSecondLevelCache.Core;
 using Hangfire;
 using Masuit.LuceneEFCore.SearchEngine.Interfaces;
+using Masuit.LuceneEFCore.SearchEngine.Linq;
 using Masuit.MyBlogs.Core.Common;
 using Masuit.MyBlogs.Core.Configs;
 using Masuit.MyBlogs.Core.Extensions;
@@ -21,11 +23,13 @@ using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc.Rendering;
+using Microsoft.EntityFrameworkCore.Internal;
 using Microsoft.Net.Http.Headers;
 using System;
-using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
 using System.IO;
 using System.Linq;
+using System.Linq.Expressions;
 using System.Net.Http;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
@@ -67,39 +71,28 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("{id:int}/{kw}"), Route("{id:int}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "id" }, VaryByHeader = HeaderNames.Cookie)]
         public ActionResult Details(int id, string kw)
         {
-            Post post = PostService.GetById(id);
-            if (post != null)
+            var post = PostService.GetFirstEntity(p => p.Id == id && (p.Status == Status.Pended || CurrentUser.IsAdmin)) ?? throw new NotFoundException("文章未找到");
+            ViewBag.Keyword = post.Keyword + "," + post.Label;
+            var modifyDate = post.ModifyDate;
+            ViewBag.Next = PostService.GetFirstEntity<DateTime, PostModelBase>(p => p.ModifyDate > modifyDate && (p.Status == Status.Pended || CurrentUser.IsAdmin), p => p.ModifyDate);
+            ViewBag.Prev = PostService.GetFirstEntity<DateTime, PostModelBase>(p => p.ModifyDate < modifyDate && (p.Status == Status.Pended || CurrentUser.IsAdmin), p => p.ModifyDate, false);
+            if (!string.IsNullOrEmpty(kw))
             {
-                ViewBag.Keyword = post.Keyword + "," + post.Label;
-                UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
-                DateTime modifyDate = post.ModifyDate;
-                ViewBag.Next = PostService.GetFirstEntity<DateTime, PostModelBase>(p => p.ModifyDate > modifyDate && (p.Status == Status.Pended || user.IsAdmin), p => p.ModifyDate);
-                ViewBag.Prev = PostService.GetFirstEntity<DateTime, PostModelBase>(p => p.ModifyDate < modifyDate && (p.Status == Status.Pended || user.IsAdmin), p => p.ModifyDate, false);
-                if (!string.IsNullOrEmpty(kw))
-                {
-                    ViewData["keywords"] = post.Content.Contains(kw) ? $"['{kw}']" : SearchEngine.LuceneIndexSearcher.CutKeywords(kw).ToJsonString();
-                }
-
-                if (user.IsAdmin)
-                {
-                    return View("Details_Admin", post);
-                }
-
-                if (post.Status != Status.Pended)
-                {
-                    return RedirectToAction("Post", "Home");
-                }
+                ViewData["keywords"] = post.Content.Contains(kw) ? $"['{kw}']" : SearchEngine.LuceneIndexSearcher.CutKeywords(kw).ToJsonString();
+            }
 
-                if (!HttpContext.Request.IsRobot() && string.IsNullOrEmpty(HttpContext.Session.Get<string>("post" + id)))
-                {
-                    HangfireHelper.CreateJob(typeof(IHangfireBackJob), nameof(HangfireBackJob.RecordPostVisit), args: id);
-                    HttpContext.Session.Set("post" + id, id.ToString());
-                }
+            if (CurrentUser.IsAdmin)
+            {
+                return View("Details_Admin", post);
+            }
 
-                return View(post);
+            if (!HttpContext.Request.IsRobot() && string.IsNullOrEmpty(HttpContext.Session.Get<string>("post" + id)))
+            {
+                HangfireHelper.CreateJob(typeof(IHangfireBackJob), nameof(HangfireBackJob.RecordPostVisit), args: id);
+                HttpContext.Session.Set("post" + id, id.ToString());
             }
 
-            return RedirectToAction("Index", "Error");
+            return View(post);
         }
 
         /// <summary>
@@ -112,25 +105,12 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("{id:int}/history"), Route("{id:int}/history/{page:int}/{size:int}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "id", "page", "size" }, VaryByHeader = HeaderNames.Cookie)]
         public ActionResult History(int id, int page = 1, int size = 20)
         {
-            var p = PostService.GetById(id).Mapper<PostOutputDto>();
-            if (p != null)
-            {
-                ViewBag.Primary = p;
-                var list = PostHistoryVersionService.LoadPageEntitiesNoTracking(page, size, out int total, v => v.PostId == id, v => v.ModifyDate, false).Select(v => new PostHistoryVersion()
-                {
-                    PostId = id,
-                    Category = v.Category,
-                    ModifyDate = v.ModifyDate,
-                    Title = v.Title,
-                    Id = v.Id,
-                    CategoryId = v.CategoryId
-                }).Cacheable().ToList();
-                ViewBag.Total = total;
-                ViewBag.PageCount = Math.Ceiling(total * 1.0 / size).ToInt32();
-                return View(list);
-            }
-
-            return RedirectToAction("Details", "Post", new { id });
+            var post = PostService.GetFirstEntity(p => p.Id == id && (p.Status == Status.Pended || CurrentUser.IsAdmin)).Mapper<PostOutputDto>() ?? throw new NotFoundException("文章未找到");
+            ViewBag.Primary = post;
+            var list = PostHistoryVersionService.LoadPageEntitiesNoTracking(page, size, out int total, v => v.PostId == id, v => v.ModifyDate, false).Cacheable().ToList();
+            ViewBag.Total = total;
+            ViewBag.PageCount = Math.Ceiling(total * 1.0 / size).ToInt32();
+            return View(list);
         }
 
         /// <summary>
@@ -142,21 +122,10 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("{id:int}/history/{hid:int}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "id", "hid" }, VaryByHeader = HeaderNames.Cookie)]
         public ActionResult HistoryVersion(int id, int hid)
         {
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
-            var post = PostHistoryVersionService.GetById(hid);
-            if (post is null)
-            {
-                return RedirectToAction("History", new { id });
-            }
-
+            var post = PostHistoryVersionService.GetById(hid) ?? throw new NotFoundException("文章未找到");
             ViewBag.Next = PostHistoryVersionService.GetFirstEntityNoTracking(p => p.PostId == id && p.ModifyDate > post.ModifyDate, p => p.ModifyDate);
             ViewBag.Prev = PostHistoryVersionService.GetFirstEntityNoTracking(p => p.PostId == id && p.ModifyDate < post.ModifyDate, p => p.ModifyDate, false);
-            if (user.IsAdmin)
-            {
-                return View("HistoryVersion_Admin", post);
-            }
-
-            return View(post);
+            return CurrentUser.IsAdmin ? View("HistoryVersion_Admin", post) : View(post);
         }
 
         /// <summary>
@@ -169,17 +138,12 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("{id:int}/history/{v1:int}-{v2:int}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "id", "v1", "v2" }, VaryByHeader = HeaderNames.Cookie)]
         public ActionResult CompareVersion(int id, int v1, int v2)
         {
-            var main = PostService.GetById(id).Mapper<PostHistoryVersion>();
+            var main = PostService.GetFirstEntity(p => p.Id == id && (p.Status == Status.Pended || CurrentUser.IsAdmin)).Mapper<PostHistoryVersion>() ?? throw new NotFoundException("文章未找到");
+            var left = v1 <= 0 ? main : PostHistoryVersionService.GetById(v1) ?? throw new NotFoundException("文章未找到");
+            var right = v2 <= 0 ? main : PostHistoryVersionService.GetById(v2) ?? throw new NotFoundException("文章未找到");
             main.Id = id;
-            var left = v1 <= 0 ? main : PostHistoryVersionService.GetById(v1);
-            var right = v2 <= 0 ? main : PostHistoryVersionService.GetById(v2);
-            if (left is null || right is null)
-            {
-                return RedirectToAction("History", "Post", new { id });
-            }
-
-            var diffHelper = new HtmlDiff.HtmlDiff(right.Content, left.Content);
-            string diffOutput = diffHelper.Build();
+            var diff = new HtmlDiff.HtmlDiff(right.Content, left.Content);
+            var diffOutput = diff.Build();
             right.Content = Regex.Replace(Regex.Replace(diffOutput, "<ins.+?</ins>", string.Empty), @"<\w+></\w+>", string.Empty);
             left.Content = Regex.Replace(Regex.Replace(diffOutput, "<del.+?</del>", string.Empty), @"<\w+></\w+>", string.Empty);
             return View(new[] { main, left, right });
@@ -198,16 +162,17 @@ namespace Masuit.MyBlogs.Core.Controllers
                 return ResultData(null, false, "您刚才已经投过票了,感谢您的参与!");
             }
 
-            if (post != null)
+            if (post == null)
             {
-                HttpContext.Session.Set("post-vote" + id, id.GetBytes());
-                ++post.VoteDownCount;
-                PostService.UpdateEntity(post);
-                var b = PostService.SaveChanges() > 0;
-                return ResultData(null, b, b ? "投票成功!" : "投票失败!");
+                return ResultData(null, false, "非法操作");
             }
 
-            return ResultData(null, false, "非法操作");
+            HttpContext.Session.Set("post-vote" + id, id.GetBytes());
+            post.VoteDownCount += 1;
+            PostService.UpdateEntity(post);
+            var b = PostService.SaveChanges() > 0;
+            return ResultData(null, b, b ? "投票成功!" : "投票失败!");
+
         }
 
         /// <summary>
@@ -223,16 +188,17 @@ namespace Masuit.MyBlogs.Core.Controllers
                 return ResultData(null, false, "您刚才已经投过票了,感谢您的参与!");
             }
 
-            if (post != null)
+            if (post == null)
             {
-                HttpContext.Session.Set("post-vote" + id, id.GetBytes());
-                ++post.VoteUpCount;
-                PostService.UpdateEntity(post);
-                var b = PostService.SaveChanges() > 0;
-                return ResultData(null, b, b ? "投票成功!" : "投票失败!");
+                return ResultData(null, false, "非法操作");
             }
 
-            return ResultData(null, false, "非法操作");
+            HttpContext.Session.Set("post-vote" + id, id.GetBytes());
+            post.VoteUpCount += 1;
+            PostService.UpdateEntity(post);
+            var b = PostService.SaveChanges() > 0;
+            return ResultData(null, b, b ? "投票成功!" : "投票失败!");
+
         }
 
         /// <summary>
@@ -241,23 +207,16 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <returns></returns>
         public ActionResult Publish()
         {
-            List<string> list = PostService.GetAll().Select(p => p.Label).ToList();
-            List<string> result = new List<string>();
-            list.ForEach(s =>
-            {
-                if (!string.IsNullOrEmpty(s))
-                {
-                    result.AddRange(s.Split(',', ','));
-                }
-            });
+            var list = PostService.LoadEntities(p => !string.IsNullOrEmpty(p.Label)).Select(p => p.Label).Distinct().SelectMany(s => s.Split(',', ',')).OrderBy(s => s).ToHashSet();
             ViewBag.Category = CategoryService.LoadEntitiesNoTracking(c => c.Status == Status.Available).ToList();
-            return View(result.Distinct().OrderBy(s => s));
+            return View(list);
         }
 
         /// <summary>
         /// 发布投稿
         /// </summary>
         /// <param name="post"></param>
+        /// <param name="code"></param>
         /// <returns></returns>
         [HttpPost, ValidateAntiForgeryToken]
         public async Task<ActionResult> Publish(PostInputDto post, string code)
@@ -272,28 +231,15 @@ namespace Masuit.MyBlogs.Core.Controllers
                 return ResultData(null, false, "您提交的内容包含敏感词,被禁止发表,请注意改善您的言辞!");
             }
 
-            if (!CategoryService.Any(c => c.Id == post.CategoryId && c.Status == Status.Available))
+            if (!CategoryService.Any(c => c.Id == post.CategoryId))
             {
                 return ResultData(null, message: "请选择一个分类");
             }
 
-            if (string.IsNullOrEmpty(post.Label?.Trim()))
-            {
-                post.Label = null;
-            }
-            else if (post.Label.Trim().Length > 50)
-            {
-                post.Label = post.Label.Replace(",", ",").Trim().Substring(0, 50);
-            }
-            else
-            {
-                post.Label = post.Label.Replace(",", ",");
-            }
-
+            post.Label = string.IsNullOrEmpty(post.Label?.Trim()) ? null : post.Label.Replace(",", ",");
             post.Status = Status.Pending;
             post.PostDate = DateTime.Now;
             post.ModifyDate = DateTime.Now;
-            post.Status = Status.Pended;
             post.Content = await _imagebedClient.ReplaceImgSrc(post.Content.HtmlSantinizerStandard().ClearImgAttributes());
             ViewBag.CategoryId = new SelectList(CategoryService.LoadEntitiesNoTracking(c => c.Status == Status.Available), "Id", "Name", post.CategoryId);
             Post p = post.Mapper<Post>();
@@ -301,25 +247,18 @@ namespace Masuit.MyBlogs.Core.Controllers
             p.Modifier = p.Author;
             p.ModifierEmail = p.Email;
             p = PostService.AddEntitySaved(p);
-            if (p != null)
+            if (p == null)
             {
-                RedisHelper.Expire("code:" + p.Email, 1);
-                if (p.Status == Status.Pending)
-                {
-                    var email = CommonHelper.SystemSettings["ReceiveEmail"];
-                    string link = Url.Action("Details", "Post", new { id = p.Id }, Request.Scheme);
-                    string content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/publish.html")
-                        .Replace("{{link}}", link)
-                        .Replace("{{time}}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
-                        .Replace("{{title}}", p.Title);
-                    BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Title"] + "有访客投稿:", content, email));
-                    return ResultData(p.Mapper<PostOutputDto>(), message: "文章发表成功,待站长审核通过以后将显示到列表中!");
-                }
-
-                return ResultData(p.Mapper<PostOutputDto>(), message: "文章发表成功!");
+                return ResultData(null, false, "文章发表失败!");
             }
 
-            return ResultData(null, false, "文章发表失败!");
+            RedisHelper.Expire("code:" + p.Email, 1);
+            var content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/publish.html")
+                .Replace("{{link}}", Url.Action("Details", "Post", new { id = p.Id }, Request.Scheme))
+                .Replace("{{time}}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
+                .Replace("{{title}}", p.Title);
+            BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Title"] + "有访客投稿:", content, CommonHelper.SystemSettings["ReceiveEmail"]));
+            return ResultData(p.Mapper<PostOutputDto>(), message: "文章发表成功,待站长审核通过以后将显示到列表中!");
         }
 
         /// <summary>
@@ -329,16 +268,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         [ResponseCache(Duration = 600, VaryByHeader = HeaderNames.Cookie)]
         public ActionResult GetTag()
         {
-            List<string> list = PostService.GetAll().Select(p => p.Label).ToList();
-            List<string> result = new List<string>();
-            list.ForEach(s =>
-            {
-                if (!string.IsNullOrEmpty(s))
-                {
-                    result.AddRange(s.Split(',', ','));
-                }
-            });
-            return ResultData(result.Distinct().OrderBy(s => s));
+            var list = PostService.LoadEntities(p => !string.IsNullOrEmpty(p.Label)).Select(p => p.Label).Distinct().SelectMany(s => s.Split(',', ',')).OrderBy(s => s).ToHashSet();
+            return ResultData(list);
         }
 
         /// <summary>
@@ -348,28 +279,19 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("all"), ResponseCache(Duration = 600, VaryByHeader = HeaderNames.Cookie)]
         public ActionResult All()
         {
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
-            List<string> tags = PostService.GetAll().Select(p => p.Label).ToList(); //tag
-            List<string> result = new List<string>();
-            tags.ForEach(s =>
-            {
-                if (!string.IsNullOrEmpty(s))
-                {
-                    result.AddRange(s.Split(',', ','));
-                }
-            });
-            ViewBag.tags = result.GroupBy(t => t).OrderByDescending(g => g.Count()).ThenBy(g => g.Key);
+            var tags = PostService.LoadEntities(p => !string.IsNullOrEmpty(p.Label)).Select(p => p.Label).SelectMany(s => s.Split(',', ',')).OrderBy(s => s).ToList(); //tag
+            ViewBag.tags = tags.GroupBy(t => t).OrderByDescending(g => g.Count()).ThenBy(g => g.Key);
             ViewBag.cats = CategoryService.GetAll(c => c.Post.Count, false).Select(c => new TagCloudViewModel
             {
                 Id = c.Id,
                 Name = c.Name,
-                Count = c.Post.Count(p => p.Status == Status.Pended || user.IsAdmin)
+                Count = c.Post.Count(p => p.Status == Status.Pended || CurrentUser.IsAdmin)
             }).ToList(); //category
             ViewBag.seminars = SeminarService.GetAll(c => c.Post.Count, false).Select(c => new TagCloudViewModel
             {
                 Id = c.Id,
                 Name = c.Title,
-                Count = c.Post.Count(p => p.Post.Status == Status.Pended || user.IsAdmin)
+                Count = c.Post.Count(p => p.Post.Status == Status.Pended || CurrentUser.IsAdmin)
             }).ToList(); //seminars
             return View();
         }
@@ -392,6 +314,14 @@ namespace Masuit.MyBlogs.Core.Controllers
             if (token.Equals(s))
             {
                 HttpContext.Session.Set("AccessViewToken", token);
+                Response.Cookies.Append("Email", email, new CookieOptions
+                {
+                    Expires = DateTime.Now.AddYears(1)
+                });
+                Response.Cookies.Append("PostAccessToken", email.MDString3(AppConfig.BaiduAK), new CookieOptions
+                {
+                    Expires = DateTime.Now.AddYears(1)
+                });
                 return ResultData(null);
             }
 
@@ -417,16 +347,17 @@ namespace Masuit.MyBlogs.Core.Controllers
                 return ResultData(null, false, "发送频率限制,请在2分钟后重新尝试发送邮件!请检查你的邮件,若未收到,请检查你的邮箱地址或邮件垃圾箱!");
             }
 
-            if (BroadcastService.Any(b => b.Email.Equals(email) && b.SubscribeType == SubscribeType.ArticleToken))
+            if (!BroadcastService.Any(b => b.Email.Equals(email) && b.SubscribeType == SubscribeType.ArticleToken))
             {
-                string token = SnowFlake.GetInstance().GetUniqueShortId(6);
-                RedisHelper.Set("token:" + email, token, 86400);
-                BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Domain"] + "博客访问验证码", $"{CommonHelper.SystemSettings["Domain"]}本次验证码是:<span style='color:red'>{token}</span>,有效期为24h,请按时使用!", email));
-                RedisHelper.Set("get:" + email, token, 120);
-                return ResultData(null);
+                return ResultData(null, false, "您目前没有权限访问这个链接,请联系站长开通访问权限!");
             }
 
-            return ResultData(null, false, "您目前没有权限访问这个链接,请联系站长开通访问权限!");
+            var token = SnowFlake.GetInstance().GetUniqueShortId(6);
+            RedisHelper.Set("token:" + email, token, 86400);
+            BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Domain"] + "博客访问验证码", $"{CommonHelper.SystemSettings["Domain"]}本次验证码是:<span style='color:red'>{token}</span>,有效期为24h,请按时使用!", email));
+            RedisHelper.Set("get:" + email, token, 120);
+            return ResultData(null);
+
         }
 
         /// <summary>
@@ -501,19 +432,21 @@ namespace Masuit.MyBlogs.Core.Controllers
             }
 
             var b = PostService.UpdateEntitySaved(post);
-            if (b)
+            if (!b)
             {
-                RedisHelper.Expire("code:" + dto.ModifierEmail, 1);
-                MessageService.AddEntitySaved(new InternalMessage()
-                {
-                    Title = $"来自【{dto.Modifier}】的文章修改合并请求",
-                    Content = dto.Title,
-                    Link = "#/merge/compare?id=" + merge.Id
-                });
-                var content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/merge-request.html").Replace("{{title}}", post.Title).Replace("{{link}}", Url.Action("Index", "Dashboard", new { }, Request.Scheme) + "#/merge/compare?id=" + merge.Id);
-                BackgroundJob.Enqueue(() => CommonHelper.SendMail("博客文章修改请求:", content, CommonHelper.SystemSettings["ReceiveEmail"]));
+                return ResultData(null, b, b ? "您的修改请求已提交,已进入审核状态,感谢您的参与!" : "操作失败!");
             }
 
+            RedisHelper.Expire("code:" + dto.ModifierEmail, 1);
+            MessageService.AddEntitySaved(new InternalMessage()
+            {
+                Title = $"来自【{dto.Modifier}】的文章修改合并请求",
+                Content = dto.Title,
+                Link = "#/merge/compare?id=" + merge.Id
+            });
+            var content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/merge-request.html").Replace("{{title}}", post.Title).Replace("{{link}}", Url.Action("Index", "Dashboard", new { }, Request.Scheme) + "#/merge/compare?id=" + merge.Id);
+            BackgroundJob.Enqueue(() => CommonHelper.SendMail("博客文章修改请求:", content, CommonHelper.SystemSettings["ReceiveEmail"]));
+
             return ResultData(null, b, b ? "您的修改请求已提交,已进入审核状态,感谢您的参与!" : "操作失败!");
         }
 
@@ -555,31 +488,34 @@ namespace Masuit.MyBlogs.Core.Controllers
             {
                 return ResultData(null, false, "审核失败!");
             }
-            if ("false" == CommonHelper.SystemSettings["DisabledEmailBroadcast"])
+
+            if ("true" == CommonHelper.SystemSettings["DisabledEmailBroadcast"])
             {
-                var cast = BroadcastService.LoadEntities(c => c.Status == Status.Subscribed).ToList();
-                string link = Request.Scheme + "://" + Request.Host + "/" + id;
-                cast.ForEach(c =>
-                {
-                    var ts = DateTime.Now.GetTotalMilliseconds();
-                    string content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/broadcast.html")
-                        .Replace("{{link}}", link + "?email=" + c.Email)
-                        .Replace("{{time}}", post.ModifyDate.ToString("yyyy-MM-dd HH:mm:ss"))
-                        .Replace("{{title}}", post.Title)
-                        .Replace("{{author}}", post.Author)
-                        .Replace("{{content}}", post.Content.RemoveHtmlTag(150))
-                        .Replace("{{cancel}}", Url.Action("Subscribe", "Subscribe", new
-                        {
-                            c.Email,
-                            act = "cancel",
-                            validate = c.ValidateCode,
-                            timespan = ts,
-                            hash = (c.Email + "cancel" + c.ValidateCode + ts).AESEncrypt(AppConfig.BaiduAK)
-                        }, Request.Scheme));
-                    BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Title"] + "博客有新文章发布了", content, c.Email));
-                });
+                return ResultData(null, true, "审核通过!");
             }
 
+            var cast = BroadcastService.LoadEntities(c => c.Status == Status.Subscribed).ToList();
+            var link = Request.Scheme + "://" + Request.Host + "/" + id;
+            cast.ForEach(c =>
+            {
+                var ts = DateTime.Now.GetTotalMilliseconds();
+                var content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/broadcast.html")
+                    .Replace("{{link}}", link + "?email=" + c.Email)
+                    .Replace("{{time}}", post.ModifyDate.ToString("yyyy-MM-dd HH:mm:ss"))
+                    .Replace("{{title}}", post.Title)
+                    .Replace("{{author}}", post.Author)
+                    .Replace("{{content}}", post.Content.RemoveHtmlTag(150))
+                    .Replace("{{cancel}}", Url.Action("Subscribe", "Subscribe", new
+                    {
+                        c.Email,
+                        act = "cancel",
+                        validate = c.ValidateCode,
+                        timespan = ts,
+                        hash = (c.Email + "cancel" + c.ValidateCode + ts).AESEncrypt(AppConfig.BaiduAK)
+                    }, Request.Scheme));
+                BackgroundJob.Enqueue(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Title"] + "博客有新文章发布了", content, c.Email));
+            });
+
             return ResultData(null, true, "审核通过!");
         }
 
@@ -667,7 +603,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         {
             Post post = PostService.GetById(id);
             PostOutputDto model = post.Mapper<PostOutputDto>();
-            model.Seminars = string.Join(",", post.Seminar.Select(s => s.Seminar.Title));
+            model.Seminars = post.Seminar.Select(s => s.Seminar.Title).Join(",");
             return ResultData(model);
         }
 
@@ -684,21 +620,10 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// </summary>
         /// <returns></returns>
         [Authority]
-        public ActionResult GetPageData(int page = 1, int size = 10, OrderBy orderby = OrderBy.ModifyDate, string kw = "")
+        public ActionResult GetPageData([Range(1, int.MaxValue, ErrorMessage = "页数必须大于0")]int page = 1, [Range(1, int.MaxValue, ErrorMessage = "页大小必须大于0")]int size = 10, OrderBy orderby = OrderBy.ModifyDate, string kw = "")
         {
-            if (page < 1)
-            {
-                page = 1;
-            }
-
-            if (size < 1)
-            {
-                page = 10;
-            }
-
-            var list = new List<PostDataModel>();
             IOrderedQueryable<Post> temp;
-            var query = string.IsNullOrEmpty(kw) ? PostService.GetAllNoTracking() : PostService.LoadEntitiesNoTracking(p => p.Title.Contains(kw) || p.Author.Contains(kw) || p.Email.Contains(kw) || p.Label.Contains(kw) || p.Content.Contains(kw));
+            var query = string.IsNullOrEmpty(kw) ? PostService.GetAll() : PostService.LoadEntities(p => p.Title.Contains(kw) || p.Author.Contains(kw) || p.Email.Contains(kw) || p.Label.Contains(kw) || p.Content.Contains(kw));
             var total = query.Count();
             var order = query.OrderByDescending(p => p.Status).ThenByDescending(p => p.IsFixedTop);
             switch (orderby)
@@ -723,33 +648,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                     break;
             }
 
-            var plist = temp.Skip((page - 1) * size).Take(size).Select(p => new
-            {
-                p.Id,
-                p.Author,
-                CategoryName = p.Category.Name,
-                p.Email,
-                p.IsFixedTop,
-                p.Label,
-                md = p.ModifyDate,
-                pd = p.PostDate,
-                p.Title,
-                ViewCount = p.TotalViewCount,
-                p.VoteDownCount,
-                p.VoteUpCount,
-                stat = p.Status,
-                ModifyCount = p.PostHistoryVersion.Count,
-                p.DisableComment
-            }).ToList();
-            plist.ForEach(item =>
-            {
-                PostDataModel model = item.MapTo<PostDataModel>();
-                model.PostDate = item.pd.ToString("yyyy-MM-dd HH:mm");
-                model.ModifyDate = item.md.ToString("yyyy-MM-dd HH:mm");
-                model.Status = item.stat.GetDisplay();
-                model.ModifyCount = item.ModifyCount;
-                list.Add(model);
-            });
+            var list = temp.Skip((page - 1) * size).Take(size).ProjectTo<PostDataModel>(MapperConfig).ToList();
             var pageCount = Math.Ceiling(total * 1.0 / size).ToInt32();
             return PageResult(list, pageCount, total);
         }
@@ -764,42 +663,14 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Authority]
         public ActionResult GetPending(int page = 1, int size = 10, string search = "")
         {
-            int total;
-            IQueryable<Post> temp;
-            if (string.IsNullOrEmpty(search))
-            {
-                temp = PostService.LoadPageEntitiesNoTracking(page, size, out total, p => p.Status == Status.Pending, p => p.Id);
-            }
-            else
+            Expression<Func<Post, bool>> where = p => p.Status == Status.Pending;
+            if (!string.IsNullOrEmpty(search))
             {
-                temp = PostService.LoadPageEntitiesNoTracking(page, size, out total, p => p.Status == Status.Pending && (p.Title.Contains(search) || p.Author.Contains(search) || p.Email.Contains(search) || p.Label.Contains(search)), p => p.Id);
+                where = where.And(p => p.Title.Contains(search) || p.Author.Contains(search) || p.Email.Contains(search) || p.Label.Contains(search));
             }
 
-            var plist = temp.OrderByDescending(p => p.IsFixedTop).ThenByDescending(p => p.ModifyDate).Select(p => new
-            {
-                p.Id,
-                p.Author,
-                CategoryName = p.Category.Name,
-                p.Email,
-                p.IsFixedTop,
-                p.Label,
-                md = p.ModifyDate,
-                pd = p.PostDate,
-                p.Title,
-                ViewCount = p.TotalViewCount,
-                p.VoteDownCount,
-                p.VoteUpCount,
-                stat = p.Status
-            }).ToList();
-            var list = new List<PostDataModel>();
-            plist.ForEach(item =>
-            {
-                PostDataModel model = item.MapTo<PostDataModel>();
-                model.PostDate = item.pd.ToString("yyyy-MM-dd HH:mm");
-                model.ModifyDate = item.md.ToString("yyyy-MM-dd HH:mm");
-                model.Status = item.stat.GetDisplay();
-                list.Add(model);
-            });
+            var temp = PostService.LoadPageEntitiesNoTracking(page, size, out var total, where, p => p.Id);
+            var list = temp.OrderByDescending(p => p.IsFixedTop).ThenByDescending(p => p.ModifyDate).ProjectTo<PostDataModel>(MapperConfig).ToList();
             var pageCount = Math.Ceiling(total * 1.0 / size).ToInt32();
             return PageResult(list, pageCount, total);
         }
@@ -984,43 +855,43 @@ namespace Masuit.MyBlogs.Core.Controllers
                     p.PostDate = timespan.Value;
                     p.ModifyDate = timespan.Value;
                     HangfireHelper.CreateJob(typeof(IHangfireBackJob), nameof(HangfireBackJob.PublishPost), args: p);
-                    return ResultData(p.Mapper<PostOutputDto>(), message: schedule ? $"文章于{timespan.Value:yyyy-MM-dd HH:mm:ss}将会自动发表!" : "文章发表成功!");
+                    return ResultData(p.Mapper<PostOutputDto>(), message: $"文章于{timespan.Value:yyyy-MM-dd HH:mm:ss}将会自动发表!");
                 }
 
                 return ResultData(null, false, "如果要定时发布,请选择正确的一个将来时间点!");
             }
 
             bool b = PostService.AddEntitySaved(p) != null;
-            if (b)
+            if (!b)
             {
-                if ("false" == CommonHelper.SystemSettings["DisabledEmailBroadcast"])
-                {
-                    var cast = BroadcastService.LoadEntities(c => c.Status == Status.Subscribed).ToList();
-                    string link = Request.Scheme + "://" + Request.Host + "/" + p.Id;
-                    cast.ForEach(c =>
-                    {
-                        var ts = DateTime.Now.GetTotalMilliseconds();
-                        string content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/broadcast.html")
-                            .Replace("{{link}}", link + "?email=" + c.Email)
-                            .Replace("{{time}}", post.ModifyDate.ToString("yyyy-MM-dd HH:mm:ss"))
-                            .Replace("{{title}}", post.Title).Replace("{{author}}", post.Author)
-                            .Replace("{{content}}", post.Content.RemoveHtmlTag(150))
-                            .Replace("{{cancel}}", Url.Action("Subscribe", "Subscribe", new
-                            {
-                                c.Email,
-                                act = "cancel",
-                                validate = c.ValidateCode,
-                                timespan = ts,
-                                hash = (c.Email + "cancel" + c.ValidateCode + ts).AESEncrypt(AppConfig.BaiduAK)
-                            }, Request.Scheme));
-                        BackgroundJob.Schedule(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Title"] + "博客有新文章发布了", content, c.Email), (p.ModifyDate - DateTime.Now));
-                    });
-                }
+                return ResultData(null, false, "文章发表失败!");
+            }
 
+            if ("true" == CommonHelper.SystemSettings["DisabledEmailBroadcast"])
+            {
                 return ResultData(null, true, "文章发表成功!");
             }
-
-            return ResultData(null, false, "文章发表失败!");
+            var cast = BroadcastService.LoadEntities(c => c.Status == Status.Subscribed).ToList();
+            string link = Request.Scheme + "://" + Request.Host + "/" + p.Id;
+            cast.ForEach(c =>
+            {
+                var ts = DateTime.Now.GetTotalMilliseconds();
+                string content = System.IO.File.ReadAllText(HostingEnvironment.WebRootPath + "/template/broadcast.html")
+                    .Replace("{{link}}", link + "?email=" + c.Email)
+                    .Replace("{{time}}", post.ModifyDate.ToString("yyyy-MM-dd HH:mm:ss"))
+                    .Replace("{{title}}", post.Title).Replace("{{author}}", post.Author)
+                    .Replace("{{content}}", post.Content.RemoveHtmlTag(150))
+                    .Replace("{{cancel}}", Url.Action("Subscribe", "Subscribe", new
+                    {
+                        c.Email,
+                        act = "cancel",
+                        validate = c.ValidateCode,
+                        timespan = ts,
+                        hash = (c.Email + "cancel" + c.ValidateCode + ts).AESEncrypt(AppConfig.BaiduAK)
+                    }, Request.Scheme));
+                BackgroundJob.Schedule(() => CommonHelper.SendMail(CommonHelper.SystemSettings["Title"] + "博客有新文章发布了", content, c.Email), (p.ModifyDate - DateTime.Now));
+            });
+            return ResultData(null, true, "文章发表成功!");
         }
 
         /// <summary>
@@ -1088,31 +959,32 @@ namespace Masuit.MyBlogs.Core.Controllers
         public ActionResult Revert(int id)
         {
             var history = PostHistoryVersionService.GetById(id);
-            if (history != null)
+            if (history == null)
+            {
+                return ResultData(null, false, "版本不存在");
+            }
+
+            history.Post.Category = history.Category;
+            history.Post.CategoryId = history.CategoryId;
+            history.Post.Content = history.Content;
+            history.Post.Title = history.Title;
+            history.Post.Label = history.Label;
+            history.Post.ModifyDate = history.ModifyDate;
+            history.Post.Seminar.Clear();
+            foreach (var s in history.Seminar)
             {
-                history.Post.Category = history.Category;
-                history.Post.CategoryId = history.CategoryId;
-                history.Post.Content = history.Content;
-                history.Post.Title = history.Title;
-                history.Post.Label = history.Label;
-                history.Post.Seminar.Clear();
-                foreach (var s in history.Seminar)
+                history.Post.Seminar.Add(new SeminarPost()
                 {
-                    history.Post.Seminar.Add(new SeminarPost()
-                    {
-                        Post = history.Post,
-                        PostId = history.PostId,
-                        Seminar = s.Seminar,
-                        SeminarId = s.SeminarId
-                    });
-                }
-                history.Post.ModifyDate = history.ModifyDate;
-                bool b = PostHistoryVersionService.UpdateEntitySaved(history);
-                PostHistoryVersionService.DeleteByIdSaved(id);
-                return ResultData(null, b, b ? "回滚成功" : "回滚失败");
+                    Post = history.Post,
+                    PostId = history.PostId,
+                    Seminar = s.Seminar,
+                    SeminarId = s.SeminarId
+                });
             }
+            bool b = PostHistoryVersionService.UpdateEntitySaved(history);
+            PostHistoryVersionService.DeleteByIdSaved(id);
+            return ResultData(null, b, b ? "回滚成功" : "回滚失败");
 
-            return ResultData(null, false, "版本不存在");
         }
 
         /// <summary>

+ 9 - 9
src/Masuit.MyBlogs.Core/Controllers/SearchController.cs

@@ -1,4 +1,5 @@
-using Masuit.MyBlogs.Core.Common;
+using CacheManager.Core;
+using Masuit.MyBlogs.Core.Common;
 using Masuit.MyBlogs.Core.Extensions;
 using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
 using Masuit.MyBlogs.Core.Models.DTO;
@@ -24,6 +25,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         public ISearchDetailsService SearchDetailsService { get; set; }
         public IPostService PostService { get; set; }
 
+        public ICacheManager<string> CacheManager { get; set; }
+
         /// <summary>
         /// 搜索页
         /// </summary>
@@ -44,8 +47,9 @@ namespace Masuit.MyBlogs.Core.Controllers
                 return RedirectToAction("Search");
             }
 
-            string key = "Search:" + HttpContext.Session.Id;
-            if (RedisHelper.Exists(key) && !RedisHelper.Get(key).Equals(wd))
+            string ip = HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
+            string key = "Search:" + ip;
+            if (CacheManager.Exists(key))
             {
                 var hotSearches = RedisHelper.Get<List<KeywordsRankOutputDto>>("SearchRank:Week").Take(10).ToList();
                 ViewBag.hotSearches = hotSearches;
@@ -73,8 +77,8 @@ namespace Masuit.MyBlogs.Core.Controllers
                 ViewBag.Total = posts.Total;
                 if (posts.Total > 1)
                 {
-                    RedisHelper.Set(key, wd);
-                    RedisHelper.Expire(key, TimeSpan.FromSeconds(10));
+                    CacheManager.AddOrUpdate(key, wd, s => wd);
+                    CacheManager.Expire(key, TimeSpan.FromSeconds(10));
                 }
 
                 ViewBag.hotSearches = new List<KeywordsRankOutputDto>();
@@ -95,10 +99,6 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Authority, HttpPost, ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "page", "size", "search" }, VaryByHeader = HeaderNames.Cookie)]
         public ActionResult SearchList(int page = 1, int size = 10, string search = "")
         {
-            if (page <= 0)
-            {
-                page = 1;
-            }
             var where = string.IsNullOrEmpty(search) ? (Expression<Func<SearchDetails, bool>>)(s => true) : s => s.KeyWords.Contains(search);
             var list = SearchDetailsService.LoadPageEntities<DateTime, SearchDetailsOutputDto>(page, size, out int total, where, s => s.SearchTime, false).ToList();
             var pageCount = Math.Ceiling(total * 1.0 / size).ToInt32();

+ 6 - 13
src/Masuit.MyBlogs.Core/Controllers/SeminarController.cs

@@ -4,8 +4,6 @@ using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
 using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.Entity;
 using Masuit.MyBlogs.Core.Models.Enum;
-using Masuit.MyBlogs.Core.Models.ViewModel;
-using Masuit.Tools.Core.Net;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Net.Http.Headers;
 using System;
@@ -43,13 +41,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         public ActionResult Index(int id, int page = 1, int size = 15, OrderBy orderBy = OrderBy.ModifyDate)
         {
             IList<Post> posts;
-            UserInfoOutputDto user = HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
-            var s = SeminarService.GetById(id);
-            if (s is null)
-            {
-                return RedirectToAction("Index", "Error");
-            }
-            var temp = PostService.LoadEntities(p => p.Seminar.Any(x => x.SeminarId == id) && (p.Status == Status.Pended || user.IsAdmin)).OrderByDescending(p => p.IsFixedTop);
+            var s = SeminarService.GetById(id) ?? throw new NotFoundException("文章未找到");
+            var temp = PostService.LoadEntities(p => p.Seminar.Any(x => x.SeminarId == id) && (p.Status == Status.Pended || CurrentUser.IsAdmin)).OrderByDescending(p => p.IsFixedTop);
             switch (orderBy)
             {
                 case OrderBy.CommentCount:
@@ -103,8 +96,8 @@ namespace Masuit.MyBlogs.Core.Controllers
             {
                 return ResultData(null, false, $"{seminar.Title} 已经存在了");
             }
-            //var b = SeminarService.AddOrUpdateSaved(s => s.Id, seminar) > 0;
-            Seminar entry = SeminarService.GetById(seminar.Id);
+
+            var entry = SeminarService.GetById(seminar.Id);
             bool b;
             if (entry is null)
             {
@@ -153,7 +146,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Authority]
         public ActionResult GetPageData(int page, int size)
         {
-            List<SeminarOutputDto> list = SeminarService.LoadPageEntities<int, SeminarOutputDto>(page, size, out int total, s => true, s => s.Id, false).ToList();
+            var list = SeminarService.LoadPageEntities<int, SeminarOutputDto>(page, size, out int total, s => true, s => s.Id, false).ToList();
             var pageCount = Math.Ceiling(total * 1.0 / size).ToInt32();
             return PageResult(list, pageCount, total);
         }
@@ -165,7 +158,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Authority]
         public ActionResult GetAll()
         {
-            List<SeminarOutputDto> list = SeminarService.GetAll<string, SeminarOutputDto>(s => s.Title).ToList();
+            var list = SeminarService.GetAll<string, SeminarOutputDto>(s => s.Title).ToList();
             return ResultData(list);
         }
 

+ 20 - 19
src/Masuit.MyBlogs.Core/Controllers/SubscribeController.cs

@@ -215,27 +215,28 @@ namespace Masuit.MyBlogs.Core.Controllers
                 return Content("操作失败,链接已被非法篡改");
             }
             Broadcast entity = BroadcastService.GetFirstEntity(b => b.Email.Equals(email, StringComparison.InvariantCultureIgnoreCase) && b.ValidateCode.Equals(validate));
-            if (entity != null)
+            if (entity == null)
             {
-                switch (act)
-                {
-                    case "verify":
-                        entity.Status = Status.Subscribed;
-                        entity.ValidateCode = Guid.NewGuid().ToString();
-                        entity.UpdateTime = DateTime.Now;
-                        BroadcastService.UpdateEntity(entity);
-                        BroadcastService.SaveChanges();
-                        return Content("订阅成功!");
-                    case "cancel":
-                        entity.Status = Status.Canceled;
-                        entity.UpdateTime = DateTime.Now;
-                        BroadcastService.UpdateEntity(entity);
-                        BroadcastService.SaveChanges();
-                        return Content("取消订阅成功,您将不会再接收到文章更新,如果您以后需要再次接收更新推送,可以到主站点重新进行订阅操作!");
-                    default: return RedirectToAction("Index", "Home");
-                }
+                return Content("该邮箱账户未使用邮件订阅!");
+            }
+
+            switch (act)
+            {
+                case "verify":
+                    entity.Status = Status.Subscribed;
+                    entity.ValidateCode = Guid.NewGuid().ToString();
+                    entity.UpdateTime = DateTime.Now;
+                    BroadcastService.UpdateEntity(entity);
+                    BroadcastService.SaveChanges();
+                    return Content("订阅成功!");
+                case "cancel":
+                    entity.Status = Status.Canceled;
+                    entity.UpdateTime = DateTime.Now;
+                    BroadcastService.UpdateEntity(entity);
+                    BroadcastService.SaveChanges();
+                    return Content("取消订阅成功,您将不会再接收到文章更新,如果您以后需要再次接收更新推送,可以到主站点重新进行订阅操作!");
+                default: return RedirectToAction("Index", "Home");
             }
-            return Content("该邮箱账户未使用邮件订阅!");
         }
 
 

+ 15 - 15
src/Masuit.MyBlogs.Core/Controllers/SystemController.cs

@@ -376,17 +376,17 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <returns></returns>
         public ActionResult AddToWhiteList(string ip)
         {
-            if (ip.MatchInetAddress())
+            if (!ip.MatchInetAddress())
             {
-                string ips = System.IO.File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "whitelist.txt"));
-                List<string> list = ips.Split(',').Where(s => !string.IsNullOrEmpty(s)).ToList();
-                list.Add(ip);
-                System.IO.File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "whitelist.txt"), string.Join(",", list.Distinct()), Encoding.UTF8);
-                CommonHelper.IPWhiteList = list;
-                return ResultData(null);
+                return ResultData(null, false);
             }
 
-            return ResultData(null, false);
+            string ips = System.IO.File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "whitelist.txt"));
+            List<string> list = ips.Split(',').Where(s => !string.IsNullOrEmpty(s)).ToList();
+            list.Add(ip);
+            System.IO.File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "whitelist.txt"), string.Join(",", list.Distinct()), Encoding.UTF8);
+            CommonHelper.IPWhiteList = list;
+            return ResultData(null);
         }
 
         /// <summary>
@@ -396,16 +396,16 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <returns></returns>
         public ActionResult AddToBlackList(string ip)
         {
-            if (ip.MatchInetAddress())
+            if (!ip.MatchInetAddress())
             {
-                CommonHelper.DenyIP += "," + ip;
-                System.IO.File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "denyip.txt"), CommonHelper.DenyIP, Encoding.UTF8);
-                CommonHelper.IPWhiteList.Remove(ip);
-                System.IO.File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "whitelist.txt"), string.Join(",", CommonHelper.IPWhiteList.Distinct()), Encoding.UTF8);
-                return ResultData(null);
+                return ResultData(null, false);
             }
 
-            return ResultData(null, false);
+            CommonHelper.DenyIP += "," + ip;
+            System.IO.File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "denyip.txt"), CommonHelper.DenyIP, Encoding.UTF8);
+            CommonHelper.IPWhiteList.Remove(ip);
+            System.IO.File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "whitelist.txt"), string.Join(",", CommonHelper.IPWhiteList.Distinct()), Encoding.UTF8);
+            return ResultData(null);
         }
 
         #endregion

+ 47 - 46
src/Masuit.MyBlogs.Core/Controllers/UploadController.cs

@@ -72,59 +72,60 @@ namespace Masuit.MyBlogs.Core.Controllers
         public ActionResult UploadWord()
         {
             var files = Request.Form.Files;
-            if (files.Count > 0 && files[0] != null)
+            if (files.Count <= 0)
             {
-                var file = files[0];
-                string fileName = file.FileName;
-                if (fileName != null && !Regex.IsMatch(Path.GetExtension(fileName), "doc|docx"))
-                {
-                    return ResultData(null, false, "文件格式不支持,只能上传doc或者docx的文档!");
-                }
+                return ResultData(null, false, "请先选择您需要上传的文件!");
+            }
 
-                if (fileName != null)
-                {
-                    string upload = HostingEnvironment.WebRootPath + "/upload";
-                    if (!Directory.Exists(upload))
-                    {
-                        Directory.CreateDirectory(upload);
-                    }
+            var file = files[0];
+            string fileName = file.FileName;
+            if (fileName == null)
+            {
+                return ResultData(null, false, "请先选择您需要上传的文件!");
+            }
+            if (!Regex.IsMatch(Path.GetExtension(fileName), "doc|docx"))
+            {
+                return ResultData(null, false, "文件格式不支持,只能上传doc或者docx的文档!");
+            }
 
-                    string resourceName = string.Empty.CreateShortToken(9);
-                    string ext = Path.GetExtension(fileName);
-                    string docPath = Path.Combine(upload, resourceName + ext);
-                    using (FileStream fs = new FileStream(docPath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
-                    {
-                        file.CopyTo(fs);
-                    }
-                    string htmlDir = docPath.Replace(".docx", "").Replace(".doc", "");
-                    DocumentConvert.Doc2Html(docPath, htmlDir);
-                    string htmlfile = Path.Combine(htmlDir, "index.html");
-                    string html = System.IO.File.ReadAllText(htmlfile).ReplaceHtmlImgSource("/upload/" + resourceName).ClearHtml().HtmlSantinizerStandard();
-                    ThreadPool.QueueUserWorkItem(state => System.IO.File.Delete(htmlfile));
-                    if (html.Length < 10)
-                    {
-                        Directory.Delete(htmlDir, true);
-                        System.IO.File.Delete(docPath);
-                        return ResultData(null, false, "读取文件内容失败,请检查文件的完整性,建议另存后重新上传!");
-                    }
+            string upload = HostingEnvironment.WebRootPath + "/upload";
+            if (!Directory.Exists(upload))
+            {
+                Directory.CreateDirectory(upload);
+            }
 
-                    if (html.Length > 1000000)
-                    {
-                        Directory.Delete(htmlDir, true);
-                        System.IO.File.Delete(docPath);
-                        return ResultData(null, false, "文档内容超长,服务器拒绝接收,请优化文档内容后再尝试重新上传!");
-                    }
+            string resourceName = string.Empty.CreateShortToken(9);
+            string ext = Path.GetExtension(fileName);
+            string docPath = Path.Combine(upload, resourceName + ext);
+            using (FileStream fs = new FileStream(docPath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
+            {
+                file.CopyTo(fs);
+            }
+            string htmlDir = docPath.Replace(".docx", "").Replace(".doc", "");
+            DocumentConvert.Doc2Html(docPath, htmlDir);
+            string htmlfile = Path.Combine(htmlDir, "index.html");
+            string html = System.IO.File.ReadAllText(htmlfile).ReplaceHtmlImgSource("/upload/" + resourceName).ClearHtml().HtmlSantinizerStandard();
+            ThreadPool.QueueUserWorkItem(state => System.IO.File.Delete(htmlfile));
+            if (html.Length < 10)
+            {
+                Directory.Delete(htmlDir, true);
+                System.IO.File.Delete(docPath);
+                return ResultData(null, false, "读取文件内容失败,请检查文件的完整性,建议另存后重新上传!");
+            }
 
-                    return ResultData(new
-                    {
-                        Title = Path.GetFileNameWithoutExtension(fileName),
-                        Content = html,
-                        ResourceName = resourceName + ext
-                    });
-                }
+            if (html.Length > 1000000)
+            {
+                Directory.Delete(htmlDir, true);
+                System.IO.File.Delete(docPath);
+                return ResultData(null, false, "文档内容超长,服务器拒绝接收,请优化文档内容后再尝试重新上传!");
             }
 
-            return ResultData(null, false, "请先选择您需要上传的文件!");
+            return ResultData(new
+            {
+                Title = Path.GetFileNameWithoutExtension(fileName),
+                Content = html,
+                ResourceName = resourceName + ext
+            });
         }
 
         #endregion

+ 0 - 35
src/Masuit.MyBlogs.Core/Extensions/AllowAccessFirewallAttribute.cs

@@ -6,48 +6,13 @@ namespace Masuit.MyBlogs.Core.Extensions
     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
     public class AllowAccessFirewallAttribute : Attribute, IFilterFactory, IOrderedFilter
     {
-        /// <summary>Creates an instance of the executable filter.</summary>
-        /// <param name="serviceProvider">The request <see cref="T:System.IServiceProvider" />.</param>
-        /// <returns>An instance of the executable filter.</returns>
         public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
         {
             return new AllowAccessFirewallAttribute();
         }
 
-        /// <summary>
-        /// Gets a value that indicates if the result of <see cref="M:Microsoft.AspNetCore.Mvc.Filters.IFilterFactory.CreateInstance(System.IServiceProvider)" />
-        /// can be reused across requests.
-        /// </summary>
         public bool IsReusable => true;
 
-        /// <summary>
-        /// Gets the order value for determining the order of execution of filters. Filters execute in
-        /// ascending numeric value of the <see cref="P:Microsoft.AspNetCore.Mvc.Filters.IOrderedFilter.Order" /> property.
-        /// </summary>
-        /// <remarks>
-        /// <para>
-        /// Filters are executed in an ordering determined by an ascending sort of the <see cref="P:Microsoft.AspNetCore.Mvc.Filters.IOrderedFilter.Order" /> property.
-        /// </para>
-        /// <para>
-        /// Asynchronous filters, such as <see cref="T:Microsoft.AspNetCore.Mvc.Filters.IAsyncActionFilter" />, surround the execution of subsequent
-        /// filters of the same filter kind. An asynchronous filter with a lower numeric <see cref="P:Microsoft.AspNetCore.Mvc.Filters.IOrderedFilter.Order" />
-        /// value will have its filter method, such as <see cref="M:Microsoft.AspNetCore.Mvc.Filters.IAsyncActionFilter.OnActionExecutionAsync(Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext,Microsoft.AspNetCore.Mvc.Filters.ActionExecutionDelegate)" />,
-        /// executed before that of a filter with a higher value of <see cref="P:Microsoft.AspNetCore.Mvc.Filters.IOrderedFilter.Order" />.
-        /// </para>
-        /// <para>
-        /// Synchronous filters, such as <see cref="T:Microsoft.AspNetCore.Mvc.Filters.IActionFilter" />, have a before-method, such as
-        /// <see cref="M:Microsoft.AspNetCore.Mvc.Filters.IActionFilter.OnActionExecuting(Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext)" />, and an after-method, such as
-        /// <see cref="M:Microsoft.AspNetCore.Mvc.Filters.IActionFilter.OnActionExecuted(Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext)" />. A synchronous filter with a lower numeric <see cref="P:Microsoft.AspNetCore.Mvc.Filters.IOrderedFilter.Order" />
-        /// value will have its before-method executed before that of a filter with a higher value of
-        /// <see cref="P:Microsoft.AspNetCore.Mvc.Filters.IOrderedFilter.Order" />. During the after-stage of the filter, a synchronous filter with a lower
-        /// numeric <see cref="P:Microsoft.AspNetCore.Mvc.Filters.IOrderedFilter.Order" /> value will have its after-method executed after that of a filter with a higher
-        /// value of <see cref="P:Microsoft.AspNetCore.Mvc.Filters.IOrderedFilter.Order" />.
-        /// </para>
-        /// <para>
-        /// If two filters have the same numeric value of <see cref="P:Microsoft.AspNetCore.Mvc.Filters.IOrderedFilter.Order" />, then their relative execution order
-        /// is determined by the filter scope.
-        /// </para>
-        /// </remarks>
         public int Order { get; }
     }
 }

+ 27 - 25
src/Masuit.MyBlogs.Core/Extensions/AuthorityAttribute.cs

@@ -29,38 +29,29 @@ namespace Masuit.MyBlogs.Core.Extensions
             }
 #if !DEBUG
             UserInfoOutputDto user = filterContext.HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo);
-            if (user == null || !user.IsAdmin)
+            if (user != null && user.IsAdmin)
             {
-                //先尝试自动登录
-                if (filterContext.HttpContext.Request.Cookies.Count > 2)
-                {
-                    string name = filterContext.HttpContext.Request.Cookies["username"] ?? "";
-                    string pwd = filterContext.HttpContext.Request.Cookies["password"]?.DesDecrypt(AppConfig.BaiduAK) ?? "";
+                return;
+            }
 
-                    var userInfo = (Startup.AutofacContainer.GetService(typeof(IUserInfoService)) as IUserInfoService).Login(name, pwd);
-                    if (userInfo != null)
-                    {
-                        filterContext.HttpContext.Response.Cookies.Append("username", name, new CookieOptions() { Expires = DateTime.Now.AddDays(7) });
-                        filterContext.HttpContext.Response.Cookies.Append("password", filterContext.HttpContext.Request.Cookies["password"], new CookieOptions() { Expires = DateTime.Now.AddDays(7) });
-                        filterContext.HttpContext.Session.Set(SessionKey.UserInfo, userInfo);
-                    }
-                    else
-                    {
-                        if (filterContext.HttpContext.Request.Method.ToLower().Equals("get"))
-                        {
-                            filterContext.Result = new RedirectResult("/passport/login?from=" + HttpUtility.UrlEncode(filterContext.HttpContext.Request.Path.ToString())?.Replace("#", "%23"));
-                        }
-                        else
-                        {
-                            filterContext.Result = new UnauthorizedObjectResult(new { StatusCode = 401, Success = false, IsLogin = false, Message = "未登录系统,请先登录!" });
-                        }
-                    }
+            //先尝试自动登录
+            if (filterContext.HttpContext.Request.Cookies.Any(x => x.Key == "username" || x.Key == "password"))
+            {
+                string name = filterContext.HttpContext.Request.Cookies["username"] ?? "";
+                string pwd = filterContext.HttpContext.Request.Cookies["password"]?.DesDecrypt(AppConfig.BaiduAK) ?? "";
+
+                var userInfo = (Startup.AutofacContainer.GetService(typeof(IUserInfoService)) as IUserInfoService).Login(name, pwd);
+                if (userInfo != null)
+                {
+                    filterContext.HttpContext.Response.Cookies.Append("username", name, new CookieOptions() { Expires = DateTime.Now.AddDays(7) });
+                    filterContext.HttpContext.Response.Cookies.Append("password", filterContext.HttpContext.Request.Cookies["password"], new CookieOptions() { Expires = DateTime.Now.AddDays(7) });
+                    filterContext.HttpContext.Session.Set(SessionKey.UserInfo, userInfo);
                 }
                 else
                 {
                     if (filterContext.HttpContext.Request.Method.ToLower().Equals("get"))
                     {
-                        filterContext.Result = new RedirectResult("/passport/login?from=" + HttpUtility.UrlEncode(filterContext.HttpContext.Request.Path.ToString()));
+                        filterContext.Result = new RedirectResult("/passport/login?from=" + HttpUtility.UrlEncode(filterContext.HttpContext.Request.Path.ToString())?.Replace("#", "%23"));
                     }
                     else
                     {
@@ -68,6 +59,17 @@ namespace Masuit.MyBlogs.Core.Extensions
                     }
                 }
             }
+            else
+            {
+                if (filterContext.HttpContext.Request.Method.ToLower().Equals("get"))
+                {
+                    filterContext.Result = new RedirectResult("/passport/login?from=" + HttpUtility.UrlEncode(filterContext.HttpContext.Request.Path.ToString()));
+                }
+                else
+                {
+                    filterContext.Result = new UnauthorizedObjectResult(new { StatusCode = 401, Success = false, IsLogin = false, Message = "未登录系统,请先登录!" });
+                }
+            }
 #endif
         }
     }

+ 0 - 89
src/Masuit.MyBlogs.Core/Extensions/ExceptionMiddleware.cs

@@ -1,89 +0,0 @@
-using Masuit.Tools.Logging;
-using Microsoft.AspNetCore.Http;
-using Microsoft.EntityFrameworkCore;
-using Newtonsoft.Json;
-using System;
-using System.Text;
-using System.Threading.Tasks;
-using System.Web;
-
-namespace Masuit.MyBlogs.Core.Extensions
-{
-    /// <summary>
-    /// 异常拦截中间件
-    /// </summary>
-    public class ExceptionMiddleware
-    {
-        private readonly RequestDelegate _next;
-
-        /// <summary>
-        /// 异常拦截中间件
-        /// </summary>
-        /// <param name="next"></param>
-        public ExceptionMiddleware(RequestDelegate next)
-        {
-            _next = next;
-        }
-
-        /// <summary>
-        /// 执行调用
-        /// </summary>
-        /// <param name="context"></param>
-        /// <returns></returns>
-        public async Task Invoke(HttpContext context)
-        {
-            try
-            {
-                await _next.Invoke(context);
-            }
-            catch (DbUpdateConcurrencyException ex)
-            {
-                var err = $"异常源:{ex.Source},异常类型:{ex.GetType().Name},\n请求路径:{context.Request.Scheme}://{context.Request.Host}{HttpUtility.UrlDecode(context.Request.Path)},请求参数:{HttpUtility.UrlDecode(context.Request.Body.ReadToEnd(Encoding.UTF8))},客户端用户代理:{context.Request.Headers["User-Agent"]},客户端IP:{context.Connection.RemoteIpAddress}\t{ex.InnerException?.Message}\t";
-                LogManager.Error(err, ex);
-                await RedirectError(context);
-            }
-            catch (DbUpdateException ex)
-            {
-                var err = $"异常源:{ex.Source},异常类型:{ex.GetType().Name},\n请求路径:{context.Request.Scheme}://{context.Request.Host}{HttpUtility.UrlDecode(context.Request.Path)},请求参数:{HttpUtility.UrlDecode(context.Request.Body.ReadToEnd(Encoding.UTF8))},客户端用户代理:{context.Request.Headers["User-Agent"]},客户端IP:{context.Connection.RemoteIpAddress}\t{ex?.InnerException?.Message}\t";
-                LogManager.Error(err, ex);
-                await RedirectError(context);
-            }
-            catch (AggregateException ex)
-            {
-                LogManager.Debug("↓↓↓" + ex.Message + "↓↓↓");
-                ex.Handle(e =>
-                {
-                    LogManager.Error($"异常源:{e.Source},异常类型:{e.GetType().Name},\n请求路径:{context.Request.Scheme}://{context.Request.Host}{HttpUtility.UrlDecode(context.Request.Path)},请求参数:{HttpUtility.UrlDecode(context.Request.Body.ReadToEnd(Encoding.UTF8))},客户端用户代理:{context.Request.Headers["User-Agent"]},客户端IP:{context.Connection.RemoteIpAddress}\t", e);
-                    return true;
-                });
-                await RedirectError(context);
-            }
-            catch (Exception ex)
-            {
-                //LogManager.Error(ex);
-                LogManager.Error($"异常源:{ex.Source},异常类型:{ex.GetType().Name},\n请求路径:{context.Request.Scheme}://{context.Request.Host}{HttpUtility.UrlDecode(context.Request.Path)},请求参数:{HttpUtility.UrlDecode(context.Request.Body.ReadToEnd(Encoding.UTF8))},客户端用户代理:{context.Request.Headers["User-Agent"]},客户端IP:{context.Connection.RemoteIpAddress}\t", ex);
-                await RedirectError(context);
-            }
-        }
-
-        private static async Task RedirectError(HttpContext context)
-        {
-            switch (context.Request.Method.ToLower())
-            {
-                case "get":
-                    context.Response.Redirect("/ServiceUnavailable");
-                    break;
-                default:
-                    context.Response.ContentType = "application/json";
-                    context.Response.StatusCode = 503;
-                    await context.Response.WriteAsync(JsonConvert.SerializeObject(new
-                    {
-                        StatusCode = 503,
-                        Success = false,
-                        Message = "服务器发生错误!"
-                    }));
-                    break;
-            }
-        }
-    }
-}

+ 34 - 13
src/Masuit.MyBlogs.Core/Extensions/FirewallAttribute.cs

@@ -1,17 +1,22 @@
-using System;
-using System.Linq;
-using System.Web;
+using CacheManager.Core;
 using Hangfire;
 using Masuit.MyBlogs.Core.Common;
+using Masuit.MyBlogs.Core.Configs;
 using Masuit.MyBlogs.Core.Extensions.Hangfire;
 using Masuit.Tools.Core.Net;
+using Masuit.Tools.Security;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc.Filters;
+using System;
+using System.Linq;
+using System.Web;
 
 namespace Masuit.MyBlogs.Core.Extensions
 {
     public class FirewallAttribute : ActionFilterAttribute
     {
+        public ICacheManager<int> CacheManager { get; set; }
+
         /// <inheritdoc />
         public override void OnActionExecuting(ActionExecutingContext context)
         {
@@ -26,8 +31,13 @@ namespace Masuit.MyBlogs.Core.Extensions
                 return;
             }
 
+            if (context.HttpContext.Request.Cookies["Email"].MDString3(AppConfig.BaiduAK).Equals(context.HttpContext.Request.Cookies["FullAccessToken"]))
+            {
+                return;
+            }
+
             string ip = context.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
-            if (ip.IsDenyIpAddress() && string.IsNullOrEmpty(context.HttpContext.Session.Get<string>("AccessViewToken")))
+            if (ip.IsDenyIpAddress() && string.IsNullOrEmpty(context.HttpContext.Session.Get<string>("FullAccessViewToken")))
             {
                 BackgroundJob.Enqueue(() => HangfireBackJob.InterceptLog(new IpIntercepter()
                 {
@@ -44,19 +54,30 @@ namespace Masuit.MyBlogs.Core.Extensions
                 return;
             }
 
-            try
+            var times = CacheManager.AddOrUpdate("Frequency:" + ip, 1, i =>
             {
-                var times = RedisHelper.IncrBy("Frequency:" + context.HttpContext.Session.Id);
-                RedisHelper.Expire("Frequency:" + context.HttpContext.Session.Id, TimeSpan.FromMinutes(1));
-                if (times > 300)
-                {
-                    context.Result = new RedirectToActionResult("TempDeny", "Error", null);
-                }
+                i++;
+                return i;
+            }, 5);
+            CacheManager.Expire("Frequency:" + ip, ExpirationMode.Sliding, TimeSpan.FromSeconds(CommonHelper.SystemSettings.GetOrAdd("LimitIPFrequency", "60").ToInt32()));
+            var limit = CommonHelper.SystemSettings.GetOrAdd("LimitIPRequestTimes", "90").ToInt32();
+            if (times <= limit)
+            {
+                return;
             }
-            catch
+
+            if (times > limit * 1.2)
             {
-                // ignore
+                CacheManager.Expire("Frequency:" + ip, ExpirationMode.Sliding, TimeSpan.FromMinutes(CommonHelper.SystemSettings.GetOrAdd("BanIPTimespan", "10").ToInt32()));
+                BackgroundJob.Enqueue(() => HangfireBackJob.InterceptLog(new IpIntercepter()
+                {
+                    IP = ip,
+                    RequestUrl = HttpUtility.UrlDecode(context.HttpContext.Request.Scheme + "://" + context.HttpContext.Request.Host + context.HttpContext.Request.Path),
+                    Time = DateTime.Now
+                }));
             }
+
+            context.Result = new RedirectResult("/tempdeny");
         }
     }
 }

+ 0 - 85
src/Masuit.MyBlogs.Core/Extensions/FirewallMiddleware.cs

@@ -1,85 +0,0 @@
-using Hangfire;
-using Masuit.MyBlogs.Core.Common;
-using Masuit.MyBlogs.Core.Extensions.Hangfire;
-using Masuit.Tools;
-using Masuit.Tools.Core.Net;
-using Microsoft.AspNetCore.Http;
-using System;
-using System.Threading.Tasks;
-using System.Web;
-
-namespace Masuit.MyBlogs.Core.Extensions
-{
-    /// <summary>
-    /// 网站防火墙
-    /// </summary>
-    public class FirewallMiddleware
-    {
-        private readonly RequestDelegate _next;
-
-        /// <summary>
-        /// 构造函数
-        /// </summary>
-        /// <param name="next"></param>
-        public FirewallMiddleware(RequestDelegate next)
-        {
-            _next = next;
-        }
-
-        /// <summary>
-        /// 执行调用
-        /// </summary>
-        /// <param name="context"></param>
-        /// <returns></returns>
-        public async Task Invoke(HttpContext context)
-        {
-            string httpMethod = context.Request.Method;
-            if (httpMethod.Equals("OPTIONS", StringComparison.InvariantCultureIgnoreCase) || httpMethod.Equals("HEAD", StringComparison.InvariantCultureIgnoreCase))
-            {
-                return;
-            }
-
-            if (context.Request.IsRobot())
-            {
-                await _next.Invoke(context);
-                return;
-            }
-
-            if (context.Request.Path.ToString().Contains(new[] { "error", "serviceunavailable", "accessdeny", "tempdeny" }))
-            {
-                await _next.Invoke(context);
-                return;
-            }
-
-            string ip = context.Connection.RemoteIpAddress.MapToIPv4().ToString();
-            if (ip.IsDenyIpAddress() && string.IsNullOrEmpty(context.Session.Get<string>("AccessViewToken")))
-            {
-                BackgroundJob.Enqueue(() => HangfireBackJob.InterceptLog(new IpIntercepter()
-                {
-                    IP = ip,
-                    RequestUrl = HttpUtility.UrlDecode(context.Request.Scheme + "://" + context.Request.Host + context.Request.Path),
-                    Time = DateTime.Now
-                }));
-                context.Response.Redirect("/accessdeny", true);
-                return;
-            }
-
-            try
-            {
-                var times = RedisHelper.IncrBy("Frequency:" + context.Session.Id);
-                RedisHelper.Expire("Frequency:" + context.Session.Id, TimeSpan.FromMinutes(1));
-                if (times > 300)
-                {
-                    context.Response.Redirect("/tempdeny", true);
-                    return;
-                }
-            }
-            catch
-            {
-                // ignore
-            }
-
-            await _next.Invoke(context);
-        }
-    }
-}

+ 37 - 30
src/Masuit.MyBlogs.Core/Extensions/Hangfire/HangfireBackJob.cs

@@ -65,24 +65,30 @@ namespace Masuit.MyBlogs.Core.Extensions.Hangfire
         public void LoginRecord(UserInfoOutputDto userInfo, string ip, LoginType type)
         {
             var result = ip.GetPhysicsAddressInfo().Result;
-            if (result?.Status == 0)
+            if (result?.Status != 0)
             {
-                string addr = result.AddressResult.FormattedAddress;
-                string prov = result.AddressResult.AddressComponent.Province;
-                LoginRecord record = new LoginRecord()
-                {
-                    IP = ip,
-                    LoginTime = DateTime.Now,
-                    LoginType = type,
-                    PhysicAddress = addr,
-                    Province = prov
-                };
-                UserInfo u = _userInfoService.GetByUsername(userInfo.Username);
-                u.LoginRecord.Add(record);
-                _userInfoService.UpdateEntitySaved(u);
-                string content = File.ReadAllText(Path.Combine(_hostingEnvironment.WebRootPath, "template", "login.html")).Replace("{{name}}", u.Username).Replace("{{time}}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")).Replace("{{ip}}", record.IP).Replace("{{address}}", record.PhysicAddress);
-                CommonHelper.SendMail(_settingService.GetFirstEntity(s => s.Name.Equals("Title")).Value + "账号登录通知", content, _settingService.GetFirstEntity(s => s.Name.Equals("ReceiveEmail")).Value);
+                return;
             }
+
+            string addr = result.AddressResult.FormattedAddress;
+            string prov = result.AddressResult.AddressComponent.Province;
+            var record = new LoginRecord()
+            {
+                IP = ip,
+                LoginTime = DateTime.Now,
+                LoginType = type,
+                PhysicAddress = addr,
+                Province = prov
+            };
+            var u = _userInfoService.GetByUsername(userInfo.Username);
+            u.LoginRecord.Add(record);
+            _userInfoService.UpdateEntitySaved(u);
+            var content = File.ReadAllText(Path.Combine(_hostingEnvironment.WebRootPath, "template", "login.html"))
+                .Replace("{{name}}", u.Username)
+                .Replace("{{time}}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
+                .Replace("{{ip}}", record.IP)
+                .Replace("{{address}}", record.PhysicAddress);
+            CommonHelper.SendMail(_settingService.GetFirstEntity(s => s.Name.Equals("Title")).Value + "账号登录通知", content, _settingService.GetFirstEntity(s => s.Name.Equals("ReceiveEmail")).Value);
         }
 
         /// <summary>
@@ -94,10 +100,10 @@ namespace Masuit.MyBlogs.Core.Extensions.Hangfire
             p.Status = Status.Pended;
             p.PostDate = DateTime.Now;
             p.ModifyDate = DateTime.Now;
-            Post post = _postService.GetById(p.Id);
+            var post = _postService.GetById(p.Id);
             if (post is null)
             {
-                _postService.AddEntitySaved(post);
+                _postService.AddEntitySaved(p);
             }
             else
             {
@@ -114,14 +120,16 @@ namespace Masuit.MyBlogs.Core.Extensions.Hangfire
         /// <param name="pid"></param>
         public void RecordPostVisit(int pid)
         {
-            Post post = _postService.GetById(pid);
-            if (post != null)
+            var post = _postService.GetById(pid);
+            if (post == null)
             {
-                post.TotalViewCount += 1;
-                post.AverageViewCount = post.TotalViewCount / (DateTime.Now - post.PostDate).TotalDays;
-                _postService.UpdateEntity(post);
-                _postService.SaveChanges();
+                return;
             }
+
+            post.TotalViewCount += 1;
+            post.AverageViewCount = post.TotalViewCount / (DateTime.Now - post.PostDate).TotalDays;
+            _postService.UpdateEntity(post);
+            _postService.SaveChanges();
         }
 
         /// <summary>
@@ -163,14 +171,13 @@ namespace Masuit.MyBlogs.Core.Extensions.Hangfire
         public void CheckLinks()
         {
             var links = _linksService.LoadEntities(l => !l.Except).AsParallel();
+            var client = _httpClientFactory.CreateClient();
+            client.DefaultRequestHeaders.UserAgent.Add(ProductInfoHeaderValue.Parse("Mozilla/5.0"));
+            client.DefaultRequestHeaders.Referrer = new Uri("https://masuit.com");
+            client.Timeout = TimeSpan.FromSeconds(10);
             Parallel.ForEach(links, link =>
             {
-                Uri uri = new Uri(link.Url);
-                HttpClient client = _httpClientFactory.CreateClient();
-                client.DefaultRequestHeaders.UserAgent.Add(ProductInfoHeaderValue.Parse("Mozilla/5.0"));
-                client.DefaultRequestHeaders.Referrer = new Uri("https://masuit.com");
-                client.Timeout = TimeSpan.FromHours(10);
-                client.GetAsync(uri).ContinueWith(async t =>
+                client.GetAsync(link.Url).ContinueWith(async t =>
                 {
                     if (t.IsCanceled || t.IsFaulted)
                     {

+ 7 - 7
src/Masuit.MyBlogs.Core/Extensions/MyExceptionFilter.cs

@@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc.Filters;
 using Microsoft.EntityFrameworkCore;
 using System;
-using System.Text;
 using System.Web;
 
 namespace Masuit.MyBlogs.Core.Extensions
@@ -19,18 +18,18 @@ namespace Masuit.MyBlogs.Core.Extensions
             switch (context.Exception)
             {
                 case DbUpdateConcurrencyException ex:
-                    err = $"异常源:{ex.Source},异常类型:{ex.GetType().Name},\n请求路径:{req.Scheme}://{req.Host}{HttpUtility.UrlDecode(req.Path)},请求参数:{HttpUtility.UrlDecode(req.Body.ReadToEnd(Encoding.UTF8))},客户端用户代理:{req.Headers["User-Agent"]},客户端IP:{context.HttpContext.Connection.RemoteIpAddress}\t{ex.InnerException?.Message}\t";
+                    err = $"异常源:{ex.Source},异常类型:{ex.GetType().Name},\n请求路径:{req.Scheme}://{req.Host}{HttpUtility.UrlDecode(req.Path)},客户端用户代理:{req.Headers["User-Agent"]},客户端IP:{context.HttpContext.Connection.RemoteIpAddress}\t{ex.InnerException?.Message}\t";
                     LogManager.Error(err, ex);
                     break;
                 case DbUpdateException ex:
-                    err = $"异常源:{ex.Source},异常类型:{ex.GetType().Name},\n请求路径:{req.Scheme}://{req.Host}{HttpUtility.UrlDecode(req.Path)},请求参数:{HttpUtility.UrlDecode(req.Body.ReadToEnd(Encoding.UTF8))},客户端用户代理:{req.Headers["User-Agent"]},客户端IP:{context.HttpContext.Connection.RemoteIpAddress}\t{ex?.InnerException?.Message}\t";
+                    err = $"异常源:{ex.Source},异常类型:{ex.GetType().Name},\n请求路径:{req.Scheme}://{req.Host}{HttpUtility.UrlDecode(req.Path)},客户端用户代理:{req.Headers["User-Agent"]},客户端IP:{context.HttpContext.Connection.RemoteIpAddress}\t{ex?.InnerException?.Message}\t";
                     LogManager.Error(err, ex);
                     break;
                 case AggregateException ex:
                     LogManager.Debug("↓↓↓" + ex.Message + "↓↓↓");
                     ex.Handle(e =>
                     {
-                        LogManager.Error($"异常源:{e.Source},异常类型:{e.GetType().Name},\n请求路径:{req.Scheme}://{req.Host}{HttpUtility.UrlDecode(req.Path)},请求参数:{HttpUtility.UrlDecode(req.Body.ReadToEnd(Encoding.UTF8))},客户端用户代理:{req.Headers["User-Agent"]},客户端IP:{context.HttpContext.Connection.RemoteIpAddress}\t", e);
+                        LogManager.Error($"异常源:{e.Source},异常类型:{e.GetType().Name},\n请求路径:{req.Scheme}://{req.Host}{HttpUtility.UrlDecode(req.Path)},客户端用户代理:{req.Headers["User-Agent"]},客户端IP:{context.HttpContext.Connection.RemoteIpAddress}\t", e);
                         return true;
                     });
                     break;
@@ -39,12 +38,13 @@ namespace Masuit.MyBlogs.Core.Extensions
                     context.ExceptionHandled = true;
                     return;
                 default:
-                    LogManager.Error($"异常源:{context.Exception.Source},异常类型:{context.Exception.GetType().Name},\n请求路径:{req.Scheme}://{req.Host}{HttpUtility.UrlDecode(req.Path)},请求参数:{HttpUtility.UrlDecode(req.Body.ReadToEnd(Encoding.UTF8))},客户端用户代理:{req.Headers["User-Agent"]},客户端IP:{context.HttpContext.Connection.RemoteIpAddress}\t", context.Exception);
+                    LogManager.Error($"异常源:{context.Exception.Source},异常类型:{context.Exception.GetType().Name},\n请求路径:{req.Scheme}://{req.Host}{HttpUtility.UrlDecode(req.Path)},客户端用户代理:{req.Headers["User-Agent"]},客户端IP:{context.HttpContext.Connection.RemoteIpAddress}\t", context.Exception);
                     break;
             }
-
+#if !DEBUG
             context.Result = new RedirectToActionResult("ServiceUnavailable", "Error", null);
-            context.ExceptionHandled = true;
+            context.ExceptionHandled = true; 
+#endif
         }
     }
 }

+ 1 - 1
src/Masuit.MyBlogs.Core/Extensions/UEditor/UeditorConfig.cs

@@ -10,7 +10,7 @@ namespace Masuit.MyBlogs.Core.Extensions.UEditor
     /// </summary>
     public static class UeditorConfig
     {
-        private static bool noCache = true;
+        private static bool noCache = false;
         private static JObject BuildItems()
         {
             var json = File.ReadAllText(AppContext.BaseDirectory + "ueconfig.json");

+ 1 - 1
src/Masuit.MyBlogs.Core/Hubs/MyHub.cs

@@ -185,7 +185,7 @@ namespace Masuit.MyBlogs.Core.Hubs
 
             var up = SystemInfo.GetNetData(NetData.Received) / 1024;
             var down = SystemInfo.GetNetData(NetData.Sent) / 1024;
-            PerformanceCounter counter = new PerformanceCounter()
+            var counter = new PerformanceCounter()
             {
                 Time = time,
                 CpuLoad = load,

+ 22 - 1
src/Masuit.MyBlogs.Core/Infrastructure/Services/PostService.cs

@@ -6,6 +6,7 @@ using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
 using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.Entity;
 using Masuit.MyBlogs.Core.Models.Enum;
+using Microsoft.EntityFrameworkCore.Internal;
 using Microsoft.Extensions.Caching.Memory;
 using PanGu;
 using PanGu.HighLight;
@@ -34,7 +35,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
                 return value;
             }
 
-            var searchResult = _searchEngine.ScoredSearch<Post>(new SearchOptions(keyword, page, size, typeof(Post)));
+            var searchResult = _searchEngine.ScoredSearch<Post>(BuildSearchOptions(page, size, keyword));
             var posts = searchResult.Results.Select(p => p.Entity.Mapper<PostOutputDto>()).Where(p => p.Status == Status.Pended).ToList();
             var simpleHtmlFormatter = new SimpleHTMLFormatter("<span style='color:red;background-color:yellow;font-size: 1.1em;font-weight:700;'>", "</span>");
             var highlighter = new Highlighter(simpleHtmlFormatter, new Segment()) { FragmentSize = 200 };
@@ -79,6 +80,26 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
             return _memoryCache.Set(cacheKey, result, TimeSpan.FromHours(1));
         }
 
+        private static SearchOptions BuildSearchOptions(int page, int size, string keyword)
+        {
+            var fields = new List<string>();
+            var newkeywords = new List<string>();
+            if (keyword.Contains("intitle:"))
+            {
+                fields.Add("Title");
+                newkeywords.Add(keyword.Split(' ', ' ').FirstOrDefault(s => s.Contains("intitle")).Split(':')[1]);
+            }
+
+            if (keyword.Contains("content:"))
+            {
+                fields.Add("Content");
+                newkeywords.Add(keyword.Split(' ', ' ').FirstOrDefault(s => s.Contains("content")).Split(':')[1]);
+            }
+
+            var searchOptions = fields.Any() ? new SearchOptions(newkeywords.Join(" "), page, size, fields.Join(",")) : new SearchOptions(keyword, page, size, typeof(Post));
+            return searchOptions;
+        }
+
         /// <summary>
         /// 添加实体并保存
         /// </summary>

+ 31 - 7
src/Masuit.MyBlogs.Core/Masuit.MyBlogs.Core.csproj

@@ -5,6 +5,8 @@
     <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
 	<ServerGarbageCollection>true</ServerGarbageCollection>
     <ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
+    <Authors>懒得勤快</Authors>
+    <Company>masuit.com</Company>
   </PropertyGroup>
 
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -22,20 +24,39 @@
 
   <ItemGroup>
     <None Include="bundleconfig.json" />
+    <None Include="wwwroot\Assets\layui\font\iconfont.svg" />
+    <None Include="wwwroot\Assets\layui\lay\dest\layui.all.js" />
+    <None Include="wwwroot\Assets\layui\lay\lib\jquery.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\carousel.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\code.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\element.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\flow.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\form.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\jquery.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\laydate.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\layedit.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\layer.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\laypage.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\laytpl.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\mobile.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\table.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\tree.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\upload.js" />
+    <None Include="wwwroot\Assets\layui\lay\modules\util.js" />
   </ItemGroup>
 
   <ItemGroup>
     <PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.9.1" />
-    <PackageReference Include="Aspose.Words" Version="18.11.0" />
-    <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.4.0" />
+    <PackageReference Include="Aspose.Words" Version="19.6.0" />
+    <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="5.0.0" />
     <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
     <PackageReference Include="CacheManager.Microsoft.Extensions.Caching.Memory" Version="1.2.0" />
     <PackageReference Include="CacheManager.Serialization.Json" Version="1.2.0" />
-    <PackageReference Include="CSRedisCore" Version="3.1.5" />
-    <PackageReference Include="EFSecondLevelCache.Core" Version="2.6.2" />
+    <PackageReference Include="CSRedisCore" Version="3.1.8" />
+    <PackageReference Include="EFSecondLevelCache.Core" Version="2.7.0" />
     <PackageReference Include="Hangfire" Version="1.7.6" />
     <PackageReference Include="Hangfire.Autofac" Version="2.3.1" />
-    <PackageReference Include="Hangfire.MemoryStorage" Version="1.6.2" />
+    <PackageReference Include="Hangfire.MemoryStorage" Version="1.6.3" />
     <PackageReference Include="Hangfire.Redis.StackExchange" Version="1.8.0" />
     <PackageReference Include="htmldiff.net-core" Version="1.3.6" />
     <PackageReference Include="IP2Region" Version="1.2.0" />
@@ -46,8 +67,8 @@
     <PackageReference Include="Polly" Version="7.1.1" />
     <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" />
     <PackageReference Include="WilderMinds.RssSyndication" Version="1.5.0" />
-    <PackageReference Include="Z.EntityFramework.Extensions.EFCore" Version="2.6.16" />
-    <PackageReference Include="Z.EntityFramework.Plus.EFCore" Version="2.0.7" />
+    <PackageReference Include="Z.EntityFramework.Extensions.EFCore" Version="2.7.2" />
+    <PackageReference Include="Z.EntityFramework.Plus.EFCore" Version="2.0.8" />
     <PackageReference Include="Z.ExtensionMethods" Version="2.1.1" />
   </ItemGroup>
 
@@ -93,6 +114,9 @@
     <None Update="App_Data\whitelist.txt">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
+    <None Update="Aspose.Words.dll">
+      <CopyToOutputDirectory>Never</CopyToOutputDirectory>
+    </None>
   </ItemGroup>
 
 </Project>

+ 1 - 31
src/Masuit.MyBlogs.Core/Models/DTO/CommentInputDto.cs

@@ -1,8 +1,6 @@
 using Masuit.MyBlogs.Core.Models.Entity;
 using Masuit.MyBlogs.Core.Models.Enum;
 using Masuit.MyBlogs.Core.Models.Validation;
-using System;
-using System.ComponentModel;
 using System.ComponentModel.DataAnnotations;
 
 namespace Masuit.MyBlogs.Core.Models.DTO
@@ -15,8 +13,8 @@ namespace Masuit.MyBlogs.Core.Models.DTO
         public CommentInputDto()
         {
             Status = Status.Pending;
-            IsMaster = false;
         }
+
         /// <summary>
         /// 昵称
         /// </summary>
@@ -51,11 +49,6 @@ namespace Masuit.MyBlogs.Core.Models.DTO
         /// </summary>
         public int PostId { get; set; }
 
-        /// <summary>
-        /// 发表时间
-        /// </summary>
-        public DateTime CommentDate { get; set; }
-
         /// <summary>
         /// 浏览器版本
         /// </summary>
@@ -67,28 +60,5 @@ namespace Masuit.MyBlogs.Core.Models.DTO
         /// </summary>
         [StringLength(255)]
         public string OperatingSystem { get; set; }
-
-        /// <summary>
-        /// 是否是博主
-        /// </summary>
-        [DefaultValue(false)]
-        public bool IsMaster { get; set; }
-
-        /// <summary>
-        /// 支持数
-        /// </summary>
-        public int VoteCount { get; set; }
-
-        /// <summary>
-        /// 反对数
-        /// </summary>
-        public int AgainstCount { get; set; }
-
-        /// <summary>
-        /// 访问者IP
-        /// </summary>
-        public string IP { get; set; }
-
     }
-
 }

+ 10 - 0
src/Masuit.MyBlogs.Core/Models/DTO/CommentOutputDto.cs

@@ -66,5 +66,15 @@ namespace Masuit.MyBlogs.Core.Models.DTO
         /// 反对数
         /// </summary>
         public int AgainstCount { get; set; }
+
+        /// <summary>
+        /// 提交人IP地址
+        /// </summary>
+        public string IP { get; set; }
+
+        /// <summary>
+        /// 地理信息
+        /// </summary>
+        public string Location { get; set; }
     }
 }

+ 0 - 20
src/Masuit.MyBlogs.Core/Models/DTO/LeaveMessageInputDto.cs

@@ -1,8 +1,6 @@
 using Masuit.MyBlogs.Core.Models.Entity;
 using Masuit.MyBlogs.Core.Models.Enum;
 using Masuit.MyBlogs.Core.Models.Validation;
-using System;
-using System.ComponentModel;
 using System.ComponentModel.DataAnnotations;
 
 namespace Masuit.MyBlogs.Core.Models.DTO
@@ -14,9 +12,7 @@ namespace Masuit.MyBlogs.Core.Models.DTO
     {
         public LeaveMessageInputDto()
         {
-            PostDate = DateTime.Now;
             Status = Status.Pending;
-            IsMaster = false;
         }
 
         /// <summary>
@@ -31,11 +27,6 @@ namespace Masuit.MyBlogs.Core.Models.DTO
         [Required(ErrorMessage = "留言内容不能为空!"), SubmitCheck(2, 500)]
         public string Content { get; set; }
 
-        /// <summary>
-        /// 发表时间
-        /// </summary>
-        public DateTime PostDate { get; set; }
-
         /// <summary>
         /// 邮箱
         /// </summary>
@@ -63,16 +54,5 @@ namespace Masuit.MyBlogs.Core.Models.DTO
         /// </summary>
         [StringLength(255)]
         public string OperatingSystem { get; set; }
-
-        /// <summary>
-        /// 是否是博主
-        /// </summary>
-        [DefaultValue(false)]
-        public bool IsMaster { get; set; }
-
-        /// <summary>
-        /// 提交人IP地址
-        /// </summary>
-        public string IP { get; set; }
     }
 }

+ 10 - 0
src/Masuit.MyBlogs.Core/Models/DTO/LeaveMessageOutputDto.cs

@@ -51,5 +51,15 @@ namespace Masuit.MyBlogs.Core.Models.DTO
         /// 是否是博主
         /// </summary>
         public bool IsMaster { get; set; }
+
+        /// <summary>
+        /// 提交人IP地址
+        /// </summary>
+        public string IP { get; set; }
+
+        /// <summary>
+        /// 地理信息
+        /// </summary>
+        public string Location { get; set; }
     }
 }

+ 5 - 0
src/Masuit.MyBlogs.Core/Models/Entity/Comment.cs

@@ -90,6 +90,11 @@ namespace Masuit.MyBlogs.Core.Models.Entity
         /// </summary>
         public string IP { get; set; }
 
+        /// <summary>
+        /// µØÀíÐÅÏ¢
+        /// </summary>
+        public string Location { get; set; }
+
         [ForeignKey("PostId")]
         public virtual Post Post { get; set; }
     }

+ 5 - 0
src/Masuit.MyBlogs.Core/Models/Entity/LeaveMessage.cs

@@ -75,5 +75,10 @@ namespace Masuit.MyBlogs.Core.Models.Entity
         /// Ìá½»ÈËIP
         /// </summary>
         public string IP { get; set; }
+
+        /// <summary>
+        /// µØÀíÐÅÏ¢
+        /// </summary>
+        public string Location { get; set; }
     }
 }

+ 10 - 0
src/Masuit.MyBlogs.Core/Models/ViewModel/CommentViewModel.cs

@@ -66,5 +66,15 @@ namespace Masuit.MyBlogs.Core.Models.ViewModel
         /// 反对数
         /// </summary>
         public int AgainstCount { get; set; }
+
+        /// <summary>
+        /// 提交人IP地址
+        /// </summary>
+        public string IP { get; set; }
+
+        /// <summary>
+        /// 地理信息
+        /// </summary>
+        public string Location { get; set; }
     }
 }

+ 10 - 0
src/Masuit.MyBlogs.Core/Models/ViewModel/LeaveMessageViewModel.cs

@@ -56,5 +56,15 @@ namespace Masuit.MyBlogs.Core.Models.ViewModel
         /// 是否是博主
         /// </summary>
         public bool IsMaster { get; set; }
+
+        /// <summary>
+        /// 提交人IP地址
+        /// </summary>
+        public string IP { get; set; }
+
+        /// <summary>
+        /// 地理信息
+        /// </summary>
+        public string Location { get; set; }
     }
 }

+ 18 - 19
src/Masuit.MyBlogs.Core/Startup.cs

@@ -45,6 +45,7 @@ using System.Linq;
 using System.Reflection;
 using System.Text.Encodings.Web;
 using System.Text.Unicode;
+using System.Threading.Tasks;
 using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode;
 
 namespace Masuit.MyBlogs.Core
@@ -97,7 +98,7 @@ namespace Masuit.MyBlogs.Core
             }); //配置数据库
             services.AddCors(opt => opt.AddDefaultPolicy(p => p.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().AllowCredentials())); //配置跨域
 
-            services.AddHttpClient(); //注入HttpClient
+            services.AddHttpClient("", c => c.Timeout = TimeSpan.FromSeconds(30)); //注入HttpClient
             services.AddHttpContextAccessor(); //注入静态HttpContext
             services.Configure<FormOptions>(options =>
             {
@@ -119,7 +120,7 @@ namespace Masuit.MyBlogs.Core
             services.AddEFSecondLevelCache();
             // 配置EF二级缓存策略
             services.AddSingleton(typeof(ICacheManager<>), typeof(BaseCacheManager<>));
-            services.AddSingleton(new CacheManager.Core.ConfigurationBuilder().WithJsonSerializer().WithMicrosoftMemoryCacheHandle().WithExpiration(ExpirationMode.Absolute, TimeSpan.FromMinutes(10)).Build());
+            services.AddSingleton(new CacheManager.Core.ConfigurationBuilder().WithJsonSerializer().WithMicrosoftMemoryCacheHandle().WithExpiration(ExpirationMode.Sliding, TimeSpan.FromMinutes(10)).Build());
 
             services.AddWebSockets(opt => opt.ReceiveBufferSize = 4096 * 1024).AddSignalR();
 
@@ -130,9 +131,7 @@ namespace Masuit.MyBlogs.Core
 
             services.AddMvc(options =>
             {
-#if !DEBUG
-                options.Filters.Add<MyExceptionFilter>(); 
-#endif
+                options.Filters.Add<MyExceptionFilter>();
                 options.CacheProfiles.Add("Default", new CacheProfile()
                 {
                     Location = ResponseCacheLocation.Any,
@@ -156,8 +155,9 @@ namespace Masuit.MyBlogs.Core
             services.AddAutofac();
             ContainerBuilder builder = new ContainerBuilder();
             builder.Populate(services);
-            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces().Where(t => t.Name.EndsWith("Repository") || t.Name.EndsWith("Service") || t.Name.EndsWith("Controller")).PropertiesAutowired().AsSelf().InstancePerDependency(); //注册控制器为属性注入
+            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces().Where(t => t.Name.EndsWith("Repository") || t.Name.EndsWith("Service") || t.Name.EndsWith("Controller") || t.Name.EndsWith("Attribute")).PropertiesAutowired().AsSelf().InstancePerDependency(); //注册控制器为属性注入
             builder.RegisterType<BackgroundJobClient>().SingleInstance(); //指定生命周期为单例
+            builder.RegisterType<FirewallAttribute>().PropertiesAutowired().AsSelf().InstancePerDependency(); //指定生命周期为单例
             builder.RegisterType<HangfireBackJob>().As<IHangfireBackJob>().PropertiesAutowired(PropertyWiringOptions.PreserveSetValues).InstancePerDependency();
             AutofacContainer = new AutofacServiceProvider(builder.Build());
             return AutofacContainer;
@@ -253,26 +253,25 @@ namespace Masuit.MyBlogs.Core
 
         private static void ConfigureLuceneSearch(IHostingEnvironment env, IHangfireBackJob hangfire, LuceneIndexerOptions luceneIndexerOptions)
         {
-            #region 导词库
-
-            Console.WriteLine("正在导入自定义词库...");
-            double time = HiPerfTimer.Execute(() =>
+            Task.Run(() =>
             {
-                var lines = File.ReadAllLines(Path.Combine(env.ContentRootPath, "App_Data", "CustomKeywords.txt"));
-                var segmenter = new JiebaSegmenter();
-                foreach (var word in lines)
+                Console.WriteLine("正在导入自定义词库...");
+                double time = HiPerfTimer.Execute(() =>
                 {
-                    segmenter.AddWord(word);
-                }
+                    var lines = File.ReadAllLines(Path.Combine(env.ContentRootPath, "App_Data", "CustomKeywords.txt"));
+                    var segmenter = new JiebaSegmenter();
+                    foreach (var word in lines)
+                    {
+                        segmenter.AddWord(word);
+                    }
+                });
+                Console.WriteLine($"导入自定义词库完成,耗时{time}s");
             });
-            Console.WriteLine($"导入自定义词库完成,耗时{time}s");
-
-            #endregion
 
             string lucenePath = Path.Combine(env.ContentRootPath, luceneIndexerOptions.Path);
             if (!Directory.Exists(lucenePath) || Directory.GetFiles(lucenePath).Length < 1)
             {
-                Console.WriteLine("索引库不存在,开始自动创建Lucene索引库...");
+                Console.WriteLine("索引库不存在,开始自动创建Lucene索引库...");
                 hangfire.CreateLuceneIndex();
                 Console.WriteLine("索引库创建完成!");
             }

+ 2 - 2
src/Masuit.MyBlogs.Core/Views/Error/AccessDeny.cshtml

@@ -102,7 +102,7 @@
 <script>
     $("#code-token").on("submit", function (e) {
         e.preventDefault();
-        $.post("/post/CheckViewToken", $(this).serialize(), function (data) {
+        $.post("/error/CheckViewToken", $(this).serialize(), function (data) {
             if (data.Success) {
                 window.location.href = "/";
             } else {
@@ -117,7 +117,7 @@
     });
     $(".getcode").on("click", function (e) {
         e.preventDefault();
-        $.post("/post/getviewtoken",
+        $.post("/error/getviewtoken",
             {
                 __RequestVerificationToken: $("[name=__RequestVerificationToken]").val(),
                 email: $("#email3").val()

+ 1 - 0
src/Masuit.MyBlogs.Core/Views/Msg/Index.cshtml

@@ -50,6 +50,7 @@
                 <button type="submit" class="btn btn-info btn-lg">
                     发表留言
                 </button>
+                <a class="text-red">留言框可粘贴网络图片</a>
             </div>
         </div>
     </form>

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

@@ -29,6 +29,7 @@
                 <button type="submit" class="btn btn-info btn-lg">
                     发表留言
                 </button>
+                <a class="text-red">留言框可粘贴网络图片</a>
             </div>
         </div>
     </form>
@@ -106,22 +107,27 @@
             var startfloor = data.parentTotal - (page - 1) * size;
             for (let i = 0; i < rows.length; i++) {
                 html += `<li class="msg-list media animated fadeInRight" id='${rows[i].Id}'>
-                                <div class="media-body">
-                                    <article class="panel panel-info">
-                                        <header class="panel-heading">${startfloor}# ${rows[i].IsMaster ? `<i class="icon icon-user"></i>` : ""}${rows[i].NickName}${rows[i].IsMaster ? `(管理员)` : ""}
-                                            <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> | ${rows[i].PostDate}<span class="hidden-sm hidden-xs"> | ${GetOperatingSystem(rows[i].OperatingSystem) + " | " + GetBrowser(rows[i].Browser)}</span></span>
-                                        </header>
-                                        <div class="panel-body">
-                                            ${rows[i].Content} <a class="label label-info" href="?uid=${rows[i].Id}"><i class="icon-comment"></i></a>
-                                            <div class="pull-right">
-                                                <span class="label label-success">${rows[i].Email}</span>
-                                                <span class="label label-primary">${rows[i].QQorWechat}</span>
-                                            </div>
-                                            ${loadMsgs(data.rows, Enumerable.From(data.rows).Where(c => c.ParentId === rows[i].Id).OrderBy(c => c.PostDate).ToArray(), startfloor--)}
+                            <div class="media-body">
+                                <article class="panel panel-info">
+                                    <header class="panel-heading">${startfloor}# ${rows[i].IsMaster ? `<i class="icon icon-user"></i>` : ""}${rows[i].NickName}${rows[i].IsMaster ? `(管理员)` : ""}
+                                        <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> | ${rows[i].PostDate}<span class="hidden-sm hidden-xs"> | ${GetOperatingSystem(rows[i].OperatingSystem) + " | " + GetBrowser(rows[i].Browser)}</span></span>
+                                    </header>
+                                    <div class="panel-body">
+                                        ${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="pull-left">
+                                            <span class="label label-success">${rows[i].IP}</span>
+                                            <span class="label label-primary">${rows[i].Location}</span>
                                         </div>
-                                    </article>
-                                </div>
-                            </li>`;
+                                        <div class="pull-right">
+                                            <span class="label label-success">${rows[i].Email}</span>
+                                            <span class="label label-primary">${rows[i].QQorWechat}</span>
+                                        </div><br/>
+                                        ${loadMsgs(data.rows, Enumerable.From(data.rows).Where(c => c.ParentId === rows[i].Id).OrderBy(c => c.PostDate).ToArray(), startfloor--)}
+                                    </div>
+                                </article>
+                            </div>
+                        </li>`;
             }
 		}
         loadingDone();
@@ -137,20 +143,25 @@
         Enumerable.From(msg).ForEach((item, index) => {
             var color = colors[depth % 5];
             html += `<article id="${item.Id}" class="panel panel-${color}">
-                            <div class="panel-heading">
-                                ${depth}-${floor++}# ${item.IsMaster ?`<i class="icon icon-user"></i>`:""}${item.NickName}${item.IsMaster ?`(管理员)`:""}
-                                <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> | ${item.PostDate}<span class="hidden-sm hidden-xs"> | ${GetOperatingSystem(item.OperatingSystem) + " | " + GetBrowser(item.Browser)}</span></span>
-                            </div>
-                            <div class="panel-body">
-                                ${item.Content}
-                                <a class="label label-${color}" href="?uid=${item.Id}"><i class="icon-comment"></i></a>
-                                <div class="pull-right">
-                                    <span class="label label-success">${item.Email}</span>
-                                    <span class="label label-primary">${item.QQorWechat}</span>
-                                </div>
-                                ${loadMsgs(data, Enumerable.From(data).Where(c => c.ParentId === item.Id).OrderBy(c => c.PostDate), root, depth)}
+                        <div class="panel-heading">
+                            ${depth}-${floor++}# ${item.IsMaster ?`<i class="icon icon-user"></i>`:""}${item.NickName}${item.IsMaster ?`(管理员)`:""}
+                            <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> | ${item.PostDate}<span class="hidden-sm hidden-xs"> | ${GetOperatingSystem(item.OperatingSystem) + " | " + GetBrowser(item.Browser)}</span></span>
+                        </div>
+                        <div class="panel-body">
+                            ${item.Content}
+                            <a class="label label-${color}" href="?uid=${item.Id}"><i class="icon-comment"></i></a>
+                            <div class="margin-top10"></div>
+                            <div class="pull-left">
+                                <span class="label label-success">${item.IP}</span>
+                                <span class="label label-primary">${item.Location}</span>
                             </div>
-                        </article>`;
+                            <div class="pull-right">
+                                <span class="label label-success">${item.Email}</span>
+                                <span class="label label-primary">${item.QQorWechat}</span>
+                            </div><br/>
+                            ${loadMsgs(data, Enumerable.From(data).Where(c => c.ParentId === item.Id).OrderBy(c => c.PostDate), root, depth)}
+                        </div>
+                    </article>`;
         });
         return html;
     }
@@ -181,8 +192,7 @@
 			    swal(res.Message, "", res.Success ? "success" : "error");
 			    getmsgs();
 		    });
-        }, function () {
-        });
+        }, function () {});
     }
 
     /**

+ 11 - 7
src/Masuit.MyBlogs.Core/Views/Post/Details.cshtml

@@ -1,10 +1,12 @@
 @using System.Text.RegularExpressions
 @using System.Web
 @using Masuit.MyBlogs.Core.Common
+@using Masuit.MyBlogs.Core.Configs
 @using Masuit.MyBlogs.Core.Models.Entity
 @using Masuit.MyBlogs.Core.Models.Enum
 @using Masuit.MyBlogs.Core.Models.ViewModel
 @using Masuit.Tools.Core.Net
+@using Masuit.Tools.Security
 @model Masuit.MyBlogs.Core.Models.Entity.Post
 @{
     ViewBag.Title = Model.Title;
@@ -41,7 +43,7 @@
                                 </a>
                             </div>
                             <div class="row">
-                                <div class="col-sm-7">
+                                <div class="col-sm-8">
                                     <div class="padding-bot10">
                                         <span class="label label-@colors[new Random().Next() % colors.Length]">@Model.Author</span>发表于<span class="text-info">@Model.PostDate.ToString("yyyy-MM-dd HH:mm:ss")</span> |
                                         <span class="label label-@colors[new Random().Next() % colors.Length]">@Model.Modifier</span>最后修改于<span class="text-success">@Model.ModifyDate.ToString("yyyy-MM-dd HH:mm:ss")</span>
@@ -89,7 +91,13 @@
                             @if (!string.IsNullOrEmpty(Model.ProtectContent))
                             {
                                 <div class="row protected">
-                                    @if (string.IsNullOrEmpty(Context.Session.Get<string>("AccessViewToken")))
+                                    @if (!string.IsNullOrEmpty(Context.Session.Get<string>("AccessViewToken")) || Context.Request.Cookies["Email"].MDString3(AppConfig.BaiduAK).Equals(Context.Request.Cookies["PostAccessToken"]))
+                                    {
+                                        <p class="text-red text-center size20">↓↓↓以下是文章加密部分↓↓↓</p>
+                                        @Html.Raw(Model.ProtectContent.ReplaceImgAttribute())
+
+                                    }
+                                    else
                                     {
                                         <p class="text-red text-center size24 margin-bot10">↓↓↓恭喜你发现镇站之宝,请注意,前方高能!非战斗人员请及时撤离,这不是演习!↓↓↓</p>
                                         <div class="col-md-12">
@@ -115,11 +123,6 @@
                                             <p class="text-green">温馨提示:<span class="text-red">获取密码后本站所有加密文章将永久开放!请确保您的邮箱已经通过站长的登记,如果您未使用邮箱登记,请先联系站长获取文章密码并登记邮箱即可!</span></p>
                                         </div>
                                     }
-                                    else
-                                    {
-                                        <p class="text-red text-center size20">↓↓↓以下是文章加密部分↓↓↓</p>
-                                        @Html.Raw(Model.ProtectContent.ReplaceImgAttribute())
-                                    }
                                 </div>
                             }
                         </article>
@@ -260,6 +263,7 @@
                                 <div class="form-group">
                                     <div class="col-xs-12">
                                         <button type="submit" class="btn btn-info btn-lg">提交</button>
+                                        <a class="text-red">评论框可粘贴网络图片</a>
                                     </div>
                                 </div>
                             </form>

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

@@ -51,7 +51,7 @@
                                 </a>
                             </div>
                             <div class="row">
-                                <div class="col-sm-7">
+                                <div class="col-sm-8">
                                     <div class="padding-bot10">
                                         <span class="label label-@colors[new Random().Next() % colors.Length]">@Model.Author</span>发表于<span class="text-info">@Model.PostDate.ToString("yyyy-MM-dd HH:mm:ss")</span> |
                                         <span class="label label-@colors[new Random().Next() % colors.Length]">@Model.Modifier</span>最后修改于<span class="text-success">@Model.ModifyDate.ToString("yyyy-MM-dd HH:mm:ss")</span>
@@ -215,6 +215,7 @@
                             <div class="form-group">
                                 <div class="col-xs-12">
                                     <button type="submit" class="btn btn-info btn-lg">提交</button>
+                                    <a class="text-red">评论框可粘贴网络图片</a>
                                 </div>
                             </div>
                         </form>
@@ -286,8 +287,6 @@
 <script src="https://cdn.staticfile.org/jqueryui/1.12.1/jquery-ui.min.js"></script>
 <script src="~/Assets/UEditor/third-party/SyntaxHighlighter/scripts/shCore.min.js"></script>
 <script src="~/Assets/UEditor/third-party/SyntaxHighlighter/scripts/bundle.min.js"></script>
-@*<script src="~/Assets/directory/script.min.js"></script>*@
-@*<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tocify/1.9.0/javascripts/jquery.tocify.min.js"></script>*@
 <script src="~/Assets/jquery.tocify/jquery.tocify.js"></script>
 <script src="~/Scripts/global/article.js"></script>
 <script src="~/Assets/highlight/js/highlight.js"></script>
@@ -422,11 +421,15 @@
                                     <div class="panel-body">
                                         ${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>
-                                        <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><div class="margin-top10"></div>
+                                        <div class="pull-left">
+                                            <span class="label label-success">${rows[i].IP}</span>
+                                            <span class="label label-primary">${rows[i].Location}</span>
+                                        </div>
                                         <div class="pull-right">
                                             <span class="label label-success">${rows[i].Email}</span>
                                             <span class="label label-warning">${rows[i].QQorWechat}</span>
-                                        </div>
+                                        </div><br/>
                                         ${loadComments(data.rows, Enumerable.From(data.rows).Where(c => c.ParentId === rows[i].Id).OrderBy(c => c.CommentDate).ToArray(), startfloor--)}
                                     </div>
                                 </article>
@@ -455,10 +458,15 @@
                                 ${item.Content}
                                 <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>
+                                <div class="margin-top10"></div>
+                                <div class="pull-left">
+                                    <span class="label label-success">${item.IP}</span>
+                                    <span class="label label-primary">${item.Location}</span>
+                                </div>
                                 <div class="pull-right">
                                     <span class="label label-success">${item.Email}</span>
                                     <span class="label label-primary">${item.QQorWechat}</span>
-                                </div>
+                                </div><br/>
                                 ${loadComments(data, Enumerable.From(data).Where(c => c.ParentId === item.Id).OrderBy(c => c.CommentDate), root, depth)}
                             </div>
                         </article>`;
@@ -495,7 +503,5 @@
 			swal(res.Message, "", res.Success ? "success" : "error");
 			getcomments();
 		});
-
 	}
-
 </script>

+ 0 - 85
src/Masuit.MyBlogs.Core/Views/Post/Publish_Admin.cshtml

@@ -1,85 +0,0 @@
-@model IEnumerable<string>
-@using Masuit.MyBlogs.Core.Models.DTO
-@using Masuit.MyBlogs.Core.Models.Entity
-@using Masuit.MyBlogs.Core.Models.ViewModel
-@using Masuit.Tools.Core.Net
-@using Masuit.Tools.Win32
-@{
-    ViewBag.Title = "投稿";
-    Layout = "~/Views/Shared/_Layout.cshtml";
-    string[] colors = { "success", "info", "warning", "danger", "default" };
-    UserInfoOutputDto user = Context.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo);
-    List<Category> cats = ViewBag.Category;
-}
-<link href="~/Assets/fileupload/filestyle.css" rel="stylesheet" />
-<link href="~/Assets/semantic/semantic.css" rel="stylesheet" />
-<div class="container">
-    <ol class="cd-breadcrumb triangle">
-        <li>@Html.ActionLink("首页", "Index", "Home")</li>
-        <li class="current"><em>@ViewBag.Title</em></li>
-    </ol>
-    <hr />
-    <form class="form-group" id="article-form" method="post" onsubmit="return false">
-        @*文章表单*@
-        @Html.AntiForgeryToken()
-        <input type="hidden" name="IsWordDocument" id="IsWordDocument" value="false" />
-        <input type="hidden" name="ResourceName" id="ResourceName" />
-        <input type="hidden" class="form-control" id="Author" name="Author" value="@user.NickName">
-        <input type="hidden" class="form-control" id="Email" name="Email" value="@user.Email">
-        <div class="input-group">
-            <span class="input-group-addon size18"><label for="article">文章标题:</label></span>
-            <input type="text" id="article" class="form-control input-lg" name="Title" required placeholder="请输入文章标题">
-            <span class="input-group-btn">
-                <button type="button" class="btn btn-@colors[new Random().Next() % 5] btn-lg show-fileup"><i class="icon-upload4 size18"></i>上传word文档</button>
-            </span>
-        </div>
-        <!-- 加载编辑器的容器 -->
-        <div class="form-group overlay animated bounceInDown">
-            <textarea id="editor" style="height: calc(100vh - 350px);" class="ueditor" name="Content" type="text/plain"></textarea>
-        </div>
-        <div class="row">
-            <div class="col-md-4 col-sm-6">
-                <div class="input-group">
-                    <span class="input-group-addon">分类:</span>
-                    <div class="ui fluid search selection dropdown category">
-                        <input name="CategoryId" type="hidden" id="CategoryId">
-                        <i class="dropdown icon"></i>
-                        <div class="default text">选择分类</div>
-                        <div class="menu">
-                            @foreach (var s in cats)
-                            {
-                                <div class="item" data-value="@s.Id">@s.Name</div>
-                            }
-                        </div>
-                    </div>
-                </div>
-            </div>
-            <div class="col-md-8 col-sm-6">
-                <div class="input-group">
-                    <span class="input-group-addon"><label for="Label">标签:</label></span>
-                    <div class="ui fluid multiple search selection dropdown tags">
-                        <input name="Label" type="hidden" id="Label">
-                        <i class="dropdown icon"></i>
-                        <div class="default text">标签(选择或输入新标签)</div>
-                        <div class="menu">
-                            @foreach (var s in Model)
-                            {
-                                <div class="item" data-value="@s">@s</div>
-                            }
-                        </div>
-                    </div>
-                    <span class="input-group-btn">
-                        <button type="button" id="submit" class="btn btn-info">
-                            <i class="icon-rocket2"></i>
-                            发布
-                        </button>
-                    </span>
-                </div>
-            </div>
-        </div>
-    </form>
-</div>
-<script src="https://cdn.staticfile.org/semantic-ui/2.4.1/semantic.min.js"></script>
-<script src="~/Assets/UEditor/ueditor.config.admin.min.js"></script>
-<script src="https://apps.bdimg.com/libs/ueditor/1.4.3.1/ueditor.all.min.js"></script>
-<script src="~/Scripts/publish/publish.js"></script>

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Search/Search.cshtml

@@ -27,7 +27,7 @@
                         <div class="search-form">
                             <form action="/s" method="get">
                                 <div class="input-group">
-                                    <input type="text" placeholder="在这里发现任何你需要的东西" id="search" name="wd" maxlength="32" value="@ViewBag.Keyword" class="form-control input-lg">
+                                    <input type="text" placeholder="你要查找的关键词,支持部分指令:intitle,content,如:intitle:会声会影 content:懒得勤快,指令支持组合" id="search" name="wd" maxlength="32" value="@ViewBag.Keyword" class="form-control input-lg">
                                     <div class="input-group-btn">
                                         <button type="button" class="sr-only btn btn-default dropdown-toggle" data-toggle="dropdown" style="display: none"><span class="caret"></span></button>
                                         <ul class="dropdown-menu dropdown-menu-right" role="menu"></ul>

+ 2 - 12
src/Masuit.MyBlogs.Core/Views/Shared/_Layout.cshtml

@@ -19,11 +19,6 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
     <meta name="referrer" content="no-referrer">
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=8">
-    <meta http-equiv="Expires" content="0">
-    <meta http-equiv="Pragma" content="no-cache">
-    <meta http-equiv="Cache-control" content="no-cache">
-    <meta http-equiv="Cache" content="no-cache">
     <title>@(ViewBag.Title + "_" + CommonHelper.SystemSettings["Title"])</title>
     <meta name="keywords" content="@(CommonHelper.SystemSettings["Keyword"]+","+ViewBag.Keyword)" />
     <meta name="description" content="@(CommonHelper.SystemSettings["Description"]+","+ViewBag.Keyword)" />
@@ -70,8 +65,6 @@
     <script src="~/Scripts/global/scripts.js"></script>
     <script src="~/Assets/tippy/tippy.js"></script>
     <script src="~/Assets/newsbox/jquery.bootstrap.newsbox.js"></script>
-    @*<script src="~/Assets/tab/stopExecutionOnTimeout.js"></script>
-        <script src="~/Assets/tab/init.js"></script>*@
     <script src="~/Assets/tagcloud/js/tagcloud.js"></script>
     <script src="~/Assets/scrolltop/js/scrolltop.js"></script>
     <script src="~/Assets/nav/js/main.js"></script>
@@ -81,14 +74,12 @@
         <a class="close">
             <i class="icon-cross"></i>
         </a>
-    </div><!--/.well -->
+    </div>
     <div class="sr-only">
         <img src="~/images/hx.png" />
         <img src="~/images/bg1.png" />
         <img src="~/images/bg0.png" />
     </div>
-    @*<canvas id="c" width="300" height="150"></canvas>
-        <a class="btn btn-default close-bg">关闭网页背景</a>*@
     <div class="header">
         <header class="cd-main-header">
             <a class="cd-logo" href="/"><img src="@CommonHelper.SystemSettings["logo"]" alt="@CommonHelper.SystemSettings["Title"]"><span class="slogan">@CommonHelper.SystemSettings["Slogan"]</span></a>
@@ -117,7 +108,6 @@
                                 <ul class="cd-secondary-nav is-hidden">
                                     <li class="go-back"><a href="#">上级菜单</a></li>
                                     <li class="see-all"><a href="/all">所有 @m.Name</a></li>
-                                    @*@NodeHelper(menus.Where(s => s.ParentId == m.Id).OrderBy(c => c.Sort))*@
                                     @foreach (MenuOutputDto m2 in menus.Where(s => s.ParentId == m.Id).OrderBy(c => c.Sort))
                                     {
                                         if (menus.Any(mm => mm.ParentId == m2.Id))
@@ -262,7 +252,7 @@
         </nav>
         <div id="cd-search" class="cd-search">
             <form action="/s" method="get">
-                <input type="search" name="wd" placeholder="请在此处输入您想要的任何东西的关键词...">
+                <input type="search" name="wd" placeholder="请在此处输入您想要的任何东西的关键词,支持部分指令:intitle,content,如:intitle:会声会影 content:懒得勤快,指令支持组合">
             </form>
         </div>
 

+ 6 - 0
src/Masuit.MyBlogs.Core/bundleconfig.json

@@ -40,5 +40,11 @@
     "inputFiles": [
       "wwwroot/Assets/UEditor/ueditor.all.js"
     ]
+  },
+  {
+    "outputFileName": "wwwroot/Scripts/global/leavemsg.min.js",
+    "inputFiles": [
+      "wwwroot/Scripts/global/leavemsg.js"
+    ]
   }
 ]

+ 1 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/css/modules/code.css

@@ -1,2 +1,2 @@
-/** layui-v2.2.45 MIT License By http://www.layui.com */
+/** layui-v2.5.5 MIT License By https://www.layui.com */
  html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #e2e2e2;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:32px;line-height:32px;border-bottom:1px solid #e2e2e2}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 5px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none}

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/css/modules/laydate/default/laydate.css


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/css/modules/layer/default/layer.css


BIN
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/font/iconfont.eot


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1 - 8
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/font/iconfont.svg


BIN
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/font/iconfont.ttf


BIN
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/font/iconfont.woff


BIN
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/font/iconfont.woff2


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/carousel.js


+ 1 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/code.js

@@ -1,2 +1,2 @@
-/** layui-v2.2.45 MIT License By http://www.layui.com */
+/** layui-v2.5.5 MIT License By https://www.layui.com */
  ;layui.define("jquery",function(e){"use strict";var a=layui.$,l="http://www.layui.com/doc/modules/code.html";e("code",function(e){var t=[];e=e||{},e.elem=a(e.elem||".layui-code"),e.about=!("about"in e)||e.about,e.elem.each(function(){t.push(this)}),layui.each(t.reverse(),function(t,i){var c=a(i),o=c.html();(c.attr("lay-encode")||e.encode)&&(o=o.replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/'/g,"&#39;").replace(/"/g,"&quot;")),c.html('<ol class="layui-code-ol"><li>'+o.replace(/[\r\t\n]+/g,"</li><li>")+"</li></ol>"),c.find(">.layui-code-h3")[0]||c.prepend('<h3 class="layui-code-h3">'+(c.attr("lay-title")||e.title||"code")+(e.about?'<a href="'+l+'" target="_blank">layui.code</a>':"")+"</h3>");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss");

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1 - 0
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/colorpicker.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/element.js


+ 1 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/flow.js

@@ -1,2 +1,2 @@
-/** layui-v2.2.45 MIT License By http://www.layui.com */
+/** layui-v2.5.5 MIT License By https://www.layui.com */
  ;layui.define("jquery",function(e){"use strict";var l=layui.$,o=function(e){},t='<i class="layui-anim layui-anim-rotate layui-anim-loop layui-icon ">&#xe63e;</i>';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var f=l(e.elem);if(f[0]){var m=l(e.scrollElem||document),u=e.mb||50,s=!("isAuto"in e)||e.isAuto,v=e.end||"没有更多了",y=e.scrollElem&&e.scrollElem!==document,d="<cite>加载更多</cite>",h=l('<div class="layui-flow-more"><a href="javascript:;">'+d+"</a></div>");f.find(".layui-flow-more")[0]||f.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(v):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(m.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),i||(r=setTimeout(function(){var i=y?e.height():l(window).height(),n=y?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=u&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var m=e.attr("lay-src");layui.img(m,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",m).removeAttr("lay-src"),l[0]&&f(l),i++})}},f=function(e,o){var f=a?(o||n).height():l(window).height(),m=n.scrollTop(),u=m+f;if(t.lazyimg.elem=l(r),e)c(e,f);else for(var s=0;s<t.lazyimg.elem.length;s++){var v=t.lazyimg.elem.eq(s),y=a?function(){return v.offset().top-n.offset().top+m}():v.offset().top;if(c(v,f),i=s,y>u)break}};if(f(),!o){var m;n.on("scroll",function(){var e=l(this);m&&clearTimeout(m),m=setTimeout(function(){f(null,e)},50)}),o=!0}return f},e("flow",new o)});

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/form.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/jquery.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/laydate.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/layedit.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/layer.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/laypage.js


+ 1 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/laytpl.js

@@ -1,2 +1,2 @@
-/** layui-v2.2.45 MIT License By http://www.layui.com */
+/** layui-v2.5.5 MIT License By https://www.layui.com */
  ;layui.define(function(e){"use strict";var r={open:"{{",close:"}}"},c={exp:function(e){return new RegExp(e,"g")},query:function(e,c,t){var o=["#([\\s\\S])+?","([^{#}])*?"][e||0];return n((c||"")+r.open+o+r.close+(t||""))},escape:function(e){return String(e||"").replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/'/g,"&#39;").replace(/"/g,"&quot;")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)});

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/mobile.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1 - 0
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/rate.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1 - 0
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/slider.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/table.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1 - 0
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/transfer.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/tree.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/upload.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/lay/modules/util.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/layui.all.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/layui/layui.js


+ 0 - 347
src/Masuit.MyBlogs.Core/wwwroot/Assets/timeline.css

@@ -1,347 +0,0 @@
-.main-timeline{ position: relative; }
-.main-timeline:before,
-.main-timeline:after{
-    content: "";
-    display: block;
-    width: 100%;
-    clear: both;
-}
-.main-timeline:before{
-    content: "";
-    width: 3px;
-    height: 100%;
-    background: #302124;
-    position: absolute;
-    top: 0;
-    left: 50%;
-}
-.main-timeline .timeline{
-    width: 50%;
-    float: left;
-    position: relative;
-    z-index: 1;
-}
-.main-timeline .timeline:before,
-.main-timeline .timeline:after{
-    content: "";
-    display: block;
-    width: 100%;
-    clear: both;
-}
-.main-timeline .timeline:first-child:before,
-.main-timeline .timeline:last-child:before{
-    content: "";
-    width: 25px;
-    height: 25px;
-    border-radius: 50%;
-    background: #fff;
-    border: 4px solid #cca872;
-    position: absolute;
-    top: 0;
-    right: -14px;
-    z-index: 1;
-}
-.main-timeline .timeline:last-child:before{
-    top: auto;
-    bottom: 0;
-}
-.main-timeline .timeline:last-child:nth-child(even):before{
-    right: auto;
-    left: -12px;
-    bottom: -2px;
-}
-.main-timeline .timeline-content{
-    text-align: center;
-    margin-top: 8px;
-    position: relative;
-    transition: all 0.3s ease 0s;
-}
-.main-timeline .timeline-content:before{
-    content: "";
-    width: 100%;
-    height: 5px;
-    background: #cca872;
-    position: absolute;
-    top: 88px;
-    left: 0;
-    z-index: -1;
-}
-.main-timeline .circle{
-    width: 180px;
-    height: 180px;
-    border-radius: 50%;
-    background: #fff;
-    border: 8px solid #cca872;
-    float: left;
-    margin-right: 25px;
-    position: relative;
-}
-.main-timeline .circle:before{
-    content: "";
-    width: 26px;
-    height: 30px;
-    background: #cca872;
-    margin: auto;
-    position: absolute;
-    top: 0;
-    right: -33px;
-    bottom: 0;
-    z-index: -1;
-    box-shadow: inset 7px 0 9px -7px #444;
-}
-.main-timeline .circle span{
-    display: block;
-    width: 100%;
-    height: 100%;
-    border-radius: 50%;
-    line-height: 160px;
-    border: 3px solid #adabab;
-    font-size: 80px;
-    color: #454344;
-}
-.main-timeline .circle span:before,
-.main-timeline .circle span:after{
-    content: "";
-    width: 28px;
-    height: 50px;
-    background: #fff;
-    border-radius: 0 0 0 21px;
-    margin: auto;
-    position: absolute;
-    top: -54px;
-    right: -33px;
-    bottom: 0;
-    z-index: -1;
-}
-.main-timeline .circle span:after{
-    border-radius: 21px 0 0 0;
-    top: 0;
-    bottom: -56px;
-}
-.main-timeline .content{
-    display: table;
-    padding-right: 40px;
-    position: relative;
-}
-.main-timeline .content p{
-    max-width: 325px;
-}
-.main-timeline .year{
-    display: block;
-    padding: 10px;
-    margin: 38px 0 12px 0;
-    background: #cca872;
-    border-radius: 7px;
-    font-size: 25px;
-    color: #fff;
-}
-.main-timeline .title{
-    font-size: 25px;
-    font-weight: bold;
-    color: #cca872;
-    margin-top: 0;
-}
-.main-timeline .description{
-    font-size: 16px ;
-    line-height: 20px;
-    padding: 5px 0px;
-    text-align: justify;
-}
-.main-timeline .description+p{
-    width: 325px;
-}
-.main-timeline .content img{
-    max-height: 200px;
-}
-.main-timeline .icon{
-    width: 25px;
-    height: 25px;
-    border-radius: 50%;
-    background: #fff;
-    border: 4px solid #cca872;
-    position: absolute;
-    top: 78px;
-    right: -14px;
-}
-.main-timeline .icon:before{
-    content: "";
-    width: 15px;
-    height: 25px;
-    background: #cca872;
-    margin: auto;
-    position: absolute;
-    top: -1px;
-    left: -15px;
-    bottom: 0;
-    z-index: -1;
-}
-.main-timeline .icon span:before,
-.main-timeline .icon span:after{
-    content: "";
-    width: 21px;
-    height: 25px;
-    background: #fff;
-    border-radius: 0 0 21px 0;
-    margin: auto;
-    position: absolute;
-    top: -30px;
-    left: -15px;
-    bottom: 0;
-    z-index: -1;
-}
-.main-timeline .icon span:after{
-    border-radius: 0 21px 0 0;
-    top: 0;
-    left: -15px;
-    bottom: -30px;
-}
-.main-timeline .timeline:nth-child(2n) .timeline-content,
-.main-timeline .timeline:nth-child(2n) .circle{
-    float: right;
-}
-.main-timeline .timeline:nth-child(2n) .circle{
-    margin: 0 0 0 25px;
-}
-.main-timeline .timeline:nth-child(2n) .circle:before{
-    right: auto;
-    left: -33px;
-    box-shadow: -7px 0 9px -7px #444 inset;
-}
-.main-timeline .timeline:nth-child(2n) .circle span:before,
-.main-timeline .timeline:nth-child(2n) .circle span:after{
-    right: auto;
-    left: -33px;
-    border-radius: 0 0 21px 0;
-}
-.main-timeline .timeline:nth-child(2n) .circle span:after{
-    border-radius: 0 21px 0 0;
-}
-.main-timeline .timeline:nth-child(2n) .content{
-    padding: 0 0 0 40px;
-    margin-left: 2px;
-}
-.main-timeline .timeline:nth-child(2n) .icon{
-    right: auto;
-    left: -14px;
-}
-.main-timeline .timeline:nth-child(2n) .icon:before,
-.main-timeline .timeline:nth-child(2n) .icon span:before,
-.main-timeline .timeline:nth-child(2n) .icon span:after{
-    left: auto;
-    right: -15px;
-}
-.main-timeline .timeline:nth-child(2n) .icon span:before{
-    border-radius: 0 0 0 21px;
-}
-.main-timeline .timeline:nth-child(2n) .icon span:after{
-    border-radius: 21px 0 0 0;
-}
-.main-timeline .timeline:nth-child(2){
-    margin-top: 180px;
-}
-.main-timeline .timeline:nth-child(odd){
-    margin: -175px 0 0 0;
-}
-.main-timeline .timeline:nth-child(even){
-    margin-bottom: 80px;
-}
-.main-timeline .timeline:first-child,
-.main-timeline .timeline:last-child:nth-child(even){
-    margin: 0;
-}
-@media only screen and (max-width: 990px){
-    .main-timeline:before{ left: 100%; }
-    .main-timeline .timeline{
-        width: 100%;
-        float: none;
-        margin-bottom: 20px !important;
-    }
-    .main-timeline .timeline:first-child:before,
-    .main-timeline .timeline:last-child:before{
-        left: auto !important;
-        right: -13px !important;
-    }
-    .main-timeline .timeline:nth-child(2n) .circle{
-        float: left;
-        margin: 0 25px 0 0;
-    }
-    .main-timeline .timeline:nth-child(2n) .circle:before{
-        right: -33px;
-        left: auto;
-        box-shadow: 7px 0 9px -7px #444 inset;
-    }
-    .main-timeline .timeline:nth-child(2n) .circle span:before,
-    .main-timeline .timeline:nth-child(2n) .circle span:after{
-        right: -33px;
-        left: auto;
-        border-radius: 0 0 0 21px;
-    }
-    .main-timeline .timeline:nth-child(2n) .circle span:after{
-        border-radius: 21px 0 0 0;
-    }
-    .main-timeline .timeline:nth-child(2n) .content{
-        padding: 0 40px 0 0;
-        margin-left: 0;
-    }
-    .main-timeline .timeline:nth-child(2n) .icon{
-        right: -14px;
-        left: auto;
-    }
-    .main-timeline .timeline:nth-child(2n) .icon:before,
-    .main-timeline .timeline:nth-child(2n) .icon span:before,
-    .main-timeline .timeline:nth-child(2n) .icon span:after{
-        left: -15px;
-        right: auto;
-    }
-    .main-timeline .timeline:nth-child(2n) .icon span:before{
-        border-radius: 0 0 21px 0;
-    }
-    .main-timeline .timeline:nth-child(2n) .icon span:after{
-        border-radius: 0 21px 0 0;
-    }
-    .main-timeline .timeline:nth-child(2),
-    .main-timeline .timeline:nth-child(odd),
-    .main-timeline .timeline:nth-child(even){
-        margin: 0;
-    }
-}
-@media only screen and (max-width: 480px){
-    .main-timeline:before{ left: 0; }
-    .main-timeline .timeline:first-child:before,
-    .main-timeline .timeline:last-child:before{
-        left: -12px !important;
-        right: auto !important;
-    }
-    .main-timeline .circle,
-    .main-timeline .timeline:nth-child(2n) .circle{
-        width: 130px;
-        height: 130px;
-        float: none;
-        margin: 0 auto;
-    }
-    .main-timeline .timeline-content:before{
-        width: 99.5%;
-        top: 68px;
-        left: 0.5%;
-    }
-    .main-timeline .circle span{
-        line-height: 115px;
-        font-size: 60px;
-    }
-    .main-timeline .circle:before,
-    .main-timeline .circle span:before,
-    .main-timeline .circle span:after,
-    .main-timeline .icon{
-        display: none;
-    }
-    .main-timeline .content,
-    .main-timeline .timeline:nth-child(2n) .content{
-        padding: 0 10px;
-    }
-    .main-timeline .year{
-        margin-bottom: 15px;
-    }
-    .main-timeline .description{
-        text-align: center;
-    }
-}

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

@@ -349,4 +349,63 @@ function bindVote() {
 			}
 		});
 	});
+}
+
+//递归加载评论
+//加载父楼层
+function loadParentComments(data) {
+    loading();
+    var html = '';
+	if (data) {
+        var rows = Enumerable.From(data.rows).Where(c => c.ParentId === 0).ToArray();
+        var page = data.page;
+        var size = data.size;
+        var maxPage = Math.ceil(data.total / size);
+        page = page > maxPage ? maxPage : page;
+        page = page < 1 ? 1 : page;
+        var startfloor = data.parentTotal - (page - 1) * size;
+        for (let i = 0; i < rows.length; i++) {
+            html += `<li class="msg-list media animated fadeInRight" id='${rows[i].Id}'>
+                        <div class="media-body">
+                            <article class="panel panel-info">
+                                <header class="panel-heading">${startfloor}# ${rows[i].IsMaster ? `<i class="icon icon-user"></i>` : ""}${rows[i].NickName}${rows[i].IsMaster ? `(管理员)` : ""}
+                                    <span class="pull-right" style="font-size: 10px;">${rows[i].CommentDate}<span class="hidden-sm hidden-xs"> | ${GetOperatingSystem(rows[i].OperatingSystem) + " | " + GetBrowser(rows[i].Browser)}</span></span>
+                                </header>
+                                <div class="panel-body">
+                                    ${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>
+                                    <a class="label label-info" href="?uid=${rows[i].Id}"><i class="icon-comment"></i></a>
+                                    ${loadComments(data.rows, Enumerable.From(data.rows).Where(c => c.ParentId === rows[i].Id).OrderBy(c => c.CommentDate).ToArray(), startfloor--)}
+                                </div>
+                            </article>
+                        </div>
+                    </li>`;
+        }
+	}
+    loadingDone();
+    return html;
+}
+
+//加载子楼层
+function loadComments(data, comments, root, depth = 0) {
+    var colors = ["info", "success", "primary", "warning", "danger"];
+    var floor = 1;
+    depth++;
+    var html = '';
+    Enumerable.From(comments).ForEach((item, index) => {
+	    var color = colors[depth%5];
+		html += `<article id="${item.Id}" class="panel panel-${color}">
+                        <div class="panel-heading">
+                            ${depth}-${floor++}# ${item.IsMaster ?`<i class="icon icon-user"></i>`:""}${item.NickName}${item.IsMaster ?`(管理员)`:""}
+                            <span class="pull-right" style="font-size: 10px;">${item.CommentDate}<span class="hidden-sm hidden-xs"> | ${GetOperatingSystem(item.OperatingSystem) + " | " + GetBrowser(item.Browser)}</span></span>
+                        </div>
+                        <div class="panel-body">
+                            ${item.Content} 
+                            <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>
+                            ${loadComments(data, Enumerable.From(data).Where(c => c.ParentId === item.Id).OrderBy(c => c.CommentDate), root, depth)}
+                        </div>
+                    </article>`;
+    });
+    return html;
 }

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/article.min.js


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

@@ -191,4 +191,61 @@ function bindReplyBtn() {
 			height: 100
 		});
 	});
+}
+
+//递归加载留言
+//加载父楼层
+function loadParentMsgs(data) {
+	loading();
+	var html = '';
+	if (data) {
+		var rows = Enumerable.From(data.rows).Where(c => c.ParentId === 0).ToArray();
+		var page = data.page;
+		var size = data.size;
+		var maxPage = Math.ceil(data.total / size);
+		page = page > maxPage ? maxPage : page;
+		page = page < 1 ? 1 : page;
+		var startfloor = data.parentTotal - (page - 1) * size;
+		for (let i = 0; i < rows.length; i++) {
+			html += `<li class="msg-list media animated fadeInRight" id='${rows[i].Id}'>
+						   <div class="media-body">
+								<article class="panel panel-info">
+									<header class="panel-heading">${startfloor}# ${rows[i].IsMaster? `<i class="icon icon-user"></i>` : ""}${rows[i].NickName}${rows  [i].IsMaster ? `(管理员)` : ""}
+										<span class="pull-right" style="font-size: 10px;">${rows[i].PostDate}<span class="hidden-sm hidden-xs"> | ${GetOperatingSystem(rows[i].OperatingSystem) + " | " + GetBrowser(rows[i].Browser)}</span></span>
+									</header>
+									<div class="panel-body">
+										${rows[i].Content}
+										<a class="label label-info" href="?uid=${rows[i].Id}"><i class="icon-comment"></i></a>
+										${loadMsgs(data.rows,Enumerable.From(data.rows).Where(c => c.ParentId === rows[i].Id).OrderBy(c => c.PostDate).ToArray(), startfloor--)}
+									</div>
+								</article>
+							</div>
+						</li>`;
+		}
+	}
+	loadingDone();
+	return html;
+}
+
+//加载子楼层
+function loadMsgs(data, msg, root, depth = 0) {
+	var colors = ["info", "success", "primary", "warning", "danger"];
+	var floor = 1;
+	depth++;
+	var html = '';
+	Enumerable.From(msg).ForEach((item, index) => {
+		var color = colors[depth % 5];
+		html += `<article id="${item.Id}" class="panel panel-${color}">
+						<div class="panel-heading">
+							${depth}-${floor++}# ${item.IsMaster ? `<i class="icon icon-user"></i>` : ""}${item.NickName}${item.IsMaster ? `(管理员)` : ""}<span class="pull-right" style="font-size: 10px;">${item.PostDate}<span class="hidden-sm hidden-xs"> | ${GetOperatingSystem(item.OperatingSystem) + " | " + GetBrowser(item.Browser)}</span>
+							</span>
+						</div>
+						<div class="panel-body">
+							${item.Content}
+							<a class="label label-${color}" href="?uid=${item.Id}"><i class="icon-comment"></i></a>
+							${loadMsgs(data,Enumerable.From(data).Where(c => c.ParentId === item.Id).OrderBy(c => c.PostDate),root, depth)}
+						</div>
+					</article>`;
+	});
+	return html;
 }

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/leavemsg.min.js


+ 0 - 116
src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/scripts.js

@@ -295,122 +295,6 @@ function getFile(obj, inputName) {
 	$("input[name='" + inputName + "']").val(file_name);
 }
 
-//递归加载评论
-//加载父楼层
-function loadParentComments(data) {
-    loading();
-    var html = '';
-	if (data) {
-        var rows = Enumerable.From(data.rows).Where(c => c.ParentId === 0).ToArray();
-        var page = data.page;
-        var size = data.size;
-        var maxPage = Math.ceil(data.total / size);
-        page = page > maxPage ? maxPage : page;
-        page = page < 1 ? 1 : page;
-        var startfloor = data.parentTotal - (page - 1) * size;
-        for (let i = 0; i < rows.length; i++) {
-            html += `<li class="msg-list media animated fadeInRight" id='${rows[i].Id}'>
-                        <div class="media-body">
-                            <article class="panel panel-info">
-                                <header class="panel-heading">${startfloor}# ${rows[i].IsMaster ? `<i class="icon icon-user"></i>` : ""}${rows[i].NickName}${rows[i].IsMaster ? `(管理员)` : ""}
-                                    <span class="pull-right" style="font-size: 10px;">${rows[i].CommentDate}<span class="hidden-sm hidden-xs"> | ${GetOperatingSystem(rows[i].OperatingSystem) + " | " + GetBrowser(rows[i].Browser)}</span></span>
-                                </header>
-                                <div class="panel-body">
-                                    ${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>
-                                    <a class="label label-info" href="?uid=${rows[i].Id}"><i class="icon-comment"></i></a>
-                                    ${loadComments(data.rows, Enumerable.From(data.rows).Where(c => c.ParentId === rows[i].Id).OrderBy(c => c.CommentDate).ToArray(), startfloor--)}
-                                </div>
-                            </article>
-                        </div>
-                    </li>`;
-        }
-	}
-    loadingDone();
-    return html;
-}
-
-//加载子楼层
-function loadComments(data, comments, root, depth = 0) {
-    var colors = ["info", "success", "primary", "warning", "danger"];
-    var floor = 1;
-    depth++;
-    var html = '';
-    Enumerable.From(comments).ForEach((item, index) => {
-	    var color = colors[depth%5];
-		html += `<article id="${item.Id}" class="panel panel-${color}">
-                        <div class="panel-heading">
-                            ${depth}-${floor++}# ${item.IsMaster ?`<i class="icon icon-user"></i>`:""}${item.NickName}${item.IsMaster ?`(管理员)`:""}
-                            <span class="pull-right" style="font-size: 10px;">${item.CommentDate}<span class="hidden-sm hidden-xs"> | ${GetOperatingSystem(item.OperatingSystem) + " | " + GetBrowser(item.Browser)}</span></span>
-                        </div>
-                        <div class="panel-body">
-                            ${item.Content} 
-                            <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>
-                            ${loadComments(data, Enumerable.From(data).Where(c => c.ParentId === item.Id).OrderBy(c => c.CommentDate), root, depth)}
-                        </div>
-                    </article>`;
-    });
-    return html;
-}
-
-//递归加载留言
-//加载父楼层
-function loadParentMsgs(data) {
-	loading();
-	var html = '';
-	if (data) {
-		var rows = Enumerable.From(data.rows).Where(c => c.ParentId === 0).ToArray();
-		var page = data.page;
-		var size = data.size;
-		var maxPage = Math.ceil(data.total / size);
-		page = page > maxPage ? maxPage : page;
-		page = page < 1 ? 1 : page;
-		var startfloor = data.parentTotal - (page - 1) * size;
-		for (let i = 0; i < rows.length; i++) {
-			html += `<li class="msg-list media animated fadeInRight" id='${rows[i].Id}'>
-						   <div class="media-body">
-								<article class="panel panel-info">
-									<header class="panel-heading">${startfloor}# ${rows[i].IsMaster? `<i class="icon icon-user"></i>` : ""}${rows[i].NickName}${rows  [i].IsMaster ? `(管理员)` : ""}
-										<span class="pull-right" style="font-size: 10px;">${rows[i].PostDate}<span class="hidden-sm hidden-xs"> | ${GetOperatingSystem(rows[i].OperatingSystem) + " | " + GetBrowser(rows[i].Browser)}</span></span>
-									</header>
-									<div class="panel-body">
-										${rows[i].Content}
-										<a class="label label-info" href="?uid=${rows[i].Id}"><i class="icon-comment"></i></a>
-										${loadMsgs(data.rows,Enumerable.From(data.rows).Where(c => c.ParentId === rows[i].Id).OrderBy(c => c.PostDate).ToArray(), startfloor--)}
-									</div>
-								</article>
-							</div>
-						</li>`;
-		}
-	}
-	loadingDone();
-	return html;
-}
-
-//加载子楼层
-function loadMsgs(data, msg, root, depth = 0) {
-	var colors = ["info", "success", "primary", "warning", "danger"];
-	var floor = 1;
-	depth++;
-	var html = '';
-	Enumerable.From(msg).ForEach((item, index) => {
-		var color = colors[depth % 5];
-		html += `<article id="${item.Id}" class="panel panel-${color}">
-						<div class="panel-heading">
-							${depth}-${floor++}# ${item.IsMaster ? `<i class="icon icon-user"></i>` : ""}${item.NickName}${item.IsMaster ? `(管理员)` : ""}<span class="pull-right" style="font-size: 10px;">${item.PostDate}<span class="hidden-sm hidden-xs"> | ${GetOperatingSystem(item.OperatingSystem) + " | " + GetBrowser(item.Browser)}</span>
-							</span>
-						</div>
-						<div class="panel-body">
-							${item.Content}
-							<a class="label label-${color}" href="?uid=${item.Id}"><i class="icon-comment"></i></a>
-							${loadMsgs(data,Enumerable.From(data).Where(c => c.ParentId === item.Id).OrderBy(c => c.PostDate),root, depth)}
-						</div>
-					</article>`;
-	});
-	return html;
-}
-
 function popBrowserTips() {
 	if (window.sessionStorage) {
 		var deny = window.sessionStorage.getItem("deny") || false;

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

@@ -261,7 +261,7 @@ myApp.controller("writeblog", ["$scope", "$http", "$timeout", function ($scope,
 	//上传Word文档
 	$scope.upload = function() {
 		$scope.loading();
-		$("#fileform").ajaxSubmit({
+		$("#docform").ajaxSubmit({
 			url: "/Upload/UploadWord",
 			type: "post",
 			success: function(data) {
@@ -299,7 +299,7 @@ myApp.controller("writeblog", ["$scope", "$http", "$timeout", function ($scope,
 				type: 1,
 				title: '上传Word文档',
 				area: ['420px', '150px'], //宽高
-				content: $("#upfile")
+				content: $("#docfile")
 			});
 		});
 	}
@@ -477,7 +477,7 @@ myApp.controller("postedit", ["$scope", "$http", "$location", "$timeout", functi
 	//上传Word文档
 	$scope.upload = function () {
 		$scope.loading();
-		$("#fileform").ajaxSubmit({
+		$("#docform").ajaxSubmit({
 			url: "/Upload/UploadWord",
 			type: "post",
 			success: function (data) {
@@ -515,7 +515,7 @@ myApp.controller("postedit", ["$scope", "$http", "$location", "$timeout", functi
 				type: 1,
 				title: '上传Word文档',
 				area: ['420px', '150px'], //宽高
-				content: $("#upfile")
+				content: $("#docfile")
 			});
 		});
 	}

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/post.min.js


+ 2 - 2
src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/post/edit.html

@@ -109,8 +109,8 @@
     </div>
 </form>
 <div style="position: absolute; left: -20000px;">
-    <div id="upfile">
-        <form id="fileform" onsubmit="return false" enctype="multipart/form-data">
+    <div id="docfile">
+        <form id="docform" onsubmit="return false" enctype="multipart/form-data">
             <h4 class="h4">注意:重复上传将会覆盖之前上传的内容!</h4>
             <div class="file-box">
                 <div class="input-group">

+ 2 - 2
src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/post/writeblog.html

@@ -115,8 +115,8 @@
     </div>
 </form>
 <div style="position: absolute; left: -20000px; ">
-    <div id="upfile">
-        <form id="fileform" onsubmit="return false" enctype="multipart/form-data">
+    <div id="docfile">
+        <form id="docform" onsubmit="return false" enctype="multipart/form-data">
             <h4 class="h4">注意:重复上传将会覆盖之前上传的内容!</h4>
             <div class="file-box">
                 <div class="input-group">

+ 43 - 22
src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/system/firewall.html

@@ -12,13 +12,25 @@
     }
 </style>
 <div class="card">
-    <div class="card-header">
-        <h2>防火墙策略配置</h2>
+    <div class="card-header p-b-0">
+        <div class="row">
+            <div class="col-md-6">
+                <h2>防火墙策略配置</h2>
+            </div>
+            <div class="col-md-6 text-right">
+                <div class="btn-group">
+                    <button class="btn btn-danger" ng-click="getIPBlackList()" type="button">编辑全局IP黑名单</button>
+                    <button class="btn btn-success" ng-click="getIPRangeBlackList()" type="button">编辑IP地址段黑名单</button>
+                    <button class="btn btn-primary" ng-click="getIPWhiteList()" type="button">编辑IP白名单</button>
+                    <button class="btn btn-info" ng-click="save()" type="button">保存配置</button>
+                </div>
+            </div>
+        </div>
     </div>
-    <div class="card-body">
+    <div class="card-body p-t-0">
         <div class="card-body card-padding">
             <div class="row">
-                <div class="col-md-6">
+                <div class="col-lg-2 col-md-4 col-sm-6">
                     <div class="input-group">
                         <span class="input-group-addon">启用地区限制访问策略:</span>
                         <span class="el-switch">
@@ -27,24 +39,33 @@
                         </span>
                     </div>
                 </div>
-                <div class="col-md-6 text-right">
-                    <div class="btn-group">
-                        <button class="btn btn-danger" ng-click="getIPBlackList()" type="button">编辑全局IP黑名单</button>
-                        <button class="btn btn-success" ng-click="getIPRangeBlackList()" type="button">编辑IP地址段黑名单</button>
-                        <button class="btn btn-primary" ng-click="getIPWhiteList()" type="button">编辑IP白名单</button>
+                <div class="col-lg-10 col-md-8 col-sm-6">
+                    <div class="input-group">
+                        <span class="input-group-addon">受限制的地区:</span>
+                        <div class="fg-line">
+                            <input class="form-control" ng-model="Settings.DenyArea" ng-readonly="Settings.EnableDenyArea=='false'" placeholder="禁止访问的地区,逗号分隔" type="text" />
+                        </div>
                     </div>
                 </div>
             </div>
-            <div class="input-group">
-                <span class="el-switch input-group-addon">
-                    <span>受限制的地区:</span>
-                </span>
-                <div class="fg-line">
-                    <textarea class="form-control" ng-model="Settings.DenyArea" ng-readonly="Settings.EnableDenyArea=='false'" placeholder="禁止访问的地区,逗号分隔" rows="3" type="text"></textarea>
+            <div class="row">
+                <div class="col-md-5 col-sm-6">
+                    <div class="input-group">
+                        <span class="input-group-addon">单IP访问频次:每</span>
+                        <div class="fg-line">
+                            <input class="form-control" ng-model="Settings.LimitIPFrequency" type="text" />
+                        </div>
+                        <span class="input-group-addon">秒内,最大请求</span>
+                        <div class="fg-line">
+                            <input class="form-control" ng-model="Settings.LimitIPRequestTimes" type="text" />
+                        </div>
+                        <span class="input-group-addon">次,冻结该IP:</span>
+                        <div class="fg-line">
+                            <input class="form-control" ng-model="Settings.BanIPTimespan" type="text" />
+                        </div>
+                        <span class="input-group-addon">分钟</span>
+                    </div>
                 </div>
-                <span class="input-group-btn">
-                    <button class="btn btn-primary" ng-click="save()" type="button">保存配置</button>
-                </span>
             </div>
         </div>
     </div>
@@ -91,15 +112,15 @@
         <div class="input-group">
             <span class="input-group-addon">搜索 IP :</span>
             <div class="fg-line">
-                <input class="form-control" placeholder="114.114.114.114" type="text" ng-model="ip" ng-change="searchIP(ip)"/>
+                <input class="form-control" ng-change="searchIP(ip)" ng-model="ip" placeholder="114.114.114.114" type="text"/>
             </div>
         </div>
-        <div ng-if="AreaIPs.length<=0" class="text-center">
-            <p class="text-danger size18">暂无记录</p>
+        <div class="text-center" ng-if="AreaIPs.length<=0">
+            <p class="size18 text-danger">暂无记录</p>
         </div>
         <ul class="iplist list-group">
             <li class="list-group-item" ng-repeat="item in AreaIPs track by $index">
-                <a ng-href="/tools/ip?ip={{item}}" target="_blank" data-microtip="查看{{item}}的详细信息" data-microtip-position="bottom">{{item}}</a>
+                <a data-microtip="查看{{item}}的详细信息" data-microtip-position="bottom" ng-href="/tools/ip?ip={{item}}" target="_blank">{{item}}</a>
                 <button class="badge btn btn-primary" ng-click="addToWhiteList(item)">添加到白名单</button>
             </li>
         </ul>

+ 0 - 10
src/Masuit.MyBlogs.Core/wwwroot/template/bugfeed.html

@@ -1,10 +0,0 @@
-<h2>网站bug提交反馈通知!</h2>
-<div>
-    <p>非常感谢您关于{{title}}提交的问题。</p>
-    存在该问题的相关链接:<a href="{{link}}">{{link}}</a>,已经修复!
-    <p>处理完成时间:{{date}}</p>
-    <p>站长留言:{{text}}</p>
-</div>
-<p>
-    本邮件由系统自动发出,请勿回复本邮件!
-</p>

+ 0 - 10
src/Masuit.MyBlogs.Core/wwwroot/template/bugreport.html

@@ -1,10 +0,0 @@
-<h2>网站bug提交通知!</h2>
-<div>
-    <p>访客{{name}}({{email}})关于{{title}}提交了一个BUG:</p>
-    <p>{{desc}}</p>
-    存在该问题的相关链接:<a href="{{link}}">{{link}}</a>,请及时处理!
-    <p>提交时间:{{date}}</p>
-</div>
-<p>
-    本邮件由系统自动发出,请勿回复本邮件!
-</p>

BIN
src/packages/Aspose.Words.18.11.0.nupkg


Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels