懒得勤快 3 år sedan
förälder
incheckning
14586de906

+ 2 - 1
src/Masuit.MyBlogs.Core/Configs/MappingProfile.cs

@@ -16,6 +16,7 @@ namespace Masuit.MyBlogs.Core.Configs
         public MappingProfile()
         {
             CreateMap<Category, CategoryCommand>().ReverseMap();
+            CreateMap<Category, CategoryDto_P>().ReverseMap();
             CreateMap<Category, CategoryDto>().ForMember(c => c.TotalPostCount, e => e.MapFrom(c => c.Post.Count)).ForMember(c => c.PendedPostCount, e => e.MapFrom(c => c.Post.Count(p => p.Status == Status.Published))).ReverseMap();
             CreateMap<CategoryCommand, CategoryDto>().ReverseMap();
 
@@ -43,7 +44,7 @@ namespace Masuit.MyBlogs.Core.Configs
             CreateMap<PostCommand, Post>().ReverseMap();
             CreateMap<Post, PostModelBase>();
             CreateMap<Post, PostHistoryVersion>().ForMember(p => p.Id, e => e.Ignore()).ForMember(v => v.PostId, e => e.MapFrom(p => p.Id));
-            CreateMap<Post, PostDto>().ForMember(p => p.CategoryName, e => e.MapFrom(p => p.Category.Name)).ForMember(p => p.LimitMode, e => e.MapFrom(p => p.LimitMode ?? RegionLimitMode.All)).ReverseMap();
+            CreateMap<Post, PostDto>().ForMember(p => p.CategoryName, e => e.MapFrom(p => p.Category.Name)).ForMember(p => p.LimitMode, e => e.MapFrom(p => p.LimitMode ?? RegionLimitMode.All)).ForMember(p => p.Category, e => e.Ignore()).ReverseMap();
             CreateMap<PostCommand, PostDto>().ReverseMap();
             CreateMap<PostHistoryVersion, PostDto>().ForMember(p => p.CategoryName, e => e.MapFrom(p => p.Category.Name)).ReverseMap();
             CreateMap<Post, PostViewModel>().ForMember(p => p.CategoryName, e => e.MapFrom(p => p.Category.Name)).ForMember(p => p.PostDate, e => e.MapFrom(p => p.PostDate.ToString("yyyy-MM-dd HH:mm:ss"))).ForMember(p => p.ModifyDate, e => e.MapFrom(p => p.ModifyDate.ToString("yyyy-MM-dd HH:mm:ss"))).ReverseMap();

+ 13 - 3
src/Masuit.MyBlogs.Core/Infrastructure/Services/PostService.cs

@@ -1,5 +1,9 @@
 using AngleSharp;
+using AngleSharp.Dom;
+using AngleSharp.Html.Parser;
+using AutoMapper;
 using CacheManager.Core;
+using EFCoreSecondLevelCacheInterceptor;
 using Masuit.LuceneEFCore.SearchEngine;
 using Masuit.LuceneEFCore.SearchEngine.Interfaces;
 using Masuit.MyBlogs.Core.Infrastructure.Repository.Interface;
@@ -10,13 +14,12 @@ using Masuit.MyBlogs.Core.Models.Enum;
 using Masuit.MyBlogs.Core.Models.ViewModel;
 using Masuit.Tools;
 using Masuit.Tools.Html;
+using Microsoft.EntityFrameworkCore;
 using PanGu;
 using PanGu.HighLight;
 using System.Linq.Expressions;
 using System.Reflection;
 using System.Text.RegularExpressions;
-using AngleSharp.Dom;
-using AngleSharp.Html.Parser;
 
 namespace Masuit.MyBlogs.Core.Infrastructure.Services
 {
@@ -24,11 +27,15 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
     {
         private readonly ICacheManager<SearchResult<PostDto>> _cacheManager;
         private readonly ICacheManager<Dictionary<string, int>> _tagCacheManager;
+        private readonly ICategoryRepository _categoryRepository;
+        private readonly IMapper _mapper;
 
-        public PostService(IPostRepository repository, ISearchEngine<DataContext> searchEngine, ILuceneIndexSearcher searcher, ICacheManager<SearchResult<PostDto>> cacheManager, ICacheManager<Dictionary<string, int>> tagCacheManager) : base(repository, searchEngine, searcher)
+        public PostService(IPostRepository repository, ISearchEngine<DataContext> searchEngine, ILuceneIndexSearcher searcher, ICacheManager<SearchResult<PostDto>> cacheManager, ICacheManager<Dictionary<string, int>> tagCacheManager, ICategoryRepository categoryRepository, IMapper mapper) : base(repository, searchEngine, searcher)
         {
             _cacheManager = cacheManager;
             _tagCacheManager = tagCacheManager;
+            _categoryRepository = categoryRepository;
+            _mapper = mapper;
         }
 
         /// <summary>
@@ -98,6 +105,9 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
                 var highlighter = new Highlighter(simpleHtmlFormatter, new Segment()) { FragmentSize = 200 };
                 var keywords = Searcher.CutKeywords(keyword);
                 HighlightSegment(posts, keywords, highlighter);
+                var cids = posts.Select(p => p.CategoryId).Distinct().ToArray();
+                var categories = _categoryRepository.GetQuery(c => cids.Contains(c.Id)).Include(c => c.Parent).Cacheable().ToDictionary(c => c.Id);
+                posts.ForEach(p => p.Category = _mapper.Map<CategoryDto_P>(categories[p.CategoryId]));
                 return new SearchResult<PostDto>()
                 {
                     Results = posts,

+ 19 - 20
src/Masuit.MyBlogs.Core/Models/DTO/CategoryDto.cs

@@ -1,31 +1,30 @@
 using Masuit.Tools.Models;
 
-namespace Masuit.MyBlogs.Core.Models.DTO
+namespace Masuit.MyBlogs.Core.Models.DTO;
+
+/// <summary>
+/// 文章分类输出模型
+/// </summary>
+public class CategoryDto : BaseDto, ITreeChildren<CategoryDto>
 {
     /// <summary>
-    /// 文章分类输出模型
+    /// 分类名
     /// </summary>
-    public class CategoryDto : BaseDto, ITreeChildren<CategoryDto>
-    {
-        /// <summary>
-        /// 分类名
-        /// </summary>
-        public string Name { get; set; }
+    public string Name { get; set; }
 
-        /// <summary>
-        /// 分类描述
-        /// </summary>
-        public string Description { get; set; }
+    /// <summary>
+    /// 分类描述
+    /// </summary>
+    public string Description { get; set; }
 
-        public int? ParentId { get; set; }
+    public int? ParentId { get; set; }
 
-        public virtual int TotalPostCount { get; set; }
+    public virtual int TotalPostCount { get; set; }
 
-        public virtual int PendedPostCount { get; set; }
+    public virtual int PendedPostCount { get; set; }
 
-        /// <summary>
-        /// 子级
-        /// </summary>
-        public ICollection<CategoryDto> Children { get; set; }
-    }
+    /// <summary>
+    /// 子级
+    /// </summary>
+    public ICollection<CategoryDto> Children { get; set; }
 }

+ 24 - 0
src/Masuit.MyBlogs.Core/Models/DTO/CategoryDto_P.cs

@@ -0,0 +1,24 @@
+using Masuit.Tools.Models;
+
+namespace Masuit.MyBlogs.Core.Models.DTO;
+
+/// <summary>
+/// 文章分类输出模型
+/// </summary>
+public class CategoryDto_P : BaseDto, ITreeParent<CategoryDto_P>
+{
+    /// <summary>
+    /// 分类名
+    /// </summary>
+    public string Name { get; set; }
+
+    /// <summary>
+    /// 分类描述
+    /// </summary>
+    public string Description { get; set; }
+
+    /// <summary>
+    /// 父节点
+    /// </summary>
+    public CategoryDto_P Parent { get; set; }
+}

+ 2 - 0
src/Masuit.MyBlogs.Core/Models/DTO/PostDto.cs

@@ -161,5 +161,7 @@ namespace Masuit.MyBlogs.Core.Models.DTO
         /// 跳转到第三方链接
         /// </summary>
         public string Redirect { get; set; }
+
+        public CategoryDto_P Category { get; set; }
     }
 }

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Post/CategoryPath.cshtml

@@ -1,6 +1,6 @@
 @model Masuit.MyBlogs.Core.Models.Entity.Category
 @if(Model.ParentId>0) {
     await Html.RenderPartialAsync("CategoryPath", Model.Parent);
-    <text> / </text>
+    <text>/</text>
 }
 <a asp-controller="Home" asp-action="Category" asp-route-id="@Model.Id" class="label label-info">@Model.Name</a>

+ 6 - 0
src/Masuit.MyBlogs.Core/Views/Search/CategoryPath.cshtml

@@ -0,0 +1,6 @@
+@model Masuit.MyBlogs.Core.Models.DTO.CategoryDto_P
+@if(Model.Parent!=null) {
+    await Html.RenderPartialAsync("CategoryPath", Model.Parent);
+    <text>/</text>
+}
+<a asp-controller="Home" asp-action="Category" asp-route-id="@Model.Id" class="label label-info">@Model.Name</a>

+ 149 - 146
src/Masuit.MyBlogs.Core/Views/Search/Search.cshtml

@@ -6,154 +6,157 @@
 @using Microsoft.AspNetCore.Mvc.Rendering
 @model SearchResult<Masuit.MyBlogs.Core.Models.DTO.PostDto>
 @{
-    ViewBag.Title = "站内搜索:" + ViewBag.Keyword;
-    Layout = "~/Views/Shared/_Layout.cshtml";
-    List<KeywordsRank> hotSearches = ViewBag.hotSearches;
-    List<string> relateKeywords = ViewBag.RelateKeywords;
-    Advertisement ad = ViewBag.Ads;
+	ViewBag.Title = "站内搜索:" + ViewBag.Keyword;
+	Layout = "~/Views/Shared/_Layout.cshtml";
+	List<KeywordsRank> hotSearches = ViewBag.hotSearches;
+	List<string> relateKeywords = ViewBag.RelateKeywords;
+	Advertisement ad = ViewBag.Ads;
 }
 <div class="container min-height610">
-    <ol class="cd-breadcrumb triangle">
-        <li><a asp-controller="Home" asp-action="Index">首页</a></li>
-        <li class="current"><em>@ViewBag.Title</em></li>
-    </ol>
-    <div class="wrapper wrapper-content animated fadeInRight">
-        <div class="row">
-            <div class="col-sm-12">
-                @if (!string.IsNullOrEmpty(ViewBag.Keyword))
-                {
-                    <small>搜索用时(@Model.Elapsed 毫秒)</small>
-                }
-                <div class="search-form">
-                    <form action="/search" method="get">
-                        <div class="input-group">
-                            <input type="text" placeholder="你要查找的关键词,支持部分指令:intitle,content,如:intitle:会声会影 content:懒得勤快,指令支持组合" id="search" name="wd" maxlength="32" value="@ViewBag.Keyword" class="form-control input-lg">
-                            <div class="input-group-btn">
-                                <button class="btn btn-lg btn-primary" type="submit">搜索</button>
-                            </div>
-                        </div>
-                    </form>
-                    @if (hotSearches.Any())
-                    {
-                        <span>搜索推荐:</span>
-                        foreach (var item in hotSearches)
-                        {
-                            @Html.ActionLink(item.Keywords + "(" + item.Count + ")", "Search", new { wd = item.Keywords }, new { @class = "label label-danger" }) <span>  </span>
-                        }
-                    }
-                </div>
-                @if (Model.Results.Any())
-                {
-                    <div class="hr-line-dashed"></div>
-                    var rnd = new Random().Next(Model.Results.Count > 2 ? 2 : 1, Model.Results.Count);
-                    for (var i = 0; i < Model.Results.Count; i++)
-                    {
-                        if (rnd > 1 && rnd == i && ad != null)
-                        {
-                            await Html.RenderPartialAsync("_ArticleListAdvertisement", ad);
-                        }
+	<ol class="cd-breadcrumb triangle">
+		<li><a asp-controller="Home" asp-action="Index">首页</a></li>
+		<li class="current"><em>@ViewBag.Title</em></li>
+	</ol>
+	<div class="wrapper wrapper-content animated fadeInRight">
+		<div class="row">
+			<div class="col-sm-12">
+				@if (!string.IsNullOrEmpty(ViewBag.Keyword))
+				{
+					<small>搜索用时(@Model.Elapsed 毫秒)</small>
+				}
+				<div class="search-form">
+					<form action="/search" method="get">
+						<div class="input-group">
+							<input type="text" placeholder="你要查找的关键词,支持部分指令:intitle,content,如:intitle:会声会影 content:懒得勤快,指令支持组合" id="search" name="wd" maxlength="32" value="@ViewBag.Keyword" class="form-control input-lg">
+							<div class="input-group-btn">
+								<button class="btn btn-lg btn-primary" type="submit">搜索</button>
+							</div>
+						</div>
+					</form>
+					@if (hotSearches.Any())
+					{
+						<span>搜索推荐:</span>
+						foreach (var item in hotSearches)
+						{
+							@Html.ActionLink(item.Keywords + "(" + item.Count + ")", "Search", new { wd = item.Keywords }, new { @class = "label label-danger" }) <span>  </span>
+						}
+					}
+				</div>
+				@if (Model.Results.Any())
+				{
+					<div class="hr-line-dashed"></div>
+					var rnd = new Random().Next(Model.Results.Count > 2 ? 2 : 1, Model.Results.Count);
+					for (var i = 0; i < Model.Results.Count; i++)
+					{
+						if (rnd > 1 && rnd == i && ad != null)
+						{
+							await Html.RenderPartialAsync("_ArticleListAdvertisement", ad);
+						}
 
-                        var p = Model.Results[i];
-                        <div class="ibox">
-                            <div class="ibox-content">
-                                <a asp-controller="Post" asp-action="Details" asp-route-id="@p.Id" asp-route-kw="@ViewBag.Keyword" target="_blank">
-                                    <h2 class="size20 margin-bot10">
-                                        @Html.Raw(p.Title)
-                                    </h2>
-                                </a>
-                                <div>
-                                    作者:
-                                    <label class="label label-info">@Html.Raw(p.Author)</label> | 发表时间:<a asp-controller="Home" asp-action="Archieve" asp-route-yyyy="@p.ModifyDate.Year" asp-route-mm="@p.ModifyDate.Month" asp-route-dd="@p.ModifyDate.Day" asp-route-mode="@nameof(p.ModifyDate)"><time>@(p.ModifyDate.ToTimeZoneF(Context.Session.Get<string>(SessionKey.TimeZone)))</time></a> | 分类:<a asp-controller="Home" asp-action="Category" asp-route-id="@p.CategoryId">@p.CategoryName</a>
-                                </div>
-                                <p class="size14">@Html.Raw(p.Content)</p>
-                                <div class="row">
-                                    <div class="col-md-6">
-                                        <a asp-controller="Post" asp-action="Details" asp-route-id="@p.Id" class="search-link" target="_blank">@(Context.Request.Scheme + "://" + Context.Request.Host + "/" + p.Id)</a>
-                                    </div>
-                                </div>
-                            </div>
-                        </div>
-                    }
-                }
-                @if(relateKeywords?.Count>0) {
-                    <div class="margin-bot10">
-                        <h3>相关搜索:</h3>
-                        @foreach (var kw in relateKeywords)
-                        {
-                            <a asp-action="Search" asp-route-wd="@kw">@kw</a><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
-                        }
-                    </div>
-                }
-                @if (!string.IsNullOrEmpty((string)ViewBag.ErrorMsg))
-                {
-                    <h3>@ViewBag.ErrorMsg</h3>
-                }
+						var p = Model.Results[i];
+						<div class="ibox">
+							<div class="ibox-content">
+								<a asp-controller="Post" asp-action="Details" asp-route-id="@p.Id" asp-route-kw="@ViewBag.Keyword" target="_blank">
+									<h2 class="size20 margin-bot10">
+										@Html.Raw(p.Title)
+									</h2>
+								</a>
+								<div>
+									作者:
+									<label class="label label-info">@Html.Raw(p.Author)</label> | 发表时间:<a asp-controller="Home" asp-action="Archieve" asp-route-yyyy="@p.ModifyDate.Year" asp-route-mm="@p.ModifyDate.Month" asp-route-dd="@p.ModifyDate.Day" asp-route-mode="@nameof(p.ModifyDate)"><time>@(p.ModifyDate.ToTimeZoneF(Context.Session.Get<string>(SessionKey.TimeZone)))</time></a> | 分类:
+									@{
+										await Html.RenderPartialAsync("CategoryPath", p.Category);
+									}
+								</div>
+								<p class="size14">@Html.Raw(p.Content)</p>
+								<div class="row">
+									<div class="col-md-6">
+										<a asp-controller="Post" asp-action="Details" asp-route-id="@p.Id" class="search-link" target="_blank">@(Context.Request.Scheme + "://" + Context.Request.Host + "/" + p.Id)</a>
+									</div>
+								</div>
+							</div>
+						</div>
+					}
+				}
+				@if(relateKeywords?.Count>0) {
+					<div class="margin-bot10">
+						<h3>相关搜索:</h3>
+						@foreach (var kw in relateKeywords)
+						{
+							<a asp-action="Search" asp-route-wd="@kw">@kw</a><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
+						}
+					</div>
+				}
+				@if (!string.IsNullOrEmpty((string)ViewBag.ErrorMsg))
+				{
+					<h3>@ViewBag.ErrorMsg</h3>
+				}
 
-                @*分页组件*@
-                @{
-                    int size = ViewBag.PageSize;
-                    int pages = Math.Ceiling(Model.Total / (size > 0 ? size : 10.0)).ToInt32();
-                    var page = Context.Request.Query["page"].ToString().ToInt32();
-                    int current = page > 0 ? page : 1;
-                    int pageStart = current - 3 > 0 ? current - 3 : 1;
-                    int pageEnd = current + 3 >= pages ? pages : current + 3;
-                    if (pageEnd - pageStart < 7)
-                    {
-                        if (pageStart == 1)
-                        {
-                            pageEnd = pageStart + 4 >= pages ? pages : pageStart + 4;
-                        }
-                        else
-                        {
-                            pageStart = pageEnd - 4 > 0 ? pageEnd - 4 : 1;
-                        }
-                    }
-                }
-                @if (pages > 1)
-                {
-                    <ul class="pagination margin-clear">
-                        @if (current == 1)
-                        {
-                            <li class="disabled"><a>首页</a></li>
-                            <li class="disabled"><a aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
-                        }
-                        else
-                        {
-                            <li><a asp-action="Search" asp-route-wd="@ViewBag.Keyword" aria-label="Previous">首页</a></li>
-                            <li><a asp-action="Search" asp-route-wd="@ViewBag.Keyword" asp-route-page="@(page - 1 <= 0 ? 1 : page - 1)" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
-                        }
-                        @if (pageStart > 1)
-                        {
-                            <li class="disabled"><a aria-label="Previous"><span aria-hidden="true">...</span></a></li>
-                        }
-                        @for (int i = pageStart; i <= pageEnd; i++)
-                        {
-                            if (i == current)
-                            {
-                                <li class="disabled"><a>@i</a></li>
-                            }
-                            else
-                            {
-                                <li><a asp-action="Search" asp-route-page="@i" asp-route-wd="@ViewBag.Keyword">@i</a></li>
-                            }
-                        }
-                        @if (pageEnd < pages)
-                        {
-                            <li class="disabled"><a aria-label="Previous"><span aria-hidden="true">...</span></a></li>
-                        }
-                        @if (current == pages)
-                        {
-                            <li class="disabled"><a><span aria-hidden="true">&raquo;</span></a></li>
-                            <li class="disabled"><a>最后一页</a></li>
-                        }
-                        else
-                        {
-                            <li><a asp-action="Search" asp-route-page="@(current + 1)" asp-route-wd="@ViewBag.Keyword" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>
-                            <li><a asp-action="Search" asp-route-page="@pages" asp-route-wd="@ViewBag.Keyword" aria-label="Next">最后一页</a></li>
-                        }
-                    </ul>
-                }
-            </div>
-        </div>
-    </div>
+				@*分页组件*@
+				@{
+					int size = ViewBag.PageSize;
+					int pages = Math.Ceiling(Model.Total / (size > 0 ? size : 10.0)).ToInt32();
+					var page = Context.Request.Query["page"].ToString().ToInt32();
+					int current = page > 0 ? page : 1;
+					int pageStart = current - 3 > 0 ? current - 3 : 1;
+					int pageEnd = current + 3 >= pages ? pages : current + 3;
+					if (pageEnd - pageStart < 7)
+					{
+						if (pageStart == 1)
+						{
+							pageEnd = pageStart + 4 >= pages ? pages : pageStart + 4;
+						}
+						else
+						{
+							pageStart = pageEnd - 4 > 0 ? pageEnd - 4 : 1;
+						}
+					}
+				}
+				@if (pages > 1)
+				{
+					<ul class="pagination margin-clear">
+						@if (current == 1)
+						{
+							<li class="disabled"><a>首页</a></li>
+							<li class="disabled"><a aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
+						}
+						else
+						{
+							<li><a asp-action="Search" asp-route-wd="@ViewBag.Keyword" aria-label="Previous">首页</a></li>
+							<li><a asp-action="Search" asp-route-wd="@ViewBag.Keyword" asp-route-page="@(page - 1 <= 0 ? 1 : page - 1)" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
+						}
+						@if (pageStart > 1)
+						{
+							<li class="disabled"><a aria-label="Previous"><span aria-hidden="true">...</span></a></li>
+						}
+						@for (int i = pageStart; i <= pageEnd; i++)
+						{
+							if (i == current)
+							{
+								<li class="disabled"><a>@i</a></li>
+							}
+							else
+							{
+								<li><a asp-action="Search" asp-route-page="@i" asp-route-wd="@ViewBag.Keyword">@i</a></li>
+							}
+						}
+						@if (pageEnd < pages)
+						{
+							<li class="disabled"><a aria-label="Previous"><span aria-hidden="true">...</span></a></li>
+						}
+						@if (current == pages)
+						{
+							<li class="disabled"><a><span aria-hidden="true">&raquo;</span></a></li>
+							<li class="disabled"><a>最后一页</a></li>
+						}
+						else
+						{
+							<li><a asp-action="Search" asp-route-page="@(current + 1)" asp-route-wd="@ViewBag.Keyword" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>
+							<li><a asp-action="Search" asp-route-page="@pages" asp-route-wd="@ViewBag.Keyword" aria-label="Next">最后一页</a></li>
+						}
+					</ul>
+				}
+			</div>
+		</div>
+	</div>
 </div>

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

@@ -71,7 +71,7 @@
 	$http.get("/category/getcategories").then(function (res) {
 		var data = res.data;
 		if (data.Success) {
-			data.Data=[{name:"全部",Id:"",children:[]}].concat(data.Data);
+			data.Data=[{Name:"全部",Id:"",Children:[]}].concat(data.Data);
 			var params = JSON.parse(localStorage.getItem("postlist-params"));
 			if (params) {
 				$scope.kw = params["kw"];