懒得勤快 2 лет назад
Родитель
Сommit
cea5859b24

+ 290 - 289
src/Masuit.MyBlogs.Core/Controllers/HomeController.cs

@@ -15,6 +15,7 @@ using System.Runtime.InteropServices;
 using System.Text;
 using System.Text.RegularExpressions;
 using Z.EntityFramework.Plus;
+using Configuration = AngleSharp.Configuration;
 
 namespace Masuit.MyBlogs.Core.Controllers;
 
@@ -23,315 +24,315 @@ namespace Masuit.MyBlogs.Core.Controllers;
 /// </summary>
 public sealed class HomeController : BaseController
 {
-	/// <summary>
-	/// 文章
-	/// </summary>
-	public IPostService PostService { get; set; }
+    /// <summary>
+    /// 文章
+    /// </summary>
+    public IPostService PostService { get; set; }
 
-	/// <summary>
-	/// 分类
-	/// </summary>
-	public ICategoryService CategoryService { get; set; }
+    /// <summary>
+    /// 分类
+    /// </summary>
+    public ICategoryService CategoryService { get; set; }
 
-	/// <summary>
-	/// 网站公告
-	/// </summary>
-	public INoticeService NoticeService { get; set; }
+    /// <summary>
+    /// 网站公告
+    /// </summary>
+    public INoticeService NoticeService { get; set; }
 
-	/// <summary>
-	/// 首页
-	/// </summary>
-	/// <returns></returns>
-	[ResponseCache(Duration = 600, VaryByHeader = nameof(HeaderNames.Cookie))]
-	public async Task<ActionResult> Index([FromServices] IFastShareService fastShareService)
-	{
-		var banners = AdsService.GetsByWeightedPrice(8, AdvertiseType.Banner, Request.Location()).OrderByRandom().ToPooledListScope();
-		var fastShares = fastShareService.GetAllFromCache(s => s.Sort);
-		var postsQuery = PostService.GetQuery(PostBaseWhere()); //准备文章的查询
-		var posts = await postsQuery.Where(p => !p.IsFixedTop).OrderBy(OrderBy.ModifyDate.GetDisplay() + " desc").ToPagedListAsync<Post, PostDto>(1, 15, MapperConfig);
-		posts.Data.InsertRange(0, postsQuery.Where(p => p.IsFixedTop).OrderByDescending(p => p.ModifyDate).ProjectTo<PostDto>(MapperConfig).FromCache().ToPooledListScope());
-		var viewModel = GetIndexPageViewModel();
-		viewModel.Banner = banners;
-		viewModel.Posts = posts;
-		ViewBag.FastShare = fastShares;
-		viewModel.PageParams = new Pagination(1, 15, posts.TotalCount, OrderBy.ModifyDate);
-		viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location());
-		viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.ListItem, Request.Location());
-		PostService.SolvePostsCategory(posts.Data);
-		foreach (var item in posts.Data)
-		{
-			item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
-		}
+    /// <summary>
+    /// 首页
+    /// </summary>
+    /// <returns></returns>
+    [ResponseCache(Duration = 600, VaryByHeader = nameof(HeaderNames.Cookie))]
+    public async Task<ActionResult> Index([FromServices] IFastShareService fastShareService)
+    {
+        var banners = AdsService.GetsByWeightedPrice(8, AdvertiseType.Banner, Request.Location()).OrderByRandom().ToPooledListScope();
+        var fastShares = fastShareService.GetAllFromCache(s => s.Sort);
+        var postsQuery = PostService.GetQuery(PostBaseWhere()); //准备文章的查询
+        var posts = await postsQuery.Where(p => !p.IsFixedTop).OrderBy(OrderBy.ModifyDate.GetDisplay() + " desc").ToPagedListAsync<Post, PostDto>(1, 15, MapperConfig);
+        posts.Data.InsertRange(0, postsQuery.Where(p => p.IsFixedTop).OrderByDescending(p => p.ModifyDate).ProjectTo<PostDto>(MapperConfig).FromCache().ToPooledListScope());
+        var viewModel = GetIndexPageViewModel();
+        viewModel.Banner = banners;
+        viewModel.Posts = posts;
+        ViewBag.FastShare = fastShares;
+        viewModel.PageParams = new Pagination(1, 15, posts.TotalCount, OrderBy.ModifyDate);
+        viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location());
+        viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.ListItem, Request.Location());
+        PostService.SolvePostsCategory(posts.Data);
+        foreach (var item in posts.Data)
+        {
+            item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
+        }
 
-		return View(viewModel);
-	}
+        return View(viewModel);
+    }
 
-	/// <summary>
-	/// 文章列表页
-	/// </summary>
-	/// <param name="page"></param>
-	/// <param name="size"></param>
-	/// <param name="orderBy"></param>
-	/// <returns></returns>
-	[Route("posts"), Route("p", Order = 1), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "page", "size", "orderBy" }, VaryByHeader = nameof(HeaderNames.Cookie))]
-	public async Task<ActionResult> Post([Optional] OrderBy? orderBy, int page = 1, [Range(1, 50, ErrorMessage = "页大小必须在0到50之间")] int size = 15)
-	{
-		page = Math.Max(1, page);
-		var viewModel = GetIndexPageViewModel();
-		var postsQuery = PostService.GetQuery(PostBaseWhere()); //准备文章的查询
-		var h24 = DateTime.Today.AddDays(-1);
-		var posts = orderBy switch
-		{
-			OrderBy.Trending => await postsQuery.Where(p => !p.IsFixedTop).OrderByDescending(p => p.PostVisitRecordStats.Where(e => e.Date >= h24).Sum(t => t.Count)).ToPagedListAsync<Post, PostDto>(page, size, MapperConfig),
-			_ => await postsQuery.Where(p => !p.IsFixedTop).OrderBy((orderBy ?? OrderBy.ModifyDate).GetDisplay() + " desc").ToPagedListAsync<Post, PostDto>(page, size, MapperConfig)
-		};
-		if (page == 1)
-		{
-			posts.Data.InsertRange(0, postsQuery.Where(p => p.IsFixedTop).OrderByDescending(p => p.ModifyDate).ProjectTo<PostDto>(MapperConfig));
-		}
+    /// <summary>
+    /// 文章列表页
+    /// </summary>
+    /// <param name="page"></param>
+    /// <param name="size"></param>
+    /// <param name="orderBy"></param>
+    /// <returns></returns>
+    [Route("posts"), Route("p", Order = 1), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "page", "size", "orderBy" }, VaryByHeader = nameof(HeaderNames.Cookie))]
+    public async Task<ActionResult> Post([Optional] OrderBy? orderBy, int page = 1, [Range(1, 50, ErrorMessage = "页大小必须在0到50之间")] int size = 15)
+    {
+        page = Math.Max(1, page);
+        var viewModel = GetIndexPageViewModel();
+        var postsQuery = PostService.GetQuery(PostBaseWhere()); //准备文章的查询
+        var h24 = DateTime.Today.AddDays(-1);
+        var posts = orderBy switch
+        {
+            OrderBy.Trending => await postsQuery.Where(p => !p.IsFixedTop).OrderByDescending(p => p.PostVisitRecordStats.Where(e => e.Date >= h24).Sum(t => t.Count)).ToPagedListAsync<Post, PostDto>(page, size, MapperConfig),
+            _ => await postsQuery.Where(p => !p.IsFixedTop).OrderBy((orderBy ?? OrderBy.ModifyDate).GetDisplay() + " desc").ToPagedListAsync<Post, PostDto>(page, size, MapperConfig)
+        };
+        if (page == 1)
+        {
+            posts.Data.InsertRange(0, postsQuery.Where(p => p.IsFixedTop).OrderByDescending(p => p.ModifyDate).ProjectTo<PostDto>(MapperConfig));
+        }
 
-		viewModel.Posts = posts;
-		viewModel.PageParams = new Pagination(page, size, posts.TotalCount, orderBy);
-		viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location());
-		viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.ListItem, Request.Location());
-		PostService.SolvePostsCategory(posts.Data);
-		foreach (var item in posts.Data)
-		{
-			item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
-		}
+        viewModel.Posts = posts;
+        viewModel.PageParams = new Pagination(page, size, posts.TotalCount, orderBy);
+        viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location());
+        viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.ListItem, Request.Location());
+        PostService.SolvePostsCategory(posts.Data);
+        foreach (var item in posts.Data)
+        {
+            item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
+        }
 
