<repo_name>yanwawav5/blog<file_sep>/Blog.Dto/Blog/TagListDto.cs
﻿namespace Blog.Dto.Blog
{
    public class TagListDto
    {
        public string TagId { get; set; }
        public string TagName { get; set; }
        public string IconFontClass { get; set; }
        public int Sequence { get; set; }
        public int BlogCount { get; set; }
    }
}
<file_sep>/Blog.Admin.WebApi/Controllers/FileController.cs
﻿using Blog.Admin.BLL.Interface;
using Blog.Dto;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace Blog.Admin.WebApi.Controllers
{
    /// <summary>
    /// 
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiExplorerSettings(GroupName = "v1")]
    [ApiController]
    public class FileController : ControllerBase
    {
        private readonly IFileService _fileService;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileService"></param>
        public FileController(IFileService fileService)
        {
            _fileService = fileService;
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        [HttpPost]
        [Consumes("application/json", "multipart/form-data")]
        public async Task<CommonResultDto<string>> Upload([FromForm]IFormFile file)
        {
            return await _fileService.Upload(file);
        }

        /// <summary>
        /// 文件删除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpDelete]
        public async Task<CommonResultDto<string>> Delete(string id)
        {
            return await _fileService.Delete(id);
        }
    }
}<file_sep>/Blog.Dto/Blog/ConditionParamDto.cs
﻿namespace Blog.Dto.Blog
{
    public class ConditionParamDto
    {
        public string Keyword { get; set; }
        public string CategoryId { get; set; }
        public string TagId { get; set; }
    }
}
<file_sep>/Blog.Admin.BLL/Interface/ITagManageService.cs
﻿using Blog.Dto;
using Blog.Dto.BlogAdmin;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Blog.Admin.BLL.Interface
{
    public interface ITagManageService
    {
        Task<CommonResultDto<List<TagViewDto>>> TagList();
    }
}
<file_sep>/Blog.Admin.BLL/Service/TagManageService.cs
﻿using AutoMapper;
using Blog.Admin.BLL.Interface;
using Blog.Dto;
using Blog.Dto.BlogAdmin;
using Blog.Model;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Blog.Admin.BLL.Service
{
    public class TagManageService:ITagManageService
    {
        private readonly BlogContext _context;
        private readonly IMapper _mapper;
        public TagManageService(BlogContext context,IMapper mapper)
        {
            _context = context;
            _mapper = mapper;
        }

        /// <summary>
        /// 博客标签列表
        /// </summary>
        /// <returns></returns>
        public async Task<CommonResultDto<List<TagViewDto>>> TagList()
        {
            var data = await _context.tbl_tag.OrderBy(i => i.Sequence).Select(i => _mapper.Map<TagViewDto>(i)).ToListAsync();

            return new CommonResultDto<List<TagViewDto>>
            {
                Msg = "查询成功",
                Success = true,
                Response = data
            };
        }
    }
}
<file_sep>/Blog.Model/Migrations/20201127140054_initial_database.cs
﻿using System;
using Microsoft.EntityFrameworkCore.Migrations;

