懒得勤快 6 лет назад
Родитель
Сommit
0f0e41fa72
42 измененных файлов с 1458 добавлено и 452 удалено
  1. 6 0
      src/Masuit.MyBlogs.Core.sln
  2. 1 1
      src/Masuit.MyBlogs.Core/Configs/RegisterAutomapper.cs
  3. 3 3
      src/Masuit.MyBlogs.Core/Controllers/ErrorController.cs
  4. 24 12
      src/Masuit.MyBlogs.Core/Controllers/FileController.cs
  5. 3 3
      src/Masuit.MyBlogs.Core/Controllers/HomeController.cs
  6. 1 1
      src/Masuit.MyBlogs.Core/Controllers/NoticeController.cs
  7. 6 14
      src/Masuit.MyBlogs.Core/Controllers/PassportController.cs
  8. 30 13
      src/Masuit.MyBlogs.Core/Controllers/PostController.cs
  9. 29 16
      src/Masuit.MyBlogs.Core/Controllers/SearchController.cs
  10. 1 0
      src/Masuit.MyBlogs.Core/Controllers/SystemController.cs
  11. 5 8
      src/Masuit.MyBlogs.Core/Extensions/AuthorityAttribute.cs
  12. 0 5
      src/Masuit.MyBlogs.Core/Extensions/FirewallMiddleware.cs
  13. 12 2
      src/Masuit.MyBlogs.Core/Extensions/Hangfire/HangfireBackJob.cs
  14. 15 4
      src/Masuit.MyBlogs.Core/Hubs/MyHub.cs
  15. 2 2
      src/Masuit.MyBlogs.Core/Infrastructure/Application/DataContext.cs
  16. 0 71
      src/Masuit.MyBlogs.Core/Infrastructure/Repository/BaseRepository.cs
  17. 0 52
      src/Masuit.MyBlogs.Core/Infrastructure/Repository/Interface/IBaseRepository.cs
  18. 0 70
      src/Masuit.MyBlogs.Core/Infrastructure/Services/BaseService.cs
  19. 0 52
      src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IBaseService.cs
  20. 1 4
      src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IPostService.cs
  21. 23 14
      src/Masuit.MyBlogs.Core/Infrastructure/Services/PostService.cs
  22. 1 1
      src/Masuit.MyBlogs.Core/Masuit.MyBlogs.Core.csproj
  23. 732 0
      src/Masuit.MyBlogs.Core/Migrations/20190201132829_Init.Designer.cs
  24. 435 0
      src/Masuit.MyBlogs.Core/Migrations/20190201132829_Init.cs
  25. 27 22
      src/Masuit.MyBlogs.Core/Migrations/DataContextModelSnapshot.cs
  26. 1 1
      src/Masuit.MyBlogs.Core/Models/DTO/PostOutputDto.cs
  27. 11 0
      src/Masuit.MyBlogs.Core/Models/DTO/SearchResult.cs
  28. 3 9
      src/Masuit.MyBlogs.Core/Models/Entity/BaseEntity.cs
  29. 17 7
      src/Masuit.MyBlogs.Core/Models/Entity/Post.cs
  30. 0 2
      src/Masuit.MyBlogs.Core/Program.cs
  31. 1 2
      src/Masuit.MyBlogs.Core/Properties/PublishProfiles/FolderProfile.pubxml
  32. 38 13
      src/Masuit.MyBlogs.Core/Startup.cs
  33. 1 4
      src/Masuit.MyBlogs.Core/Views/Error/Index.cshtml
  34. 0 1
      src/Masuit.MyBlogs.Core/Views/Error/ServiceUnavailable.cshtml
  35. 1 1
      src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListItem.cshtml
  36. 1 1
      src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListItem_Admin.cshtml
  37. 1 1
      src/Masuit.MyBlogs.Core/Views/Shared/_Aside.cshtml
  38. 24 31
      src/Masuit.MyBlogs.Core/Views/Shared/_Layout.cshtml
  39. 1 1
      src/Masuit.MyBlogs.Core/appsettings.json
  40. 0 6
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/dashboard.js
  41. 1 1
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/filemanager/js/providers/config.js
  42. 0 1
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/dashboard.html

+ 6 - 0
src/Masuit.MyBlogs.Core.sln

@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masuit.Tools.Core", "..\Mas
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masuit.MyBlogs.Core", "Masuit.MyBlogs.Core\Masuit.MyBlogs.Core.csproj", "{51A09BD3-AB54-4DF9-AB8B-C68DF0672C39}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masuit.LuceneEFCore.SearchEngine", "..\Masuit.LuceneEFCore.SearchEngine\Masuit.LuceneEFCore.SearchEngine\Masuit.LuceneEFCore.SearchEngine.csproj", "{50501B4A-0AF3-4613-949D-9FE9D26F1B10}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
 		{51A09BD3-AB54-4DF9-AB8B-C68DF0672C39}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{51A09BD3-AB54-4DF9-AB8B-C68DF0672C39}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{51A09BD3-AB54-4DF9-AB8B-C68DF0672C39}.Release|Any CPU.Build.0 = Release|Any CPU
+		{50501B4A-0AF3-4613-949D-9FE9D26F1B10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{50501B4A-0AF3-4613-949D-9FE9D26F1B10}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{50501B4A-0AF3-4613-949D-9FE9D26F1B10}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{50501B4A-0AF3-4613-949D-9FE9D26F1B10}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 1 - 1
src/Masuit.MyBlogs.Core/Configs/RegisterAutomapper.cs

@@ -84,7 +84,7 @@ namespace Masuit.MyBlogs.Core.Configs
                 m.CreateMap<Post, PostInputDto>();
                 m.CreateMap<Post, PostHistoryVersion>().ForMember(v => v.PostId, e => e.MapFrom(p => p.Id));
                 m.CreateMap<PostInputDto, Post>();
-                m.CreateMap<Post, PostOutputDto>().ForMember(p => p.CategoryName, e => e.MapFrom(p => p.Category.Name)).ForMember(p => p.ViewCount, e => e.MapFrom(p => p.PostAccessRecord.Any() ? p.PostAccessRecord.Sum(r => r.ClickCount) : 0)).ForMember(p => p.AverageViewCount, e => e.MapFrom(p => p.PostAccessRecord.Any() ? p.PostAccessRecord.Average(r => r.ClickCount) : 0));
+                m.CreateMap<Post, PostOutputDto>().ForMember(p => p.CategoryName, e => e.MapFrom(p => p.Category.Name));
                 m.CreateMap<PostOutputDto, Post>();
                 m.CreateMap<PostInputDto, PostOutputDto>();
                 m.CreateMap<PostHistoryVersion, PostOutputDto>().ForMember(p => p.CategoryName, e => e.MapFrom(p => p.Category.Name));

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

@@ -12,10 +12,10 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// 404
         /// </summary>
         /// <returns></returns>
-        [Route("error")]
+        [Route("{*url}", Order = 99999)]
         public ActionResult Index()
         {
-            //Response.StatusCode = 404;
+            Response.StatusCode = 404;
             if (Request.Method.ToLower().Equals("get"))
             {
                 return View();
@@ -35,7 +35,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("ServiceUnavailable")]
         public ActionResult ServiceUnavailable()
         {
-            //Response.StatusCode = 503;
+            Response.StatusCode = 503;
             if (Request.Method.ToLower().Equals("get"))
             {
                 return View();

+ 24 - 12
src/Masuit.MyBlogs.Core/Controllers/FileController.cs

@@ -242,20 +242,32 @@ namespace Masuit.MyBlogs.Core.Controllers
                         success = "true"
                     });
                     break;
-                default:
-                    var httpfiles = Request.Form.Files;
-                    if (httpfiles.Count > 0)
+            }
+            return Json(new
+            {
+                result = list
+            });
+        }
+
+        /// <summary>
+        /// 上传文件
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost]
+        public ActionResult Upload(string destination)
+        {
+            List<object> list = new List<object>();
+            var prefix = CommonHelper.SystemSettings["PathRoot"].Trim('\\', '/');
+            if (Request.Form.Files.Count > 0)
+            {
+                foreach (var t in Request.Form.Files)
+                {
+                    string path = Path.Combine(string.IsNullOrEmpty(prefix) && !Directory.Exists(prefix) ? _hostingEnvironment.WebRootPath + (destination) : prefix + destination, t.FileName);
+                    using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite))
                     {
-                        foreach (var t in httpfiles)
-                        {
-                            path = Path.Combine(string.IsNullOrEmpty(prefix) && !Directory.Exists(prefix) ? _hostingEnvironment.WebRootPath + (req.Destination) : prefix + req.Destination, t.FileName);
-                            using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite))
-                            {
-                                t.CopyTo(fs);
-                            }
-                        }
+                        t.CopyTo(fs);
                     }
-                    break;
+                }
             }
             return Json(new
             {

+ 3 - 3
src/Masuit.MyBlogs.Core/Controllers/HomeController.cs

@@ -137,7 +137,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                     posts = temp.ThenByDescending(p => p.PostDate).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
                     break;
                 case OrderBy.ViewCount:
-                    posts = temp.ThenByDescending(p => p.ViewCount).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
+                    posts = temp.ThenByDescending(p => p.TotalViewCount).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
                     break;
                 case OrderBy.VoteCount:
                     posts = temp.ThenByDescending(p => p.VoteUpCount).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
@@ -220,7 +220,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 KeyWords = g.FirstOrDefault().KeyWords,
                 SearchCount = g.Count()
             }).Cacheable().ToList(); //热词统计
-            Expression<Func<PostOutputDto, double>> order = p => p.ViewCount;
+            Expression<Func<PostOutputDto, double>> order = p => p.TotalViewCount;
             switch (new Random().Next() % 3)
             {
                 case 1:
@@ -267,7 +267,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                     posts = postList.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.ViewCount).Skip(size * (page - 1)).Take(size).Cacheable().ToList();
+                    posts = postList.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();

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

@@ -190,7 +190,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <returns></returns>
         public ActionResult Last()
         {
-            var notice = NoticeService.GetFirstEntityFromL2Cache(n => n.Status == Status.Display, n => n.ModifyDate, false);
+            var notice = NoticeService.GetFirstEntity(n => n.Status == Status.Display, n => n.ModifyDate, false);
             if (notice != null)
             {
                 if (HttpContext.Session.Get<object>("notice" + notice.Id) is null)

+ 6 - 14
src/Masuit.MyBlogs.Core/Controllers/PassportController.cs

@@ -113,13 +113,13 @@ namespace Masuit.MyBlogs.Core.Controllers
         [HttpPost, ValidateAntiForgeryToken]
         public ActionResult Login(string username, string password, string valid, string remem)
         {
-            string validSession = HttpContext.Session.GetByRedis<string>("valid") ?? String.Empty; //将验证码从Session中取出来,用于登录验证比较
-            if (String.IsNullOrEmpty(validSession) || !valid.Trim().Equals(validSession, StringComparison.InvariantCultureIgnoreCase))
+            string validSession = HttpContext.Session.GetByRedis<string>("valid") ?? string.Empty; //将验证码从Session中取出来,用于登录验证比较
+            if (string.IsNullOrEmpty(validSession) || !valid.Trim().Equals(validSession, StringComparison.InvariantCultureIgnoreCase))
             {
                 return ResultData(null, false, "验证码错误");
             }
             HttpContext.Session.RemoveByRedis("valid"); //验证成功就销毁验证码Session,非常重要
-            if (String.IsNullOrEmpty(username.Trim()) || String.IsNullOrEmpty(password.Trim()))
+            if (string.IsNullOrEmpty(username.Trim()) || string.IsNullOrEmpty(password.Trim()))
             {
                 return ResultData(null, false, "用户名或密码不能为空");
             }
@@ -134,11 +134,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 }
                 HangfireHelper.CreateJob(typeof(IHangfireBackJob), nameof(HangfireBackJob.LoginRecord), "default", userInfo, HttpContext.Connection.RemoteIpAddress.ToString(), LoginType.Default);
                 string refer = Request.Cookies["refer"];
-                if (string.IsNullOrEmpty(refer))
-                {
-                    return ResultData(null, true, "/");
-                }
-                return ResultData(null, true, refer);
+                return ResultData(null, true, string.IsNullOrEmpty(refer) ? "/" : refer);
             }
             return ResultData(null, false, "用户名或密码错误");
         }
@@ -164,7 +160,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         public ActionResult CheckValidateCode(string code)
         {
             string validSession = HttpContext.Session.GetByRedis<string>("valid");
-            if (String.IsNullOrEmpty(validSession) || !code.Trim().Equals(validSession, StringComparison.InvariantCultureIgnoreCase))
+            if (string.IsNullOrEmpty(validSession) || !code.Trim().Equals(validSession, StringComparison.InvariantCultureIgnoreCase))
             {
                 return ResultData(null, false, "验证码错误");
             }
@@ -194,11 +190,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             Response.Cookies.Delete("username");
             Response.Cookies.Delete("password");
             HttpContext.Session.Clear();
-            if (Request.Method.ToLower().Equals("get"))
-            {
-                return RedirectToAction("Index", "Home");
-            }
-            return ResultData(null, message: "注销成功!");
+            return Request.Method.ToLower().Equals("get") ? RedirectToAction("Index", "Home") : ResultData(null, message: "注销成功!");
         }
     }
 }