-		return View(viewModel);
-	}
+        return View(viewModel);
+    }
 
-	/// <summary>
-	/// 标签文章页
-	/// </summary>
-	/// <param name="tag"></param>
-	/// <param name="page"></param>
-	/// <param name="size"></param>
-	/// <param name="orderBy"></param>
-	/// <returns></returns>
-	[Route("tag/{tag}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "page", "size", "orderBy" }, VaryByHeader = nameof(HeaderNames.Cookie))]
-	public async Task<ActionResult> Tag(string tag, [Optional] OrderBy? orderBy, int page = 1, [Range(1, 50, ErrorMessage = "页大小必须在0到50之间")] int size = 15)
-	{
-		page = Math.Max(1, page);
-		if (string.IsNullOrWhiteSpace(tag))
-		{
-			throw new NotFoundException("");
-		}
+    /// <summary>
+    /// 标签文章页
+    /// </summary>
+    /// <param name="tag"></param>
+    /// <param name="page"></param>
+    /// <param name="size"></param>
+    /// <param name="orderBy"></param>
+    /// <returns></returns>
+    [Route("tag/{tag}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "page", "size", "orderBy" }, VaryByHeader = nameof(HeaderNames.Cookie))]
+    public async Task<ActionResult> Tag(string tag, [Optional] OrderBy? orderBy, int page = 1, [Range(1, 50, ErrorMessage = "页大小必须在0到50之间")] int size = 15)
+    {
+        page = Math.Max(1, page);
+        if (string.IsNullOrWhiteSpace(tag))
+        {
+            throw new NotFoundException("");
+        }
 
-		var where = PostBaseWhere();
-		var queryable = PostService.GetQuery(tag.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(s => Regex.Escape(s.Trim())).Aggregate(where, (current, s) => current.And(p => Regex.IsMatch(p.Label, s, RegexOptions.IgnoreCase))));
-		var h24 = DateTime.Today.AddDays(-1);
-		var posts = orderBy switch
-		{
-			OrderBy.Trending => await queryable.OrderByDescending(p => p.PostVisitRecordStats.Where(e => e.Date >= h24).Sum(e => e.Count)).ToPagedListAsync<Post, PostDto>(page, size, MapperConfig),
-			_ => await queryable.OrderBy($"{nameof(PostDto.IsFixedTop)} desc,{(orderBy ?? OrderBy.ModifyDate).GetDisplay()} desc").ToPagedListAsync<Post, PostDto>(page, size, MapperConfig)
-		};
-		var viewModel = GetIndexPageViewModel();
-		ViewBag.Tag = tag;
-		viewModel.Posts = posts;
-		viewModel.PageParams = new Pagination(page, size, posts.TotalCount, orderBy);
-		viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location(), keywords: tag);
-		viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.ListItem, Request.Location(), keywords: tag);
-		PostService.SolvePostsCategory(posts.Data);
-		foreach (var item in posts.Data)
-		{
-			item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
-		}
+        var where = PostBaseWhere();
+        var queryable = PostService.GetQuery(tag.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(s => Regex.Escape(s.Trim())).Aggregate(where, (current, s) => current.And(p => Regex.IsMatch(p.Label, s, RegexOptions.IgnoreCase))));
+        var h24 = DateTime.Today.AddDays(-1);
+        var posts = orderBy switch
+        {
+            OrderBy.Trending => await queryable.OrderByDescending(p => p.PostVisitRecordStats.Where(e => e.Date >= h24).Sum(e => e.Count)).ToPagedListAsync<Post, PostDto>(page, size, MapperConfig),
+            _ => await queryable.OrderBy($"{nameof(PostDto.IsFixedTop)} desc,{(orderBy ?? OrderBy.ModifyDate).GetDisplay()} desc").ToPagedListAsync<Post, PostDto>(page, size, MapperConfig)
+        };
+        var viewModel = GetIndexPageViewModel();
+        ViewBag.Tag = tag;
+        viewModel.Posts = posts;
+        viewModel.PageParams = new Pagination(page, size, posts.TotalCount, orderBy);
+        viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location(), keywords: tag);
+        viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.ListItem, Request.Location(), keywords: tag);
+        PostService.SolvePostsCategory(posts.Data);
+        foreach (var item in posts.Data)
+        {
+            item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
+        }
 
-		return View(viewModel);
-	}
+        return View(viewModel);
+    }
 