namespace Blog.Model.Migrations
{
    public partial class initial_database : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "tbl_blog",
                columns: table => new
                {
                    Id = table.Column<string>(maxLength: 36, nullable: false),
                    Title = table.Column<string>(maxLength: 255, nullable: false),
                    ContentId = table.Column<string>(maxLength: 36, nullable: false),
                    CategoryId = table.Column<string>(maxLength: 36, nullable: false),
                    PicUrl = table.Column<string>(maxLength: 36, nullable: false),
                    ViewTimes = table.Column<int>(maxLength: 11, nullable: false),
                    LikeTimes = table.Column<int>(maxLength: 11, nullable: false),
                    State = table.Column<string>(maxLength: 1, nullable: false),
                    CreateAt = table.Column<DateTime>(nullable: false),
                    PublishAt = table.Column<DateTime>(nullable: true),
                    DeleteAt = table.Column<DateTime>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_tbl_blog", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "tbl_blog_content",
                columns: table => new
                {
                    Id = table.Column<string>(maxLength: 36, nullable: false),
                    Content = table.Column<string>(maxLength: 5000, nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_tbl_blog_content", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "tbl_blog_tag_relationship",
                columns: table => new
                {
                    Id = table.Column<string>(maxLength: 36, nullable: false),
                    BlogId = table.Column<string>(maxLength: 36, nullable: false),
                    TagId = table.Column<string>(maxLength: 36, nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_tbl_blog_tag_relationship", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "tbl_category",
                columns: table => new
                {
                    Id = table.Column<string>(maxLength: 36, nullable: false),
                    Name = table.Column<string>(maxLength: 255, nullable: false),
                    Sequence = table.Column<string>(maxLength: 11, nullable: false),
                    Remark = table.Column<string>(maxLength: 255, nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_tbl_category", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "tbl_comment",
                columns: table => new
                {
                    Id = table.Column<string>(maxLength: 36, nullable: false),
                    BlogId = table.Column<string>(maxLength: 36, nullable: false),
                    Content = table.Column<string>(maxLength: 36, nullable: false),
                    From = table.Column<string>(maxLength: 36, nullable: false),
                    To = table.Column<string>(maxLength: 36, nullable: true),
                    CreateAt = table.Column<DateTime>(nullable: false),
                    LikeTimes = table.Column<int>(maxLength: 11, nullable: false),
                    FloorNum = table.Column<int>(maxLength: 11, nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_tbl_comment", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "tbl_file",
                columns: table => new
                {
                    Id = table.Column<string>(maxLength: 36, nullable: false),
                    Name = table.Column<string>(maxLength: 255, nullable: false),
                    Path = table.Column<string>(maxLength: 255, nullable: false),
                    ExtName = table.Column<string>(maxLength: 255, nullable: false),
                    Size = table.Column<double>(nullable: false),
                    UploadAt = table.Column<DateTime>(nullable: false),
                    Type = table.Column<string>(maxLength: 1, nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_tbl_file", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "tbl_tag",
                columns: table => new
                {
                    Id = table.Column<string>(maxLength: 36, nullable: false),
                    Name = table.Column<string>(maxLength: 255, nullable: false),
                    Sequence = table.Column<string>(maxLength: 11, nullable: false),
                    Remark = table.Column<string>(maxLength: 255, nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_tbl_tag", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "tbl_user",
                columns: table => new
                {
                    Id = table.Column<string>(maxLength: 36, nullable: false),
                    Name = table.Column<string>(maxLength: 16, nullable: false),
                    Email = table.Column<string>(maxLength: 64, nullable: false),
                    Password = table.Column<string>(maxLength: 255, nullable: false),
                    RegisterAt = table.Column<DateTime>(nullable: false),
                    LastLoginAt = table.Column<DateTime>(nullable: false),
                    LastLoginAddr = table.Column<string>(maxLength: 255, nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_tbl_user", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "tbl_blog");

            migrationBuilder.DropTable(
                name: "tbl_blog_content");

            migrationBuilder.DropTable(
                name: "tbl_blog_tag_relationship");

            migrationBuilder.DropTable(
                name: "tbl_category");

            migrationBuilder.DropTable(
                name: "tbl_comment");

            migrationBuilder.DropTable(
                name: "tbl_file");

            migrationBuilder.DropTable(
                name: "tbl_tag");

            migrationBuilder.DropTable(
                name: "tbl_user");
        }
    }
}
<file_sep>/Blog.Dto/Blog/TagViewDto.cs
﻿namespace Blog.Dto.Blog
{
    public class TagViewDto
    {
        public string TagId { get; set; }
        public string TagName { get; set; }
        public int Sequence { get; set; }
        public string BlogId { get; set; }
    }
}
<file_sep>/Blog.Model/tbl_category.cs
﻿using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace Blog.Model
{
    /// <summary>
    /// 博客类别表
    /// </summary>
    public class tbl_category
    {
        [Required, MaxLength(36)]
        [DisplayName("主键ID")]
        public string Id { get; set; }

        [Required, MaxLength(255)]
        [DisplayName("类别名称")]
        public string Name { get; set; }

        [Required, MaxLength(11)]
        [DisplayName("排序码")]
        public int Sequence { get; set; }

        [MaxLength(255)]
        [DisplayName("备注")]
        public string Remark { get; set; }
    }
}
<file_sep>/Blog.Model/tbl_blog_content.cs
﻿using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace Blog.Model
{
    /// <summary>
    /// 博客内容表
    /// </summary>
    public class tbl_blog_content
    {
        [Required, MaxLength(36)]
        [DisplayName("主键ID")]
        public string Id { get; set; }

        [Required, MaxLength(5000)]
        [DisplayName("博客内容")]
        public string Content { get; set; }
    }
}
<file_sep>/Blog.Model/tbl_tag.cs
﻿using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace Blog.Model
{
    /// <summary>
    /// 标签表
    /// </summary>
    public class tbl_tag
    {
        [Required, MaxLength(36)]
        [DisplayName("主键ID")]
        public string Id { get; set; }

        [Required, MaxLength(255)]
        [DisplayName("标签名称")]
        public string Name { get; set; }

        [Required, MaxLength(255)]
        [DisplayName("标签字体样式")]
        public string IconFontClass { get; set; }

        [Required, MaxLength(11)]
        [DisplayName("排序码")]
        public int Sequence { get; set; }

        [MaxLength(255)]
        [DisplayName("备注")]
        public string Remark { get; set; }
    }
}
<file_sep>/Blog.Admin.BLL/Interface/ICategoryManageService.cs
﻿using Blog.Dto;
using Blog.Dto.BlogAdmin;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Blog.Admin.BLL.Interface
{
    public interface ICategoryManageService
    {
        Task<CommonResultDto<List<CategoryViewDto>>> CategoryList();
    }
}
<file_sep>/Blog.Admin.BLL/Service/UserService.cs
﻿using AutoMapper;
using Blog.Admin.BLL.Interface;
using Blog.Dto;
using Blog.Dto.BlogAdmin;
using Blog.Model;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace Blog.Admin.BLL.Service
{
    public class UserService:IUserService
    {
        private readonly BlogContext _context;
        private readonly IMapper _mapper;
        public UserService(BlogContext context, IMapper mapper)
        {
            _context = context;
            _mapper = mapper;
        }

        /// <summary>
        /// 用户列表
        /// </summary>
        /// <param name="keyword"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public async Task<CommonPageResultDto<UserViewDto>> UserList(string keyword, int pageIndex, int pageSize)
        {
            CommonPageResultDto<UserViewDto> rlt = new CommonPageResultDto<UserViewDto>();
            var qry = await _context.tbl_user.OrderBy(s => s.RegisterAt).Select(i => _mapper.Map<UserViewDto>(i)).ToListAsync();

            qry = String.IsNullOrEmpty(keyword) ? qry : qry.Where(i => i.Name.Contains(keyword) || i.Email.Contains(keyword)).ToList();
            rlt.DataCount = qry.Count;
            rlt.Page = pageIndex;
            rlt.PageSize = pageSize;
            rlt.PageCount = (qry.Count + pageSize - 1) / pageSize;
            rlt.Data = qry.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();

            return rlt;
        }

        /// <summary>
        /// 根据用户id查询用户信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<CommonResultDto<UserViewDto>> GetById(string id)
        {
            return new CommonResultDto<UserViewDto>
            {
                Msg = "查询成功",
                Success = true,
                Response = await _context.tbl_user.Where(i => i.Id == id).Select(i => _mapper.Map<UserViewDto>(i)).FirstOrDefaultAsync()
            };
        }

        /// <summary>
        /// 新增用户
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public async Task<CommonResultDto<UserViewDto>> Create(UserAddDto user)
        {
            // validation
            if (string.IsNullOrWhiteSpace(user.Password))
                throw new Exception("Password is required");

            if (_context.tbl_user.Any(x => x.Name == user.Name))
                throw new Exception("Username \"" + user.Name + "\" is already 存在");

            byte[] passwordHash, passwordSalt;
            CreatePasswordHash(user.Password, out passwordHash, out passwordSalt);

            tbl_user tbl = new tbl_user
            {
                Id = Guid.NewGuid().ToString(),
                Name = user.Name,
                Email = user.Email,
                RegisterAt = DateTime.Now,
                LastLoginAt = DateTime.Now,
                LastLoginAddr = "",
                Type = '2',
                PasswordHash = <PASSWORD>,
                PasswordSalt = <PASSWORD>
            };

            await _context.tbl_user.AddAsync(tbl);
            _context.SaveChanges();

            return new CommonResultDto<UserViewDto>
            {
                Msg = "查询成功",
                Success = true,
                Response = new UserViewDto { 
                    Id = tbl.Id, 
                    Name = tbl.Name, 
                    Email = tbl.Email, 
                    LastLoginAddr = tbl.LastLoginAddr,
                    LastLoginAt = tbl.LastLoginAt,
                    RegisterAt = tbl.RegisterAt
                }
            };
        }

        public void Update(tbl_user userParam, string password = null)
        {
            var user = _context.tbl_user.Find(userParam.Id);

            if (user == null)
                throw new Exception("User not found");

            // update username if it has changed
            if (!string.IsNullOrWhiteSpace(userParam.Name) && userParam.Name != user.Name)
            {
                // throw error if the new username is already taken
                if (_context.tbl_user.Any(x => x.Name == userParam.Name))
                    throw new Exception("Name " + userParam.Name + " is already taken");

                user.Name = userParam.Name;
            }

            // update user properties if provided
            if (!string.IsNullOrWhiteSpace(userParam.Name))
                user.Name = userParam.Name;

            // update password if provided
            if (!string.IsNullOrWhiteSpace(password))
            {
                byte[] passwordHash, passwordSalt;
                CreatePasswordHash(password, out passwordHash, out passwordSalt);

                user.PasswordHash = <PASSWORD>Hash;
                user.PasswordSalt = passwordSalt;
            }

            _context.tbl_user.Update(user);
            _context.SaveChanges();
        }


        private static void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
        {
            if (password == null) throw new ArgumentNullException("password");
            if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "password");

            using (var hmac = new System.Security.Cryptography.HMACSHA512())
            {
                passwordSalt = hmac.Key;
                passwordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
            }
        }
    }
}
<file_sep>/Blog.BLL/Service/BlogService.cs
﻿using Blog.BLL.Interface;
using Blog.Dto;
using Blog.Dto.Blog;
using Blog.Model;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Blog.BLL.Service
{
    public class BlogService : IBlogService
    {
        private readonly BlogContext _context;
        public BlogService(BlogContext context)
        {
            _context = context;
        }

        /// <summary>
        /// 查询博客列表
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public async Task<CommonPageResultDto<BlogListViewDto>> BlogList(ConditionParamDto condition, int pageIndex, int pageSize)
        {
            CommonPageResultDto<BlogListViewDto> rlt = new CommonPageResultDto<BlogListViewDto>();
            List<tbl_blog> list = new List<tbl_blog>();
            //if (!String.IsNullOrEmpty(condition.Keyword))
            //{
            //    list = await (from a in _context.tbl_blog.Where(i => i.Title.Contains(condition.Keyword))
            //                 join b in _context.tbl_blog_content.Where(i => i.Content.Contains(condition.Keyword))
            //                 on a.ContentId equals b.Id
            //                 select a).ToListAsync();
            //}
            if (!String.IsNullOrEmpty(condition.CategoryId))
            {
                list = _context.tbl_blog.Where(i => i.CategoryId == condition.CategoryId && i.DeleteAt == null).ToList();
            }
            else if (!String.IsNullOrEmpty(condition.TagId))
            {
                list = (from a in _context.tbl_blog.Where(i=> i.DeleteAt == null)
                        join b in _context.tbl_blog_tag_relation.Where(i => i.TagId == condition.TagId)
                        on a.Id equals b.BlogId
                        select a).ToList();
            }
            else
            {
                list = await _context.tbl_blog.Where(i => i.DeleteAt == null).ToListAsync();
            }

            List<BlogListViewDto> commentGroup = await (from a in _context.tbl_comment
                                               group a by a.BlogId into g
                                               select new BlogListViewDto
                                               {
                                                   Id = g.Key,
                                                   CommentTimes = g.Count()
                                               }).ToListAsync();

            List<BlogListViewDto> qry = (from a in list
                                        join b in _context.tbl_blog_content
                                        on a.ContentId equals b.Id
                                        join c in _context.tbl_category
                                        on a.CategoryId equals c.Id
                                         join d in commentGroup
                                         on a.Id equals d.Id into temp
                                         from g in temp.DefaultIfEmpty()
                                         select new BlogListViewDto
                                        {
                                            Id = a.Id,
                                            Title = a.Title,
                                            Content = b.Content,
                                            CreateAt = a.CreateAt,
                                            ViewTimes = a.ViewTimes,
                                            LikeTimes = a.LikeTimes,
                                            PicUrl = a.PicUrl,
                                            CommentTimes = g == null ? 0 : g.CommentTimes // _context.tbl_comment.Count(i=>i.BlogId == a.Id)
                                        }).ToList();

            IQueryable<TagViewDto> tagRlt = (from a in _context.tbl_blog_tag_relation
                                            join b in _context.tbl_tag
                                            on a.TagId equals b.Id
                                            select new TagViewDto { BlogId = a.BlogId, TagName = b.Name, TagId = b.Id, Sequence = b.Sequence });

            qry = String.IsNullOrEmpty(condition.Keyword) ? qry : qry.Where(i => i.Title.Contains(condition.Keyword) || i.Content.Contains(condition.Keyword)).ToList();
            foreach (var item in qry)
            {
                item.TagList = await tagRlt.Where(i => i.BlogId == item.Id).Select(i => i).ToListAsync();
            }

            rlt.DataCount = qry.Count;
            rlt.Page = pageIndex;
            rlt.PageSize = pageSize;
            rlt.PageCount = (qry.Count + pageSize - 1) / pageSize;
            rlt.Data = qry.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();

            return rlt;
        }

        /// <summary>
        /// 查看博客详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<CommonResultDto<BlogViewDto>> BlogDetail(string id)
        {
            List<tbl_blog> list = _context.tbl_blog.Where(i => i.DeleteAt == null).ToList();
            List<BlogListViewDto> commentGroup = await (from a in _context.tbl_comment
                                                        group a by a.BlogId into g
                                                        select new BlogListViewDto
                                                        {
                                                            Id = g.Key,
                                                            CommentTimes = g.Count()
                                                        }).ToListAsync();

            var qry =  (from a in list
                         join b in _context.tbl_blog_content
                         on a.ContentId equals b.Id
                         join c in _context.tbl_category
                         on a.CategoryId equals c.Id
                         join d in commentGroup
                         on a.Id equals d.Id into temp
                         from g in temp.DefaultIfEmpty()
                         where a.Id == id
                         select new BlogViewDto
                        {
                            Title = a.Title,
                            PicUrl = a.PicUrl,
                            Content = b.Content,
                            Remark = a.Remark,
                            CategoryName = c.Name,
                            CreateAt = a.CreateAt,
                            ViewTimes = a.ViewTimes,
                            LikeTimes = a.LikeTimes,
                            CommentTimes = g == null ? 0 : g.CommentTimes//_context.tbl_comment.Count(i => i.BlogId == a.Id)
                        }).ToList();

            BlogViewDto rlt = qry.FirstOrDefault();
            IQueryable<TagViewDto> tagRlt = (from a in _context.tbl_blog_tag_relation
                                             join b in _context.tbl_tag
                                             on a.TagId equals b.Id
                                             select new TagViewDto { BlogId = a.BlogId, TagName = b.Name, TagId = b.Id, Sequence = b.Sequence });
            if(rlt != null)
            {
                List<string> tagNameList = tagRlt.Where(i => i.BlogId == id).Select(i => i.TagName).ToList();
                rlt.TagNameList = tagNameList;
                //if (tagNameList != null) { rlt.TagNameList.AddRange(tagNameList); };
            }

            //查看一次详情，浏览次数累加一次
            tbl_blog tbl = await _context.tbl_blog.FirstOrDefaultAsync(i => i.Id == id);
            if(tbl != null)
            {
                ++tbl.ViewTimes;
            }
            _context.tbl_blog.Update(tbl);
            await _context.SaveChangesAsync();

            return new CommonResultDto<BlogViewDto> { Msg = "查询成功", Success = true, Response = rlt };
        }

        /// <summary>
        /// 热门阅读列表
        /// </summary>
        /// <returns></returns>
        public async Task<CommonResultDto<List<TopBlogViewDto>>> TopBlogList()
        {
            List<TopBlogViewDto> rlt = await _context.tbl_blog.OrderByDescending(i => (i.ViewTimes + i.LikeTimes))
                .Select(i => new TopBlogViewDto { Id = i.Id, Name = i.Title, ViewLikeTimes = i.ViewTimes + i.LikeTimes }).Take(5).ToListAsync();

            return new CommonResultDto<List<TopBlogViewDto>> { Msg = "查询成功", Success = true,Response = rlt };
        }

        /// <summary>
        /// 点赞
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<CommonResultDto<string>> Like(string id)
        {
            tbl_blog tbl = await _context.tbl_blog.FirstOrDefaultAsync(i => i.Id == id);
            if (tbl != null)
            {
                ++tbl.LikeTimes;
            }
            _context.tbl_blog.Update(tbl);
            await _context.SaveChangesAsync();

            return new CommonResultDto<string> { Msg = "点赞成功", Success = true };
        }
    }
}
<file_sep>/Blog.Dto/BlogAdmin/UserViewDto.cs
﻿using System;

namespace Blog.Dto.BlogAdmin
{
    public class UserViewDto
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public DateTime RegisterAt { get; set; }
        public DateTime LastLoginAt { get; set; }
        public string LastLoginAddr { get; set; }
    }
}
<file_sep>/Blog.BLL/Interface/IBlogService.cs
﻿using Blog.Dto;
using Blog.Dto.Blog;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Blog.BLL.Interface
{
    public interface IBlogService
    {
        Task<CommonPageResultDto<BlogListViewDto>> BlogList(ConditionParamDto condition, int pageIndex, int pageSize);

        Task<CommonResultDto<List<TopBlogViewDto>>> TopBlogList();

        Task<CommonResultDto<BlogViewDto>> BlogDetail(string id);

        Task<CommonResultDto<string>> Like(string id);
    }
}
<file_sep>/Blog.Admin.WebApi/Controllers/UserController.cs
﻿using Blog.Admin.BLL.Interface;
using Blog.Dto;
using Blog.Dto.BlogAdmin;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace Blog.Admin.WebApi.Controllers
{
    /// <summary>
    /// 
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiExplorerSettings(GroupName = "v1")]
    [ApiController]
    public class UserController : ControllerBase
    {
        private readonly IUserService _userService;
        /// <summary>
        /// 
        /// </summary>
        public UserController(IUserService userService)
        {
            _userService = userService;
        }

        /// <summary>
        /// 获取用户列表
        /// </summary>
        /// <param name="keyword"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<CommonPageResultDto<UserViewDto>> Get(string keyword, int pageIndex, int pageSize)
        {
            return await _userService.UserList(keyword, pageIndex, pageSize);
        }

       /// <summary>
       /// 用户注册
       /// </summary>
       /// <param name="model"></param>
       /// <returns></returns>
        [HttpPost]
        public async Task<CommonResultDto<UserViewDto>> Register([FromBody]UserAddDto model)
        {
            return await _userService.Create(model);
        }

        
        [HttpPost]
        public async Task<CommonPageResultDto<UserViewDto>> Update(string keyword, int pageIndex, int pageSize)
        {
            return await _userService.UserList(keyword, pageIndex, pageSize);
        }
    }
}<file_sep>/Blog.Admin.BLL/Interface/IBlogManageService.cs
﻿using Blog.Dto;
using Blog.Dto.BlogAdmin;
using System.Threading.Tasks;

namespace Blog.Admin.BLL.Interface
{
    public interface IBlogManageService
    {
        Task<CommonPageResultDto<BlogListViewDto>> BlogList(string keyword, int pageIndex, int pageSize);

        Task<CommonResultDto<BlogViewDto>> BlogDetail(string id);

        Task<CommonResultDto<string>> Post(BlogAddDto dto);

        Task<CommonResultDto<string>> Update(BlogUpdateDto dto);

        Task<CommonResultDto<string>> Delete(string id);

        Task<CommonResultDto<string>> ReleaseBlog(string id);
    }
}
<file_sep>/Blog.Admin.BLL/Interface/IFileService.cs
﻿using Blog.Dto;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace Blog.Admin.BLL.Interface
{
    public interface IFileService
    {
        Task<CommonResultDto<string>> Upload(IFormFile file);
        Task<CommonResultDto<string>> Delete(string id);
    }
}
<file_sep>/Blog.Model/tbl_file.cs
﻿using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace Blog.Model
{
    /// <summary>
    /// 附件表
    /// </summary>
    public class tbl_file
    {
        [Required, MaxLength(36)]
        [DisplayName("主键ID")]
        public string Id { get; set; }

        [Required, MaxLength(255)]
        [DisplayName("文件名")]
        public string Name { get; set; }

        [Required, MaxLength(255)]
        [DisplayName("文件路径")]
        public string Path { get; set; }

        [Required, MaxLength(255)]
        [DisplayName("文件后缀名")]
        public string ExtName { get; set; }

        [Required]
        [DisplayName("文件大小")]
        public double Size { get; set; }

        [Required]
        [DisplayName("上传时间")]
        public DateTime UploadAt { get; set; }

        [Required, MaxLength(1)]
        [DisplayName("文件类型（1：博客图片:2：用户头像）")]
        public char Type { get; set; }
    }
}
<file_sep>/Blog.WebApi/Controllers/BlogController.cs
﻿using Blog.BLL.Interface;
using Blog.Dto;
using Blog.Dto.Blog;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Blog.WebApi.Controllers
{
    /// <summary>
    /// 
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiExplorerSettings(GroupName = "v1")]
    [ApiController]
    public class BlogController : ControllerBase
    {
        private readonly IBlogService _blogService;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="blogService"></param>
        public BlogController(IBlogService blogService)
        {
            _blogService = blogService;
        }

        /// <summary>
        /// 查询博客列表
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<CommonPageResultDto<BlogListViewDto>> BlogList([FromBody]ConditionParamDto condition, int pageIndex, int pageSize)
        {
            return await _blogService.BlogList(condition, pageIndex, pageSize);
        }

        /// <summary>
        /// 查看博客详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<CommonResultDto<BlogViewDto>> BlogDetail(string id)
        {
            return await _blogService.BlogDetail(id);
        }

        /// <summary>
        /// 热门阅读列表
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<CommonResultDto<List<TopBlogViewDto>>> TopBlogList()
        {
            return await _blogService.TopBlogList();
        }

        /// <summary>
        /// 点赞
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPut]
        public async Task<CommonResultDto<string>> Like(string id)
        {
            return await _blogService.Like(id);
        }
    }
}<file_sep>/Blog.Admin.WebApi/Startup.cs
using AutoMapper;
using Blog.Admin.BLL.Interface;
using Blog.Admin.BLL.Service;
using Blog.Model;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Blog.Admin.WebApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // swagger
            services.AddSwaggerDocument(config =>
            {
                config.DocumentName = "v1";
                config.ApiGroupNames = new[] { "v1" };
                config.PostProcess = document =>
                {
                    document.Info.Version = "v1";
                    document.Info.Title = "BlogAdmin";
                    document.Info.Description = "Blog.Admin.WebApi";
                };
            });

            // connections
            services.AddDbContextPool<BlogContext>(options => options.UseMySql(Configuration["ConnectionString"]));


            // configure jwt authentication
            var key = Encoding.ASCII.GetBytes(Configuration["AppSetting:Secret"]);
            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(x =>
            {
                x.Events = new JwtBearerEvents
                {
                    OnTokenValidated = context =>
                    {
                        var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>();
                        var userId = context.Principal.Identity.Name;
                        var user = userService.GetById(userId);
                        if (user == null)
                        {
                            // return unauthorized if user no longer exists
                            context.Fail("Unauthorized");
                        }
                        return Task.CompletedTask;
                    }
                };
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = false,
                    ValidateAudience = false
                };
            });

            services.AddScoped<IBlogManageService, BlogManageService>();
            services.AddScoped<ICategoryManageService, CategoryManageService>();
            services.AddScoped<ITagManageService, TagManageService>();
            services.AddScoped<IFileService, FileService>();
            services.AddScoped<IUserService, UserService>();

            //AutoMapper
            //var allType =
            //    Assembly
            //        .GetEntryAssembly()//获取默认程序集
            //        .GetReferencedAssemblies()//获取所有引用程序集
            //        .Select(Assembly.Load)
            //        .SelectMany(y => y.DefinedTypes)
            //        .Where(type => typeof(BLL.Mappers.MapperConfig).GetTypeInfo().IsAssignableFrom(type.AsType()));

            //services.AddAutoMapper(allType.ToArray());
            services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());

            services.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseStaticFiles();

            #region 使用swagger
            app.UseOpenApi();
            app.UseSwaggerUi3();
            #endregion

            // 解决跨域问题
            app.UseCors(builder => builder.AllowAnyOrigin()
                                     .AllowAnyMethod()
                                     .AllowAnyHeader());

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}
<file_sep>/Blog.Admin.WebApi/Controllers/LoginController.cs
﻿using Blog.Admin.BLL.Interface;
using Blog.Admin.BLL.Service;
using Blog.Dto;
using Blog.Dto.BlogAdmin;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace Blog.Admin.WebApi.Controllers
{
    /// <summary>
    /// 
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiExplorerSettings(GroupName = "v1")]
    [ApiController]
    public class LoginController : ControllerBase
    {
        private readonly ILoginService _loginService;

        private IConfiguration _configuration;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="loginService"></param>
        /// <param name="configuration"></param>
        public LoginController(LoginService loginService, IConfiguration configuration)
        {
            _loginService = loginService;
            _configuration = configuration;
        }

       /// <summary>
       /// 登陆
       /// </summary>
       /// <param name="model"></param>
       /// <returns></returns>
        [HttpPost]
        public async Task<CommonResultDto<object>> Login([FromBody]LoginDto model)
        {
            var user = await _loginService.Login(model.Username, model.Password);
            if (user == null)
                return new CommonResultDto<object>
                {
                    Msg = "登陆失败",
                    Success = false,
                    Response = "Username or password is incorrect"
                };

            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(_configuration["AppSetting:Secret"]);
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new Claim[]
                {
                    new Claim(ClaimTypes.Name, user.Response.Id)
                }),
                Expires = DateTime.UtcNow.AddDays(7),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };
            var token = tokenHandler.CreateToken(tokenDescriptor);
            var tokenString = tokenHandler.WriteToken(token);


            return new CommonResultDto<object>
            {
                Msg = "登陆成功",
                Success = true,
                Response = new
                {
                    Id = user.Response.Id,
                    Name = user.Response.Name,
                    Token = tokenString
                }
            };
        }
    }
}<file_sep>/Blog.Admin.BLL/Interface/IUserService.cs
﻿using Blog.Dto;
using Blog.Dto.BlogAdmin;
using Blog.Model;
using System.Threading.Tasks;

namespace Blog.Admin.BLL.Interface
{
    public interface IUserService
    {
        Task<CommonPageResultDto<UserViewDto>> UserList(string keyword, int pageIndex, int pageSize);

        Task<CommonResultDto<UserViewDto>> GetById(string id);

        Task<CommonResultDto<UserViewDto>> Create(UserAddDto user);
    }
}
<file_sep>/Blog.Admin.BLL/Service/BlogManageService.cs
﻿using Blog.Admin.BLL.Interface;
using Blog.Dto;
using Blog.Dto.BlogAdmin;
using Blog.Model;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Blog.Admin.BLL.Service
{
    public class BlogManageService : IBlogManageService
    {
        private readonly BlogContext _context;
        public BlogManageService(BlogContext context)
        {
            _context = context;
        }

        /// <summary>
        /// 查询博客列表
        /// </summary>
        /// <param name="keyword"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public async Task<CommonPageResultDto<BlogListViewDto>> BlogList(string keyword, int pageIndex, int pageSize)
        {
            CommonPageResultDto<BlogListViewDto> rlt = new CommonPageResultDto<BlogListViewDto>();
            List<BlogListViewDto> qry = await (from a in _context.tbl_blog 
                                         join b in _context.tbl_blog_content
                                         on a.ContentId equals b.Id
                                         join c in _context.tbl_category 
                                         on a.CategoryId equals c.Id
                                         where a.DeleteAt == null
                                         select new BlogListViewDto
                                         {
                                             Id = a.Id,
                                             Title = a.Title,
                                             Content = b.Content,
                                             CreateAt = a.CreateAt,
                                             UpdateAt = a.UpdateAt,
                                             PublishAt = a.PublishAt,
                                             CategoryName = c.Name,
                                             StateName = a.State == '1' ? "未发布" : "已发布",
                                             ViewTimes = a.ViewTimes,
                                             LikeTimes = a.LikeTimes
                                         }).OrderByDescending(i=>i.CreateAt).ThenByDescending(i=>i.UpdateAt).ToListAsync();

            IQueryable<BlogTagQryDto> tagRlt = (from a in _context.tbl_blog_tag_relation
                                                join b in _context.tbl_tag
                         on a.TagId equals b.Id
                         select new BlogTagQryDto { BlogId = a.BlogId, TagName = b.Name });
            qry = String.IsNullOrEmpty(keyword) ? qry : qry.Where(i => i.Title.Contains(keyword) || i.Content.Contains(keyword)).ToList();
            foreach (var item in qry)
            {
                item.TagNameList = await tagRlt.Where(i => i.BlogId == item.Id).Select(i => i.TagName).ToListAsync();
            }
            rlt.DataCount = qry.Count;
            rlt.Page = pageIndex;
            rlt.PageSize = pageSize;
            rlt.PageCount = (qry.Count + pageSize - 1) / pageSize;
            rlt.Data = qry.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();

            return rlt;
        }

        /// <summary>
        /// 查看博客详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<CommonResultDto<BlogViewDto>> BlogDetail(string id)
        {
            var qry = (from  a in _context.tbl_blog.Where(i => i.DeleteAt == null)
                       join b in _context.tbl_blog_content
                       on a.ContentId equals b.Id
                       join c in _context.tbl_category
                       on a.CategoryId equals c.Id
                       where a.Id == id
                       select new BlogViewDto
                       {
                           Id = a.Id,
                           Title = a.Title,
                           PicUrl = a.PicUrl,
                           Content = b.Content,
                           Remark = a.Remark,
                           Category = c.Id
                       }).ToList();

            BlogViewDto rlt = qry.FirstOrDefault();
            IQueryable<TagViewDto> tagRlt = (from a in _context.tbl_blog_tag_relation
                                             join b in _context.tbl_tag
                                             on a.TagId equals b.Id
                                             select new TagViewDto { BlogId = a.BlogId, TagName = b.Name, TagId = b.Id, Sequence = b.Sequence });
           rlt.TagIdList = await tagRlt.Where(i => i.BlogId == id).Select(i => i.TagId).ToListAsync();

            return new CommonResultDto<BlogViewDto> { Msg = "查询成功", Success = true, Response = rlt };
        }

        /// <summary>
        /// 删除博客
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<CommonResultDto<string>> Delete(string id)
        {
            using (await _context.Database.BeginTransactionAsync())
            {
                tbl_blog tbl = await _context.tbl_blog.FirstOrDefaultAsync(i => i.Id == id);
                tbl.DeleteAt = DateTime.Now;
                _context.tbl_blog.Update(tbl);
                ////删除博客内容
                //tbl_blog_content tbl_content = await _context.tbl_blog_content.FirstOrDefaultAsync(i => i.Id == tbl.ContentId);
                //_context.tbl_blog_content.Remove(tbl_content);

                ////删除博客标签管理表数据
                //IEnumerable<tbl_blog_tag_relation> tbl_tag_relations = _context.tbl_blog_tag_relation.Where(i => i.BlogId == id);
                //_context.tbl_blog_tag_relation.RemoveRange(tbl_tag_relations);
                await _context.SaveChangesAsync();
                _context.Database.CommitTransaction();
                return new CommonResultDto<string> { Msg = "更新成功", Success = true };
            }
        }

        /// <summary>
        /// 新增博客
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<CommonResultDto<string>> Post(BlogAddDto dto)
        {
            using (await _context.Database.BeginTransactionAsync())
            {
                System.ComponentModel.NullableConverter nullableDateTime = new System.ComponentModel.NullableConverter(typeof(DateTime?));
                string blogContentId = Guid.NewGuid().ToString();
                string blogId = Guid.NewGuid().ToString();
                tbl_blog_content tbl_content = new tbl_blog_content
                {
                    Id = blogContentId,
                    Content = dto.Content
                };

                tbl_blog tbl_blog = new tbl_blog
                {
                    Id = blogId,
                    Title = dto.Title,
                    ContentId = blogContentId,
                    Remark = dto.Remark,
                    CategoryId = dto.CategoryId,
                    PicUrl = dto.PicUrl,
                    ViewTimes = 0,
                    LikeTimes = 0,
                    State = dto.State == "1" ? '1' : '2', //状态（枚举类型 1未发布 2已发布）
                    CreateAt = DateTime.Now,
                    PublishAt = dto.State == "2" ? DateTime.Now : (DateTime?)nullableDateTime.ConvertFromString(null),
                    DeleteAt = null
                };

                List<tbl_blog_tag_relation> relationList = new List<tbl_blog_tag_relation>();
                foreach (var item in dto.TagIdList)
                {
                    relationList.Add(new tbl_blog_tag_relation
                    {
                        Id = Guid.NewGuid().ToString(),
                        BlogId = blogId,
                        TagId = item
                    });
                }

                await _context.tbl_blog.AddAsync(tbl_blog);
                await _context.tbl_blog_content.AddAsync(tbl_content);
                await _context.tbl_blog_tag_relation.AddRangeAsync(relationList);
                await _context.SaveChangesAsync();
                _context.Database.CommitTransaction();

                return new CommonResultDto<string> { Msg = "新增成功", Success = true };
            }
        }

        /// <summary>
        /// 更新博客
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<CommonResultDto<string>> Update(BlogUpdateDto dto)
        {
            using(await _context.Database.BeginTransactionAsync())
            {
                tbl_blog blog = await _context.tbl_blog.FirstOrDefaultAsync(i => i.Id == dto.Id);
                tbl_blog_content content = await _context.tbl_blog_content.FirstOrDefaultAsync(i => i.Id == blog.ContentId);
                List<tbl_blog_tag_relation> tagRelations = await _context.tbl_blog_tag_relation.Where(i => i.BlogId == dto.Id).ToListAsync();
                List<tbl_blog_tag_relation> insertTags = new List<tbl_blog_tag_relation>();
                foreach (var tagId in dto.TagIdList)
                {
                    insertTags.Add(new tbl_blog_tag_relation
                    {
                        Id = Guid.NewGuid().ToString(),
                        BlogId = dto.Id,
                        TagId = tagId
                    });
                }

                blog.Title = dto.Title;
                blog.PicUrl = dto.PicUrl;
                blog.CategoryId = dto.CategoryId;
                blog.UpdateAt = DateTime.Now;
                _context.tbl_blog.Update(blog);

                content.Content = dto.Content;
                _context.tbl_blog_content.Update(content);

                _context.tbl_blog_tag_relation.RemoveRange(tagRelations);
                await _context.tbl_blog_tag_relation.AddRangeAsync(insertTags);

                await _context.SaveChangesAsync();
                _context.Database.CommitTransaction();

                return new CommonResultDto<string> { Msg = "更新成功", Success = true };
            }
        }

        /// <summary>
        /// 发布博客
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<CommonResultDto<string>> ReleaseBlog(string id)
        {
            tbl_blog blog = await _context.tbl_blog.FirstOrDefaultAsync(i => i.Id == id);
            blog.State = '2';
            _context.tbl_blog.Update(blog);
            await _context.SaveChangesAsync();
            return new CommonResultDto<string> { Msg = "发布成功", Success = true };
        }
    }
}
<file_sep>/Blog.BLL/Service/TagService.cs
﻿using Blog.BLL.Interface;
using Blog.Dto;
using Blog.Dto.Blog;
using Blog.Model;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace Blog.BLL.Service
{
    public class TagService : ITagService
    {
        private readonly BlogContext _context;
        public TagService(BlogContext context)
        {
            _context = context;
        }

        /// <summary>
        /// 查询标签列表
        /// </summary>
        /// <returns></returns>
        public async Task<CommonResultDto<List<TagListDto>>> TagList()
        {
            var list = await (from a in _context.tbl_blog.Where(i=>i.DeleteAt == null)
                              join b in _context.tbl_blog_tag_relation
                              on a.Id equals b.BlogId 
                            group b by b.TagId
                            into g
                            select new TagListDto
                            {
                                TagId = g.Key,
                                BlogCount = g.Count()
                            }).ToListAsync();
            var rlt = (from a in _context.tbl_tag.ToList()
                       join b in list
                       on a.Id equals b.TagId
                       into temp 
                       from p in temp.DefaultIfEmpty()
                       select new TagListDto
                       {
                           TagId = a.Id,
                           TagName = a.Name,
                           IconFontClass = a.IconFontClass,
                           Sequence = a.Sequence,
                           BlogCount = p == null ? 0 : p.BlogCount
                       }).ToList();

            return new CommonResultDto<List<TagListDto>> { Msg = "查询成功", Success = true, Response = rlt };
        }
    }
}
<file_sep>/Blog.Model/tbl_blog.cs
﻿using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace Blog.Model
{
    /// <summary>
    /// 博客表
    /// </summary>
    public class tbl_blog
    {
        [Required, MaxLength(36)]
        [DisplayName("主键ID")]
        public string Id { get; set; }

        [Required, MaxLength(255)]
        [DisplayName("博客标题")]
        public string Title { get; set; }

        [Required, MaxLength(36)]
        [DisplayName("内容ID")]
        public string ContentId { get; set; }

        [MaxLength(500)]
        [DisplayName("备注")]
        public string Remark { get; set; }

        [Required, MaxLength(36)]
        [DisplayName("类别ID")]
        public string CategoryId { get; set; }

        [Required, MaxLength(200)]
        [DisplayName("博客图片路径")]
        public string PicUrl { get; set; }

        [Required, MaxLength(11)]
        [DisplayName("浏览次数")]
        public int ViewTimes { get; set; }

        [Required, MaxLength(11)]
        [DisplayName("点赞次数")]
        public int LikeTimes { get; set; }

        [Required, MaxLength(1)]
        [DisplayName("状态（枚举类型 1未发布 2已发布）")]
        public char State { get; set; }

        [Required]
        [DisplayName("创建时间")]
        public DateTime CreateAt { get; set; }

        [DisplayName("发布时间")]
        public DateTime? PublishAt { get; set; }

        [DisplayName("修改时间")]
        public DateTime? UpdateAt { get; set; }

        [DisplayName("datetime")]
        public DateTime? DeleteAt { get; set; }
    }
}
<file_sep>/Blog.Admin.BLL/Interface/ILoginService.cs
﻿using Blog.Dto;
using Blog.Model;
using System.Threading.Tasks;

namespace Blog.Admin.BLL.Interface
{
    public interface ILoginService
    {
        Task<CommonResultDto<tbl_user>> Login(string username, string password);
    }
}
<file_sep>/Blog.Admin.BLL/Service/FileService.cs
﻿using Blog.Admin.BLL.Interface;
using Blog.Dto;
using Blog.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.IO;
using System.Threading.Tasks;

namespace Blog.Admin.BLL.Service
{
    public class FileService : IFileService
    {
        private readonly IConfiguration _configuration;
        private readonly BlogContext _context;
        public FileService(IConfiguration configuration,BlogContext context)
        {
            _configuration = configuration;
            _context = context;
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public async Task<CommonResultDto<string>> Upload(IFormFile file)
        {
            var path = Directory.GetCurrentDirectory() + "\\wwwroot" + _configuration["FilePath"];
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
            var fileName = DateTime.UtcNow.ToString("yyyyMMddHHmmss") + new Random().Next(1000, 10000).ToString() + Path.GetExtension(file.FileName);
            var filePath = path + fileName;
            using (var stream = File.Create(filePath))
            {
                file.CopyTo(stream);
            }

            tbl_file tbl = new tbl_file
            {
                Id = Guid.NewGuid().ToString(),
                Name = file.FileName,
                //Path = filePath,
                Path = _configuration["FilePath"] + fileName,
                ExtName = Path.GetExtension(file.FileName),
                Size = file.Length,
                UploadAt = DateTime.Now,
                Type = '1'
            };

            await _context.tbl_file.AddAsync(tbl);
            await _context.SaveChangesAsync();
            return new CommonResultDto<string>
            {
                Msg = "上传成功",
                Success = true,
                Response = _configuration["FilePath"] + fileName
            };
        }

        /// <summary>
        /// 删除文件
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<CommonResultDto<string>> Delete(string id)
        {
            tbl_file file = await _context.tbl_file.FirstOrDefaultAsync(i => i.Id == id);
            _context.tbl_file.Remove(file);

            if (File.Exists(file.Path))
            {
                File.Delete(file.Path);
            }
            await _context.SaveChangesAsync();

            return new CommonResultDto<string>
            {
                Msg = "删除成功",
                Success = true,
                Response = ""
            };
        }
    }
}
<file_sep>/Blog.Model/tbl_comment.cs
﻿using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace Blog.Model
{
    /// <summary>
    /// 评论内容表
    /// </summary>
    public class tbl_comment
    {
        [Required, MaxLength(36)]
        [DisplayName("主键ID")]
        public string Id { get; set; }

        [Required, MaxLength(36)]
        [DisplayName("博客ID")]
        public string BlogId { get; set; }

        [Required, MaxLength(36)]
        [DisplayName("评论内容")]
        public string Content { get; set; }

        [Required, MaxLength(36)]
        [DisplayName("评论人ID")]
        public string From { get; set; }

        [MaxLength(36)]
        [DisplayName("被评论人ID")]
        public string To { get; set; }

        [Required]
        [DisplayName("评论时间")]
        public DateTime CreateAt { get; set; }

        [Required, MaxLength(11)]
        [DisplayName("点赞次数")]
        public int LikeTimes { get; set; }

        [Required, MaxLength(11)]
        [DisplayName("当前评论楼层数")]
        public int CurrentFloorNum { get; set; }

        [MaxLength(11)]
        [DisplayName("被评论人所处评论楼层数")]
        public int? ToFloorNum { get; set; }
    }
}
<file_sep>/Blog.BLL/Interface/ITagService.cs
﻿using Blog.Dto;
using Blog.Dto.Blog;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Blog.BLL.Interface
{
    public interface ITagService
    {
        Task<CommonResultDto<List<TagListDto>>> TagList();
    }
}
<file_sep>/Blog.Dto/Blog/BlogViewDto.cs
﻿using System;
using System.Collections.Generic;

namespace Blog.Dto.Blog
{
    public class BlogViewDto
    {
        public string Title { get; set; }
        public string PicUrl { get; set; }
        public string Content { get; set; }
        public string Remark { get; set; }
        public string CategoryName { get; set; }
        public List<string> TagNameList { get; set; }
        public DateTime CreateAt { get; set; }
        public int ViewTimes { get; set; } 
        public int LikeTimes { get; set; } 
        public int CommentTimes { get; set; } 
    }
}
<file_sep>/Blog.Admin.WebApi/Controllers/BlogController.cs
﻿using Blog.Admin.BLL.Interface;
using Blog.Dto;
using Blog.Dto.BlogAdmin;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace Blog.Admin.WebApi.Controllers
{
    /// <summary>
    /// 
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiExplorerSettings(GroupName = "v1")]
    [ApiController]
    public class BlogController : ControllerBase
    {
        private readonly ILogger<BlogController> _logger;

        private IBlogManageService _blogManageService;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="blogManageService"></param>
        public BlogController(ILogger<BlogController> logger, IBlogManageService blogManageService)
        {
            _logger = logger;
            _blogManageService = blogManageService;
        }

        /// <summary>
        /// 获取博客列表
        /// </summary>
        /// <param name="keyword">关键字</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">页数</param>
        /// <returns></returns>
        [HttpGet]
        public async Task<CommonPageResultDto<BlogListViewDto>> Get(string keyword, int pageIndex, int pageSize)
        {
            return await _blogManageService.BlogList(keyword, pageIndex, pageSize);
        }

        /// <summary>
        /// 查看博客详情
        /// </summary>
        /// <param name="id">博客id</param>
        /// <returns></returns>
        [HttpGet]
        public async Task<CommonResultDto<BlogViewDto>> BlogDetail(string id)
        {
            return await _blogManageService.BlogDetail(id);
        }

        /// <summary>
        /// 删除博客
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpDelete]
        public async Task<CommonResultDto<string>> Delete(string id)
        {
            return await _blogManageService.Delete(id);
        }

        /// <summary>
        /// 新增博客
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<CommonResultDto<string>> Post(BlogAddDto dto)
        {
            return await _blogManageService.Post(dto);
        }

        /// <summary>
        /// 更新博客
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpPut]
        public async Task<CommonResultDto<string>> Update(BlogUpdateDto dto)
        {
            return await _blogManageService.Update(dto);
        }

        /// <summary>
        /// 发布博客
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPut]
        public async Task<CommonResultDto<string>> ReleaseBlog(string id)
        {
            return await _blogManageService.ReleaseBlog(id);
        }
    }
}
<file_sep>/Blog.Model/Migrations/20210122025920_updatePassword.cs
﻿using System;
using Microsoft.EntityFrameworkCore.Migrations;

