系列文章

基于 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

{

///

/// 总数

///

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

{

///

/// 查询分类列表

///

///

Task>> 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

{

///

/// 查询分类列表

///

///

///

Task>> QueryCategoriesAsync(Func>>> 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";

///

/// 查询分类列表

///

///

///

public async Task>> QueryCategoriesAsync(Func>>> 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

{

///

/// 查询分类列表

///

///

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。

///

/// 查询分类列表

///

///

[HttpGet]

[Route("categories")]

public async Task>> QueryCategoriesAsync()

{

return await _blogService.QueryCategoriesAsync();

}

标签列表

分析:和分类列表差不多,新建模型QueryTagDto.cs继承TagDto。

//QueryTagDto.cs

namespace Meowv.Blog.Application.Contracts.Blog

{

public class QueryTagDto : TagDto

{

///

/// 总数

///

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

{

///

/// 查询标签列表

///

///

///

Task>> QueryTagsAsync(Func>>> 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

{

///

/// 查询标签列表

///

///

Task>> 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";

///

/// 查询标签列表

///

///

///

public async Task>> QueryTagsAsync(Func>>> 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

{

///

/// 查询标签列表

///

///

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。

///

/// 查询标签列表

///

///

[HttpGet]

[Route("tags")]

public async Task>> QueryTagsAsync()

{

return await _blogService.QueryTagsAsync();

}

分类名称&文章列表

分析:此页面下包含两个接口,查询分类的名称和当前分类下的文章列表,和文章列表不同的是,它不带分页。分类包含两个字段,分类名称和展示名称,我们要把真正的名称查询出来展示在页面上。

分类名称

不需要给他添加返回模型,直接返回一个string类型即可,同时给一个查询参数name,添加获取分类名称接口和缓存接口。

//IBlogService.Category.cs

///

/// 获取分类名称

///

///

///

Task> GetCategoryAsync(string name);

//IBlogCacheService.Category.cs

///

/// 获取分类名称

///

///

///

///

Task> GetCategoryAsync(string name, Func>> factory);

实现这两个接口。

//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);

}

}

...

//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。

///

/// 获取分类名称

///

///

///

[HttpGet]

[Route("category")]

public async Task> GetCategoryAsync(([Required] string name)

{

return await _blogService.GetCategoryAsync(name);

}

[Required]Attribute 指定参数name必填。

文章列表

通过分类名称查询文章列表和分页查询文章列表返回模型是一样的,只是不用分页,所以直接返回一个列表就可以了,添加通过分类名称查询文章列表和缓存的接口。

//IBlogService.Post.cs

///

/// 通过分类名称查询文章列表

///

///

///

Task>> QueryPostsByCategoryAsync(string name);

//IBlogCacheService.Post.cs

///

/// 通过分类名称查询文章列表

///

///

///

///

Task>> QueryPostsByCategoryAsync(string name, Func>>> factory);

分别实现这两个接口。

//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);

}

}

...

//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。

///

/// 通过分类名称查询文章列表

///

///

///

[HttpGet]

[Route("posts/category")]

public async Task>> QueryPostsByCategoryAsync([Required] string name)

{

return await _blogService.QueryPostsByCategoryAsync(name);

}

标签名称&文章列表

分析:此页面和分类页一样,包含两个接口,查询标签的名称和当前标签下的文章列表。

标签名称

添加获取标签名称接口和缓存接口,GetTagAsync()。

//IBlogService.Tag.cs

///

/// 获取标签名称

///

///

///

Task> GetTagAsync(string name);

//IBlogCacheService.Tag.cs

///

/// 获取标签名称

///

///

///

///

Task> GetTagAsync(string name, Func>> factory);

实现这两个接口。

//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);

}

}

...

//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。

///

/// 获取标签名称

///

///

///

[HttpGet]

[Route("tag")]

public async Task> GetTagAsync(string name)

{

return await _blogService.GetTagAsync(name);

}

[Required]Attribute 指定参数name必填。

文章列表

和上面一模一样的,添加通过标签名称查询文章列表接口和缓存接口。

//IBlogService.Post.cs

///

/// 通过标签名称查询文章列表

///

///

///

Task>> QueryPostsByTagAsync(string name);

//IBlogCacheService.Post.cs

///

/// 通过标签名称查询文章列表

///

///

///

///

Task>> QueryPostsByTagAsync(string name, Func>>> factory);

分别实现这两个接口。

//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);

}

}

...

//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。

///

/// 通过标签名称查询文章列表

///

///

///

[HttpGet]

[Route("posts/tag")]

public async Task>> QueryPostsByTagAsync(string name)

{

return await _blogService.QueryPostsByTagAsync(name);

}

至此,基本上完成了博客前端所需的所有查询接口,就还剩下友链的查询,大家可以自己完成,后面如果需要什么新的接口再回头来写就好了。

开源地址:https://github.com/Meowv/Blog/tree/blog_tutorial

搭配下方课程学习更佳 ↓ ↓ ↓

http://gk.link/a/10iQ7

查看原文