+ 30 - 13
src/Masuit.MyBlogs.Core/Controllers/PostController.cs

@@ -2,10 +2,12 @@
 using Common;
 using EFSecondLevelCache.Core;
 using Hangfire;
+using Masuit.LuceneEFCore.SearchEngine.Interfaces;
 using Masuit.MyBlogs.Core.Common;
 using Masuit.MyBlogs.Core.Configs;
 using Masuit.MyBlogs.Core.Extensions;
 using Masuit.MyBlogs.Core.Extensions.Hangfire;
+using Masuit.MyBlogs.Core.Infrastructure.Application;
 using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
 using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.Entity;
@@ -40,7 +42,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         private ISeminarService SeminarService { get; set; }
 
         private readonly IHostingEnvironment _hostingEnvironment;
-
+        private readonly ISearchEngine<DataContext> _searchEngine;
+        private readonly ILuceneIndexer _luceneIndexer;
         /// <summary>
         /// 
         /// </summary>
@@ -55,7 +58,9 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <param name="seminarService"></param>
         /// <param name="postHistoryVersionService"></param>
         /// <param name="hostingEnvironment"></param>
-        public PostController(IPostService postService, ICategoryService categoryService, IBroadcastService broadcastService, ISeminarService seminarService, IPostHistoryVersionService postHistoryVersionService, IHostingEnvironment hostingEnvironment)
+        /// <param name="searchEngine"></param>
+        /// <param name="luceneIndexer"></param>
+        public PostController(IPostService postService, ICategoryService categoryService, IBroadcastService broadcastService, ISeminarService seminarService, IPostHistoryVersionService postHistoryVersionService, IHostingEnvironment hostingEnvironment, ISearchEngine<DataContext> searchEngine, ILuceneIndexer luceneIndexer)
         {
             PostService = postService;
             CategoryService = categoryService;
@@ -63,6 +68,8 @@ namespace Masuit.MyBlogs.Core.Controllers
             SeminarService = seminarService;
             PostHistoryVersionService = postHistoryVersionService;
             _hostingEnvironment = hostingEnvironment;
+            _searchEngine = searchEngine;
+            _luceneIndexer = luceneIndexer;
         }
 
         /// <summary>
@@ -80,8 +87,8 @@ namespace Masuit.MyBlogs.Core.Controllers
                 ViewBag.Keyword = post.Keyword + "," + post.Label;
                 UserInfoOutputDto user = HttpContext.Session.GetByRedis<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
                 DateTime modifyDate = post.ModifyDate;
-                ViewBag.Next = PostService.GetFirstEntityFromL2CacheNoTracking(p => p.ModifyDate > modifyDate && (p.Status == Status.Pended || user.IsAdmin), p => p.ModifyDate);
-                ViewBag.Prev = PostService.GetFirstEntityFromL2CacheNoTracking(p => p.ModifyDate < modifyDate && (p.Status == Status.Pended || user.IsAdmin), p => p.ModifyDate, false);
+                ViewBag.Next = PostService.GetFirstEntityNoTracking(p => p.ModifyDate > modifyDate && (p.Status == Status.Pended || user.IsAdmin), p => p.ModifyDate);
+                ViewBag.Prev = PostService.GetFirstEntityNoTracking(p => p.ModifyDate < modifyDate && (p.Status == Status.Pended || user.IsAdmin), p => p.ModifyDate, false);
                 if (user.IsAdmin)
                 {
                     return View("Details_Admin", post);
@@ -153,8 +160,8 @@ namespace Masuit.MyBlogs.Core.Controllers
                 });
             }
 
-            ViewBag.Next = PostHistoryVersionService.GetFirstEntityFromL2CacheNoTracking(p => p.PostId == id && p.ModifyDate > post.ModifyDate, p => p.ModifyDate);
-            ViewBag.Prev = PostHistoryVersionService.GetFirstEntityFromL2CacheNoTracking(p => p.PostId == id && p.ModifyDate < post.ModifyDate, p => p.ModifyDate, false);
+            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);
@@ -461,6 +468,11 @@ namespace Masuit.MyBlogs.Core.Controllers
             post.ModifyDate = DateTime.Now;
             post.PostDate = DateTime.Now;
             bool b = PostService.UpdateEntitySaved(post);
+            if (!b)
+            {
+                return ResultData(null, false, "审核失败!");
+            }
+            _luceneIndexer.Add(post);
             if ("false" == CommonHelper.SystemSettings["DisabledEmailBroadcast"])
             {
                 var cast = BroadcastService.LoadEntities(c => c.Status == Status.Subscribed).ToList();
@@ -480,7 +492,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 });
             }
 
-            return ResultData(null, b, b ? "审核通过!" : "审核失败!");
+            return ResultData(null, true, "审核通过!");
         }
 
         /// <summary>
@@ -493,7 +505,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         {
             var post = PostService.GetById(id);
             post.Status = Status.Deleted;
-            bool b = PostService.UpdateEntitySaved(post);
+            PostService.UpdateEntity(post);
+            bool b = _searchEngine.SaveChanges() > 0;
             return ResultData(null, b, b ? "删除成功!" : "删除失败!");
         }
 
@@ -507,7 +520,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         {
             var post = PostService.GetById(id);
             post.Status = Status.Pended;
-            bool b = PostService.UpdateEntitySaved(post);
+            PostService.UpdateEntity(post);
+            bool b = _searchEngine.SaveChanges() > 0;
             return ResultData(null, b, b ? "恢复成功!" : "恢复失败!");
         }
 
@@ -552,7 +566,8 @@ namespace Masuit.MyBlogs.Core.Controllers
                 }
             }
 
-            bool b = PostService.DeleteByIdSaved(id);
+            PostService.DeleteById(id);
+            bool b = _searchEngine.SaveChanges() > 0;
             return ResultData(null, b, b ? "删除成功!" : "删除失败!");
         }
 
@@ -802,7 +817,8 @@ namespace Masuit.MyBlogs.Core.Controllers
                 });
             }
 
-            bool b = PostService.UpdateEntitySaved(p);
+            PostService.UpdateEntity(p);
+            bool b = _searchEngine.SaveChanges() > 0;
             if (b)
             {
 #if !DEBUG
@@ -917,8 +933,9 @@ namespace Masuit.MyBlogs.Core.Controllers
                 return ResultData(null, false, "如果要定时发布,请选择正确的一个将来时间点!");
             }
 
-            p = PostService.AddEntitySaved(p);
-            if (p != null)
+            PostService.AddEntity(p);
+            bool b = _searchEngine.SaveChanges() > 0;
+            if (b)
             {
                 if ("false" == CommonHelper.SystemSettings["DisabledEmailBroadcast"])
                 {

+ 29 - 16
src/Masuit.MyBlogs.Core/Controllers/SearchController.cs

@@ -1,13 +1,16 @@
 using Common;
+using Masuit.LuceneEFCore.SearchEngine.Interfaces;
 using Masuit.MyBlogs.Core.Extensions;
+using Masuit.MyBlogs.Core.Infrastructure.Application;
 using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
 using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.Entity;
+using Masuit.Tools;
+using Masuit.Tools.Core.Net;
 using Masuit.Tools.NoSQL;
 using Microsoft.AspNetCore.Mvc;
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.Linq;
 using System.Linq.Expressions;
 using System.Text.RegularExpressions;
@@ -24,16 +27,17 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// </summary>
         public ISearchDetailsService SearchDetailsService { get; set; }
         private readonly IPostService _postService;
-
+        private readonly ISearchEngine<DataContext> _searchEngine;
         /// <summary>
         /// 站内搜索
         /// </summary>
         /// <param name="searchDetailsService"></param>
         /// <param name="postService"></param>
-        public SearchController(ISearchDetailsService searchDetailsService, IPostService postService)
+        public SearchController(ISearchDetailsService searchDetailsService, IPostService postService, ISearchEngine<DataContext> searchEngine)
         {
             SearchDetailsService = searchDetailsService;
             _postService = postService;
+            _searchEngine = searchEngine;
         }
 
         /// <summary>
@@ -47,9 +51,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         public ActionResult Search(string wd = "", int page = 1, int size = 10)
         {
             var nul = new List<PostOutputDto>();
-            int count = 0;
             ViewBag.Elapsed = 0;
-            ViewBag.Total = count;
+            ViewBag.Total = 0;
             ViewBag.Keyword = wd;
             if (Regex.Match(wd, CommonHelper.BanRegex).Length > 0 || Regex.Match(wd, CommonHelper.ModRegex).Length > 0)
             {
@@ -64,7 +67,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 {
                     var hotSearches = SearchDetailsService.LoadEntitiesFromL2CacheNoTracking(s => s.SearchTime > start, s => s.SearchTime, false).GroupBy(s => s.KeyWords.ToLower()).OrderByDescending(g => g.Count()).Take(7).Select(g => new KeywordsRankOutputDto()
                     {
-                        KeyWords = g.FirstOrDefault().KeyWords,
+                        KeyWords = g.First().KeyWords,
                         SearchCount = g.Count()
                     }).ToList();
                     ViewBag.hotSearches = hotSearches;
@@ -74,27 +77,26 @@ namespace Masuit.MyBlogs.Core.Controllers
                 wd = wd.Trim().Replace("+", " ");
                 if (!string.IsNullOrWhiteSpace(wd) && !wd.Contains("锟斤拷"))
                 {
-                    if (page == 1)
+                    if (HttpContext.Session.TryGetValue("search:" + wd, out _))
                     {
-                        SearchDetailsService.AddEntity(new SearchDetails()
+                        SearchDetailsService.AddEntity(new SearchDetails
                         {
                             KeyWords = wd,
                             SearchTime = DateTime.Now,
                             IP = HttpContext.Connection.RemoteIpAddress.ToString()
                         });
+                        HttpContext.Session.Set("search:" + wd, wd);
                     }
-                    string[] keywords = LuceneHelper.CutKeywords(wd).ToArray();
-                    Stopwatch sw = Stopwatch.StartNew();
-                    var posts = _postService.SearchPage(page, size, out count, keywords, p => p.ModifyDate);
-                    ViewBag.Elapsed = sw.Elapsed.TotalMilliseconds;
-                    ViewBag.Total = count;
+                    var posts = _postService.SearchPage(page, size, wd);
+                    ViewBag.Elapsed = posts.Elapsed;
+                    ViewBag.Total = posts.Total;
                     SearchDetailsService.SaveChanges();
-                    if (count > 1)
+                    if (posts.Total > 1)
                     {
                         redisHelper.SetString(key, wd, TimeSpan.FromSeconds(10));
                     }
                     ViewBag.hotSearches = new List<KeywordsRankOutputDto>();
-                    return View(posts);
+                    return View(posts.Results);
                 }
                 ViewBag.hotSearches = SearchDetailsService.LoadEntitiesFromL2CacheNoTracking(s => s.SearchTime > start, s => s.SearchTime, false).GroupBy(s => s.KeyWords.ToLower()).OrderByDescending(g => g.Count()).Take(7).Select(g => new KeywordsRankOutputDto()
                 {
@@ -119,7 +121,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             {
                 page = 1;
             }
-            var @where = string.IsNullOrEmpty(search) ? (Expression<Func<SearchDetails, bool>>)(s => true) : s => s.KeyWords.Contains(search);
+            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();
             return PageResult(list, pageCount, total);
@@ -168,5 +170,16 @@ namespace Masuit.MyBlogs.Core.Controllers
             bool b = SearchDetailsService.DeleteByIdSaved(id);
             return ResultData(null, b, b ? "删除成功!" : "删除失败!");
         }
+
+        /// <summary>
+        /// 创建索引
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet, Authority]
+        public IActionResult CreateIndex()
+        {
+            _searchEngine.CreateIndex(new List<string>() { nameof(Post) });
+            return ResultData(null, true, "索引库创建完成!");
+        }
     }
 }

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

@@ -203,6 +203,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             }
 
             var b = SystemSettingService.SaveChanges() > 0;
+            CommonHelper.SystemSettings = SystemSettingService.GetAll().ToDictionary(s => s.Name, s => s.Value); //同步设置
             return ResultData(null, b, b ? "设置保存成功!" : "设置保存失败!");
         }
 

+ 5 - 8
src/Masuit.MyBlogs.Core/Extensions/AuthorityAttribute.cs

@@ -12,13 +12,10 @@ using System;
 using System.Linq;
 using System.Web;
 
-//using Autofac;
-
 namespace Masuit.MyBlogs.Core.Extensions
 {
     public class AuthorityAttribute : ActionFilterAttribute
     {
-        public IUserInfoService UserInfoService { get; set; }
         /// <summary>在执行操作方法之前由 ASP.NET MVC 框架调用。</summary>
         /// <param name="filterContext">筛选器上下文。</param>
         public override void OnActionExecuting(ActionExecutingContext filterContext)
@@ -34,10 +31,10 @@ namespace Masuit.MyBlogs.Core.Extensions
                 //先尝试自动登录
                 if (filterContext.HttpContext.Request.Cookies.Count > 2)
                 {
-                    string name = filterContext.HttpContext.Request.Cookies["username"];
-                    string pwd = filterContext.HttpContext.Request.Cookies["password"]?.DesDecrypt(AppConfig.BaiduAK);
+                    string name = filterContext.HttpContext.Request.Cookies["username"] ?? "";
+                    string pwd = filterContext.HttpContext.Request.Cookies["password"]?.DesDecrypt(AppConfig.BaiduAK) ?? "";
 
-                    var userInfo = UserInfoService.Login(name, pwd);
+                    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) });
@@ -52,7 +49,7 @@ namespace Masuit.MyBlogs.Core.Extensions
                         }
                         else
                         {
-                            filterContext.Result = new JsonResult(new { StatusCode = 200, Success = false, IsLogin = false, Message = "未登录系统,请先登录!" });
+                            filterContext.Result = new UnauthorizedObjectResult(new { StatusCode = 401, Success = false, IsLogin = false, Message = "未登录系统,请先登录!" });
                         }
                     }
                 }