namespace Blog.Model.Migrations
{
    public partial class updatePassword : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropColumn(
                name: "Password",
                table: "tbl_user");

            migrationBuilder.AddColumn<byte[]>(
                name: "PasswordHash",
                table: "tbl_user",
                maxLength: 64,
                nullable: false,
                defaultValue: new byte[] {  });

            migrationBuilder.AddColumn<byte[]>(
                name: "PasswordSalt",
                table: "tbl_user",
                maxLength: 128,
                nullable: false,
                defaultValue: new byte[] {  });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropColumn(
                name: "PasswordHash",
                table: "tbl_user");

            migrationBuilder.DropColumn(
                name: "PasswordSalt",
                table: "tbl_user");

            migrationBuilder.AddColumn<string>(
                name: "Password",
                table: "tbl_user",
                type: "varchar(255) CHARACTER SET utf8mb4",
                maxLength: 255,
                nullable: false,
                defaultValue: "");
        }
    }
}
<file_sep>/Blog.Admin.WebApi/Controllers/CategoryController.cs
﻿using Blog.Admin.BLL.Interface;
using Blog.Dto;
using Blog.Dto.BlogAdmin;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Blog.Admin.WebApi.Controllers
{
    /// <summary>
    /// 
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiExplorerSettings(GroupName = "v1")]
    [ApiController]
    public class CategoryController : ControllerBase
    {
        private readonly ICategoryManageService _categoryManageService;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="categoryManageService"></param>
        public CategoryController(ICategoryManageService categoryManageService)
        {
            _categoryManageService = categoryManageService;
        }

        /// <summary>
        /// 博客类别列表
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<CommonResultDto<List<CategoryViewDto>>> Get()
        {
            return await _categoryManageService.CategoryList();
        }
    }
}<file_sep>/Blog.Dto/Blog/CommentAddDto.cs
﻿namespace Blog.Dto.Blog
{
    public class CommentAddDto
    {
        public string BlogId { get; set; }
        public string Content { get; set; }
        public string From { get; set; }
        public string To { get; set; }
        public int CurrentFloorNum { get; set; }
        public int? ToFloorNum { get; set; }
    }
}
<file_sep>/Blog.Dto/Blog/TopBlogViewDto.cs
﻿namespace Blog.Dto.Blog
{
    public class TopBlogViewDto
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public int ViewLikeTimes { get; set; }
    }
}
<file_sep>/README.md
# 个人Blog项目
> 
> 后端技术



 .NetCore 3.1 WebApi
