Open Source Web Application Framework for ASP.NET Core
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

361 lines
13 KiB

using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Caching.Distributed;
using Volo.Abp.Caching;
using Volo.Abp.EventBus.Local;
using Volo.Blogging.Comments;
using Volo.Blogging.Tagging;
using Volo.Blogging.Tagging.Dtos;
using Volo.Blogging.Users;
using Volo.Abp.Data;
namespace Volo.Blogging.Posts
{
public class PostAppService : BloggingAppServiceBase, IPostAppService
{
protected IBlogUserLookupService UserLookupService { get; }
protected IPostRepository PostRepository { get; }
protected ITagRepository TagRepository { get; }
protected ICommentRepository CommentRepository { get; }
protected IDistributedCache<List<PostCacheItem>> PostsCache { get; }
protected ILocalEventBus LocalEventBus { get; }
public PostAppService(
IPostRepository postRepository,
ITagRepository tagRepository,
ICommentRepository commentRepository,
IBlogUserLookupService userLookupService,
IDistributedCache<List<PostCacheItem>> postsCache,
ILocalEventBus localEventBus
)
{
UserLookupService = userLookupService;
PostRepository = postRepository;
TagRepository = tagRepository;
CommentRepository = commentRepository;
PostsCache = postsCache;
LocalEventBus = localEventBus;
}
public virtual async Task<ListResultDto<PostWithDetailsDto>> GetListByBlogIdAndTagNameAsync(Guid id, string tagName)
{
var posts = await PostRepository.GetPostsByBlogId(id);
var tag = tagName.IsNullOrWhiteSpace() ? null : await TagRepository.FindByNameAsync(id, tagName);
var userDictionary = new Dictionary<Guid, BlogUserDto>();
var postDtos = new List<PostWithDetailsDto>(ObjectMapper.Map<List<Post>, List<PostWithDetailsDto>>(posts));
foreach (var postDto in postDtos)
{
postDto.Tags = await GetTagsOfPostAsync(postDto.Id);
}
if (tag != null)
{
postDtos = await FilterPostsByTagAsync(postDtos, tag);
}
foreach (var postDto in postDtos)
{
if (postDto.CreatorId.HasValue)
{
if (!userDictionary.ContainsKey(postDto.CreatorId.Value))
{
var creatorUser = await UserLookupService.FindByIdAsync(postDto.CreatorId.Value);
if (creatorUser != null)
{
userDictionary[creatorUser.Id] = ObjectMapper.Map<BlogUser, BlogUserDto>(creatorUser);
}
}
if (userDictionary.ContainsKey(postDto.CreatorId.Value))
{
postDto.Writer = userDictionary[(Guid)postDto.CreatorId];
}
}
}
return new ListResultDto<PostWithDetailsDto>(postDtos);
}
public virtual async Task<ListResultDto<PostWithDetailsDto>> GetTimeOrderedListAsync(Guid blogId)
{
var postCacheItems = await PostsCache.GetOrAddAsync(
blogId.ToString(),
async () => await GetTimeOrderedPostsAsync(blogId),
() => new DistributedCacheEntryOptions
{
AbsoluteExpiration = DateTimeOffset.Now.AddHours(1)
}
);
var postsWithDetails = ObjectMapper.Map<List<PostCacheItem>, List<PostWithDetailsDto>>(postCacheItems);
foreach (var post in postsWithDetails)
{
if (post.CreatorId.HasValue)
{
var creatorUser = await UserLookupService.FindByIdAsync(post.CreatorId.Value);
if (creatorUser != null)
{
post.Writer = ObjectMapper.Map<BlogUser, BlogUserDto>(creatorUser);
}
}
}
return new ListResultDto<PostWithDetailsDto>(postsWithDetails);
}
public virtual async Task<PostWithDetailsDto> GetForReadingAsync(GetPostInput input)
{
var post = await PostRepository.GetPostByUrl(input.BlogId, input.Url);
post.IncreaseReadCount();
var postDto = ObjectMapper.Map<Post, PostWithDetailsDto>(post);
postDto.Tags = await GetTagsOfPostAsync(postDto.Id);
if (postDto.CreatorId.HasValue)
{
var creatorUser = await UserLookupService.FindByIdAsync(postDto.CreatorId.Value);
postDto.Writer = ObjectMapper.Map<BlogUser, BlogUserDto>(creatorUser);
}
return postDto;
}
public virtual async Task<PostWithDetailsDto> GetAsync(Guid id)
{
var post = await PostRepository.GetAsync(id);
var postDto = ObjectMapper.Map<Post, PostWithDetailsDto>(post);
postDto.Tags = await GetTagsOfPostAsync(postDto.Id);
if (postDto.CreatorId.HasValue)
{
var creatorUser = await UserLookupService.FindByIdAsync(postDto.CreatorId.Value);
postDto.Writer = ObjectMapper.Map<BlogUser, BlogUserDto>(creatorUser);
}
return postDto;
}
[Authorize(BloggingPermissions.Posts.Delete)]
public virtual async Task DeleteAsync(Guid id)
{
var post = await PostRepository.GetAsync(id);
await AuthorizationService.CheckAsync(post, CommonOperations.Delete);
var tags = await GetTagsOfPostAsync(id);
await TagRepository.DecreaseUsageCountOfTagsAsync(tags.Select(t => t.Id).ToList());
await CommentRepository.DeleteOfPost(id);
await PostRepository.DeleteAsync(id);
await PublishPostChangedEventAsync(post.BlogId);
}
[Authorize(BloggingPermissions.Posts.Update)]
public virtual async Task<PostWithDetailsDto> UpdateAsync(Guid id, UpdatePostDto input)
{
var post = await PostRepository.GetAsync(id);
input.Url = await RenameUrlIfItAlreadyExistAsync(input.BlogId, input.Url, post);
await AuthorizationService.CheckAsync(post, CommonOperations.Update);
post.SetTitle(input.Title);
post.SetUrl(input.Url);
post.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp);
post.Content = input.Content;
post.Description = input.Description;
post.CoverImage = input.CoverImage;
post = await PostRepository.UpdateAsync(post);
var tagList = SplitTags(input.Tags);
await SaveTags(tagList, post);
await PublishPostChangedEventAsync(post.BlogId);
return ObjectMapper.Map<Post, PostWithDetailsDto>(post);
}
public virtual async Task<List<PostWithDetailsDto>> GetListByUserIdAsync(Guid userId)
{
var posts = await PostRepository.GetListByUserIdAsync(userId);
return ObjectMapper.Map<List<Post>, List<PostWithDetailsDto>>(posts);
}
public virtual async Task<List<PostWithDetailsDto>> GetLatestBlogPostsAsync(Guid blogId, int count)
{
var posts = await PostRepository.GetLatestBlogPostsAsync(blogId, count);
var userDictionary = new Dictionary<Guid, BlogUserDto>();
var postDtos = new List<PostWithDetailsDto>(ObjectMapper.Map<List<Post>, List<PostWithDetailsDto>>(posts));
foreach (var postDto in postDtos)
{
if (!postDto.CreatorId.HasValue)
{
continue;
}
if (userDictionary.TryGetValue(postDto.CreatorId.Value, out var creatorUserDto))
{
postDto.Writer = creatorUserDto;
continue;
}
var creatorUser = await UserLookupService.FindByIdAsync(postDto.CreatorId.Value);
if (creatorUser != null)
{
postDto.Writer = ObjectMapper.Map<BlogUser, BlogUserDto>(creatorUser);
userDictionary[creatorUser.Id] = postDto.Writer;
}
}
return new List<PostWithDetailsDto>(postDtos);
}
[Authorize(BloggingPermissions.Posts.Create)]
public virtual async Task<PostWithDetailsDto> CreateAsync(CreatePostDto input)
{
input.Url = await RenameUrlIfItAlreadyExistAsync(input.BlogId, input.Url);
var post = new Post(
id: GuidGenerator.Create(),
blogId: input.BlogId,
title: input.Title,
coverImage: input.CoverImage,
url: input.Url
)
{
Content = input.Content,
Description = input.Description
};
await PostRepository.InsertAsync(post);
var tagList = SplitTags(input.Tags);
await SaveTags(tagList, post);
await PublishPostChangedEventAsync(post.BlogId);
return ObjectMapper.Map<Post, PostWithDetailsDto>(post);
}
private async Task<List<PostCacheItem>> GetTimeOrderedPostsAsync(Guid blogId)
{
var posts = await PostRepository.GetOrderedList(blogId);
return ObjectMapper.Map<List<Post>, List<PostCacheItem>>(posts);
}
private async Task<string> RenameUrlIfItAlreadyExistAsync(Guid blogId, string url, Post existingPost = null)
{
if (await PostRepository.IsPostUrlInUseAsync(blogId, url, existingPost?.Id))
{
return url + "-" + Guid.NewGuid().ToString().Substring(0, 5);
}
return url;
}
private async Task SaveTags(ICollection<string> tags, Post post)
{
tags = tags
.Select(t => t.ToLowerInvariant())
.Distinct()
.ToList();
await RemoveOldTags(tags, post);
await AddNewTags(tags, post);
}
private async Task RemoveOldTags(ICollection<string> newTags, Post post)
{
foreach (var oldTag in post.Tags.ToList())
{
var tag = await TagRepository.GetAsync(oldTag.TagId);
var oldTagNameInNewTags = newTags.FirstOrDefault(t => t == tag.Name);
if (oldTagNameInNewTags == null)
{
post.RemoveTag(oldTag.TagId);
tag.DecreaseUsageCount();
await TagRepository.UpdateAsync(tag);
}
else
{
newTags.Remove(oldTagNameInNewTags);
}
}
}
private async Task AddNewTags(IEnumerable<string> newTags, Post post)
{
var tags = await TagRepository.GetListAsync(post.BlogId);
foreach (var newTag in newTags)
{
var tag = tags.FirstOrDefault(t => t.Name == newTag);
if (tag == null)
{
tag = await TagRepository.InsertAsync(new Tag(GuidGenerator.Create(), post.BlogId, newTag, 1));
}
else
{
tag.IncreaseUsageCount();
tag = await TagRepository.UpdateAsync(tag);
}
post.AddTag(tag.Id);
}
}
private async Task<List<TagDto>> GetTagsOfPostAsync(Guid id)
{
var tagIds = (await PostRepository.GetAsync(id)).Tags;
var tags = await TagRepository.GetListAsync(tagIds.Select(t => t.TagId));
return ObjectMapper.Map<List<Tag>, List<TagDto>>(tags);
}
private List<string> SplitTags(string tags)
{
if (tags.IsNullOrWhiteSpace())
{
return new List<string>();
}
return new List<string>(tags.Split(",").Select(t => t.Trim()));
}
private Task<List<PostWithDetailsDto>> FilterPostsByTagAsync(IEnumerable<PostWithDetailsDto> allPostDtos, Tag tag)
{
var filteredPostDtos = allPostDtos.Where(p => p.Tags?.Any(t => t.Id == tag.Id) ?? false).ToList();
return Task.FromResult(filteredPostDtos);
}
private async Task PublishPostChangedEventAsync(Guid blogId)
{
await LocalEventBus.PublishAsync(
new PostChangedEvent
{
BlogId = blogId
});
}
}
}