@@ -64,7 +61,7 @@ namespace Masuit.MyBlogs.Core.Extensions
                     }
                     else
                     {
-                        filterContext.Result = new JsonResult(new { StatusCode = 200, Success = false, IsLogin = false, Message = "未登录系统,请先登录!" });
+                        filterContext.Result = new UnauthorizedObjectResult(new { StatusCode = 401, Success = false, IsLogin = false, Message = "未登录系统,请先登录!" });
                     }
                 }
             }

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

@@ -31,11 +31,6 @@ namespace Masuit.MyBlogs.Core.Extensions
 
         public async Task Invoke(HttpContext context)
         {
-            context.Response.Headers.Add("Pragma", "no-cache");
-            context.Response.Headers.Add("Expires", "0");
-            context.Response.Headers[HeaderNames.Expires] = "0";
-            context.Response.Headers[HeaderNames.CacheControl] = "no-cache";
-
             string httpMethod = context.Request.Method;
             if (httpMethod.Equals("OPTIONS", StringComparison.InvariantCultureIgnoreCase) || httpMethod.Equals("HEAD", StringComparison.InvariantCultureIgnoreCase))
             {

+ 12 - 2
src/Masuit.MyBlogs.Core/Extensions/Hangfire/HangfireBackJob.cs

@@ -3,11 +3,9 @@ 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.Tools;
 using Masuit.Tools.Core.Net;
 using Masuit.Tools.NoSQL;
 using Masuit.Tools.Systems;
-using Microsoft.EntityFrameworkCore;
 using System;
 using System.IO;
 using System.Linq;
@@ -119,6 +117,18 @@ namespace Masuit.MyBlogs.Core.Extensions.Hangfire
             _redisHelper.StringIncrement("Interview:RunningDays");
             DateTime time = DateTime.Now.AddMonths(-1);
             _searchDetailsService.DeleteEntitySaved(s => s.SearchTime < time);
+            foreach (var p in _postService.GetAll().AsParallel())
+            {
+                try
+                {
+                    p.AverageViewCount = p.PostAccessRecord.Average(r => r.ClickCount);
+                    p.TotalViewCount = p.PostAccessRecord.Sum(r => r.ClickCount);
+                    _postService.UpdateEntitySaved(p);
+                }
+                catch (Exception)
+                {
+                }
+            }
         }
 
         /// <summary>

+ 15 - 4
src/Masuit.MyBlogs.Core/Hubs/MyHub.cs

@@ -71,6 +71,7 @@ namespace Masuit.MyBlogs.Core.Hubs
         {
             Task.Run(() =>
             {
+                int errorCount = 0;
                 while (true)
                 {
                     try
@@ -79,9 +80,14 @@ namespace Masuit.MyBlogs.Core.Hubs
                     }
                     catch (Exception e)
                     {
+                        if (errorCount > 20)
+                        {
+                            break;
+                        }
                         Console.ForegroundColor = ConsoleColor.Red;
-                        Console.WriteLine(e);
+                        Console.WriteLine(e.Message);
                         Console.ForegroundColor = ConsoleColor.White;
+                        errorCount++;
                     }
                     Thread.Sleep(5000);
                 }
@@ -140,6 +146,7 @@ namespace Masuit.MyBlogs.Core.Hubs
             {
                 return;
             }
+            byte errCount = 0;
             while (Connections.Any(s => s.Key.Equals(Context.ConnectionId)))
             {
                 Connections[Context.ConnectionId] = true;
@@ -147,17 +154,21 @@ namespace Masuit.MyBlogs.Core.Hubs
                 {
                     cancellationToken.ThrowIfCancellationRequested();
                     await writer.WriteAsync(GetCurrentPerformanceCounter(), cancellationToken);
-                    await Task.Delay(delay, cancellationToken);
                 }
                 catch (Exception e)
                 {
-                    // ignored
-                    Console.WriteLine("出现错误:" + e.Message);
+                    if (errCount > 20)
+                    {
+                        break;
+                    }
+                    Console.WriteLine("WebSocket出现错误:" + e.Message);
+                    errCount++;
                 }
                 if (cancellationToken.IsCancellationRequested)
                 {
                     break;
                 }
+                await Task.Delay(delay, cancellationToken);
             }
             writer.TryComplete();
         }

+ 2 - 2
src/Masuit.MyBlogs.Core/Infrastructure/Application/DataContext.cs

@@ -87,8 +87,8 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Application
             //IConfigurationRoot config = new ConfigurationBuilder().AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).Build();
             var conn = "Server=192.168.135.70;Database=matrixone_portfolio_test_db;Uid=portfolio;Pwd=portfolio@#$123;";
             var builder = new DbContextOptionsBuilder<DataContext>();
-            builder.UseMySql(conn);
-            //builder.UseSqlServer("Data Source=.;Initial Catalog=CoreTest;Integrated Security=True");
+            //builder.UseMySql(conn);
+            builder.UseSqlServer("Data Source=.;Initial Catalog=CoreTest;Integrated Security=True");
 
             return new DataContext(builder.Options);
         }

+ 0 - 71
src/Masuit.MyBlogs.Core/Infrastructure/Repository/BaseRepository.cs

@@ -323,29 +323,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository
             return isAsc ? DataContext.Set<T>().OrderBy(orderby).FirstOrDefault(where) : DataContext.Set<T>().OrderByDescending(orderby).FirstOrDefault(where);
         }
 
-        /// <summary>
-        /// 获取第一条数据,优先从缓存读取
-        /// </summary>
-        /// <param name="where">查询条件</param>
-        /// <returns>实体</returns>
-        public virtual T GetFirstEntityFromL2Cache(Expression<Func<T, bool>> @where)
-        {
-            return LoadEntitiesFromL2Cache(where).FirstOrDefault();
-        }
-
-        /// <summary>
-        /// 获取第一条数据,优先从缓存读取
-        /// </summary>
-        /// <typeparam name="TS">排序</typeparam>
-        /// <param name="where">查询条件</param>
-        /// <param name="orderby">排序字段</param>
-        /// <param name="isAsc">是否升序</param>
-        /// <returns>映射实体</returns>
-        public virtual T GetFirstEntityFromL2Cache<TS>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true)
-        {
-            return LoadEntitiesFromL2Cache(where, orderby, isAsc).FirstOrDefault();
-        }
-
         /// <summary>
         /// 获取第一条数据
         /// </summary>
@@ -416,54 +393,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository
             return isAsc ? DataContext.Set<T>().Where(where).OrderBy(orderby).AsNoTracking().ProjectTo<TDto>().FirstOrDefault() : DataContext.Set<T>().Where(where).OrderByDescending(orderby).AsNoTracking().ProjectTo<TDto>().FirstOrDefault();
         }
 
-        /// <summary>
-        /// 获取第一条数据,优先从二级缓存读取(不跟踪实体)
-        /// </summary>
-        /// <param name="where">查询条件</param>
-        /// <returns>实体</returns>
-        public virtual T GetFirstEntityFromL2CacheNoTracking(Expression<Func<T, bool>> @where)
-        {
-            return LoadEntitiesFromL2CacheNoTracking(where).FirstOrDefault();
-        }
-
-        /// <summary>
-        /// 获取第一条数据,优先从二级缓存读取(不跟踪实体)
-        /// </summary>
-        /// <typeparam name="TS">排序</typeparam>
-        /// <param name="where">查询条件</param>
-        /// <param name="orderby">排序字段</param>
-        /// <param name="isAsc">是否升序</param>
-        /// <returns>实体</returns>
-        public virtual T GetFirstEntityFromL2CacheNoTracking<TS>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true)
-        {
-            return LoadEntitiesFromL2CacheNoTracking(where, orderby, isAsc).FirstOrDefault();
-        }
-
-
-        /// <summary>
-        /// 获取第一条被AutoMapper映射后的数据,优先从二级缓存读取(不跟踪实体)
-        /// </summary>
-        /// <param name="where">查询条件</param>
-        /// <returns>实体</returns>
-        public virtual TDto GetFirstEntityFromL2Cache<TDto>(Expression<Func<T, bool>> @where) where TDto : class
-        {
-            return LoadEntitiesFromL2Cache<TDto>(where).FirstOrDefault();
-        }
-
-        /// <summary>
-        /// 获取第一条被AutoMapper映射后的数据,优先从二级缓存读取(不跟踪实体)
-        /// </summary>
-        /// <typeparam name="TS">排序</typeparam>
-        /// <typeparam name="TDto">映射实体</typeparam>
-        /// <param name="where">查询条件</param>
-        /// <param name="orderby">排序字段</param>
-        /// <param name="isAsc">是否升序</param>
-        /// <returns>映射实体</returns>
-        public virtual TDto GetFirstEntityFromL2Cache<TS, TDto>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true) where TDto : class
-        {
-            return LoadEntitiesFromL2Cache<TS, TDto>(where, orderby, isAsc).FirstOrDefault();
-        }
-
         /// <summary>
         /// 获取第一条数据(异步,不跟踪实体)
         /// </summary>

+ 0 - 52
src/Masuit.MyBlogs.Core/Infrastructure/Repository/Interface/IBaseRepository.cs

@@ -245,41 +245,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository.Interface
         /// <returns>映射实体</returns>
         TDto GetFirstEntity<TS, TDto>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true) where TDto : class;
 
-        /// <summary>
-        /// 获取第一条数据,优先从缓存读取
-        /// </summary>
-        /// <param name="where">查询条件</param>
-        /// <returns>实体</returns>
-        T GetFirstEntityFromL2Cache(Expression<Func<T, bool>> @where);
-
-        /// <summary>
-        /// 获取第一条数据,优先从缓存读取
-        /// </summary>
-        /// <typeparam name="TS">排序</typeparam>
-        /// <param name="where">查询条件</param>
-        /// <param name="orderby">排序字段</param>
-        /// <param name="isAsc">是否升序</param>
-        /// <returns>映射实体</returns>
-        T GetFirstEntityFromL2Cache<TS>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true);
-
-        /// <summary>
-        /// 获取第一条被AutoMapper映射后的数据,优先从缓存读取
-        /// </summary>
-        /// <param name="where">查询条件</param>
-        /// <returns>实体</returns>
-        TDto GetFirstEntityFromL2Cache<TDto>(Expression<Func<T, bool>> @where) where TDto : class;
-
-        /// <summary>
-        /// 获取第一条被AutoMapper映射后的数据,优先从缓存读取
-        /// </summary>
-        /// <typeparam name="TS">排序</typeparam>
-        /// <typeparam name="TDto">映射实体</typeparam>
-        /// <param name="where">查询条件</param>
-        /// <param name="orderby">排序字段</param>
-        /// <param name="isAsc">是否升序</param>
-        /// <returns>映射实体</returns>
-        TDto GetFirstEntityFromL2Cache<TS, TDto>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true) where TDto : class;
-
         /// <summary>
         /// 获取第一条数据
         /// </summary>
@@ -314,23 +279,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository.Interface
         /// <returns>实体</returns>
         T GetFirstEntityNoTracking<TS>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true);
 
-        /// <summary>
-        /// 获取第一条数据,优先从二级缓存读取(不跟踪实体)
-        /// </summary>
-        /// <param name="where">查询条件</param>
-        /// <returns>实体</returns>
-        T GetFirstEntityFromL2CacheNoTracking(Expression<Func<T, bool>> @where);
-
-        /// <summary>
-        /// 获取第一条数据,优先从二级缓存读取(不跟踪实体)
-        /// </summary>
-        /// <typeparam name="TS">排序</typeparam>
-        /// <param name="where">查询条件</param>
-        /// <param name="orderby">排序字段</param>
-        /// <param name="isAsc">是否升序</param>
-        /// <returns>实体</returns>
-        T GetFirstEntityFromL2CacheNoTracking<TS>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true);
-
         /// <summary>
         /// 获取第一条数据(异步,不跟踪实体)
         /// </summary>