- 
 Swagger接口文档，API基于RESTful风格编写
- 
 Async和Await 异步编程
- 



<file_sep>/Blog.BLL/Service/CategoryService.cs
﻿using Blog.BLL.Interface;
using Blog.Dto;
using Blog.Dto.Blog;
using Blog.Model;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Blog.BLL.Service
{
    public class CategoryService : ICategoryService
    {
        private readonly BlogContext _context;
        public CategoryService(BlogContext context)
        {
            _context = context;
        }

        /// <summary>
        /// 查询博客分类列表
        /// </summary>
        /// <returns></returns>
        public async Task<CommonResultDto<List<CategoryViewDto>>> CategoryList()
        {
            var list = await (from a in _context.tbl_blog.Where(i=>i.DeleteAt == null)
                             group a by a.CategoryId
                             into g
                             select new CategoryViewDto
                             {
                                 Id = g.Key,
                                 BlogCount = g.Count()
                             }).ToListAsync();
            var rlt = (from a in _context.tbl_category.ToList()
                       join b in list
                       on a.Id equals b.Id
                       select new CategoryViewDto
                       {
                          Id = a.Id,
                          BlogCount = b.BlogCount,
                          Name = a.Name,
                          Sequence = a.Sequence
                      }).OrderBy(i => i.Sequence).ToList();

            return new CommonResultDto<List<CategoryViewDto>> { Msg = "查询成功", Success = true, Response = rlt };
        }
    }
}
<file_sep>/Blog.Model/BlogContext.cs
﻿using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