-	/// <summary>
-	/// 存档文章页
-	/// </summary>
-	/// <param name="yyyy"></param>
-	/// <param name="mm"></param>
-	/// <param name="dd"></param>
-	/// <param name="page"></param>
-	/// <param name="size"></param>
-	/// <param name="orderBy"></param>
-	/// <returns></returns>
-	[Route("{yyyy:int}/{mm:int}/{dd:int}/{mode}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "page", "size", "orderBy" }, VaryByHeader = nameof(HeaderNames.Cookie))]
-	public async Task<ActionResult> Archieve([Range(2010, 2099)] int yyyy, [Range(1, 12)] int mm, [Range(1, 31)] int dd, [Optional] OrderBy? orderBy, int page = 1, [Range(1, 50, ErrorMessage = "页大小必须在0到50之间")] int size = 15, string mode = nameof(Models.Entity.Post.ModifyDate))
-	{
-		page = Math.Max(1, page);
-		if (!DateTime.TryParse(yyyy + "-" + mm + "-" + dd, out var date))
-		{
-			date = DateTime.Today;
-		}
+    /// <summary>
+    /// 存档文章页
+    /// </summary>
+    /// <param name="yyyy"></param>
+    /// <param name="mm"></param>
+    /// <param name="dd"></param>
+    /// <param name="page"></param>
+    /// <param name="size"></param>
+    /// <param name="orderBy"></param>
+    /// <returns></returns>
+    [Route("{yyyy:int}/{mm:int}/{dd:int}/{mode}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "page", "size", "orderBy" }, VaryByHeader = nameof(HeaderNames.Cookie))]
+    public async Task<ActionResult> Archieve([Range(2010, 2099)] int yyyy, [Range(1, 12)] int mm, [Range(1, 31)] int dd, [Optional] OrderBy? orderBy, int page = 1, [Range(1, 50, ErrorMessage = "页大小必须在0到50之间")] int size = 15, string mode = nameof(Models.Entity.Post.ModifyDate))
+    {
+        page = Math.Max(1, page);
+        if (!DateTime.TryParse(yyyy + "-" + mm + "-" + dd, out var date))
+        {
+            date = DateTime.Today;
+        }
 
-		var where = mode switch
-		{
-			nameof(Models.Entity.Post.PostDate) => PostBaseWhere().And(p => p.PostDate.Date == date),
-			_ => PostBaseWhere().And(p => p.ModifyDate.Date == date),
-		};
-		var queryable = PostService.GetQuery(where);
-		var h24 = DateTime.Today.AddDays(-1);
-		var posts = orderBy switch
-		{
-			OrderBy.Trending => await queryable.OrderByDescending(p => p.PostVisitRecordStats.Where(e => e.Date >= h24).Sum(e => e.Count)).ToPagedListAsync<Post, PostDto>(page, size, MapperConfig),
-			_ => await queryable.OrderBy($"{nameof(PostDto.IsFixedTop)} desc,{(orderBy ?? OrderBy.ModifyDate).GetDisplay()} desc").ToPagedListAsync<Post, PostDto>(page, size, MapperConfig)
-		};
-		var viewModel = GetIndexPageViewModel();
-		viewModel.Posts = posts;
-		viewModel.PageParams = new Pagination(page, size, posts.TotalCount, orderBy);
-		viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location());
-		viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.ListItem, Request.Location());
-		PostService.SolvePostsCategory(posts.Data);
-		foreach (var item in posts.Data)
-		{
-			item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
-		}
+        var where = mode switch
+        {
+            nameof(Models.Entity.Post.PostDate) => PostBaseWhere().And(p => p.PostDate.Date == date),
+            _ => PostBaseWhere().And(p => p.ModifyDate.Date == date),
+        };
+        var queryable = PostService.GetQuery(where);
+        var h24 = DateTime.Today.AddDays(-1);
+        var posts = orderBy switch
+        {
+            OrderBy.Trending => await queryable.OrderByDescending(p => p.PostVisitRecordStats.Where(e => e.Date >= h24).Sum(e => e.Count)).ToPagedListAsync<Post, PostDto>(page, size, MapperConfig),
+            _ => await queryable.OrderBy($"{nameof(PostDto.IsFixedTop)} desc,{(orderBy ?? OrderBy.ModifyDate).GetDisplay()} desc").ToPagedListAsync<Post, PostDto>(page, size, MapperConfig)
+        };
+        var viewModel = GetIndexPageViewModel();
+        viewModel.Posts = posts;
+        viewModel.PageParams = new Pagination(page, size, posts.TotalCount, orderBy);
+        viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location());
+        viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.ListItem, Request.Location());
+        PostService.SolvePostsCategory(posts.Data);
+        foreach (var item in posts.Data)
+        {
+            item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
+        }
 
-		return View(viewModel);
-	}
+        return View(viewModel);
+    }
 
-	/// <summary>
-	/// 作者文章页
-	/// </summary>
-	/// <param name="author"></param>
-	/// <param name="page"></param>
-	/// <param name="size"></param>
-	/// <param name="orderBy"></param>
-	/// <returns></returns>
-	[Route("author/{author}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "page", "size", "orderBy" }, VaryByHeader = nameof(HeaderNames.Cookie))]
-	public async Task<ActionResult> Author(string author, [Optional] OrderBy? orderBy, int page = 1, [Range(1, 50, ErrorMessage = "页大小必须在0到50之间")] int size = 15)
-	{
-		page = Math.Max(1, page);
-		Expression<Func<Post, bool>> where = PostBaseWhere().And(p => p.Author.Equals(author) || p.Modifier.Equals(author) || p.Email.Equals(author) || p.PostHistoryVersion.Any(v => v.Modifier.Equals(author) || v.ModifierEmail.Equals(author)));
-		var h24 = DateTime.Today.AddDays(-1);
-		var posts = orderBy switch
-		{
-			OrderBy.Trending => await PostService.GetQuery(where).OrderByDescending(p => p.PostVisitRecordStats.Where(e => e.Date >= h24).Sum(e => e.Count)).ToPagedListAsync<Post, PostDto>(page, size, MapperConfig),
-			_ => await PostService.GetQuery(where).OrderBy($"{nameof(PostDto.IsFixedTop)} desc,{(orderBy ?? OrderBy.ModifyDate).GetDisplay()} desc").ToPagedListAsync<Post, PostDto>(page, size, MapperConfig)
-		};
-		var viewModel = GetIndexPageViewModel();
-		ViewBag.Author = author;
-		viewModel.Posts = posts;
-		viewModel.PageParams = new Pagination(page, size, posts.TotalCount, orderBy);
-		viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location());
-		viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.ListItem, Request.Location());
-		PostService.SolvePostsCategory(posts.Data);
-		foreach (var item in posts.Data)
-		{
-			item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
-		}
+    /// <summary>
+    /// 作者文章页
+    /// </summary>
+    /// <param name="author"></param>
+    /// <param name="page"></param>
+    /// <param name="size"></param>
+    /// <param name="orderBy"></param>
+    /// <returns></returns>
+    [Route("author/{author}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "page", "size", "orderBy" }, VaryByHeader = nameof(HeaderNames.Cookie))]
+    public async Task<ActionResult> Author(string author, [Optional] OrderBy? orderBy, int page = 1, [Range(1, 50, ErrorMessage = "页大小必须在0到50之间")] int size = 15)
+    {
+        page = Math.Max(1, page);
+        Expression<Func<Post, bool>> where = PostBaseWhere().And(p => p.Author.Equals(author) || p.Modifier.Equals(author) || p.Email.Equals(author) || p.PostHistoryVersion.Any(v => v.Modifier.Equals(author) || v.ModifierEmail.Equals(author)));
+        var h24 = DateTime.Today.AddDays(-1);
+        var posts = orderBy switch
+        {
+            OrderBy.Trending => await PostService.GetQuery(where).OrderByDescending(p => p.PostVisitRecordStats.Where(e => e.Date >= h24).Sum(e => e.Count)).ToPagedListAsync<Post, PostDto>(page, size, MapperConfig),
+            _ => await PostService.GetQuery(where).OrderBy($"{nameof(PostDto.IsFixedTop)} desc,{(orderBy ?? OrderBy.ModifyDate).GetDisplay()} desc").ToPagedListAsync<Post, PostDto>(page, size, MapperConfig)
+        };
+        var viewModel = GetIndexPageViewModel();
+        ViewBag.Author = author;
+        viewModel.Posts = posts;
+        viewModel.PageParams = new Pagination(page, size, posts.TotalCount, orderBy);
+        viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location());
+        viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.ListItem, Request.Location());
+        PostService.SolvePostsCategory(posts.Data);
+        foreach (var item in posts.Data)
+        {
+            item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
+        }
 
-		return View(viewModel);
-	}
+        return View(viewModel);
+    }
 