+ 0 - 70
src/Masuit.MyBlogs.Core/Infrastructure/Services/BaseService.cs

@@ -343,53 +343,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
             return BaseDal.GetFirstEntity<TS, TDto>(where, orderby, isAsc);
         }
 
-        /// <summary>
-        /// 获取第一条数据,优先从缓存读取
-        /// </summary>
-        /// <param name="where">查询条件</param>
-        /// <returns>实体</returns>
-        public virtual T GetFirstEntityFromL2Cache(Expression<Func<T, bool>> @where)
-        {
-            return BaseDal.GetFirstEntityFromL2Cache(where);
-        }
-
-        /// <summary>
-        /// 获取第一条数据,优先从缓存读取
-        /// </summary>
-        /// <typeparam name="TS">排序</typeparam>
-        /// <param name="where">查询条件</param>
-        /// <param name="orderby">排序字段</param>
-        /// <param name="isAsc">是否升序</param>
-        /// <returns>映射实体</returns>
-        public virtual T GetFirstEntityFromL2Cache<TS>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true)
-        {
-            return BaseDal.GetFirstEntityFromL2Cache(where, orderby, isAsc);
-        }
-
-        /// <summary>
-        /// 获取第一条被AutoMapper映射后的数据,优先从缓存读取
-        /// </summary>
-        /// <param name="where">查询条件</param>
-        /// <returns>实体</returns>
-        public virtual TDto GetFirstEntityFromL2Cache<TDto>(Expression<Func<T, bool>> @where) where TDto : class
-        {
-            return BaseDal.GetFirstEntityFromL2Cache<TDto>(where);
-        }
-
-        /// <summary>
-        /// 获取第一条被AutoMapper映射后的数据,优先从缓存读取
-        /// </summary>
-        /// <typeparam name="TS">排序</typeparam>
-        /// <typeparam name="TDto">映射实体</typeparam>
-        /// <param name="where">查询条件</param>
-        /// <param name="orderby">排序字段</param>
-        /// <param name="isAsc">是否升序</param>
-        /// <returns>映射实体</returns>
-        public virtual TDto GetFirstEntityFromL2Cache<TS, TDto>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true) where TDto : class
-        {
-            return BaseDal.GetFirstEntityFromL2Cache<TS, TDto>(where, orderby, isAsc);
-        }
-
         /// <summary>
         /// 获取第一条数据
         /// </summary>
@@ -436,29 +389,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
             return BaseDal.GetFirstEntityNoTracking(where, orderby, isAsc);
         }
 
-        /// <summary>
-        /// 获取第一条数据,优先从二级缓存读取(不跟踪实体)
-        /// </summary>
-        /// <param name="where">查询条件</param>
-        /// <returns>实体</returns>
-        public virtual T GetFirstEntityFromL2CacheNoTracking(Expression<Func<T, bool>> @where)
-        {
-            return BaseDal.GetFirstEntityFromL2CacheNoTracking(where);
-        }
-
-        /// <summary>
-        /// 获取第一条数据,优先从二级缓存读取(不跟踪实体)
-        /// </summary>
-        /// <typeparam name="TS">排序</typeparam>
-        /// <param name="where">查询条件</param>
-        /// <param name="orderby">排序字段</param>
-        /// <param name="isAsc">是否升序</param>
-        /// <returns>实体</returns>
-        public virtual T GetFirstEntityFromL2CacheNoTracking<TS>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true)
-        {
-            return BaseDal.GetFirstEntityFromL2CacheNoTracking(where, orderby, isAsc);
-        }
-
         /// <summary>
         /// 获取第一条数据(异步,不跟踪实体)
         /// </summary>

+ 0 - 52
src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IBaseService.cs

@@ -243,41 +243,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services.Interface
         /// <returns>映射实体</returns>
         TDto GetFirstEntity<TS, TDto>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true) where TDto : class;
 
-        /// <summary>
-        /// 获取第一条数据,优先从缓存读取
-        /// </summary>
-        /// <param name="where">查询条件</param>
-        /// <returns>实体</returns>
-        T GetFirstEntityFromL2Cache(Expression<Func<T, bool>> @where);
-
-        /// <summary>
-        /// 获取第一条数据,优先从缓存读取
-        /// </summary>
-        /// <typeparam name="TS">排序</typeparam>
-        /// <param name="where">查询条件</param>
-        /// <param name="orderby">排序字段</param>
-        /// <param name="isAsc">是否升序</param>
-        /// <returns>映射实体</returns>
-        T GetFirstEntityFromL2Cache<TS>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true);
-
-        /// <summary>
-        /// 获取第一条被AutoMapper映射后的数据,优先从缓存读取
-        /// </summary>
-        /// <param name="where">查询条件</param>
-        /// <returns>实体</returns>
-        TDto GetFirstEntityFromL2Cache<TDto>(Expression<Func<T, bool>> @where) where TDto : class;
-
-        /// <summary>
-        /// 获取第一条被AutoMapper映射后的数据,优先从缓存读取
-        /// </summary>
-        /// <typeparam name="TS">排序</typeparam>
-        /// <typeparam name="TDto">映射实体</typeparam>
-        /// <param name="where">查询条件</param>
-        /// <param name="orderby">排序字段</param>
-        /// <param name="isAsc">是否升序</param>
-        /// <returns>映射实体</returns>
-        TDto GetFirstEntityFromL2Cache<TS, TDto>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true) where TDto : class;
-
         /// <summary>
         /// 获取第一条数据
         /// </summary>
@@ -312,23 +277,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services.Interface
         /// <returns>实体</returns>
         T GetFirstEntityNoTracking<TS>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true);
 
-        /// <summary>
-        /// 获取第一条数据,优先从二级缓存读取(不跟踪实体)
-        /// </summary>
-        /// <param name="where">查询条件</param>
-        /// <returns>实体</returns>
-        T GetFirstEntityFromL2CacheNoTracking(Expression<Func<T, bool>> @where);
-
-        /// <summary>
-        /// 获取第一条数据,优先从二级缓存读取(不跟踪实体)
-        /// </summary>
-        /// <typeparam name="TS">排序</typeparam>
-        /// <param name="where">查询条件</param>
-        /// <param name="orderby">排序字段</param>
-        /// <param name="isAsc">是否升序</param>
-        /// <returns>实体</returns>
-        T GetFirstEntityFromL2CacheNoTracking<TS>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true);
-
         /// <summary>
         /// 获取第一条数据(异步,不跟踪实体)
         /// </summary>

+ 1 - 4
src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IPostService.cs

@@ -1,13 +1,10 @@
 using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.Entity;
-using System;
-using System.Collections.Generic;
-using System.Linq.Expressions;
 
 namespace Masuit.MyBlogs.Core.Infrastructure.Services.Interface
 {
     public partial interface IPostService : IBaseService<Post>
     {
-        List<PostOutputDto> SearchPage<TOrder>(int page, int size, out int total, string[] keywords, Expression<Func<Post, TOrder>> orderBy);
+        SearchResult<PostOutputDto> SearchPage(int page, int size, string keyword);
     }
 }

+ 23 - 14
src/Masuit.MyBlogs.Core/Infrastructure/Services/PostService.cs

@@ -1,32 +1,35 @@
-using AutoMapper.QueryableExtensions;
+using Common;
+using Masuit.LuceneEFCore.SearchEngine;
+using Masuit.LuceneEFCore.SearchEngine.Interfaces;
+using Masuit.MyBlogs.Core.Infrastructure.Application;
 using Masuit.MyBlogs.Core.Infrastructure.Repository.Interface;
 using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
 using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.Entity;
-using Masuit.Tools.Html;
-using NinjaNye.SearchExtensions;
 using PanGu;
 using PanGu.HighLight;
-using System;
-using System.Collections.Generic;
 using System.Linq;
-using System.Linq.Expressions;
 
 namespace Masuit.MyBlogs.Core.Infrastructure.Services
 {
     public partial class PostService : BaseService<Post>, IPostService
     {
-        public List<PostOutputDto> SearchPage<TOrder>(int page, int size, out int total, string[] keywords, Expression<Func<Post, TOrder>> orderBy)
+        private readonly ILuceneIndexSearcher _searcher;
+        private readonly ISearchEngine<DataContext> _searchEngine;
+        public PostService(IPostRepository repository, ILuceneIndexSearcher searcher, ISearchEngine<DataContext> searchEngine) : base(repository)
         {
-            var query = GetAllNoTracking().Search().Containing(keywords);
-            total = query.Count();
-            var posts = query.OrderByDescending(orderBy).Skip((page - 1) * size).Take(size).ProjectTo<PostOutputDto>().ToList();
+            _searcher = searcher;
+            _searchEngine = searchEngine;
+        }
+        public SearchResult<PostOutputDto> SearchPage(int page, int size, string keyword)
+        {
+            var searchResult = _searchEngine.ScoredSearch<Post>(new SearchOptions(keyword, page, size, typeof(Post)));
+            var posts = searchResult.Results.Select(p => p.Entity.Mapper<PostOutputDto>()).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 };
-
+            var keywords = _searcher.CutKeywords(keyword);
             foreach (var p in posts)
             {
-                p.Content = p.Content.RemoveHtml();
                 foreach (var s in keywords)
                 {
                     string frag;
@@ -52,11 +55,17 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
                     p.Content = p.Content.Substring(0, 200);
                 }
             }
-            return posts;
+            return new SearchResult<PostOutputDto>()
+            {
+                Results = posts,
+                Elapsed = searchResult.Elapsed,
+                Total = searchResult.TotalHits
+            };
         }
 
-        public PostService(IBaseRepository<Post> repository) : base(repository)
+        public PostService(IBaseRepository<Post> repository, ISearchEngine<DataContext> searchEngine) : base(repository)
         {
+            _searchEngine = searchEngine;
         }
     }
 }

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

@@ -62,7 +62,6 @@
     <PackageReference Include="CacheManager.Serialization.Json" Version="1.2.0" />
     <PackageReference Include="Dapper" Version="1.50.5" />
     <PackageReference Include="EFSecondLevelCache.Core" Version="1.7.1" />
-    <PackageReference Include="FluentScheduler" Version="5.3.0" />
     <PackageReference Include="Hangfire" Version="1.6.22" />
     <PackageReference Include="Hangfire.Autofac" Version="2.3.1" />
     <PackageReference Include="Hangfire.Redis.StackExchange" Version="1.8.0" />
@@ -86,6 +85,7 @@
 
 
   <ItemGroup>
+    <ProjectReference Include="..\..\Masuit.LuceneEFCore.SearchEngine\Masuit.LuceneEFCore.SearchEngine\Masuit.LuceneEFCore.SearchEngine.csproj" />
     <ProjectReference Include="..\..\Masuit.Tools\Masuit.Tools.Core\Masuit.Tools.Core.csproj" />
   </ItemGroup>
 

+ 732 - 0
src/Masuit.MyBlogs.Core/Migrations/20190201132829_Init.Designer.cs