namespace Blog.Model
{
    public class BlogContext : DbContext
    {
        public BlogContext(DbContextOptions<BlogContext> options) : base(options)
        {
            
        }

        public DbSet<tbl_blog> tbl_blog { get; set; }
        public DbSet<tbl_blog_content> tbl_blog_content { get; set; }
        public DbSet<tbl_blog_tag_relation> tbl_blog_tag_relation { get; set; }
        public DbSet<tbl_category> tbl_category { get; set; }
        public DbSet<tbl_comment> tbl_comment { get; set; }
        public DbSet<tbl_file> tbl_file { get; set; }
        public DbSet<tbl_tag> tbl_tag { get; set; }
        public DbSet<tbl_user> tbl_user { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                IConfigurationBuilder builder = new ConfigurationBuilder();
                var configuration = builder.Build();
                optionsBuilder.UseMySql(configuration["Connection"]);
            }
        }
    }
}
<file_sep>/Blog.Dto/CommonResultDto.cs
﻿namespace Blog.Dto
{
    /// <summary>
    /// 通用返回结果类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CommonResultDto<T>
    {
        /// <summary>
        /// 状态码
        /// </summary>
        public int Status { get; set; } = 200;
        /// <summary>
        /// 操作是否成功
        /// </summary>
        public bool Success { get; set; } = false;
        /// <summary>
        /// 返回信息
        /// </summary>
        public string Msg { get; set; } = "服务器异常";
        /// <summary>
        /// 返回数据集合
        /// </summary>
        public T Response { get; set; }
    }
}
<file_sep>/Blog.Model/Migrations/20201202130101_changeTableName.cs
﻿using System;
using Microsoft.EntityFrameworkCore.Migrations;