-	/// <summary>
-	/// 分类文章页
-	/// </summary>
-	/// <param name="id"></param>
-	/// <param name="page"></param>
-	/// <param name="size"></param>
-	/// <param name="orderBy"></param>
-	/// <returns></returns>
-	[Route("cat/{id:int}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "page", "size", "orderBy" }, VaryByHeader = nameof(HeaderNames.Cookie))]
-	public async Task<ActionResult> Category(int id, [Optional] OrderBy? orderBy, int page = 1, [Range(1, 50, ErrorMessage = "页大小必须在0到50之间")] int size = 15)
-	{
-		page = Math.Max(1, page);
-		var cat = await CategoryService.GetByIdAsync(id) ?? throw new NotFoundException("文章分类未找到");
-		var cids = cat.Flatten().Select(c => c.Id).ToArray();
-		var h24 = DateTime.Today.AddDays(-1);
-		var posts = orderBy switch
-		{
-			OrderBy.Trending => await PostService.GetQuery(PostBaseWhere().And(p => cids.Contains(p.CategoryId))).OrderByDescending(p => p.PostVisitRecordStats.Where(e => e.Date >= h24).Sum(e => e.Count)).ToPagedListAsync<Post, PostDto>(page, size, MapperConfig),
-			_ => await PostService.GetQuery(PostBaseWhere().And(p => cids.Contains(p.CategoryId))).OrderBy($"{nameof(PostDto.IsFixedTop)} desc,{(orderBy ?? OrderBy.ModifyDate).GetDisplay()} desc").ToPagedListAsync<Post, PostDto>(page, size, MapperConfig)
-		};
-		var viewModel = GetIndexPageViewModel();
-		viewModel.Posts = posts;
-		ViewBag.Category = cat;
-		viewModel.PageParams = new Pagination(page, size, posts.TotalCount, orderBy);
-		viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location(), id);
-		viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.ListItem, Request.Location(), id);
-		PostService.SolvePostsCategory(posts.Data);
-		foreach (var item in posts.Data)
-		{
-			item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
-		}
+    /// <summary>
+    /// 分类文章页
+    /// </summary>
+    /// <param name="id"></param>
+    /// <param name="page"></param>
+    /// <param name="size"></param>
+    /// <param name="orderBy"></param>
+    /// <returns></returns>
+    [Route("cat/{id:int}"), ResponseCache(Duration = 600, VaryByQueryKeys = new[] { "page", "size", "orderBy" }, VaryByHeader = nameof(HeaderNames.Cookie))]
+    public async Task<ActionResult> Category(int id, [Optional] OrderBy? orderBy, int page = 1, [Range(1, 50, ErrorMessage = "页大小必须在0到50之间")] int size = 15)
+    {
+        page = Math.Max(1, page);
+        var cat = await CategoryService.GetByIdAsync(id) ?? throw new NotFoundException("文章分类未找到");
+        var cids = cat.Flatten().Select(c => c.Id).ToArray();
+        var h24 = DateTime.Today.AddDays(-1);
+        var posts = orderBy switch
+        {
+            OrderBy.Trending => await PostService.GetQuery(PostBaseWhere().And(p => cids.Contains(p.CategoryId))).OrderByDescending(p => p.PostVisitRecordStats.Where(e => e.Date >= h24).Sum(e => e.Count)).ToPagedListAsync<Post, PostDto>(page, size, MapperConfig),
+            _ => await PostService.GetQuery(PostBaseWhere().And(p => cids.Contains(p.CategoryId))).OrderBy($"{nameof(PostDto.IsFixedTop)} desc,{(orderBy ?? OrderBy.ModifyDate).GetDisplay()} desc").ToPagedListAsync<Post, PostDto>(page, size, MapperConfig)
+        };
+        var viewModel = GetIndexPageViewModel();
+        viewModel.Posts = posts;
+        ViewBag.Category = cat;
+        viewModel.PageParams = new Pagination(page, size, posts.TotalCount, orderBy);
+        viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location(), id);
+        viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.ListItem, Request.Location(), id);
+        PostService.SolvePostsCategory(posts.Data);
+        foreach (var item in posts.Data)
+        {
+            item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
+        }
 
-		return View(viewModel);
-	}
+        return View(viewModel);
+    }
 
-	/// <summary>
-	/// 切换语言
-	/// </summary>
-	/// <param name="lang"></param>
-	/// <returns></returns>
-	[Route("lang/{lang}")]
-	public ActionResult SetLang(string lang)
-	{
-		Response.Cookies.Append("lang", lang, new CookieOptions()
-		{
-			Expires = DateTime.Now.AddYears(1),
-		});
-		var referer = Request.Headers[HeaderNames.Referer].ToString();
-		return Redirect(string.IsNullOrEmpty(referer) ? "/" : referer);
-	}
+    /// <summary>
+    /// 切换语言
+    /// </summary>
+    /// <param name="lang"></param>
+    /// <returns></returns>
+    [Route("lang/{lang}")]
+    public ActionResult SetLang(string lang)
+    {
+        Response.Cookies.Append("lang", lang, new CookieOptions()
+        {
+            Expires = DateTime.Now.AddYears(1),
+        });
+        var referer = Request.Headers[HeaderNames.Referer].ToString();
+        return Redirect(string.IsNullOrEmpty(referer) ? "/" : referer);
+    }
 