@@ -0,0 +1,732 @@
+// <auto-generated />
+using System;
+using Masuit.MyBlogs.Core.Infrastructure.Application;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+namespace Masuit.MyBlogs.Core.Migrations
+{
+    [DbContext(typeof(DataContext))]
+    [Migration("20190201132829_Init")]
+    partial class Init
+    {
+        protected override void BuildTargetModel(ModelBuilder modelBuilder)
+        {
+#pragma warning disable 612, 618
+            modelBuilder
+                .HasAnnotation("ProductVersion", "2.2.1-servicing-10028")
+                .HasAnnotation("Relational:MaxIdentifierLength", 128)
+                .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Broadcast", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("Email");
+
+                    b.Property<int>("Status");
+
+                    b.Property<int>("SubscribeType");
+
+                    b.Property<DateTime>("UpdateTime");
+
+                    b.Property<string>("ValidateCode");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Broadcast");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Category", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("Description");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasMaxLength(64);
+
+                    b.Property<int>("Status");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Category");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Comment", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<int>("AgainstCount");
+
+                    b.Property<string>("Browser")
+                        .HasMaxLength(255);
+
+                    b.Property<DateTime>("CommentDate");
+
+                    b.Property<string>("Content")
+                        .IsRequired();
+
+                    b.Property<string>("Email");
+
+                    b.Property<string>("IP");
+
+                    b.Property<bool>("IsMaster");
+
+                    b.Property<string>("NickName")
+                        .IsRequired()
+                        .HasMaxLength(24);
+
+                    b.Property<string>("OperatingSystem")
+                        .HasMaxLength(255);
+
+                    b.Property<int>("ParentId");
+
+                    b.Property<int>("PostId");
+
+                    b.Property<string>("QQorWechat");
+
+                    b.Property<int>("Status");
+
+                    b.Property<int>("VoteCount");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("PostId");
+
+                    b.ToTable("Comment");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Contacts", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<int>("Status");
+
+                    b.Property<string>("Title")
+                        .IsRequired()
+                        .IsUnicode(true);
+
+                    b.Property<string>("Url")
+                        .IsRequired()
+                        .IsUnicode(true);
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Contacts");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Donate", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("Amount");
+
+                    b.Property<DateTime>("DonateTime");
+
+                    b.Property<string>("Email");
+
+                    b.Property<string>("EmailDisplay");
+
+                    b.Property<string>("NickName");
+
+                    b.Property<string>("QQorWechat");
+
+                    b.Property<string>("QQorWechatDisplay");
+
+                    b.Property<int>("Status");
+
+                    b.Property<string>("Via");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Donate");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.FastShare", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("Link");
+
+                    b.Property<int>("Sort");
+
+                    b.Property<int>("Status");
+
+                    b.Property<string>("Title");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("FastShare");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.InternalMessage", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("Content");
+
+                    b.Property<string>("Link");
+
+                    b.Property<bool>("Read");
+
+                    b.Property<int>("Status");
+
+                    b.Property<DateTime>("Time");
+
+                    b.Property<string>("Title");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("InternalMessage");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Issue", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("Description")
+                        .IsRequired();
+
+                    b.Property<string>("Email");
+
+                    b.Property<DateTime?>("HandleTime");
+
+                    b.Property<string>("IPAddress");
+
+                    b.Property<int>("Level");
+
+                    b.Property<string>("Link")
+                        .IsRequired();
+
+                    b.Property<string>("Msg");
+
+                    b.Property<string>("Name")
+                        .IsRequired();
+
+                    b.Property<int>("Status");
+
+                    b.Property<DateTime>("SubmitTime");
+
+                    b.Property<string>("Title")
+                        .IsRequired();
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Issue");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.LeaveMessage", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("Browser")
+                        .HasMaxLength(255);
+
+                    b.Property<string>("Content")
+                        .IsRequired();
+
+                    b.Property<string>("Email");
+
+                    b.Property<string>("IP");
+
+                    b.Property<bool>("IsMaster");
+
+                    b.Property<string>("NickName")
+                        .IsRequired();
+
+                    b.Property<string>("OperatingSystem")
+                        .HasMaxLength(255);
+
+                    b.Property<int>("ParentId");
+
+                    b.Property<DateTime>("PostDate");
+
+                    b.Property<string>("QQorWechat");
+
+                    b.Property<int>("Status");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("LeaveMessage");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Links", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<bool>("Except");
+
+                    b.Property<string>("Name")
+                        .IsRequired();
+
+                    b.Property<bool>("Recommend");
+
+                    b.Property<int>("Status");
+
+                    b.Property<string>("Url")
+                        .IsRequired();
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Links");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.LoginRecord", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("IP");
+
+                    b.Property<DateTime>("LoginTime");
+
+                    b.Property<int>("LoginType");
+
+                    b.Property<string>("PhysicAddress");
+
+                    b.Property<string>("Province");
+
+                    b.Property<int>("Status");
+
+                    b.Property<int>("UserInfoId");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("UserInfoId");
+
+                    b.ToTable("LoginRecord");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Menu", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("Icon");
+
+                    b.Property<int>("MenuType");
+
+                    b.Property<string>("Name")
+                        .IsRequired();
+
+                    b.Property<bool>("NewTab");
+
+                    b.Property<int>("ParentId");
+
+                    b.Property<int>("Sort");
+
+                    b.Property<int>("Status");
+
+                    b.Property<string>("Url")
+                        .IsRequired();
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Menu");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Misc", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("Content")
+                        .IsRequired();
+
+                    b.Property<DateTime>("ModifyDate");
+
+                    b.Property<DateTime>("PostDate");
+
+                    b.Property<int>("Status");
+
+                    b.Property<string>("Title")
+                        .IsRequired();
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Misc");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Notice", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("Content")
+                        .IsRequired();
+
+                    b.Property<DateTime>("ModifyDate");
+
+                    b.Property<DateTime>("PostDate");
+
+                    b.Property<int>("Status");
+
+                    b.Property<string>("Title")
+                        .IsRequired();
+
+                    b.Property<int>("ViewCount");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Notice");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Post", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("Author")
+                        .IsRequired()
+                        .HasMaxLength(24);
+
+                    b.Property<double>("AverageViewCount");
+
+                    b.Property<int>("CategoryId");
+
+                    b.Property<string>("Content")
+                        .IsRequired();
+
+                    b.Property<string>("Description")
+                        .HasMaxLength(255);
+
+                    b.Property<string>("Email")
+                        .IsRequired()
+                        .IsUnicode(true);
+
+                    b.Property<string>("ImageUrl")
+                        .HasMaxLength(255);
+
+                    b.Property<bool>("IsBanner");
+
+                    b.Property<bool>("IsFixedTop");
+
+                    b.Property<bool>("IsWordDocument");
+
+                    b.Property<string>("Keyword")
+                        .HasMaxLength(256);
+
+                    b.Property<string>("Label")
+                        .HasMaxLength(256)
+                        .IsUnicode(true);
+
+                    b.Property<DateTime>("ModifyDate");
+
+                    b.Property<DateTime>("PostDate");
+
+                    b.Property<string>("ProtectContent");
+
+                    b.Property<string>("ResourceName");
+
+                    b.Property<int>("Status");
+
+                    b.Property<string>("Title")
+                        .IsRequired();
+
+                    b.Property<int>("TotalViewCount");
+
+                    b.Property<int>("VoteDownCount")
+                        .ValueGeneratedOnAddOrUpdate();
+
+                    b.Property<int>("VoteUpCount");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("CategoryId");
+
+                    b.ToTable("Post");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.PostAccessRecord", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<DateTime>("AccessTime");
+
+                    b.Property<int>("ClickCount");
+
+                    b.Property<int>("PostId");
+
+                    b.Property<int>("Status");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("PostId");
+
+                    b.ToTable("PostAccessRecord");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.PostHistoryVersion", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<int>("CategoryId");
+
+                    b.Property<string>("Content")
+                        .IsRequired();
+
+                    b.Property<string>("Email")
+                        .HasMaxLength(255);
+
+                    b.Property<bool>("IsWordDocument");
+
+                    b.Property<string>("Label")
+                        .HasMaxLength(255);
+
+                    b.Property<DateTime>("ModifyDate");
+
+                    b.Property<int>("PostId");
+
+                    b.Property<string>("ProtectContent");
+
+                    b.Property<string>("ResourceName");
+
+                    b.Property<int>("Status");
+
+                    b.Property<string>("Title")
+                        .IsRequired()
+                        .HasMaxLength(64);
+
+                    b.Property<int>("ViewCount");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("CategoryId");
+
+                    b.HasIndex("PostId");
+
+                    b.ToTable("PostHistoryVersion");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.SearchDetails", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("IP");
+
+                    b.Property<string>("KeyWords")
+                        .IsRequired()
+                        .IsUnicode(true);
+
+                    b.Property<DateTime>("SearchTime");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("SearchDetails");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Seminar", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("Description")
+                        .IsRequired();
+
+                    b.Property<int>("Status");
+
+                    b.Property<string>("SubTitle");
+
+                    b.Property<string>("Title")
+                        .IsRequired();
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Seminar");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.SeminarPost", b =>
+                {
+                    b.Property<int>("SeminarId")
+                        .HasColumnName("Seminar_Id");
+
+                    b.Property<int>("PostId")
+                        .HasColumnName("Post_Id");
+
+                    b.HasKey("SeminarId", "PostId");
+
+                    b.HasIndex("PostId");
+
+                    b.ToTable("SeminarPost");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.SeminarPostHistoryVersion", b =>
+                {
+                    b.Property<int>("SeminarId")
+                        .HasColumnName("Seminar_Id");
+
+                    b.Property<int>("PostHistoryVersionId")
+                        .HasColumnName("PostHistoryVersion_Id");
+
+                    b.HasKey("SeminarId", "PostHistoryVersionId");
+
+                    b.HasIndex("PostHistoryVersionId");
+
+                    b.ToTable("SeminarPostHistoryVersion");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.SystemSetting", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("Name")
+                        .IsRequired();
+
+                    b.Property<int>("Status");
+
+                    b.Property<string>("Value")
+                        .IsRequired();
+
+                    b.HasKey("Id");
+
+                    b.ToTable("SystemSetting");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.UserInfo", b =>
+                {
+                    b.Property<int>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
+
+                    b.Property<string>("AccessToken");
+
+                    b.Property<string>("Avatar");
+
+                    b.Property<string>("Email");
+
+                    b.Property<bool>("IsAdmin");
+
+                    b.Property<string>("NickName")
+                        .IsRequired();
+
+                    b.Property<string>("Password")
+                        .IsRequired();
+
+                    b.Property<string>("QQorWechat");
+
+                    b.Property<string>("SaltKey")
+                        .IsRequired();
+
+                    b.Property<int>("Status");
+
+                    b.Property<string>("Username")
+                        .IsRequired();
+
+                    b.HasKey("Id");
+
+                    b.ToTable("UserInfo");
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Comment", b =>
+                {
+                    b.HasOne("Masuit.MyBlogs.Core.Models.Entity.Post", "Post")
+                        .WithMany("Comment")
+                        .HasForeignKey("PostId")
+                        .OnDelete(DeleteBehavior.Cascade);
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.LoginRecord", b =>
+                {
+                    b.HasOne("Masuit.MyBlogs.Core.Models.Entity.UserInfo", "UserInfo")
+                        .WithMany("LoginRecord")
+                        .HasForeignKey("UserInfoId")
+                        .OnDelete(DeleteBehavior.Cascade);
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Post", b =>
+                {
+                    b.HasOne("Masuit.MyBlogs.Core.Models.Entity.Category", "Category")
+                        .WithMany("Post")
+                        .HasForeignKey("CategoryId")
+                        .OnDelete(DeleteBehavior.Cascade);
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.PostAccessRecord", b =>
+                {
+                    b.HasOne("Masuit.MyBlogs.Core.Models.Entity.Post", "Post")
+                        .WithMany("PostAccessRecord")
+                        .HasForeignKey("PostId")
+                        .OnDelete(DeleteBehavior.Cascade);
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.PostHistoryVersion", b =>
+                {
+                    b.HasOne("Masuit.MyBlogs.Core.Models.Entity.Category", "Category")
+                        .WithMany("PostHistoryVersion")
+                        .HasForeignKey("CategoryId")
+                        .OnDelete(DeleteBehavior.Cascade);
+
+                    b.HasOne("Masuit.MyBlogs.Core.Models.Entity.Post", "Post")
+                        .WithMany("PostHistoryVersion")
+                        .HasForeignKey("PostId")
+                        .OnDelete(DeleteBehavior.Cascade);
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.SeminarPost", b =>
+                {
+                    b.HasOne("Masuit.MyBlogs.Core.Models.Entity.Post", "Post")
+                        .WithMany("Seminar")
+                        .HasForeignKey("PostId")
+                        .OnDelete(DeleteBehavior.Cascade);
+
+                    b.HasOne("Masuit.MyBlogs.Core.Models.Entity.Seminar", "Seminar")
+                        .WithMany("Post")
+                        .HasForeignKey("SeminarId")
+                        .OnDelete(DeleteBehavior.Cascade);
+                });
+
+            modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.SeminarPostHistoryVersion", b =>
+                {
+                    b.HasOne("Masuit.MyBlogs.Core.Models.Entity.PostHistoryVersion", "PostHistoryVersion")
+                        .WithMany("Seminar")
+                        .HasForeignKey("PostHistoryVersionId")
+                        .OnDelete(DeleteBehavior.Cascade);
+
+                    b.HasOne("Masuit.MyBlogs.Core.Models.Entity.Seminar", "Seminar")
+                        .WithMany("PostHistoryVersion")
+                        .HasForeignKey("SeminarId")
+                        .OnDelete(DeleteBehavior.Cascade);
+                });
+#pragma warning restore 612, 618
+        }
+    }
+}

+ 435 - 0
src/Masuit.MyBlogs.Core/Migrations/20190201132829_Init.cs

@@ -0,0 +1,435 @@
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using System;
+
+namespace Masuit.MyBlogs.Core.Migrations
+{
+    public partial class Init : Migration
+    {
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.CreateTable(name: "Broadcast", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Email = table.Column<string>(nullable: true),
+                ValidateCode = table.Column<string>(nullable: true),
+                UpdateTime = table.Column<DateTime>(nullable: false),
+                SubscribeType = table.Column<int>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_Broadcast", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "Category", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Name = table.Column<string>(maxLength: 64, nullable: false),
+                Description = table.Column<string>(nullable: true)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_Category", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "Contacts", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Title = table.Column<string>(nullable: false),
+                Url = table.Column<string>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_Contacts", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "Donate", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                NickName = table.Column<string>(nullable: true),
+                Email = table.Column<string>(nullable: true),
+                QQorWechat = table.Column<string>(nullable: true),
+                EmailDisplay = table.Column<string>(nullable: true),
+                QQorWechatDisplay = table.Column<string>(nullable: true),
+                Amount = table.Column<string>(nullable: true),
+                Via = table.Column<string>(nullable: true),
+                DonateTime = table.Column<DateTime>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_Donate", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "FastShare", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Title = table.Column<string>(nullable: true),
+                Link = table.Column<string>(nullable: true),
+                Sort = table.Column<int>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_FastShare", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "InternalMessage", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Title = table.Column<string>(nullable: true),
+                Content = table.Column<string>(nullable: true),
+                Link = table.Column<string>(nullable: true),
+                Time = table.Column<DateTime>(nullable: false),
+                Read = table.Column<bool>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_InternalMessage", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "Issue", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Name = table.Column<string>(nullable: false),
+                Email = table.Column<string>(nullable: true),
+                Title = table.Column<string>(nullable: false),
+                Link = table.Column<string>(nullable: false),
+                Description = table.Column<string>(nullable: false),
+                Level = table.Column<int>(nullable: false),
+                SubmitTime = table.Column<DateTime>(nullable: false),
+                HandleTime = table.Column<DateTime>(nullable: true),
+                Msg = table.Column<string>(nullable: true),
+                IPAddress = table.Column<string>(nullable: true)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_Issue", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "LeaveMessage", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                NickName = table.Column<string>(nullable: false),
+                Content = table.Column<string>(nullable: false),
+                PostDate = table.Column<DateTime>(nullable: false),
+                Email = table.Column<string>(nullable: true),
+                QQorWechat = table.Column<string>(nullable: true),
+                ParentId = table.Column<int>(nullable: false),
+                Browser = table.Column<string>(maxLength: 255, nullable: true),
+                OperatingSystem = table.Column<string>(maxLength: 255, nullable: true),
+                IsMaster = table.Column<bool>(nullable: false),
+                IP = table.Column<string>(nullable: true)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_LeaveMessage", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "Links", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Name = table.Column<string>(nullable: false),
+                Url = table.Column<string>(nullable: false),
+                Except = table.Column<bool>(nullable: false),
+                Recommend = table.Column<bool>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_Links", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "Menu", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Name = table.Column<string>(nullable: false),
+                Icon = table.Column<string>(nullable: true),
+                Url = table.Column<string>(nullable: false),
+                Sort = table.Column<int>(nullable: false),
+                ParentId = table.Column<int>(nullable: false),
+                MenuType = table.Column<int>(nullable: false),
+                NewTab = table.Column<bool>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_Menu", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "Misc", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Title = table.Column<string>(nullable: false),
+                Content = table.Column<string>(nullable: false),
+                PostDate = table.Column<DateTime>(nullable: false),
+                ModifyDate = table.Column<DateTime>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_Misc", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "Notice", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Title = table.Column<string>(nullable: false),
+                Content = table.Column<string>(nullable: false),
+                PostDate = table.Column<DateTime>(nullable: false),
+                ModifyDate = table.Column<DateTime>(nullable: false),
+                ViewCount = table.Column<int>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_Notice", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "SearchDetails", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                KeyWords = table.Column<string>(nullable: false),
+                SearchTime = table.Column<DateTime>(nullable: false),
+                IP = table.Column<string>(nullable: true)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_SearchDetails", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "Seminar", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Title = table.Column<string>(nullable: false),
+                SubTitle = table.Column<string>(nullable: true),
+                Description = table.Column<string>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_Seminar", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "SystemSetting", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Name = table.Column<string>(nullable: false),
+                Value = table.Column<string>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_SystemSetting", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "UserInfo", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Username = table.Column<string>(nullable: false),
+                NickName = table.Column<string>(nullable: false),
+                Password = table.Column<string>(nullable: false),
+                SaltKey = table.Column<string>(nullable: false),
+                IsAdmin = table.Column<bool>(nullable: false),
+                Email = table.Column<string>(nullable: true),
+                QQorWechat = table.Column<string>(nullable: true),
+                Avatar = table.Column<string>(nullable: true),
+                AccessToken = table.Column<string>(nullable: true)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_UserInfo", x => x.Id);
+            });
+
+            migrationBuilder.CreateTable(name: "Post", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Title = table.Column<string>(nullable: false),
+                Author = table.Column<string>(maxLength: 24, nullable: false),
+                Content = table.Column<string>(nullable: false),
+                ProtectContent = table.Column<string>(nullable: true),
+                PostDate = table.Column<DateTime>(nullable: false),
+                ModifyDate = table.Column<DateTime>(nullable: false),
+                IsFixedTop = table.Column<bool>(nullable: false),
+                CategoryId = table.Column<int>(nullable: false),
+                ResourceName = table.Column<string>(nullable: true),
+                IsWordDocument = table.Column<bool>(nullable: false),
+                Email = table.Column<string>(nullable: false),
+                Label = table.Column<string>(maxLength: 256, nullable: true),
+                Keyword = table.Column<string>(maxLength: 256, nullable: true),
+                VoteUpCount = table.Column<int>(nullable: false),
+                VoteDownCount = table.Column<int>(nullable: false),
+                IsBanner = table.Column<bool>(nullable: false),
+                Description = table.Column<string>(maxLength: 255, nullable: true),
+                ImageUrl = table.Column<string>(maxLength: 255, nullable: true),
+                AverageViewCount = table.Column<double>(nullable: false),
+                TotalViewCount = table.Column<int>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_Post", x => x.Id);
+                table.ForeignKey(name: "FK_Post_Category_CategoryId", column: x => x.CategoryId, principalTable: "Category", principalColumn: "Id", onDelete: ReferentialAction.Cascade);
+            });
+
+            migrationBuilder.CreateTable(name: "LoginRecord", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                IP = table.Column<string>(nullable: true),
+                LoginTime = table.Column<DateTime>(nullable: false),
+                Province = table.Column<string>(nullable: true),
+                PhysicAddress = table.Column<string>(nullable: true),
+                LoginType = table.Column<int>(nullable: false),
+                UserInfoId = table.Column<int>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_LoginRecord", x => x.Id);
+                table.ForeignKey(name: "FK_LoginRecord_UserInfo_UserInfoId", column: x => x.UserInfoId, principalTable: "UserInfo", principalColumn: "Id", onDelete: ReferentialAction.Cascade);
+            });
+
+            migrationBuilder.CreateTable(name: "Comment", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                NickName = table.Column<string>(maxLength: 24, nullable: false),
+                Email = table.Column<string>(nullable: true),
+                QQorWechat = table.Column<string>(nullable: true),
+                Content = table.Column<string>(nullable: false),
+                ParentId = table.Column<int>(nullable: false),
+                PostId = table.Column<int>(nullable: false),
+                CommentDate = table.Column<DateTime>(nullable: false),
+                Browser = table.Column<string>(maxLength: 255, nullable: true),
+                OperatingSystem = table.Column<string>(maxLength: 255, nullable: true),
+                IsMaster = table.Column<bool>(nullable: false),
+                VoteCount = table.Column<int>(nullable: false),
+                AgainstCount = table.Column<int>(nullable: false),
+                IP = table.Column<string>(nullable: true)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_Comment", x => x.Id);
+                table.ForeignKey(name: "FK_Comment_Post_PostId", column: x => x.PostId, principalTable: "Post", principalColumn: "Id", onDelete: ReferentialAction.Cascade);
+            });
+
+            migrationBuilder.CreateTable(name: "PostAccessRecord", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                PostId = table.Column<int>(nullable: false),
+                AccessTime = table.Column<DateTime>(nullable: false),
+                ClickCount = table.Column<int>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_PostAccessRecord", x => x.Id);
+                table.ForeignKey(name: "FK_PostAccessRecord_Post_PostId", column: x => x.PostId, principalTable: "Post", principalColumn: "Id", onDelete: ReferentialAction.Cascade);
+            });
+
+            migrationBuilder.CreateTable(name: "PostHistoryVersion", columns: table => new
+            {
+                Id = table.Column<int>(nullable: false).Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+                Status = table.Column<int>(nullable: false),
+                Title = table.Column<string>(maxLength: 64, nullable: false),
+                Content = table.Column<string>(nullable: false),
+                ProtectContent = table.Column<string>(nullable: true),
+                ViewCount = table.Column<int>(nullable: false),
+                ModifyDate = table.Column<DateTime>(nullable: false),
+                CategoryId = table.Column<int>(nullable: false),
+                PostId = table.Column<int>(nullable: false),
+                ResourceName = table.Column<string>(nullable: true),
+                IsWordDocument = table.Column<bool>(nullable: false),
+                Email = table.Column<string>(maxLength: 255, nullable: true),
+                Label = table.Column<string>(maxLength: 255, nullable: true)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_PostHistoryVersion", x => x.Id);
+                table.ForeignKey(name: "FK_PostHistoryVersion_Category_CategoryId", column: x => x.CategoryId, principalTable: "Category", principalColumn: "Id", onDelete: ReferentialAction.Cascade);
+                table.ForeignKey(name: "FK_PostHistoryVersion_Post_PostId", column: x => x.PostId, principalTable: "Post", principalColumn: "Id", onDelete: ReferentialAction.Cascade);
+            });
+
+            migrationBuilder.CreateTable(name: "SeminarPost", columns: table => new
+            {
+                Seminar_Id = table.Column<int>(nullable: false),
+                Post_Id = table.Column<int>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_SeminarPost", x => new
+                {
+                    x.Seminar_Id,
+                    x.Post_Id
+                });
+                table.ForeignKey(name: "FK_SeminarPost_Post_Post_Id", column: x => x.Post_Id, principalTable: "Post", principalColumn: "Id", onDelete: ReferentialAction.Cascade);
+                table.ForeignKey(name: "FK_SeminarPost_Seminar_Seminar_Id", column: x => x.Seminar_Id, principalTable: "Seminar", principalColumn: "Id", onDelete: ReferentialAction.Cascade);
+            });
+
+            migrationBuilder.CreateTable(name: "SeminarPostHistoryVersion", columns: table => new
+            {
+                Seminar_Id = table.Column<int>(nullable: false),
+                PostHistoryVersion_Id = table.Column<int>(nullable: false)
+            }, constraints: table =>
+            {
+                table.PrimaryKey("PK_SeminarPostHistoryVersion", x => new
+                {
+                    x.Seminar_Id,
+                    x.PostHistoryVersion_Id
+                });
+                table.ForeignKey(name: "FK_SeminarPostHistoryVersion_PostHistoryVersion_PostHistoryVersion_Id", column: x => x.PostHistoryVersion_Id, principalTable: "PostHistoryVersion", principalColumn: "Id", onDelete: ReferentialAction.Cascade);
+                table.ForeignKey(name: "FK_SeminarPostHistoryVersion_Seminar_Seminar_Id", column: x => x.Seminar_Id, principalTable: "Seminar", principalColumn: "Id", onDelete: ReferentialAction.Cascade);
+            });
+
+            migrationBuilder.CreateIndex(name: "IX_Comment_PostId", table: "Comment", column: "PostId");
+
+            migrationBuilder.CreateIndex(name: "IX_LoginRecord_UserInfoId", table: "LoginRecord", column: "UserInfoId");
+
+            migrationBuilder.CreateIndex(name: "IX_Post_CategoryId", table: "Post", column: "CategoryId");
+
+            migrationBuilder.CreateIndex(name: "IX_PostAccessRecord_PostId", table: "PostAccessRecord", column: "PostId");
+
+            migrationBuilder.CreateIndex(name: "IX_PostHistoryVersion_CategoryId", table: "PostHistoryVersion", column: "CategoryId");
+
+            migrationBuilder.CreateIndex(name: "IX_PostHistoryVersion_PostId", table: "PostHistoryVersion", column: "PostId");
+
+            migrationBuilder.CreateIndex(name: "IX_SeminarPost_Post_Id", table: "SeminarPost", column: "Post_Id");
+
+            migrationBuilder.CreateIndex(name: "IX_SeminarPostHistoryVersion_PostHistoryVersion_Id", table: "SeminarPostHistoryVersion", column: "PostHistoryVersion_Id");
+        }
+
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropTable(name: "Broadcast");
+
+            migrationBuilder.DropTable(name: "Comment");
+
+            migrationBuilder.DropTable(name: "Contacts");
+
+            migrationBuilder.DropTable(name: "Donate");
+
+            migrationBuilder.DropTable(name: "FastShare");
+
+            migrationBuilder.DropTable(name: "InternalMessage");
+
+            migrationBuilder.DropTable(name: "Issue");
+
+            migrationBuilder.DropTable(name: "LeaveMessage");
+
+            migrationBuilder.DropTable(name: "Links");
+
+            migrationBuilder.DropTable(name: "LoginRecord");
+
+            migrationBuilder.DropTable(name: "Menu");
+
+            migrationBuilder.DropTable(name: "Misc");
+
+            migrationBuilder.DropTable(name: "Notice");
+
+            migrationBuilder.DropTable(name: "PostAccessRecord");
+
+            migrationBuilder.DropTable(name: "SearchDetails");
+
+            migrationBuilder.DropTable(name: "SeminarPost");
+
+            migrationBuilder.DropTable(name: "SeminarPostHistoryVersion");
+
+            migrationBuilder.DropTable(name: "SystemSetting");
+
+            migrationBuilder.DropTable(name: "UserInfo");
+
+            migrationBuilder.DropTable(name: "PostHistoryVersion");
+
+            migrationBuilder.DropTable(name: "Seminar");
+
+            migrationBuilder.DropTable(name: "Post");
+
+            migrationBuilder.DropTable(name: "Category");
+        }
+    }
+}