namespace Blog.Model.Migrations
{
    public partial class changeTableName : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "tbl_blog_tag_relationship");

            migrationBuilder.AlterColumn<int>(
                name: "Sequence",
                table: "tbl_tag",
                maxLength: 11,
                nullable: false,
                oldClrType: typeof(string),
                oldType: "varchar(11) CHARACTER SET utf8mb4",
                oldMaxLength: 11);

            migrationBuilder.AlterColumn<int>(
                name: "Sequence",
                table: "tbl_category",
                maxLength: 11,
                nullable: false,
                oldClrType: typeof(string),
                oldType: "varchar(11) CHARACTER SET utf8mb4",
                oldMaxLength: 11);

            migrationBuilder.AddColumn<DateTime>(
                name: "UpdateAt",
                table: "tbl_blog",
                nullable: true);

            migrationBuilder.CreateTable(
                name: "tbl_blog_tag_relation",
                columns: table => new
                {
                    Id = table.Column<string>(maxLength: 36, nullable: false),
                    BlogId = table.Column<string>(maxLength: 36, nullable: false),
                    TagId = table.Column<string>(maxLength: 36, nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_tbl_blog_tag_relation", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "tbl_blog_tag_relation");

            migrationBuilder.DropColumn(
                name: "UpdateAt",
                table: "tbl_blog");

            migrationBuilder.AlterColumn<string>(
                name: "Sequence",
                table: "tbl_tag",
                type: "varchar(11) CHARACTER SET utf8mb4",
                maxLength: 11,
                nullable: false,
                oldClrType: typeof(int),
                oldMaxLength: 11);

            migrationBuilder.AlterColumn<string>(
                name: "Sequence",
                table: "tbl_category",
                type: "varchar(11) CHARACTER SET utf8mb4",
                maxLength: 11,
                nullable: false,
                oldClrType: typeof(int),
                oldMaxLength: 11);

            migrationBuilder.CreateTable(
                name: "tbl_blog_tag_relationship",
                columns: table => new
                {
                    Id = table.Column<string>(type: "varchar(36) CHARACTER SET utf8mb4", maxLength: 36, nullable: false),
                    BlogId = table.Column<string>(type: "varchar(36) CHARACTER SET utf8mb4", maxLength: 36, nullable: false),
                    TagId = table.Column<string>(type: "varchar(36) CHARACTER SET utf8mb4", maxLength: 36, nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_tbl_blog_tag_relationship", x => x.Id);
                });
        }
    }
}
<file_sep>/Blog.WebApi/Controllers/CommentController.cs
﻿using Blog.BLL.Interface;
using Blog.Dto;
using Blog.Dto.Blog;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace Blog.WebApi.Controllers
{
    /// <summary>
    /// 
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiExplorerSettings(GroupName = "v1")]
    [ApiController]
    public class CommentController : ControllerBase
    {
        private readonly ICommentService _commentService;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="commentService"></param>
        public CommentController(ICommentService commentService)
        {
            _commentService = commentService;
        }

        
        /// <summary>
        /// 获取评论列表
        /// </summary>
        /// <param name="blogId"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<CommonPageResultDto<CommentViewDto>> CommentList(string blogId, int pageIndex, int pageSize)
        {
            return await _commentService.CommentList(blogId, pageIndex, pageSize);
        }

        /// <summary>
        /// 新增评论
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<CommonResultDto<string>> Post([FromBody]CommentAddDto dto)
        {
            return await _commentService.Post(dto);
        }

        /// <summary>
        /// 点赞
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPut]
        public async Task<CommonResultDto<string>> Like(string id)
        {
            return await _commentService.Like(id);
        }
    }
}<file_sep>/Blog.Dto/BlogAdmin/BlogUpdateDto.cs
﻿using System.Collections.Generic;

