zoukankan      html  css  js  c++  java
  • 基于 abp vNext 和 .NET Core 开发博客项目

    系列文章

    1. 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目
    2. 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来
    3. 基于 abp vNext 和 .NET Core 开发博客项目 - 完善与美化,Swagger登场
    4. 基于 abp vNext 和 .NET Core 开发博客项目 - 数据访问和代码优先
    5. 基于 abp vNext 和 .NET Core 开发博客项目 - 自定义仓储之增删改查
    6. 基于 abp vNext 和 .NET Core 开发博客项目 - 统一规范API,包装返回模型
    7. 基于 abp vNext 和 .NET Core 开发博客项目 - 再说Swagger,分组、描述、小绿锁
    8. 基于 abp vNext 和 .NET Core 开发博客项目 - 接入GitHub,用JWT保护你的API
    9. 基于 abp vNext 和 .NET Core 开发博客项目 - 异常处理和日志记录
    10. 基于 abp vNext 和 .NET Core 开发博客项目 - 使用Redis缓存数据
    11. 基于 abp vNext 和 .NET Core 开发博客项目 - 集成Hangfire实现定时任务处理
    12. 基于 abp vNext 和 .NET Core 开发博客项目 - 用AutoMapper搞定对象映射
    13. 基于 abp vNext 和 .NET Core 开发博客项目 - 定时任务最佳实战(一)
    14. 基于 abp vNext 和 .NET Core 开发博客项目 - 定时任务最佳实战(二)
    15. 基于 abp vNext 和 .NET Core 开发博客项目 - 定时任务最佳实战(三)
    16. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(一)

    上篇文章完成了两个接口:文章列表页、文章详情页,本篇继续。

    分类列表

    1

    分析:这里多了一个统计文章数量的字段,可以直接新建一个模型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();
    }
    

    2

    标签列表

    3

    分析:和分类列表差不多,新建模型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();
    }
    

    4

    分类名称&文章列表

    5

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

    分类名称

    不需要给他添加返回模型,直接返回一个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必填。

    6

    7

    文章列表

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

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

    8

    标签名称&文章列表

    9

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

    标签名称

    添加获取标签名称接口和缓存接口,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必填。

    10

    11

    文章列表

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

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

    12

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

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


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

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

    7

  • 相关阅读:
    iOS Button选中与取消
    IOS-UIButton的文本与图片的布局
    iOS滑动tableView来改变导航栏的颜色
    Mac下Vim编辑快捷键小结
    iOS 比较版本号大小的方法
    Symbol
    call和apply的区别及用法
    关于高并发
    java.io.IOException: Stream closed 的问题
    通俗易懂的 Java 位操作运算讲解
  • 原文地址:https://www.cnblogs.com/meowv/p/12994914.html
Copyright © 2011-2022 走看看