-	/// <summary>
-	/// 站点地图
-	/// </summary>
-	/// <param name="env"></param>
-	/// <param name="ext"></param>
-	/// <returns></returns>
-	[HttpGet("/sitemaps.{ext}")]
-	public async Task<ActionResult> Sitemap([FromServices] IWebHostEnvironment env, string ext)
-	{
-		var sitemap = Path.Combine(env.WebRootPath, "sitemap." + ext);
-		if (System.IO.File.Exists(sitemap))
-		{
-			var fs = new FileInfo(sitemap).ShareReadWrite();
-			switch (ext)
-			{
-				case "txt":
-					return Content((await fs.ReadAllLinesAsync(Encoding.UTF8)).Select(s => Request.Scheme + "://" + Request.Host.Host + new Uri(s).GetComponents(UriComponents.PathAndQuery, UriFormat.UriEscaped)).Join("\r\n"), ContentType.Txt);
+    /// <summary>
+    /// 站点地图
+    /// </summary>
+    /// <param name="env"></param>
+    /// <param name="ext"></param>
+    /// <returns></returns>
+    [HttpGet("/sitemaps.{ext}")]
+    public async Task<ActionResult> Sitemap([FromServices] IWebHostEnvironment env, string ext)
+    {
+        var sitemap = Path.Combine(env.WebRootPath, "sitemap." + ext);
+        if (System.IO.File.Exists(sitemap))
+        {
+            var fs = new FileInfo(sitemap).ShareReadWrite();
+            switch (ext)
+            {
+                case "txt":
+                    return Content((await fs.ReadAllLinesAsync(Encoding.UTF8)).Select(s => Request.Scheme + "://" + Request.Host.Host + new Uri(s).GetComponents(UriComponents.PathAndQuery, UriFormat.UriEscaped)).Join("\r\n"), ContentType.Txt);
 
-				case "html":
-					var context = BrowsingContext.New(Configuration.Default);
-					var doc = await context.OpenAsync(req => req.Content(fs.ReadAllText(Encoding.UTF8)));
-					foreach (var e in doc.Body.QuerySelectorAll("li a"))
-					{
-						e.SetAttribute("href", Request.Scheme + "://" + Request.Host.Host + new Uri(e.GetAttribute("href")).GetComponents(UriComponents.PathAndQuery, UriFormat.UriEscaped));
-					}
+                case "html":
+                    var context = BrowsingContext.New(Configuration.Default);
+                    var doc = await context.OpenAsync(req => req.Content(fs.ReadAllText(Encoding.UTF8)));
+                    foreach (var e in doc.Body.QuerySelectorAll("li a"))
+                    {
+                        e.SetAttribute("href", Request.Scheme + "://" + Request.Host.Host + new Uri(e.GetAttribute("href")).GetComponents(UriComponents.PathAndQuery, UriFormat.UriEscaped));
+                    }
 
-					return Content(doc.DocumentElement.OuterHtml, ContentType.Html);
-			}
-			return File("/sitemap." + ext, new MimeMapper().GetMimeFromExtension("." + ext));
-		}
+                    return Content(doc.DocumentElement.OuterHtml, ContentType.Html);
+            }
+            return File("/sitemap." + ext, new MimeMapper().GetMimeFromExtension("." + ext));
+        }
 
-		return NotFound();
-	}
+        return NotFound();
+    }
 
-	/// <summary>
-	/// 获取页面视图模型
-	/// </summary>
-	/// <returns></returns>
-	private HomePageViewModel GetIndexPageViewModel()
-	{
-		var postsQuery = PostService.GetQuery<PostDto>(PostBaseWhere()); //准备文章的查询
-		var notices = NoticeService.GetPagesFromCache<DateTime, NoticeDto>(1, 5, n => n.NoticeStatus == NoticeStatus.Normal, n => n.ModifyDate, false); //加载前5条公告
-		var cats = CategoryService.GetQuery(c => c.Status == Status.Available && c.Post.Count > 0).Include(c => c.Parent).OrderBy(c => c.Name).ThenBy(c => c.Path).AsNoTracking().FromCache().ToPooledListScope(); //加载分类目录
-		var hotSearches = RedisHelper.Get<List<KeywordsRank>>("SearchRank:Week").AsNotNull().Take(10).ToPooledListScope(); //热词统计
-		var hot5Post = postsQuery.OrderBy((new Random().Next() % 3) switch
-		{
-			1 => nameof(OrderBy.VoteUpCount),
-			2 => nameof(OrderBy.AverageViewCount),
-			_ => nameof(OrderBy.TotalViewCount)
-		} + " desc").Skip(0).Take(5).FromCache().ToPooledListScope(); //热门文章
-		var tagdic = PostService.GetTags().OrderByRandom().Take(20).ToDictionary(x => x.Key, x => Math.Min(x.Value + 12, 32)); //统计标签
-		return new HomePageViewModel
-		{
-			Categories = Mapper.Map<List<CategoryDto_P>>(cats.ToTree(c => c.Id, c => c.ParentId).Flatten()),
-			HotSearch = hotSearches,
-			Notices = notices.Data,
-			Tags = tagdic,
-			Top5Post = hot5Post,
-			PostsQueryable = postsQuery
-		};
-	}
-}
+    /// <summary>
+    /// 获取页面视图模型
+    /// </summary>
+    /// <returns></returns>
+    private HomePageViewModel GetIndexPageViewModel()
+    {
+        var postsQuery = PostService.GetQuery<PostDto>(PostBaseWhere()); //准备文章的查询
+        var notices = NoticeService.GetPagesFromCache<DateTime, NoticeDto>(1, 5, n => n.NoticeStatus == NoticeStatus.Normal, n => n.ModifyDate, false); //加载前5条公告
+        var cats = CategoryService.GetQuery(c => c.Status == Status.Available && c.Post.Count > 0).Include(c => c.Parent).OrderBy(c => c.Name).ThenBy(c => c.Path).AsNoTracking().FromCache().ToPooledListScope(); //加载分类目录
+        var hotSearches = RedisHelper.Get<List<KeywordsRank>>("SearchRank:Week").AsNotNull().Take(10).ToPooledListScope(); //热词统计
+        var hot5Post = postsQuery.OrderBy((new Random().Next() % 3) switch
+        {
+            1 => nameof(OrderBy.VoteUpCount),
+            2 => nameof(OrderBy.AverageViewCount),
+            _ => nameof(OrderBy.TotalViewCount)
+        } + " desc").Skip(0).Take(5).FromCache().ToPooledListScope(); //热门文章
+        var tagdic = PostService.GetTags().OrderByRandom().Take(20).ToDictionary(x => x.Key, x => Math.Min(x.Value + 12, 32)); //统计标签
+        return new HomePageViewModel
+        {
+            Categories = Mapper.Map<List<CategoryDto_P>>(cats.ToTree(c => c.Id, c => c.ParentId).Flatten()),
+            HotSearch = hotSearches,
+            Notices = notices.Data,
+            Tags = tagdic,
+            Top5Post = hot5Post,
+            PostsQueryable = postsQuery
+        };
+    }
+}

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

@@ -13,6 +13,7 @@ using Newtonsoft.Json;
 using Polly;
 using System.Net;
 using TimeZoneConverter;
+using Configuration = AngleSharp.Configuration;
 
 namespace Masuit.MyBlogs.Core.Controllers;
 
@@ -202,4 +203,4 @@ public sealed class ToolsController : BaseController
 
         return Json(physicsAddress?.AddressResult?.Location);
     }
-}
+}

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

@@ -14,6 +14,7 @@ using System.ComponentModel.DataAnnotations;
 using System.Diagnostics;
 using System.Text.RegularExpressions;
 using System.Xml.Linq;
+using Configuration = AngleSharp.Configuration;
 
 namespace Masuit.MyBlogs.Core.Controllers;
 
@@ -300,4 +301,4 @@ public sealed class UploadController : Controller
             return ResultData(null, false, "文件上传失败!");
         }
     }
-}
+}

+ 258 - 257
src/Masuit.MyBlogs.Core/Infrastructure/Services/PostService.cs

@@ -14,288 +14,289 @@ using PanGu.HighLight;
 using System.Reflection;
 using System.Text.RegularExpressions;
 using Z.EntityFramework.Plus;