namespace Blog.Dto.BlogAdmin
{
    public class BlogUpdateDto
    {
        public string Id { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public string PicUrl { get; set; }
        public string CategoryId { get; set; }
        public List<string> TagIdList { get; set; }
    }
}
<file_sep>/Blog.WebApi/Controllers/CategoryController.cs
﻿using Blog.BLL.Interface;
using Blog.Dto;
using Blog.Dto.Blog;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Blog.WebApi.Controllers
{
    /// <summary>
    /// 
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiExplorerSettings(GroupName = "v1")]
    [ApiController]
    public class CategoryController : ControllerBase
    {
        private readonly ICategoryService _categoryService;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="categoryService"></param>
        public CategoryController(ICategoryService categoryService)
        {
            _categoryService = categoryService;
        }

        /// <summary>
        /// 查询博客分类列表
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<CommonResultDto<List<CategoryViewDto>>> CategoryList()
        {
            return await _categoryService.CategoryList();
        }
    }
}<file_sep>/Blog.Admin.BLL/Service/CategoryManageService.cs
﻿using AutoMapper;
using Blog.Admin.BLL.Interface;
using Blog.Dto;
using Blog.Dto.BlogAdmin;
using Blog.Model;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Blog.Admin.BLL.Service
{
    public class CategoryManageService : ICategoryManageService
    {
        private readonly BlogContext _context;
        private readonly IMapper _mapper;
        public CategoryManageService(BlogContext context,IMapper mapper)
        {
            _context = context;
            _mapper = mapper;
        }

        /// <summary>
        /// 博客类别列表
        /// </summary>
        /// <returns></returns>
        public async Task<CommonResultDto<List<CategoryViewDto>>> CategoryList()
        {
            var data = await _context.tbl_category.OrderBy(s => s.Sequence).Select(i => _mapper.Map<CategoryViewDto>(i)).ToListAsync();

            return new CommonResultDto<List<CategoryViewDto>>
            {
                Msg = "查询成功",
                Success = true,
                Response = data
            };
        }
    }
}
<file_sep>/Blog.Model/tbl_blog_tag_relation.cs
﻿using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace Blog.Model
{
    /// <summary>
    /// 博客标签关联表
    /// </summary>
    public class tbl_blog_tag_relation
    {
        [Required, MaxLength(36)]
        [DisplayName("主键ID")]
        public string Id { get; set; }

        [Required, MaxLength(36)]
        [DisplayName("博客ID")]
        public string BlogId { get; set; }

        [Required, MaxLength(36)]
        [DisplayName("标签ID")]
        public string TagId { get; set; }
    }
}
<file_sep>/Blog.BLL/Interface/ICommentService.cs
﻿using Blog.Dto;
using Blog.Dto.Blog;
using System.Threading.Tasks;

namespace Blog.BLL.Interface
{
    public interface ICommentService
    {
        Task<CommonPageResultDto<CommentViewDto>> CommentList(string blogId, int pageIndex, int pageSize);

        Task<CommonResultDto<string>> Post(CommentAddDto dto);

        Task<CommonResultDto<string>> Like(string id);
    }
}
<file_sep>/Blog.WebApi/Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Blog.BLL.Interface;
using Blog.BLL.Service;
using Blog.Model;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Blog.WebApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            // swagger
            services.AddSwaggerDocument(config =>
            {
                config.DocumentName = "v1";
                config.ApiGroupNames = new[] { "v1" };
                config.PostProcess = document =>
                {
                    document.Info.Version = "v1";
                    document.Info.Title = "Blog";
                    document.Info.Description = "Blog.WebApi";
                };
            });

            // connections
            services.AddDbContextPool<BlogContext>(options => options.UseMySql(Configuration["ConnectionString"]));

            services.AddScoped<IBlogService, BlogService>();
            services.AddScoped<ICategoryService, CategoryService>();
            services.AddScoped<ITagService, TagService>();
            services.AddScoped<ICommentService, CommentService>();

            services.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseStaticFiles();

            #region 使用swagger
            app.UseOpenApi();
            app.UseSwaggerUi3();
            #endregion
            // 解决跨域问题
            app.UseCors(builder => builder.AllowAnyOrigin()
                                     .AllowAnyMethod()
                                     .AllowAnyHeader());

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}
<file_sep>/Blog.Dto/Blog/CommentViewDto.cs
﻿using System;

