系列文章
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 使用 abp cli 搭建專案
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 給專案瘦身,讓它跑起來
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 完善與美化,Swagger登場
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 資料訪問和程式碼優先
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 自定義倉儲之增刪改查
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 統一規範API,包裝返回模型
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 再說Swagger,分組、描述、小綠鎖
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 接入GitHub,用JWT保護你的API
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 異常處理和日誌記錄
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 使用Redis快取資料
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 整合Hangfire實現定時任務處理
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 用AutoMapper搞定物件對映
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 定時任務最佳實戰(一)
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 定時任務最佳實戰(二)
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 定時任務最佳實戰(三)
- 基於 abp vNext 和 .NET Core 開發部落格專案 - 部落格介面實戰篇(一)
上篇文章完成了兩個介面:文章列表頁、文章詳情頁,本篇繼續。
分類列表
分析:這裡多了一個統計文章數量的欄位,可以直接新建一個模型QueryCategoryDto.cs
繼承CategoryDto
。
//QueryCategoryDto.cs
namespace Meowv.Blog.Application.Contracts.Blog
{
public class QueryCategoryDto : CategoryDto
{
/// <summary>
/// 總數
/// </summary>
public int Count { get; set; }
}
}
新增查詢分類列表介面和快取介面。
//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
{
/// <summary>
/// 查詢分類列表
/// </summary>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryCategoryDto>>> QueryCategoriesAsync();
}
}
//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
{
/// <summary>
/// 查詢分類列表
/// </summary>
/// <param name="factory"></param>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryCategoryDto>>> QueryCategoriesAsync(Func<Task<ServiceResult<IEnumerable<QueryCategoryDto>>>> factory);
}
}
分別實現這兩個介面。
//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";
/// <summary>
/// 查詢分類列表
/// </summary>
/// <param name="factory"></param>
/// <returns></returns>
public async Task<ServiceResult<IEnumerable<QueryCategoryDto>>> QueryCategoriesAsync(Func<Task<ServiceResult<IEnumerable<QueryCategoryDto>>>> factory)
{
return await Cache.GetOrAddAsync(KEY_QueryCategories, factory, CacheStrategy.ONE_DAY);
}
}
}
//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
{
/// <summary>
/// 查詢分類列表
/// </summary>
/// <returns></returns>
public async Task<ServiceResult<IEnumerable<QueryCategoryDto>>> QueryCategoriesAsync()
{
return await _blogCacheService.QueryCategoriesAsync(async () =>
{
var result = new ServiceResult<IEnumerable<QueryCategoryDto>>();
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。
/// <summary>
/// 查詢分類列表
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("categories")]
public async Task<ServiceResult<IEnumerable<QueryCategoryDto>>> QueryCategoriesAsync()
{
return await _blogService.QueryCategoriesAsync();
}
標籤列表
分析:和分類列表差不多,新建模型QueryTagDto.cs
繼承TagDto
。
//QueryTagDto.cs
namespace Meowv.Blog.Application.Contracts.Blog
{
public class QueryTagDto : TagDto
{
/// <summary>
/// 總數
/// </summary>
public int Count { get; set; }
}
}
新增查詢標籤列表介面和快取介面。
//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
{
/// <summary>
/// 查詢標籤列表
/// </summary>
/// <param name="factory"></param>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryTagDto>>> QueryTagsAsync(Func<Task<ServiceResult<IEnumerable<QueryTagDto>>>> factory);
}
}
//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
{
/// <summary>
/// 查詢標籤列表
/// </summary>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryTagDto>>> QueryTagsAsync();
}
}
分別實現這兩個介面。
//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";
/// <summary>
/// 查詢標籤列表
/// </summary>
/// <param name="factory"></param>
/// <returns></returns>
public async Task<ServiceResult<IEnumerable<QueryTagDto>>> QueryTagsAsync(Func<Task<ServiceResult<IEnumerable<QueryTagDto>>>> factory)
{
return await Cache.GetOrAddAsync(KEY_QueryTags, factory, CacheStrategy.ONE_DAY);
}
}
}
//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
{
/// <summary>
/// 查詢標籤列表
/// </summary>
/// <returns></returns>
public async Task<ServiceResult<IEnumerable<QueryTagDto>>> QueryTagsAsync()
{
return await _blogCacheService.QueryTagsAsync(async () =>
{
var result = new ServiceResult<IEnumerable<QueryTagDto>>();
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。
/// <summary>
/// 查詢標籤列表
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("tags")]
public async Task<ServiceResult<IEnumerable<QueryTagDto>>> QueryTagsAsync()
{
return await _blogService.QueryTagsAsync();
}
分類名稱&文章列表
分析:此頁面下包含兩個介面,查詢分類的名稱和當前分類下的文章列表,和文章列表不同的是,它不帶分頁。分類包含兩個欄位,分類名稱和展示名稱,我們要把真正的名稱查詢出來展示在頁面上。
分類名稱
不需要給他新增返回模型,直接返回一個string型別即可,同時給一個查詢引數name,新增獲取分類名稱介面和快取介面。
//IBlogService.Category.cs
/// <summary>
/// 獲取分類名稱
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
Task<ServiceResult<string>> GetCategoryAsync(string name);
//IBlogCacheService.Category.cs
/// <summary>
/// 獲取分類名稱
/// </summary>
/// <param name="name"></param>
/// <param name="factory"></param>
/// <returns></returns>
Task<ServiceResult<string>> GetCategoryAsync(string name, Func<Task<ServiceResult<string>>> factory);
實現這兩個介面。
//BlogCacheService.Category.cs
...
public partial class BlogCacheService
{
private const string KEY_GetCategory = "Blog:Category:GetCategory-{0}";
/// <summary>
/// 獲取分類名稱
/// </summary>
/// <param name="name"></param>
/// <param name="factory"></param>
/// <returns></returns>
public async Task<ServiceResult<string>> GetCategoryAsync(string name, Func<Task<ServiceResult<string>>> factory)
{
return await Cache.GetOrAddAsync(KEY_GetCategory.FormatWith(name), factory, CacheStrategy.ONE_DAY);
}
}
...
//BlogService.Category.cs
/// <summary>
/// 獲取分類名稱
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public async Task<ServiceResult<string>> GetCategoryAsync(string name)
{
return await _blogCacheService.GetCategoryAsync(name, async () =>
{
var result = new ServiceResult<string>();
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。
/// <summary>
/// 獲取分類名稱
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[HttpGet]
[Route("category")]
public async Task<ServiceResult<string>> GetCategoryAsync(([Required] string name)
{
return await _blogService.GetCategoryAsync(name);
}
[Required]
Attribute 指定引數name必填。
文章列表
通過分類名稱查詢文章列表和分頁查詢文章列表返回模型是一樣的,只是不用分頁,所以直接返回一個列表就可以了,新增通過分類名稱查詢文章列表和快取的介面。
//IBlogService.Post.cs
/// <summary>
/// 通過分類名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByCategoryAsync(string name);
//IBlogCacheService.Post.cs
/// <summary>
/// 通過分類名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <param name="factory"></param>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByCategoryAsync(string name, Func<Task<ServiceResult<IEnumerable<QueryPostDto>>>> factory);
分別實現這兩個介面。
//BlogCacheService.Post.cs
...
public partial class BlogCacheService
{
private const string KEY_QueryPostsByCategory = "Blog:Post:QueryPostsByCategory-{0}";
/// <summary>
/// 通過分類名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <param name="factory"></param>
/// <returns></returns>
public async Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByCategoryAsync(string name, Func<Task<ServiceResult<IEnumerable<QueryPostDto>>>> factory)
{
return await Cache.GetOrAddAsync(KEY_QueryPostsByCategory.FormatWith(name), factory, CacheStrategy.ONE_DAY);
}
}
...
//BlogService.Post.cs
/// <summary>
/// 通過分類名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public async Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByCategoryAsync(string name)
{
return await _blogCacheService.QueryPostsByCategoryAsync(name, async () =>
{
var result = new ServiceResult<IEnumerable<QueryPostDto>>();
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。
/// <summary>
/// 通過分類名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[HttpGet]
[Route("posts/category")]
public async Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByCategoryAsync([Required] string name)
{
return await _blogService.QueryPostsByCategoryAsync(name);
}
標籤名稱&文章列表
分析:此頁面和分類頁一樣,包含兩個介面,查詢標籤的名稱和當前標籤下的文章列表。
標籤名稱
新增獲取標籤名稱介面和快取介面,GetTagAsync()
。
//IBlogService.Tag.cs
/// <summary>
/// 獲取標籤名稱
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
Task<ServiceResult<string>> GetTagAsync(string name);
//IBlogCacheService.Tag.cs
/// <summary>
/// 獲取標籤名稱
/// </summary>
/// <param name="name"></param>
/// <param name="factory"></param>
/// <returns></returns>
Task<ServiceResult<string>> GetTagAsync(string name, Func<Task<ServiceResult<string>>> factory);
實現這兩個介面。
//BlogCacheService.Tag.cs
...
public partial class BlogCacheService
{
private const string KEY_GetTag = "Blog:Tag:GetTag-{0}";
/// <summary>
/// 獲取標籤名稱
/// </summary>
/// <param name="name"></param>
/// <param name="factory"></param>
/// <returns></returns>
public async Task<ServiceResult<string>> GetTagAsync(string name, Func<Task<ServiceResult<string>>> factory)
{
return await Cache.GetOrAddAsync(KEY_GetTag.FormatWith(name), factory, CacheStrategy.ONE_DAY);
}
}
...
//BlogService.Tag.cs
/// <summary>
/// 獲取標籤名稱
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public async Task<ServiceResult<string>> GetTagAsync(string name)
{
return await _blogCacheService.GetTagAsync(name, async () =>
{
var result = new ServiceResult<string>();
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。
/// <summary>
/// 獲取標籤名稱
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[HttpGet]
[Route("tag")]
public async Task<ServiceResult<string>> GetTagAsync(string name)
{
return await _blogService.GetTagAsync(name);
}
[Required]
Attribute 指定引數name必填。
文章列表
和上面一模一樣的,新增通過標籤名稱查詢文章列表介面和快取介面。
//IBlogService.Post.cs
/// <summary>
/// 通過標籤名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByTagAsync(string name);
//IBlogCacheService.Post.cs
/// <summary>
/// 通過標籤名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <param name="factory"></param>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByTagAsync(string name, Func<Task<ServiceResult<IEnumerable<QueryPostDto>>>> factory);
分別實現這兩個介面。
//BlogCacheService.Post.cs
...
public partial class BlogCacheService
{
private const string KEY_QueryPostsByTag = "Blog:Post:QueryPostsByTag-{0}";
/// <summary>
/// 通過標籤名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <param name="factory"></param>
/// <returns></returns>
public async Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByTagAsync(string name, Func<Task<ServiceResult<IEnumerable<QueryPostDto>>>> factory)
{
return await Cache.GetOrAddAsync(KEY_QueryPostsByTag.FormatWith(name), factory, CacheStrategy.ONE_DAY);
}
}
...
//BlogService.Post.cs
/// <summary>
/// 通過標籤名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public async Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByTagAsync(string name)
{
return await _blogCacheService.QueryPostsByTagAsync(name, async () =>
{
var result = new ServiceResult<IEnumerable<QueryPostDto>>();
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。
/// <summary>
/// 通過標籤名稱查詢文章列表
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[HttpGet]
[Route("posts/tag")]
public async Task<ServiceResult<IEnumerable<QueryPostDto>>> QueryPostsByTagAsync(string name)
{
return await _blogService.QueryPostsByTagAsync(name);
}
至此,基本上完成了部落格前端所需的所有查詢介面,就還剩下友鏈的查詢,大家可以自己完成,後面如果需要什麼新的介面再回頭來寫就好了。
開源地址:https://github.com/Meowv/Blog/tree/blog_tutorial
搭配下方課程學習更佳 ↓ ↓ ↓