+using Configuration = AngleSharp.Configuration;
 
 namespace Masuit.MyBlogs.Core.Infrastructure.Services;
 
 public sealed class PostService : BaseService<Post>, IPostService
 {
-	private readonly ICacheManager<SearchResult<PostDto>> _cacheManager;
-	private readonly ICategoryRepository _categoryRepository;
-	private readonly IMapper _mapper;
-	private readonly IPostTagsRepository _postTagsRepository;
+    private readonly ICacheManager<SearchResult<PostDto>> _cacheManager;
+    private readonly ICategoryRepository _categoryRepository;
+    private readonly IMapper _mapper;
+    private readonly IPostTagsRepository _postTagsRepository;
 
-	public PostService(IPostRepository repository, ISearchEngine<DataContext> searchEngine, ILuceneIndexSearcher searcher, ICacheManager<SearchResult<PostDto>> cacheManager, ICategoryRepository categoryRepository, IMapper mapper, IPostTagsRepository postTagsRepository) : base(repository, searchEngine, searcher)
-	{
-		_cacheManager = cacheManager;
-		_categoryRepository = categoryRepository;
-		_mapper = mapper;
-		_postTagsRepository = postTagsRepository;
-	}
+    public PostService(IPostRepository repository, ISearchEngine<DataContext> searchEngine, ILuceneIndexSearcher searcher, ICacheManager<SearchResult<PostDto>> cacheManager, ICategoryRepository categoryRepository, IMapper mapper, IPostTagsRepository postTagsRepository) : base(repository, searchEngine, searcher)
+    {
+        _cacheManager = cacheManager;
+        _categoryRepository = categoryRepository;
+        _mapper = mapper;
+        _postTagsRepository = postTagsRepository;
+    }
 
-	/// <summary>
-	/// 文章高亮关键词处理
-	/// </summary>
-	/// <param name="p"></param>
-	/// <param name="keyword"></param>
-	public async Task Highlight(Post p, string keyword)
-	{
-		try
-		{
-			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 = int.MaxValue };
-			keyword = Regex.Replace(keyword, @"<|>|\(|\)|\{|\}|\[|\]", " ");
-			var keywords = Searcher.CutKeywords(keyword);
-			var context = BrowsingContext.New(Configuration.Default);
-			var document = await context.OpenAsync(req => req.Content(p.Content));
-			var elements = document.DocumentElement.GetElementsByTagName("p");
-			foreach (var e in elements)
-			{
-				for (var index = 0; index < e.ChildNodes.Length; index++)
-				{
-					var node = e.ChildNodes[index];
-					bool handled = false;
-					foreach (var s in keywords)
-					{
-						string frag;
-						if (handled == false && node.TextContent.Contains(s, StringComparison.CurrentCultureIgnoreCase) && !string.IsNullOrEmpty(frag = highlighter.GetBestFragment(s, node.TextContent)))
-						{
-							switch (node)
-							{
-								case IElement el:
-									el.InnerHtml = frag;
-									handled = true;
-									break;
+    /// <summary>
+    /// 文章高亮关键词处理
+    /// </summary>
+    /// <param name="p"></param>
+    /// <param name="keyword"></param>
+    public async Task Highlight(Post p, string keyword)
+    {
+        try
+        {
+            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 = int.MaxValue };
+            keyword = Regex.Replace(keyword, @"<|>|\(|\)|\{|\}|\[|\]", " ");
+            var keywords = Searcher.CutKeywords(keyword);
+            var context = BrowsingContext.New(Configuration.Default);
+            var document = await context.OpenAsync(req => req.Content(p.Content));
+            var elements = document.DocumentElement.GetElementsByTagName("p");
+            foreach (var e in elements)
+            {
+                for (var index = 0; index < e.ChildNodes.Length; index++)
+                {
+                    var node = e.ChildNodes[index];
+                    bool handled = false;
+                    foreach (var s in keywords)
+                    {
+                        string frag;
+                        if (handled == false && node.TextContent.Contains(s, StringComparison.CurrentCultureIgnoreCase) && !string.IsNullOrEmpty(frag = highlighter.GetBestFragment(s, node.TextContent)))
+                        {
+                            switch (node)
+                            {
+                                case IElement el:
+                                    el.InnerHtml = frag;
+                                    handled = true;
+                                    break;
 
-								case IText t:
-									var parser = new HtmlParser();
-									var parseDoc = parser.ParseDocument(frag).Body;
-									e.ReplaceChild(parseDoc, t);
-									handled = true;
-									break;
-							}
-						}
-					}
-				}
-			}
-			p.Content = document.Body.InnerHtml;
-		}
-		catch
-		{
-			// ignored
-		}
-	}
+                                case IText t:
+                                    var parser = new HtmlParser();
+                                    var parseDoc = parser.ParseDocument(frag).Body;
+                                    e.ReplaceChild(parseDoc, t);
+                                    handled = true;
+                                    break;
+                            }
+                        }
+                    }
+                }
+            }
+            p.Content = document.Body.InnerHtml;
+        }
+        catch
+        {
+            // ignored
+        }
+    }
 
-	public SearchResult<PostDto> SearchPage(Expression<Func<Post, bool>> whereBase, int page, int size, string keyword)
-	{
-		var cacheKey = $"search:{keyword}:{page}:{size}";
-		var result = _cacheManager.GetOrAdd(cacheKey, _ =>
-		{
-			var searchResult = SearchEngine.ScoredSearch<Post>(BuildSearchOptions(page, size, keyword));
-			var entities = searchResult.Results.Where(s => s.Entity.Status == Status.Published).DistinctBy(s => s.Entity.Id).ToList();
-			var ids = entities.Select(s => s.Entity.Id).ToArray();
-			var dic = GetQuery(whereBase.And(p => ids.Contains(p.Id) && p.LimitMode != RegionLimitMode.OnlyForSearchEngine)).ProjectTo<PostDto>(_mapper.ConfigurationProvider).ToDictionary(p => p.Id);
-			var posts = entities.Where(s => dic.ContainsKey(s.Entity.Id)).Select(s => dic[s.Entity.Id]).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);
-			HighlightSegment(posts, keywords, highlighter);
-			SolvePostsCategory(posts);
-			return new SearchResult<PostDto>()
-			{
-				Results = posts,
-				Elapsed = searchResult.Elapsed,
-				Total = searchResult.TotalHits
-			};
-		});
-		return result;
-	}
+    public SearchResult<PostDto> SearchPage(Expression<Func<Post, bool>> whereBase, int page, int size, string keyword)
+    {
+        var cacheKey = $"search:{keyword}:{page}:{size}";
+        var result = _cacheManager.GetOrAdd(cacheKey, _ =>
+        {
+            var searchResult = SearchEngine.ScoredSearch<Post>(BuildSearchOptions(page, size, keyword));
+            var entities = searchResult.Results.Where(s => s.Entity.Status == Status.Published).DistinctBy(s => s.Entity.Id).ToList();
+            var ids = entities.Select(s => s.Entity.Id).ToArray();
+            var dic = GetQuery(whereBase.And(p => ids.Contains(p.Id) && p.LimitMode != RegionLimitMode.OnlyForSearchEngine)).ProjectTo<PostDto>(_mapper.ConfigurationProvider).ToDictionary(p => p.Id);
+            var posts = entities.Where(s => dic.ContainsKey(s.Entity.Id)).Select(s => dic[s.Entity.Id]).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);
+            HighlightSegment(posts, keywords, highlighter);
+            SolvePostsCategory(posts);
+            return new SearchResult<PostDto>()
+            {
+                Results = posts,
+                Elapsed = searchResult.Elapsed,
+                Total = searchResult.TotalHits
+            };
+        });
+        return result;
+    }
 