namespace Blog.Dto.Blog
{
    public class CommentViewDto
    {
        public string Id { get; set; }
        public string FromUserName { get; set; }
        public string ToUserName { get; set; }
        public int CurrentFloorNum { get; set; }
        public int? ToFloorNum { get; set; }
        public DateTime CreateAt { get; set; }
        public string Address { get; set; }
        public int LikeTimes { get; set; }
        public string Content { get; set; }
    }
}
<file_sep>/Blog.BLL/Service/CommentService.cs
﻿using Blog.BLL.Interface;
using Blog.Dto;
using Blog.Dto.Blog;
using Blog.Model;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace Blog.BLL.Service
{
    public class CommentService : ICommentService
    {
        private readonly BlogContext _context;
        public CommentService(BlogContext context)
        {
            _context = context;
        }

        /// <summary>
        /// 获取评论列表
        /// </summary>
        /// <param name="blogId"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public async Task<CommonPageResultDto<CommentViewDto>> CommentList(string blogId, int pageIndex, int pageSize)
        {
            CommonPageResultDto<CommentViewDto> rlt = new CommonPageResultDto<CommentViewDto>();
            var qry = await (from a in _context.tbl_comment.Where(i => i.BlogId == blogId)
                             join b in _context.tbl_user
                             on a.From equals b.Id
                             join c in _context.tbl_user
                             on a.To equals c.Id into temp
                             from g in temp.DefaultIfEmpty()
                             select new CommentViewDto
                             {
                                 Id = a.Id,
                                 FromUserName = b.Name,
                                 ToUserName = g == null ? null : g.Name,
                                 CurrentFloorNum = a.CurrentFloorNum,
                                 ToFloorNum = a.ToFloorNum,
                                 CreateAt = a.CreateAt,
                                 Address = b.LastLoginAddr,
                                 LikeTimes = a.LikeTimes,
                                 Content = a.Content
                             }).ToListAsync();
            rlt.DataCount = qry.Count;
            rlt.Page = pageIndex;
            rlt.PageSize = pageSize;
            rlt.PageCount = (qry.Count + pageSize - 1) / pageSize;
            rlt.Data = qry.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();

            return rlt;
        }

        /// <summary>
        /// 新增评论
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<CommonResultDto<string>> Post(CommentAddDto dto)
        {
            tbl_comment tbl = new tbl_comment
            {
                Id = Guid.NewGuid().ToString(),
                BlogId = dto.BlogId,
                Content = dto.Content,
                From = dto.From,
                To = dto.To,
                CurrentFloorNum = dto.CurrentFloorNum,
                ToFloorNum = dto.ToFloorNum,
                CreateAt = DateTime.Now,
                LikeTimes = 0
            };
            await _context.tbl_comment.AddAsync(tbl);
            await _context.SaveChangesAsync();

            return new CommonResultDto<string> { Msg = "新增成功", Success = true };
        }

        /// <summary>
        /// 点赞
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<CommonResultDto<string>> Like(string id)
        {
            tbl_comment tbl = await _context.tbl_comment.FirstOrDefaultAsync(i => i.Id == id);
            if(tbl != null)
            {
                ++tbl.LikeTimes;
            }
            _context.tbl_comment.Update(tbl);
            await _context.SaveChangesAsync();

            return new CommonResultDto<string> { Msg = "点赞成功", Success = true };
        }
    }
}
<file_sep>/Blog.Model/tbl_user.cs
﻿using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace Blog.Model
{
    /// <summary>
    /// 用户表
    /// </summary>
    public class tbl_user
    {
        [Required, MaxLength(36)]
        [DisplayName("主键ID")]
        public string Id { get; set; }

        [Required, MaxLength(16)]
        [DisplayName("用户名")]
        public string Name { get; set; }

        [Required, MaxLength(64)]
        [DisplayName("邮箱地址")]
        public string Email { get; set; }

        //[Required, MaxLength(255)]
        //[DisplayName("密码加密密文")]
        //public string Password { get; set; }

        [Required, MaxLength(64)]
        [DisplayName("密码加密密文")]
        public byte[] PasswordHash { get; set; }

        [Required, MaxLength(128)]
        [DisplayName("密码盐")]
        public byte[] PasswordSalt { get; set; }

        [Required]
        [DisplayName("注册时间")]
        public DateTime RegisterAt { get; set; }

        [Required]
        [DisplayName("最后一次登录时间")]
        public DateTime LastLoginAt { get; set; }

        [Required, MaxLength(255)]
        [DisplayName("最后一次登陆地点")]
        public string LastLoginAddr { get; set; }

        [Required, MaxLength(1)]
        [DisplayName("人员类型(1：普通用户:2：管理用户)")]
        public char Type { get; set; }
    }
}
<file_sep>/Blog.Dto/CommonPageResultDto.cs
﻿using System.Collections.Generic;

namespace Blog.Dto
{
    /// <summary>
    /// 通用分页返回结果类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CommonPageResultDto<T>
    {
        /// <summary>
        /// 当前页标
        /// </summary>
        public int Page { get; set; } = 1;
        /// <summary>
        /// 总页数
        /// </summary>
        public int PageCount { get; set; } = 5;
        /// <summary>
        /// 数据总数
        /// </summary>
        public int DataCount { get; set; } = 0;
        /// <summary>
        /// 每页大小
        /// </summary>
        public int PageSize { set; get; }
        /// <summary>
        /// 返回数据
        /// </summary>
        public List<T> Data { get; set; }
    }
}
<file_sep>/Blog.Dto/BlogAdmin/BlogTagQryDto.cs
﻿namespace Blog.Dto.BlogAdmin
{
    public class BlogTagQryDto
    {
        public string BlogId { get; set; }
        public string TagName { get; set; }
    }
}
<file_sep>/Blog.BLL/Interface/ICategoryService.cs
﻿using Blog.Dto;
using Blog.Dto.Blog;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Blog.BLL.Interface
{
    public interface ICategoryService
    {
        Task<CommonResultDto<List<CategoryViewDto>>> CategoryList();
    }
}
<file_sep>/Blog.Admin.BLL/Mappers/MapperConfig.cs
﻿using AutoMapper;
using Blog.Dto.BlogAdmin;
using Blog.Model;

namespace Blog.Admin.BLL.Mappers
{
    public class MapperConfig:Profile
    {
        public MapperConfig()
        {
            // tbl_category转CategoryViewDto
            CreateMap<tbl_category, CategoryViewDto>()
                .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
                .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name))
                .ForMember(dest => dest.Sequence, opt => opt.MapFrom(src => src.Sequence));

            // tbl_tag转TagViewDto
            CreateMap<tbl_tag, TagViewDto>()
                .ForMember(dest => dest.TagId, opt => opt.MapFrom(src => src.Id))
                .ForMember(dest => dest.TagName, opt => opt.MapFrom(src => src.Name))
                .ForMember(dest => dest.IconFontClass, opt => opt.MapFrom(src => src.IconFontClass))
                .ForMember(dest => dest.Sequence, opt => opt.MapFrom(src => src.Sequence));

            // tbl_user转UserViewDto
            CreateMap<tbl_user, UserViewDto>()
                .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
                .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name))
                .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email))
                .ForMember(dest => dest.RegisterAt, opt => opt.MapFrom(src => src.RegisterAt))
                .ForMember(dest => dest.LastLoginAt, opt => opt.MapFrom(src => src.LastLoginAt))
                .ForMember(dest => dest.LastLoginAddr, opt => opt.MapFrom(src => src.LastLoginAddr));
        }
    }
}
<file_sep>/Blog.Dto/BlogAdmin/BlogListViewDto.cs
﻿using System;
using System.Collections.Generic;

namespace Blog.Dto.BlogAdmin
{
    public class BlogListViewDto
    {
        public string Id { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public DateTime CreateAt { get; set; }
        public DateTime? UpdateAt { get; set; }
        public DateTime? PublishAt { get; set; }
        public string CategoryName { get; set; }
        public List<string> TagNameList { get; set; }
        public string StateName { get; set; }
        public int ViewTimes { get; set; }
        public int LikeTimes { get; set; }
    }
}
<file_sep>/Blog.Admin.WebApi/Controllers/TagController.cs
﻿using Blog.Admin.BLL.Interface;
using Blog.Dto;
using Blog.Dto.BlogAdmin;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Blog.Admin.WebApi.Controllers
{
    /// <summary>
    /// 
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiExplorerSettings(GroupName = "v1")]
    [ApiController]
    public class TagController : ControllerBase
    {
        private readonly ITagManageService _tagManageService;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="tagManageService"></param>
        public TagController(ITagManageService tagManageService)
        {
            _tagManageService = tagManageService;
        }

        /// <summary>
        /// 博客标签列表
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<CommonResultDto<List<TagViewDto>>> Get()
        {
            return await _tagManageService.TagList();
        }
    }
}<file_sep>/Blog.Dto/BlogAdmin/TagViewDto.cs
﻿namespace Blog.Dto.BlogAdmin
{
    public class TagViewDto
    {
        public string TagId { get; set; }
        public string TagName { get; set; }
        public string IconFontClass { get; set; }
        public int Sequence { get; set; }
        public string BlogId { get; set; }
    }
}