+ 27 - 22
src/Masuit.MyBlogs.Core/Migrations/DataContextModelSnapshot.cs

@@ -3,6 +3,7 @@
 using Masuit.MyBlogs.Core.Infrastructure.Application;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
 using System;
 
 namespace Masuit.MyBlogs.Core.Migrations
@@ -13,11 +14,11 @@ namespace Masuit.MyBlogs.Core.Migrations
         protected override void BuildModel(ModelBuilder modelBuilder)
         {
 #pragma warning disable 612, 618
-            modelBuilder.HasAnnotation("ProductVersion", "2.2.1-servicing-10028").HasAnnotation("Relational:MaxIdentifierLength", 64);
+            modelBuilder.HasAnnotation("ProductVersion", "2.2.1-servicing-10028").HasAnnotation("Relational:MaxIdentifierLength", 128).HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Broadcast", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("Email");
 
@@ -36,7 +37,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Category", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("Description");
 
@@ -51,7 +52,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Comment", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<int>("AgainstCount");
 
@@ -90,7 +91,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Contacts", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<int>("Status");
 
@@ -105,7 +106,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Donate", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("Amount");
 
@@ -132,7 +133,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.FastShare", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("Link");
 
@@ -149,7 +150,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.InternalMessage", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("Content");
 
@@ -170,7 +171,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Issue", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("Description").IsRequired();
 
@@ -201,7 +202,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.LeaveMessage", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("Browser").HasMaxLength(255);
 
@@ -232,7 +233,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Links", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<bool>("Except");
 
@@ -251,7 +252,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.LoginRecord", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("IP");
 
@@ -276,7 +277,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Menu", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("Icon");
 
@@ -301,7 +302,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Misc", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("Content").IsRequired();
 
@@ -320,7 +321,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Notice", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("Content").IsRequired();
 
@@ -341,10 +342,12 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Post", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("Author").IsRequired().HasMaxLength(24);
 
+                b.Property<double>("AverageViewCount");
+
                 b.Property<int>("CategoryId");
 
                 b.Property<string>("Content").IsRequired();
@@ -377,6 +380,8 @@ namespace Masuit.MyBlogs.Core.Migrations
 
                 b.Property<string>("Title").IsRequired();
 
+                b.Property<int>("TotalViewCount");
+
                 b.Property<int>("VoteDownCount").ValueGeneratedOnAddOrUpdate();
 
                 b.Property<int>("VoteUpCount");
@@ -390,7 +395,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.PostAccessRecord", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<DateTime>("AccessTime");
 
@@ -409,7 +414,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.PostHistoryVersion", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<int>("CategoryId");
 
@@ -446,7 +451,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.SearchDetails", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("IP");
 
@@ -461,7 +466,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.Seminar", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("Description").IsRequired();
 
@@ -504,7 +509,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.SystemSetting", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("Name").IsRequired();
 
@@ -519,7 +524,7 @@ namespace Masuit.MyBlogs.Core.Migrations
 
             modelBuilder.Entity("Masuit.MyBlogs.Core.Models.Entity.UserInfo", b =>
             {
-                b.Property<int>("Id").ValueGeneratedOnAdd();
+                b.Property<int>("Id").ValueGeneratedOnAdd().HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
 
                 b.Property<string>("AccessToken");
 

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

@@ -46,7 +46,7 @@ namespace Masuit.MyBlogs.Core.Models.DTO
         /// <summary>
         /// 浏览次数
         /// </summary>
-        public int ViewCount { get; set; }
+        public int TotalViewCount { get; set; }
 
         /// <summary>
         /// 发表时间

+ 11 - 0
src/Masuit.MyBlogs.Core/Models/DTO/SearchResult.cs

@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace Masuit.MyBlogs.Core.Models.DTO
+{
+    public class SearchResult<T>
+    {
+        public int Total { get; set; }
+        public double Elapsed { get; set; }
+        public List<T> Results { get; set; }
+    }
+}

+ 3 - 9
src/Masuit.MyBlogs.Core/Models/Entity/BaseEntity.cs

@@ -1,20 +1,14 @@
-using Masuit.MyBlogs.Core.Models.Enum;
+using Masuit.LuceneEFCore.SearchEngine;
+using Masuit.MyBlogs.Core.Models.Enum;
 using System.ComponentModel;
-using System.ComponentModel.DataAnnotations;
 
 namespace Masuit.MyBlogs.Core.Models.Entity
 {
     /// <summary>
     /// 基类型
     /// </summary>
-    public class BaseEntity
+    public class BaseEntity : LuceneIndexableBaseEntity
     {
-        /// <summary>
-        /// 主键
-        /// </summary>
-        [Key]
-        public int Id { get; set; }
-
         [DefaultValue(Status.Default)]
         public Status Status { get; set; }
     }

+ 17 - 7
src/Masuit.MyBlogs.Core/Models/Entity/Post.cs

@@ -1,6 +1,6 @@
+using Masuit.LuceneEFCore.SearchEngine;
 using Masuit.MyBlogs.Core.Models.Enum;
 using Masuit.MyBlogs.Core.Models.Validation;
-using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -30,19 +30,19 @@ namespace Masuit.MyBlogs.Core.Models.Entity
         /// <summary>
         /// 标题
         /// </summary>
-        [Required(ErrorMessage = "文章标题不能为空!")]
+        [Required(ErrorMessage = "文章标题不能为空!"), LuceneIndex]
         public string Title { get; set; }
 
         /// <summary>
         /// 作者
         /// </summary>
-        [Required, MaxLength(24, ErrorMessage = "作者名最长支持24个字符!")]
+        [Required, MaxLength(24, ErrorMessage = "作者名最长支持24个字符!"), LuceneIndex]
         public string Author { get; set; }
 
         /// <summary>
         /// 内容
         /// </summary>
-        [Required(ErrorMessage = "文章内容不能为空!"), SubmitCheck(20, 1000000, false)]
+        [Required(ErrorMessage = "文章内容不能为空!"), SubmitCheck(20, 1000000, false), LuceneIndex(IsHtml = true)]
         public string Content { get; set; }
 
         /// <summary>
@@ -85,19 +85,19 @@ namespace Masuit.MyBlogs.Core.Models.Entity
         /// <summary>
         /// 作者邮箱
         /// </summary>
-        [Required(ErrorMessage = "作者邮箱不能为空!"), IsEmail]
+        [Required(ErrorMessage = "作者邮箱不能为空!"), EmailAddress, LuceneIndex]
         public string Email { get; set; }
 
         /// <summary>
         /// 标签
         /// </summary>
-        [StringLength(256, ErrorMessage = "标签最大允许255个字符")]
+        [StringLength(256, ErrorMessage = "标签最大允许255个字符"), LuceneIndex]
         public string Label { get; set; }
 
         /// <summary>
         /// 文章关键词
         /// </summary>
-        [StringLength(256, ErrorMessage = "文章关键词最大允许255个字符")]
+        [StringLength(256, ErrorMessage = "文章关键词最大允许255个字符"), LuceneIndex]
         public string Keyword { get; set; }
 
         /// <summary>
@@ -130,6 +130,16 @@ namespace Masuit.MyBlogs.Core.Models.Entity
         [StringLength(255)]
         public string ImageUrl { get; set; }
 
+        /// <summary>
+        /// 每日平均访问量
+        /// </summary>
+        public double AverageViewCount { get; set; }
+
+        /// <summary>
+        /// 总访问量
+        /// </summary>
+        public int TotalViewCount { get; set; }
+
         /// <summary>
         /// 分类
         /// </summary>

+ 0 - 2
src/Masuit.MyBlogs.Core/Program.cs

@@ -1,5 +1,4 @@
 using Masuit.MyBlogs.Core.Configs;
-using Masuit.MyBlogs.Core.Hubs;
 using Microsoft.AspNetCore;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.Extensions.Configuration;
@@ -15,7 +14,6 @@ namespace Masuit.MyBlogs.Core
         {
             LicenseManager.AddLicense("67;100-MASUIT", "809739091397182EC1ECEA8770EB4218");
             RegisterAutomapper.Excute();
-            MyHub.Init();
             CreateWebHostBuilder(args).Build().Run();
         }
 

+ 1 - 2
src/Masuit.MyBlogs.Core/Properties/PublishProfiles/FolderProfile.pubxml

@@ -16,8 +16,7 @@
     <ProjectGuid>51a09bd3-ab54-4df9-ab8b-c68df0672c39</ProjectGuid>
     <SelfContained>false</SelfContained>
     <_IsPortable>true</_IsPortable>
-    <publishUrl>bin\Debug\netcoreapp2.2\publish\</publishUrl>
+    <publishUrl>bin\Release\netcoreapp2.2\publish\</publishUrl>
     <DeleteExistingFiles>True</DeleteExistingFiles>
-    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
   </PropertyGroup>
 </Project>

+ 38 - 13
src/Masuit.MyBlogs.Core/Startup.cs

@@ -5,6 +5,9 @@ using Common;
 using EFSecondLevelCache.Core;
 using Hangfire;
 using Hangfire.Dashboard;
+using Masuit.LuceneEFCore.SearchEngine;
+using Masuit.LuceneEFCore.SearchEngine.Extensions;
+using Masuit.LuceneEFCore.SearchEngine.Interfaces;
 using Masuit.MyBlogs.Core.Configs;
 using Masuit.MyBlogs.Core.Controllers;
 using Masuit.MyBlogs.Core.Extensions;
@@ -15,6 +18,7 @@ using Masuit.MyBlogs.Core.Infrastructure.Repository;
 using Masuit.MyBlogs.Core.Infrastructure.Services;
 using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.ViewModel;
+using Masuit.Tools.AspNetCore.Mime;
 using Masuit.Tools.Core.AspNetCore;
 using Masuit.Tools.Core.Net;
 using Microsoft.AspNetCore.Builder;
@@ -22,20 +26,24 @@ using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http.Features;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.StaticFiles;
 using Microsoft.AspNetCore.WebSockets;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.WebEncoders;
+using Microsoft.Net.Http.Headers;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Serialization;
 using Swashbuckle.AspNetCore.Swagger;
 using System;
 using System.Data;
 using System.Data.SqlClient;
+using System.IO;
 using System.Linq;
 using System.Text.Encodings.Web;
 using System.Text.Unicode;
+using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode;
 
 namespace Masuit.MyBlogs.Core
 {
@@ -99,18 +107,19 @@ namespace Masuit.MyBlogs.Core
             services.AddResponseCaching(); //注入响应缓存
             services.Configure<FormOptions>(options =>
             {
-                options.MultipartBodyLengthLimit = 2048000;
+                options.MultipartBodyLengthLimit = 104857600;// 100MB
             }); //配置请求长度
 
             services.AddSession(); //注入Session
             services.AddHangfire(x => x.UseRedisStorage(AppConfig.Redis)); //配置hangfire
 
-            services.AddSevenZipCompressor().AddResumeFileResult().AddDefaultRedisHelper(AppConfig.Redis); //配置7z和断点续传和Redis
+            services.AddSevenZipCompressor().AddResumeFileResult().AddDefaultRedisHelper(AppConfig.Redis).AddSearchEngine<DataContext>(new LuceneIndexerOptions() { Path = "lucene" });// 配置7z和断点续传和Redis和Lucene搜索引擎
+
             //配置EF二级缓存
             services.AddEFSecondLevelCache();
             // 配置EF二级缓存策略
             services.AddSingleton(typeof(ICacheManager<>), typeof(BaseCacheManager<>));
-            services.AddSingleton(typeof(ICacheManagerConfiguration), new CacheManager.Core.ConfigurationBuilder().WithJsonSerializer().WithMicrosoftMemoryCacheHandle().WithExpiration(ExpirationMode.Absolute, TimeSpan.FromMinutes(10)).Build());
+            services.AddSingleton(new CacheManager.Core.ConfigurationBuilder().WithJsonSerializer().WithMicrosoftMemoryCacheHandle().WithExpiration(ExpirationMode.Absolute, TimeSpan.FromMinutes(10)).Build());
 
             services.AddWebSockets(opt => opt.ReceiveBufferSize = 4096 * 1024).AddSignalR();
 
@@ -124,12 +133,12 @@ namespace Masuit.MyBlogs.Core
                 opt.SerializerSettings.ContractResolver = new DefaultContractResolver();
                 //opt.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
                 opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
-            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddControllersAsServices();
+            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddControllersAsServices().AddViewComponentsAsServices().AddTagHelpersAsServices();
 
             services.Configure<WebEncoderOptions>(options =>
             {
                 options.TextEncoderSettings = new TextEncoderSettings(UnicodeRanges.All);
-            });//解决razor视图中中文被编码的问题
+            }); //解决razor视图中中文被编码的问题
 
             ContainerBuilder builder = new ContainerBuilder();
             builder.Populate(services);
@@ -153,8 +162,9 @@ namespace Masuit.MyBlogs.Core
         /// <param name="app"></param>
         /// <param name="env"></param>
         /// <param name="db"></param>
-        /// <param name="appLifetime"></param>
-        public void Configure(IApplicationBuilder app, IHostingEnvironment env, DataContext db, IApplicationLifetime appLifetime)
+        /// <param name="searchEngine"></param>
+        /// <param name="luceneIndexerOptions"></param>
+        public void Configure(IApplicationBuilder app, IHostingEnvironment env, DataContext db, ISearchEngine<DataContext> searchEngine, LuceneIndexerOptions luceneIndexerOptions)
         {
             if (env.IsDevelopment())
             {
@@ -166,15 +176,33 @@ namespace Masuit.MyBlogs.Core
                 app.UseHsts();
                 app.UseException();
             }
+
+            app.UseHttpsRedirection().UseStaticFiles(new StaticFileOptions //静态资源缓存策略
+            {
+                OnPrepareResponse = context =>
+                {
+                    context.Context.Response.Headers[HeaderNames.CacheControl] = "public,no-cache";
+                    context.Context.Response.Headers[HeaderNames.Expires] = DateTime.UtcNow.AddDays(7).ToString("R");
+                },
+                ContentTypeProvider = new FileExtensionContentTypeProvider(MimeMapper.MimeTypes)
+            }).UseCookiePolicy();
+
             app.UseFirewall(); //启用网站防火墙
             //db.Database.Migrate();
             CommonHelper.SystemSettings = db.SystemSetting.ToDictionary(s => s.Name, s => s.Value); //初始化系统设置参数
 
+            string lucenePath = Path.Combine(env.ContentRootPath, luceneIndexerOptions.Path);
+            if (!Directory.Exists(lucenePath) || Directory.GetFiles(lucenePath).Length < 1)
+            {
+                Console.WriteLine("开始自动创建Lucene索引库...");
+                searchEngine.CreateIndex();
+                Console.WriteLine("索引库创建完成!");
+            }
+
             app.UseStaticHttpContext(); //注入静态HttpContext对象
             app.UseSession(); //注入Session
 
             app.UseEFSecondLevelCache(); //启动EF二级缓存
-            app.UseHttpsRedirection().UseStaticFiles().UseCookiePolicy();
             app.UseHangfireServer().UseHangfireDashboard("/taskcenter", new DashboardOptions()
             {
                 Authorization = new[]
@@ -192,14 +220,11 @@ namespace Masuit.MyBlogs.Core
             app.UseResponseCaching(); //启动Response缓存
             app.UseSwagger().UseSwaggerUI(c =>
             {
-                c.SwaggerEndpoint($"/swagger/v1/swagger.json", "懒得勤快的博客");
+                c.SwaggerEndpoint("/swagger/v1/swagger.json", CommonHelper.SystemSettings["Title"]);
             }); //配置swagger
             app.UseSignalR(hub => hub.MapHub<MyHub>("/hubs"));
             HangfireJobInit.Start(); //初始化定时任务
-            app.UseMvc(routes =>
-            {
-                routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}");
-            });
+            app.UseMvcWithDefaultRoute();
         }
     }
 

+ 1 - 4
src/Masuit.MyBlogs.Core/Views/Error/Index.cshtml

@@ -1,12 +1,9 @@
 @inject IHostingEnvironment HostingEnvironment
 @using System.IO
-@using System.Net.Http.Headers
 @using AngleSharp.Network
-@using AngleSharp.Network.Default
 @using Masuit.Tools
 @using Masuit.Tools.Win32
 @using Microsoft.AspNetCore.Hosting
-@using HttpHeaders = Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders
 
 @{
     Layout = null;
@@ -98,7 +95,7 @@
             <div id="container">
                 @{
                     Random r = new Random();
-                    string imgPath = $@"../Assets/images/404/404{r.StrictNext(Directory.GetFiles(HostingEnvironment.WebRootPath + "Assets/images/404").Length) + 1}.jpg";
+                    string imgPath = $@"../Assets/images/404/404{r.StrictNext(Directory.GetFiles(HostingEnvironment.WebRootPath + "/Assets/images/404").Length) + 1}.jpg";
                 }
                 <img class="img img-responsive img-thumbnail" width="100%" src="@imgPath" alt="@ViewBag.Keywords" />
                 <h3 class="margintop20">

+ 0 - 1
src/Masuit.MyBlogs.Core/Views/Error/ServiceUnavailable.cshtml

@@ -1,6 +1,5 @@
 @inject IHostingEnvironment HostingEnvironment
 @using System.IO
-@using AngleSharp.Network.Default
 @using Masuit.Tools
 @using Masuit.Tools.Win32
 @using Microsoft.AspNetCore.Hosting

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListItem.cshtml

@@ -78,7 +78,7 @@
             <div class="col-md-6 pull-right">
                 <div class="small text-right">
                     <i class="fa fa-comments-o"></i>@Model.Comment.Count(c => c.Status == Status.Pended) 评论 |
-                    <i class="fa fa-eye"></i>@Model.ViewCount 浏览 |
+                    <i class="fa fa-eye"></i>@Model.TotalViewCount 浏览 |
                     <i class="icon-thumbsup"></i>@Model.VoteUpCount 支持
                 </div>
             </div>

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListItem_Admin.cshtml

@@ -77,7 +77,7 @@
             <div class="col-md-7 pull-right">
                 <div class="small text-right">
                     <i class="fa fa-comments-o"></i>@Model.Comment.Count 评论 |
-                    <i class="fa fa-eye"></i>@Model.ViewCount 浏览 |
+                    <i class="fa fa-eye"></i>@Model.TotalViewCount 浏览 |
                     <i class="icon-thumbsup"></i>@Model.VoteUpCount 支持 |
                     <span class="text-danger"><i class="icon-recycle"></i>状态:@Model.Status.GetDisplay()</span>
                 </div>

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Shared/_Aside.cshtml

@@ -138,7 +138,7 @@
                         <li>
                             <a href="/@post.Id">
                                 <mark>@(post.Title.Length >= 20 ? post.Title.Substring(0, 20) + "..." : post.Title) </mark>
-                                <small style="color:#00ffff"> @post.ViewCount</small>
+                                <small style="color:#00ffff"> @post.TotalViewCount</small>
                             </a>
                         </li>
                     }

+ 24 - 31
src/Masuit.MyBlogs.Core/Views/Shared/_Layout.cshtml

@@ -19,7 +19,6 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    @*上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后!*@
     <meta http-equiv="X-UA-Compatible" content="IE=8">
     <meta http-equiv="Expires" content="0">
     <meta http-equiv="Pragma" content="no-cache">
@@ -28,7 +27,6 @@
     <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)" />
-    @*导入css*@
     <link href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css" rel="stylesheet">
     <link href="https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/6.6.9/sweetalert2.min.css" rel="stylesheet">
     <link href="https://cdnjs.cloudflare.com/ajax/libs/notie/4.3.1/notie.min.css" rel="stylesheet">
@@ -37,22 +35,19 @@
     <link href="~/Content/bootstrap.min.css" rel="stylesheet">
     <link href="~/Assets/layui/css/layui.min.css" rel="stylesheet" />
     <link href="~/fonts/icomoon.min.css" rel="stylesheet" />
+    <link href="~/Content/jquery.paging.css" rel="stylesheet" />
+    <link href="~/Content/common/reset.css" rel="stylesheet" />
+    <link href="~/Content/common/loading.css" rel="stylesheet" />
+    <link href="~/Content/common/style.css" rel="stylesheet" />
+    <link href="~/Content/common/articlestyle.css" rel="stylesheet" />
+    <link href="~/Content/common/leaderboard.css" rel="stylesheet" />
+    <link href="~/Content/microtip.min.css" rel="stylesheet" />
+    <link href="~/Assets/breadcrumb/style.css" rel="stylesheet" />
+    <link href="~/Assets/nav/css/style.css" rel="stylesheet" />
+    <link href="~/Assets/tab/styles.css" rel="stylesheet" />
+    <link href="~/Assets/tagcloud/css/tagcloud.css" />
+    <link href="~/Assets/tippy/tippy.css" rel="stylesheet" />
 
-    <link href="~/Content/jquery.paging.css" rel="stylesheet" asp-append-version="true" />
-    <link href="~/Content/common/reset.css" rel="stylesheet" asp-append-version="true" />
-    <link href="~/Content/common/loading.css" rel="stylesheet" asp-append-version="true" />
-    <link href="~/Content/common/style.css" rel="stylesheet" asp-append-version="true" />
-    <link href="~/Content/common/articlestyle.css" rel="stylesheet" asp-append-version="true" />
-    <link href="~/Content/common/leaderboard.css" rel="stylesheet" asp-append-version="true" />
-    <link href="~/Content/microtip.min.css" rel="stylesheet" asp-append-version="true" />
-    <link href="~/Assets/breadcrumb/style.css" rel="stylesheet" asp-append-version="true" />
-    <link href="~/Assets/nav/css/style.css" rel="stylesheet" asp-append-version="true" />
-    <link href="~/Assets/tab/styles.css" rel="stylesheet" asp-append-version="true" />
-    <link href="~/Assets/tagcloud/css/tagcloud.css" rel="stylesheet" asp-append-version="true" />
-    <link href="~/Assets/tippy/tippy.css" rel="stylesheet" asp-append-version="true" />
-    @*@RenderSection("styles")*@
-
-    @*导入Scripts*@
     <script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.5.0/bluebird.min.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.min.js"></script>
@@ -68,20 +63,18 @@
     <script src="~/Scripts/bootstrap.min.js"></script>
     <script src="~/Scripts/layer/layer.js"></script>
     <script src="~/Assets/layui/layui.min.js"></script>
-
-    <script src="~/Scripts/bootstrap-suggest.min.js" asp-append-version="true"></script>
-    <script src="~/Scripts/jquery.query.js" asp-append-version="true"></script>
-    <script src="~/Scripts/jquery.paging.js" asp-append-version="true"></script>
-    <script src="~/Scripts/ripplet.js" asp-append-version="true"></script>
-    <script src="~/Scripts/global/scripts.js" asp-append-version="true"></script>
-    <script src="~/Assets/tippy/tippy.js" asp-append-version="true"></script>
-    <script src="~/Assets/newsbox/jquery.bootstrap.newsbox.js" asp-append-version="true"></script>
-    <script src="~/Assets/tab/stopExecutionOnTimeout.js" asp-append-version="true"></script>
-    <script src="~/Assets/tab/init.js" asp-append-version="true"></script>
-    <script src="~/Assets/tagcloud/js/tagcloud.js" asp-append-version="true"></script>
-    <script src="~/Assets/scrolltop/js/scrolltop.js" asp-append-version="true"></script>
-    <script src="~/Assets/nav/js/main.js" asp-append-version="true"></script>
-    @*@RenderSection("scripts")*@
+    <script src="~/Scripts/bootstrap-suggest.min.js"></script>
+    <script src="~/Scripts/jquery.query.js"></script>
+    <script src="~/Scripts/jquery.paging.js"></script>
+    <script src="~/Scripts/ripplet.js"></script>
+    <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>
 </head>
 <body>
     <div id="toc" style="display: none;">

+ 1 - 1
src/Masuit.MyBlogs.Core/appsettings.json

@@ -11,6 +11,6 @@
 	},
 	//"ConnString": "Server=127.0.0.1;Port=3306;Database=MyBlogs;Uid=root;Pwd=;Charset=utf8mb4",
 	"ConnString": "Data Source=.;Initial Catalog=MyBlogs;Integrated Security=True",
-	"BaiduAK": "ÄúµÄ°Ù¶ÈAK",
+	"BaiduAK": "ty7ygYPCUV6gf7F73RTGcK0ETaFGdLS0",
 	"Redis": "127.0.0.1:6379"
 }