-	public void SolvePostsCategory(IList<PostDto> posts)
-	{
-		var cids = posts.Select(p => p.CategoryId).Distinct().ToArray();
-		var categories = _categoryRepository.GetQuery(c => cids.Contains(c.Id)).Include(c => c.Parent).ToDictionary(c => c.Id);
-		posts.ForEach(p => p.Category = _mapper.Map<CategoryDto_P>(categories[p.CategoryId]));
-	}
+    public void SolvePostsCategory(IList<PostDto> posts)
+    {
+        var cids = posts.Select(p => p.CategoryId).Distinct().ToArray();
+        var categories = _categoryRepository.GetQuery(c => cids.Contains(c.Id)).Include(c => c.Parent).ToDictionary(c => c.Id);
+        posts.ForEach(p => p.Category = _mapper.Map<CategoryDto_P>(categories[p.CategoryId]));
+    }
 
-	/// <summary>
-	/// 高亮截取处理
-	/// </summary>
-	/// <param name="posts"></param>
-	/// <param name="keywords"></param>
-	/// <param name="highlighter"></param>
-	private static void HighlightSegment(IList<PostDto> posts, List<string> keywords, Highlighter highlighter)
-	{
-		foreach (var p in posts)
-		{
-			p.Content = p.Content.RemoveHtmlTag();
-			foreach (var s in keywords)
-			{
-				string frag;
-				if (p.Title.Contains(s) && !string.IsNullOrEmpty(frag = highlighter.GetBestFragment(s, p.Title)))
-				{
-					p.Title = frag;
-					break;
-				}
-			}
+    /// <summary>
+    /// 高亮截取处理
+    /// </summary>
+    /// <param name="posts"></param>
+    /// <param name="keywords"></param>
+    /// <param name="highlighter"></param>
+    private static void HighlightSegment(IList<PostDto> posts, List<string> keywords, Highlighter highlighter)
+    {
+        foreach (var p in posts)
+        {
+            p.Content = p.Content.RemoveHtmlTag();
+            foreach (var s in keywords)
+            {
+                string frag;
+                if (p.Title.Contains(s) && !string.IsNullOrEmpty(frag = highlighter.GetBestFragment(s, p.Title)))
+                {
+                    p.Title = frag;
+                    break;
+                }
+            }
 
-			bool handled = false;
-			foreach (var s in keywords)
-			{
-				string frag;
-				if (p.Content.Contains(s) && !string.IsNullOrEmpty(frag = highlighter.GetBestFragment(s, p.Content)))
-				{
-					p.Content = frag;
-					handled = true;
-					break;
-				}
-			}
+            bool handled = false;
+            foreach (var s in keywords)
+            {
+                string frag;
+                if (p.Content.Contains(s) && !string.IsNullOrEmpty(frag = highlighter.GetBestFragment(s, p.Content)))
+                {
+                    p.Content = frag;
+                    handled = true;
+                    break;
+                }
+            }
 
-			if (p.Content.Length > 200 && !handled)
-			{
-				p.Content = p.Content[..200];
-			}
-		}
-	}
+            if (p.Content.Length > 200 && !handled)
+            {
+                p.Content = p.Content[..200];
+            }
+        }
+    }
 
-	private static SearchOptions BuildSearchOptions(int page, int size, string keyword)
-	{
-		keyword = Regex.Replace(keyword, @":\s+", ":");
-		var fields = new List<string>();
-		var newkeywords = new List<string>();
-		foreach (var item in keyword.Split(' ', ' ').Where(s => s.Contains(new[] { ":", ":" })))
-		{
-			var part = item.Split(':', ':');
-			var field = typeof(Post).GetProperty(part[0], BindingFlags.IgnoreCase)?.Name;
-			if (!string.IsNullOrEmpty(field))
-			{
-				fields.Add(field);
-			}
+    private static SearchOptions BuildSearchOptions(int page, int size, string keyword)
+    {
+        keyword = Regex.Replace(keyword, @":\s+", ":");
+        var fields = new List<string>();
+        var newkeywords = new List<string>();
+        foreach (var item in keyword.Split(' ', ' ').Where(s => s.Contains(new[] { ":", ":" })))
+        {
+            var part = item.Split(':', ':');
+            var field = typeof(Post).GetProperty(part[0], BindingFlags.IgnoreCase)?.Name;
+            if (!string.IsNullOrEmpty(field))
+            {
+                fields.Add(field);
+            }
 
-			newkeywords.Add(part[1]);
-		}
+            newkeywords.Add(part[1]);
+        }
 
-		var searchOptions = fields.Any() ? new SearchOptions(newkeywords.Join(" "), page, size, fields.Join(",")) : new SearchOptions(keyword, page, size, typeof(Post));
-		if (keyword.Contains(new[] { " ", ",", ";" }))
-		{
-			searchOptions.Score = 0.3f;
-		}
+        var searchOptions = fields.Any() ? new SearchOptions(newkeywords.Join(" "), page, size, fields.Join(",")) : new SearchOptions(keyword, page, size, typeof(Post));
+        if (keyword.Contains(new[] { " ", ",", ";" }))
+        {
+            searchOptions.Score = 0.3f;
+        }
 
-		return searchOptions;
-	}
+        return searchOptions;
+    }
 
-	/// <summary>
-	/// 文章所有tag
-	/// </summary>
-	/// <returns></returns>
-	public Dictionary<string, int> GetTags()
-	{
-		return _postTagsRepository.GetAll(t => t.Count, false).FromCache().ToDictionary(g => g.Name, g => g.Count);
-	}
+    /// <summary>
+    /// 文章所有tag
+    /// </summary>
+    /// <returns></returns>
+    public Dictionary<string, int> GetTags()
+    {
+        return _postTagsRepository.GetAll(t => t.Count, false).FromCache().ToDictionary(g => g.Name, g => g.Count);
+    }
 
-	/// <summary>
-	/// 添加实体并保存
-	/// </summary>
-	/// <param name="t">需要添加的实体</param>
-	/// <returns>添加成功</returns>
-	public override Post AddEntitySaved(Post t)
-	{
-		t = base.AddEntity(t);
-		SearchEngine.SaveChanges(t.Status == Status.Published);
-		return t;
-	}
+    /// <summary>
+    /// 添加实体并保存
+    /// </summary>
+    /// <param name="t">需要添加的实体</param>
+    /// <returns>添加成功</returns>
+    public override Post AddEntitySaved(Post t)
+    {
+        t = base.AddEntity(t);
+        SearchEngine.SaveChanges(t.Status == Status.Published);
+        return t;
+    }
 
