1. 程式人生 > >基於 abp vNext 和 .NET Core 開發部落格專案 - 部落格介面實戰篇(二)

基於 abp vNext 和 .NET Core 開發部落格專案 - 部落格介面實戰篇(二)

## 系列文章 1. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 使用 abp cli 搭建專案](https://www.cnblogs.com/meowv/p/12896177.html)** 2. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 給專案瘦身,讓它跑起來](https://www.cnblogs.com/meowv/p/12896898.html)** 3. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 完善與美化,Swagger登場](https://www.cnblogs.com/meowv/p/12909558.html)** 4. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 資料訪問和程式碼優先](https://www.cnblogs.com/meowv/p/12913676.html)** 5. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 自定義倉儲之增刪改查](https://www.cnblogs.com/meowv/p/12916613.html)** 6. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 統一規範API,包裝返回模型](https://www.cnblogs.com/meowv/p/12924409.html)** 7. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 再說Swagger,分組、描述、小綠鎖](https://www.cnblogs.com/meowv/p/12924859.html)** 8. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 接入GitHub,用JWT保護你的API](https://www.cnblogs.com/meowv/p/12935693.html)** 9. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 異常處理和日誌記錄](https://www.cnblogs.com/meowv/p/12943699.html)** 10. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 使用Redis快取資料](https://www.cnblogs.com/meowv/p/12956696.html)** 11. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 整合Hangfire實現定時任務處理](https://www.cnblogs.com/meowv/p/12961014.html)** 12. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 用AutoMapper搞定物件對映](https://www.cnblogs.com/meowv/p/12966092.html)** 13. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 定時任務最佳實戰(一)](https://www.cnblogs.com/meowv/p/12971041.html)** 14. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 定時任務最佳實戰(二)](https://www.cnblogs.com/meowv/p/12974439.html)** 15. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 定時任務最佳實戰(三)](https://www.cnblogs.com/meowv/p/12980301.html)** 16. **[基於 abp vNext 和 .NET Core 開發部落格專案 - 部落格介面實戰篇(一)](https://www.cnblogs.com/meowv/p/12987623.html)** --- [上篇文章](https://www.cnblogs.com/meowv/p/12987623.html)完成了兩個介面:文章列表頁、文章詳情頁,本篇繼續。 ## 分類列表 ![1](https://img2020.cnblogs.com/blog/891843/202005/891843-20200530205934508-1023085331.png) 分析:這裡多了一個統計文章數量的欄位,可以直接新建一個模型`QueryCategoryDto.cs`繼承`CategoryDto`。 ```CSharp //QueryCategoryDto.cs namespace Meowv.Blog.Application.Contracts.Blog { public class QueryCategoryDto : CategoryDto { /// /// 總數 /// public int Count { get; set; } } } ``` 新增查詢分類列表介面和快取介面。 ```CSharp //IBlogService.Category.cs using Meowv.Blog.Application.Contracts.Blog; using Meowv.Blog.ToolKits.Base; using System.Collections.Generic; using System.Threading.Tasks; namespace Meowv.Blog.Application.Blog { public partial interface IBlogService { /// /// 查詢分類列表 ///
/// Task>> QueryCategoriesAsync(); } } ``` ```CSharp //IBlogCacheService.Category.cs using Meowv.Blog.Application.Contracts.Blog; using Meowv.Blog.ToolKits.Base; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Meowv.Blog.Application.Caching.Blog { public partial interface IBlogCacheService { /// /// 查詢分類列表 ///
/// /// Task>> QueryCategoriesAsync(Func>>> factory); } } ``` 分別實現這兩個介面。 ```CSharp //BlogCacheService.Category.cs using Meowv.Blog.Application.Contracts.Blog; using Meowv.Blog.ToolKits.Base; using System; using System.Collections.Generic; using System.Threading.Tasks; using static Meowv.Blog.Domain.Shared.MeowvBlogConsts; namespace Meowv.Blog.Application.Caching.Blog.Impl { public partial class BlogCacheService { private const string KEY_QueryCategories = "Blog:Category:QueryCategories"; /// /// 查詢分類列表 ///
/// /// public async Task>> QueryCategoriesAsync(Func>>> factory) { return await Cache.GetOrAddAsync(KEY_QueryCategories, factory, CacheStrategy.ONE_DAY); } } } ``` ```CSharp //BlogService.Category.cs using Meowv.Blog.Application.Contracts.Blog; using Meowv.Blog.ToolKits.Base; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Meowv.Blog.Application.Blog.Impl { public partial class BlogService { /// /// 查詢分類列表 /// /// public async Task>> QueryCategoriesAsync() { return await _blogCacheService.QueryCategoriesAsync(async () => { var result = new ServiceResult>(); var list = from category in await _categoryRepository.GetListAsync() join posts in await _postRepository.GetListAsync() on category.Id equals posts.CategoryId group category by new { category.CategoryName, category.DisplayName } into g select new QueryCategoryDto { CategoryName = g.Key.CategoryName, DisplayName = g.Key.DisplayName, Count = g.Count() }; result.IsSuccess(list); return result; }); } } } ``` 快取就不說了,查詢分類列表,聯合查詢文章和分類兩張表,關聯欄位為CategoryId,然後分組,計算出對應的數量,在`BlogController`中新增API。 ```CSharp /// /// 查詢分類列表 /// /// [HttpGet] [Route("categories")] public async Task>> QueryCategoriesAsync() { return await _blogService.QueryCategoriesAsync(); } ``` ![2](https://img2020.cnblogs.com/blog/891843/202005/891843-20200530221606938-125033028.png) ## 標籤列表 ![3](https://img2020.cnblogs.com/blog/891843/202005/891843-20200530210035938-936314470.png) 分析:和分類列表差不多,新建模型`QueryTagDto.cs`繼承`TagDto`。 ```CSharp //QueryTagDto.cs namespace Meowv.Blog.Application.Contracts.Blog { public class QueryTagDto : TagDto { /// /// 總數 /// public int Count { get; set; } } } ``` 新增查詢標籤列表介面和快取介面。 ```CSharp //IBlogCacheService.Tag.cs using Meowv.Blog.Application.Contracts.Blog; using Meowv.Blog.ToolKits.Base; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Meowv.Blog.Application.Caching.Blog { public partial interface IBlogCacheService { /// /// 查詢標籤列表 /// /// /// Task>> QueryTagsAsync(Func>>> factory); } } ``` ```CSharp //IBlogService.Tag.cs using Meowv.Blog.Application.Contracts.Blog; using Meowv.Blog.ToolKits.Base; using System.Collections.Generic; using System.Threading.Tasks; namespace Meowv.Blog.Application.Blog { public partial interface IBlogService { /// /// 查詢標籤列表 /// /// Task>> QueryTagsAsync(); } } ``` 分別實現這兩個介面。 ```CSharp //BlogCacheService.Tag.cs using Meowv.Blog.Application.Contracts.Blog; using Meowv.Blog.ToolKits.Base; using System; using System.Collections.Generic; using System.Threading.Tasks; using static Meowv.Blog.Domain.Shared.MeowvBlogConsts; namespace Meowv.Blog.Application.Caching.Blog.Impl { public partial class BlogCacheService { private const string KEY_QueryTags = "Blog:Tag:QueryTags"; /// /// 查詢標籤列表 /// /// /// public async Task>> QueryTagsAsync(Func>>> factory) { return await Cache.GetOrAddAsync(KEY_QueryTags, factory, CacheStrategy.ONE_DAY); } } } ``` ```CSharp //BlogService.Tag.cs using Meowv.Blog.Application.Contracts.Blog; using Meowv.Blog.ToolKits.Base; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Meowv.Blog.Application.Blog.Impl { public partial class BlogService { /// /// 查詢標籤列表 /// /// public async Task>> QueryTagsAsync() { return await _blogCacheService.QueryTagsAsync(async () => { var result = new ServiceResult>(); var list = from tags in await _tagRepository.GetListAsync() join post_tags in await _postTagRepository.GetListAsync() on tags.Id equals post_tags.TagId group tags by new { tags.TagName, tags.DisplayName } into g select new QueryTagDto { TagName = g.Key.TagName, DisplayName = g.Key.DisplayName, Count = g.Count() }; result.IsSuccess(list); return result; }); } } } ``` 查詢標籤列表需要聯合查詢tags和post_tags,根據TagId進行關聯,然後分組從而獲取標籤下文章的總數,在`BlogController`中新增API。 ```CSharp /// /// 查詢標籤列表 /// /// [HttpGet] [Route("tags")] public async Task>> QueryTagsAsync() { return await _blogService.QueryTagsAsync(); } ``` ![4](https://img2020.cnblogs.com/blog/891843/202005/891843-20200531203709733-747030904.png) ## 分類名稱&文章列表 ![5](https://img2020.cnblogs.com/blog/891843/202005/891843-20200531204711870-406154660.png) 分析:此頁面下包含兩個介面,查詢分類的名稱和當前分類下的文章列表,和文章列表不同的是,它不帶分頁。分類包含兩個欄位,分類名稱和展示名稱,我們要把真正的名稱查詢出來展示在頁面上。 ### 分類名稱 不需要給他新增返回模型,直接返回一個string型別即可,同時給一個查詢引數name,新增獲取分類名稱介面和快取介面。 ```CSharp //IBlogService.Category.cs /// /// 獲取分類名稱 /// /// /// Task> GetCategoryAsync(string name); ``` ```CSharp //IBlogCacheService.Category.cs /// /// 獲取分類名稱 /// /// /// /// Task> GetCategoryAsync(string name, Func>> factory); ``` 實現這兩個介面。 ```CSharp //BlogCacheService.Category.cs ... public partial class BlogCacheService { private const string KEY_GetCategory = "Blog:Category:GetCategory-{0}"; /// /// 獲取分類名稱 /// /// /// /// public async Task> GetCategoryAsync(string name, Func>> factory) { return await Cache.GetOrAddAsync(KEY_GetCategory.FormatWith(name), factory, CacheStrategy.ONE_DAY); } } ... ``` ```CSharp //BlogService.Category.cs /// /// 獲取分類名稱 /// /// /// public async Task> GetCategoryAsync(string name) { return await _blogCacheService.GetCategoryAsync(name, async () => { var result = new ServiceResult(); var category = await _categoryRepository.FindAsync(x => x.DisplayName.Equals(name)); if (null == category) { result.IsFailed(ResponseText.WHAT_NOT_EXIST.FormatWith("分類", name)); return result; } result.IsSuccess(category.CategoryName); return result; }); } ``` `FormatWith()`是擴充套件方法,`ResponseText.WHAT_NOT_EXIST`是之前說過的常量,直接查詢是否存在當前name的分類,如果不存在給出錯誤提示,存在的話,則只返回分類名稱,在`BlogController`中新增API。 ```CSharp /// /// 獲取分類名稱 /// /// /// [HttpGet] [Route("category")] public async Task> GetCategoryAsync(([Required] string name) { return await _blogService.GetCategoryAsync(name); } ``` `[Required]`Attribute 指定引數name必填。 ![6](https://img2020.cnblogs.com/blog/891843/202005/891843-20200531211747782-1793939137.png) ![7](https://img2020.cnblogs.com/blog/891843/202005/891843-20200531211754464-1880035472.png) ### 文章列表 通過分類名稱查詢文章列表和分頁查詢文章列表返回模型是一樣的,只是不用分頁,所以直接返回一個列表就可以了,新增通過分類名稱查詢文章列表和快取的介面。 ```CSharp //IBlogService.Post.cs /// /// 通過分類名稱查詢文章列表 /// /// /// Task>> QueryPostsByCategoryAsync(string name); ``` ```CSharp //IBlogCacheService.Post.cs /// /// 通過分類名稱查詢文章列表 /// /// /// /// Task>> QueryPostsByCategoryAsync(string name, Func>>> factory); ``` 分別實現這兩個介面。 ```CSharp //BlogCacheService.Post.cs ... public partial class BlogCacheService { private const string KEY_QueryPostsByCategory = "Blog:Post:QueryPostsByCategory-{0}"; /// /// 通過分類名稱查詢文章列表 /// /// /// /// public async Task>> QueryPostsByCategoryAsync(string name, Func>>> factory) { return await Cache.GetOrAddAsync(KEY_QueryPostsByCategory.FormatWith(name), factory, CacheStrategy.ONE_DAY); } } ... ``` ```CSharp //BlogService.Post.cs /// /// 通過分類名稱查詢文章列表 /// /// /// public async Task>> QueryPostsByCategoryAsync(string name) { return await _blogCacheService.QueryPostsByCategoryAsync(name, async () => { var result = new ServiceResult>(); var list = (from posts in await _postRepository.GetListAsync() join categories in await _categoryRepository.GetListAsync() on posts.CategoryId equals categories.Id where categories.DisplayName.Equals(name) orderby posts.CreationTime descending select new PostBriefDto { Title = posts.Title, Url = posts.Url, Year = posts.CreationTime.Year, CreationTime = posts.CreationTime.TryToDateTime() }) .GroupBy(x => x.Year) .Select(x => new QueryPostDto { Year = x.Key, Posts = x.ToList() }); result.IsSuccess(list); return result; }); } ``` 這個邏輯和分頁查詢文章列表是差不多的,聯合查詢文章表和分類表,關聯欄位為CategoryId,指定查詢條件`categories.DisplayName==name`,以CreationTime倒序排序,年份分組,篩選出所需欄位返回,在`BlogController`中新增API。 ```CSharp /// /// 通過分類名稱查詢文章列表 /// /// /// [HttpGet] [Route("posts/category")] public async Task>> QueryPostsByCategoryAsync([Required] string name) { return await _blogService.QueryPostsByCategoryAsync(name); } ``` ![8](https://img2020.cnblogs.com/blog/891843/202005/891843-20200531220321132-1666394468.png) ## 標籤名稱&文章列表 ![9](https://img2020.cnblogs.com/blog/891843/202005/891843-20200531204717274-1679439636.png) 分析:此頁面和分類頁一樣,包含兩個介面,查詢標籤的名稱和當前標籤下的文章列表。 ### 標籤名稱 新增獲取標籤名稱介面和快取介面,`GetTagAsync()`。 ```CSharp //IBlogService.Tag.cs /// /// 獲取標籤名稱 /// /// /// Task> GetTagAsync(string name); ``` ```CSharp //IBlogCacheService.Tag.cs /// /// 獲取標籤名稱 /// /// /// /// Task> GetTagAsync(string name, Func>> factory); ``` 實現這兩個介面。 ```CSharp //BlogCacheService.Tag.cs ... public partial class BlogCacheService { private const string KEY_GetTag = "Blog:Tag:GetTag-{0}"; /// /// 獲取標籤名稱 /// /// /// /// public async Task> GetTagAsync(string name, Func>> factory) { return await Cache.GetOrAddAsync(KEY_GetTag.FormatWith(name), factory, CacheStrategy.ONE_DAY); } } ... ``` ```CSharp //BlogService.Tag.cs /// /// 獲取標籤名稱 /// /// /// public async Task> GetTagAsync(string name) { return await _blogCacheService.GetTagAsync(name, async () => { var result = new ServiceResult(); var tag = await _tagRepository.FindAsync(x => x.DisplayName.Equals(name)); if (null == tag) { result.IsFailed(ResponseText.WHAT_NOT_EXIST.FormatWith("標籤", name)); return result; } result.IsSuccess(tag.TagName); return result; }); } ``` `FormatWith()`是擴充套件方法,`ResponseText.WHAT_NOT_EXIST`是之前說過的常量,直接查詢是否存在當前name的分類,如果不存在給出錯誤提示,存在的話,則只返回分類名稱,在`BlogController`中新增API。 ```CSharp /// /// 獲取標籤名稱 /// /// /// [HttpGet] [Route("tag")] public async Task> GetTagAsync(string name) { return await _blogService.GetTagAsync(name); } ``` `[Required]`Attribute 指定引數name必填。 ![10](https://img2020.cnblogs.com/blog/891843/202006/891843-20200601160443559-1169185098.png) ![11](https://img2020.cnblogs.com/blog/891843/202006/891843-20200601160449415-1542518908.png) ### 文章列表 和上面一模一樣的,新增通過標籤名稱查詢文章列表介面和快取介面。 ```CSharp //IBlogService.Post.cs /// /// 通過標籤名稱查詢文章列表 /// /// /// Task>> QueryPostsByTagAsync(string name); ``` ```CSharp //IBlogCacheService.Post.cs /// /// 通過標籤名稱查詢文章列表 /// /// /// /// Task>> QueryPostsByTagAsync(string name, Func>>> factory); ``` 分別實現這兩個介面。 ```CSharp //BlogCacheService.Post.cs ... public partial class BlogCacheService { private const string KEY_QueryPostsByTag = "Blog:Post:QueryPostsByTag-{0}"; /// /// 通過標籤名稱查詢文章列表 /// /// /// /// public async Task>> QueryPostsByTagAsync(string name, Func>>> factory) { return await Cache.GetOrAddAsync(KEY_QueryPostsByTag.FormatWith(name), factory, CacheStrategy.ONE_DAY); } } ... ``` ```CSharp //BlogService.Post.cs /// /// 通過標籤名稱查詢文章列表 /// /// /// public async Task>> QueryPostsByTagAsync(string name) { return await _blogCacheService.QueryPostsByTagAsync(name, async () => { var result = new ServiceResult>(); var list = (from post_tags in await _postTagRepository.GetListAsync() join tags in await _tagRepository.GetListAsync() on post_tags.TagId equals tags.Id join posts in await _postRepository.GetListAsync() on post_tags.PostId equals posts.Id where tags.DisplayName.Equals(name) orderby posts.CreationTime descending select new PostBriefDto { Title = posts.Title, Url = posts.Url, Year = posts.CreationTime.Year, CreationTime = posts.CreationTime.TryToDateTime() }) .GroupBy(x => x.Year) .Select(x => new QueryPostDto { Year = x.Key, Posts = x.ToList() }); result.IsSuccess(list); return result; }); } ``` 這個查詢有點特殊,聯合查詢了3張表,先查post_tags和tags,關聯欄位TagId,再根據PostId查詢posts,指定查詢條件tags.DisplayName==name,以CreationTime倒序排序,年份分組,篩選出所需欄位返回,在BlogController中新增API。 ```CSharp /// /// 通過標籤名稱查詢文章列表 /// /// /// [HttpGet] [Route("posts/tag")] public async Task>> QueryPostsByTagAsync(string name) { return await _blogService.QueryPostsByTagAsync(name); } ``` ![12](https://img2020.cnblogs.com/blog/891843/202006/891843-20200601163237731-1951619840.png) 至此,基本上完成了部落格前端所需的所有查詢介面,就還剩下友鏈的查詢,大家可以自己完成,後面如果需要什麼新的介面再回頭來寫就好了。 開源地址:https://github.com/Meowv/Blog/tree/blog_tutorial --- **搭配下方課程學習更佳 ↓ ↓ ↓** [http://gk.link/a/10iQ7](http://gk.link/a/10iQ7) ![7](https://img2020.cnblogs.com/blog/891843/202006/891843-20200603101718800-1586400