+ 0 - 6
src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/dashboard.js

@@ -63,12 +63,6 @@
 			$scope.connectWebsocket();
 		}
 	});
-	$scope.ClearMemory = function() {
-		console.log("111");
-		$scope.request("/system/CollectMemory", null, function(res) {
-			swal(res.Message, "", "info");
-		});
-	}
 	$http.post("/system/GetBaseInfo", null).then(function(res) {
 		var data = res.data;
 		$scope.cpu = data.cpuInfo[0];

+ 1 - 1
src/Masuit.MyBlogs.Core/wwwroot/ng-views/filemanager/js/providers/config.js

@@ -7,7 +7,7 @@
             defaultLang: 'zh_cn',
 
             listUrl: 'file/handle',
-            uploadUrl: 'file/handle',
+            uploadUrl: 'file/upload',
             renameUrl: 'file/handle',
             copyUrl: 'file/handle',
             moveUrl: 'file/handle',

+ 0 - 1
src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/dashboard.html

@@ -91,7 +91,6 @@
                 </div>
                 <div class="btn-group pull-right">
                     <button class="btn btn-success waves-effect" ng-click="StopPush()">{{push}}推送</button>
-                    <button class="btn btn-info waves-effect" ng-click="ClearMemory()">清理内存</button>
                 </div>
             </div>
         </div>