-	/// <summary>
-	/// 添加实体并保存(异步)
-	/// </summary>
-	/// <param name="t">需要添加的实体</param>
-	/// <returns>添加成功</returns>
-	public override Task<int> AddEntitySavedAsync(Post t)
-	{
-		base.AddEntity(t);
-		return SearchEngine.SaveChangesAsync(t.Status == Status.Published);
-	}
+    /// <summary>
+    /// 添加实体并保存(异步)
+    /// </summary>
+    /// <param name="t">需要添加的实体</param>
+    /// <returns>添加成功</returns>
+    public override Task<int> AddEntitySavedAsync(Post t)
+    {
+        base.AddEntity(t);
+        return SearchEngine.SaveChangesAsync(t.Status == Status.Published);
+    }
 
-	/// <summary>
-	/// 根据ID删除实体并保存
-	/// </summary>
-	/// <param name="id">实体id</param>
-	/// <returns>删除成功</returns>
-	public override bool DeleteById(int id)
-	{
-		DeleteEntity(GetById(id));
-		return SearchEngine.SaveChanges() > 0;
-	}
+    /// <summary>
+    /// 根据ID删除实体并保存
+    /// </summary>
+    /// <param name="id">实体id</param>
+    /// <returns>删除成功</returns>
+    public override bool DeleteById(int id)
+    {
+        DeleteEntity(GetById(id));
+        return SearchEngine.SaveChanges() > 0;
+    }
 
-	/// <summary>
-	/// 根据ID删除实体并保存(异步)
-	/// </summary>
-	/// <param name="id">实体id</param>
-	/// <returns>删除成功</returns>
-	public override Task<int> DeleteByIdAsync(int id)
-	{
-		base.DeleteById(id);
-		return SearchEngine.SaveChangesAsync();
-	}
+    /// <summary>
+    /// 根据ID删除实体并保存(异步)
+    /// </summary>
+    /// <param name="id">实体id</param>
+    /// <returns>删除成功</returns>
+    public override Task<int> DeleteByIdAsync(int id)
+    {
+        base.DeleteById(id);
+        return SearchEngine.SaveChangesAsync();
+    }
 
-	/// <summary>
-	/// 删除多个实体并保存(异步)
-	/// </summary>
-	/// <param name="list">实体集合</param>
-	/// <returns>删除成功</returns>
-	public override Task<int> DeleteEntitiesSavedAsync(IEnumerable<Post> list)
-	{
-		base.DeleteEntities(list);
-		return SearchEngine.SaveChangesAsync();
-	}
+    /// <summary>
+    /// 删除多个实体并保存(异步)
+    /// </summary>
+    /// <param name="list">实体集合</param>
+    /// <returns>删除成功</returns>
+    public override Task<int> DeleteEntitiesSavedAsync(IEnumerable<Post> list)
+    {
+        base.DeleteEntities(list);
+        return SearchEngine.SaveChangesAsync();
+    }
 
-	/// <summary>
-	/// 根据条件删除实体
-	/// </summary>
-	/// <param name="where">查询条件</param>
-	/// <returns>删除成功</returns>
-	public override int DeleteEntitySaved(Expression<Func<Post, bool>> where)
-	{
-		base.DeleteEntity(where);
-		return SearchEngine.SaveChanges();
-	}
+    /// <summary>
+    /// 根据条件删除实体
+    /// </summary>
+    /// <param name="where">查询条件</param>
+    /// <returns>删除成功</returns>
+    public override int DeleteEntitySaved(Expression<Func<Post, bool>> where)
+    {
+        base.DeleteEntity(where);
+        return SearchEngine.SaveChanges();
+    }
 
-	/// <summary>
-	/// 删除实体并保存
-	/// </summary>
-	/// <param name="t">需要删除的实体</param>
-	/// <returns>删除成功</returns>
-	public override bool DeleteEntitySaved(Post t)
-	{
-		base.DeleteEntity(t);
-		return SearchEngine.SaveChanges() > 0;
-	}
+    /// <summary>
+    /// 删除实体并保存
+    /// </summary>
+    /// <param name="t">需要删除的实体</param>
+    /// <returns>删除成功</returns>
+    public override bool DeleteEntitySaved(Post t)
+    {
+        base.DeleteEntity(t);
+        return SearchEngine.SaveChanges() > 0;
+    }
 
-	/// <summary>
-	/// 根据条件删除实体
-	/// </summary>
-	/// <param name="where">查询条件</param>
-	/// <returns>删除成功</returns>
-	public override Task<int> DeleteEntitySavedAsync(Expression<Func<Post, bool>> where)
-	{
-		base.DeleteEntity(where);
-		return SearchEngine.SaveChangesAsync();
-	}
+    /// <summary>
+    /// 根据条件删除实体
+    /// </summary>
+    /// <param name="where">查询条件</param>
+    /// <returns>删除成功</returns>
+    public override Task<int> DeleteEntitySavedAsync(Expression<Func<Post, bool>> where)
+    {
+        base.DeleteEntity(where);
+        return SearchEngine.SaveChangesAsync();
+    }
 
-	/// <summary>
-	/// 统一保存的方法
-	/// </summary>
-	/// <returns>受影响的行数</returns>
-	public int SaveChanges(bool flushIndex)
-	{
-		return flushIndex ? SearchEngine.SaveChanges() : base.SaveChanges();
-	}
+    /// <summary>
+    /// 统一保存的方法
+    /// </summary>
+    /// <returns>受影响的行数</returns>
+    public int SaveChanges(bool flushIndex)
+    {
+        return flushIndex ? SearchEngine.SaveChanges() : base.SaveChanges();
+    }
 
-	/// <summary>
-	/// 统一保存数据
-	/// </summary>
-	/// <returns>受影响的行数</returns>
-	public async Task<int> SaveChangesAsync(bool flushIndex)
-	{
-		return flushIndex ? await SearchEngine.SaveChangesAsync() : await base.SaveChangesAsync();
-	}
-}
+    /// <summary>
+    /// 统一保存数据
+    /// </summary>
+    /// <returns>受影响的行数</returns>
+    public async Task<int> SaveChangesAsync(bool flushIndex)
+    {
+        return flushIndex ? await SearchEngine.SaveChangesAsync() : await base.SaveChangesAsync();
+    }
+}

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

@@ -68,7 +68,7 @@
         <PackageReference Include="OpenXmlPowerTools-NetStandard" Version="4.6.23-alpha4" />
         <PackageReference Include="MiniProfiler.EntityFrameworkCore" Version="4.2.22" />
         <PackageReference Include="PanGu.HighLight" Version="1.0.0" />
-        <PackageReference Include="SixLabors.ImageSharp.Web" Version="2.0.2" />
+        <PackageReference Include="SixLabors.ImageSharp.Web" Version="3.0.1" />
         <PackageReference Include="System.Linq.Dynamic.Core" Version="1.3.2" />
         <PackageReference Include="TimeZoneConverter" Version="6.1.0" />
         <PackageReference Include="WilderMinds.RssSyndication" Version="1.7.0" />