erp-platform/api/src/Erp.Platform.Application/Forum/ForumAppService.cs
2025-11-14 23:59:46 +03:00

618 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Authorization;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
namespace Erp.Platform.Forum;
[Authorize]
public class ForumAppService : PlatformAppService, IForumAppService
{
private readonly IRepository<ForumCategory, Guid> _categoryRepository;
private readonly IRepository<ForumTopic, Guid> _topicRepository;
private readonly IRepository<ForumPost, Guid> _postRepository;
private readonly IIdentityUserRepository _identityUserRepository;
public ForumAppService(
IRepository<ForumCategory, Guid> categoryRepository,
IRepository<ForumTopic, Guid> topicRepository,
IRepository<ForumPost, Guid> postRepository,
IIdentityUserRepository identityUserRepository)
{
_categoryRepository = categoryRepository;
_topicRepository = topicRepository;
_postRepository = postRepository;
_identityUserRepository = identityUserRepository;
}
// Search functionality
public async Task<ForumSearchResultDto> SearchAsync(SearchForumInput input)
{
var result = new ForumSearchResultDto
{
Categories = [],
Topics = [],
Posts = [],
TotalCount = 0
};
if (string.IsNullOrWhiteSpace(input.Query))
return result;
var query = input.Query.ToLower();
// Search in categories
if (input.SearchInCategories)
{
var categoryQuery = await _categoryRepository.GetQueryableAsync();
var categories = await AsyncExecuter.ToListAsync(
categoryQuery.Where(c => c.IsActive &&
(c.Name.ToLower().Contains(query) ||
c.Description.ToLower().Contains(query)))
.Take(10)
);
result.Categories = ObjectMapper.Map<List<ForumCategory>, List<ForumCategoryDto>>(categories);
}
// Search in topics
if (input.SearchInTopics)
{
var topicQuery = await _topicRepository.GetQueryableAsync();
var topics = await AsyncExecuter.ToListAsync(
topicQuery.Where(t =>
t.Title.ToLower().Contains(query) ||
t.Content.ToLower().Contains(query) ||
t.AuthorName.ToLower().Contains(query))
.OrderByDescending(t => t.CreationTime)
.Take(20)
);
result.Topics = ObjectMapper.Map<List<ForumTopic>, List<ForumTopicDto>>(topics);
}
// Search in posts
if (input.SearchInPosts)
{
var postQuery = await _postRepository.GetQueryableAsync();
var posts = await AsyncExecuter.ToListAsync(
postQuery.Where(p =>
p.Content.ToLower().Contains(query) ||
p.AuthorName.ToLower().Contains(query))
.OrderByDescending(p => p.CreationTime)
.Take(30)
);
result.Posts = ObjectMapper.Map<List<ForumPost>, List<ForumPostDto>>(posts);
}
result.TotalCount = result.Categories.Count + result.Topics.Count + result.Posts.Count;
return result;
}
// Category management
public async Task<PagedResultDto<ForumCategoryDto>> GetCategoriesAsync(GetCategoriesInput input)
{
var queryable = await _categoryRepository.GetQueryableAsync();
if (input.IsActive.HasValue)
{
queryable = queryable.Where(c => c.IsActive == input.IsActive.Value);
}
if (!string.IsNullOrWhiteSpace(input.Search))
{
var search = input.Search.ToLower();
queryable = queryable.Where(c =>
c.Name.ToLower().Contains(search) ||
c.Description.ToLower().Contains(search));
}
queryable = queryable.OrderBy(c => c.DisplayOrder);
var totalCount = await AsyncExecuter.CountAsync(queryable);
var skipCount = input.SkipCount >= 0 ? input.SkipCount : 0;
var maxResultCount = input.MaxResultCount > 0 ? input.MaxResultCount : 10;
var categories = await AsyncExecuter.ToListAsync(
queryable.Skip(input.SkipCount).Take(input.MaxResultCount)
);
return new PagedResultDto<ForumCategoryDto>(
totalCount,
ObjectMapper.Map<List<ForumCategory>, List<ForumCategoryDto>>(categories)
);
}
public async Task<ForumCategoryDto> GetCategoryAsync(Guid id)
{
var category = await _categoryRepository.GetAsync(id);
return ObjectMapper.Map<ForumCategory, ForumCategoryDto>(category);
}
public async Task<ForumCategoryDto> GetCategoryBySlugAsync(string slug)
{
var category = await _categoryRepository.FirstOrDefaultAsync(c => c.Slug == slug);
if (category == null)
throw new EntityNotFoundException(typeof(ForumCategory), slug);
return ObjectMapper.Map<ForumCategory, ForumCategoryDto>(category);
}
[Authorize("App.ForumManagement.Create")]
public async Task<ForumCategoryDto> CreateCategoryAsync(CreateForumCategoryDto input)
{
var category = new ForumCategory
{
Name = input.Name,
Slug = input.Slug,
Description = input.Description,
Icon = input.Icon,
DisplayOrder = input.DisplayOrder,
IsActive = input.IsActive,
IsLocked = input.IsLocked
};
await _categoryRepository.InsertAsync(category);
return ObjectMapper.Map<ForumCategory, ForumCategoryDto>(category);
}
[Authorize("App.ForumManagement.Update")]
public async Task<ForumCategoryDto> UpdateCategoryAsync(Guid id, UpdateForumCategoryDto input)
{
var category = await _categoryRepository.GetAsync(id);
category.Name = input.Name;
category.Slug = input.Slug;
category.Description = input.Description;
category.Icon = input.Icon;
category.DisplayOrder = input.DisplayOrder;
category.IsActive = input.IsActive;
category.IsLocked = input.IsLocked;
await _categoryRepository.UpdateAsync(category);
return ObjectMapper.Map<ForumCategory, ForumCategoryDto>(category);
}
[Authorize("App.ForumManagement.Update")]
public async Task<ForumCategoryDto> UpdateCategoryLockAsync(Guid id)
{
var category = await _categoryRepository.GetAsync(id);
category.IsLocked = !category.IsLocked;
await _categoryRepository.UpdateAsync(category);
return ObjectMapper.Map<ForumCategory, ForumCategoryDto>(category);
}
[Authorize("App.ForumManagement.Update")]
public async Task<ForumCategoryDto> UpdateCategoryActiveAsync(Guid id)
{
var category = await _categoryRepository.GetAsync(id);
category.IsActive = !category.IsActive;
await _categoryRepository.UpdateAsync(category);
return ObjectMapper.Map<ForumCategory, ForumCategoryDto>(category);
}
[Authorize("App.ForumManagement.Delete")]
public async Task DeleteCategoryAsync(Guid id)
{
// Delete all topics and posts in this category
var topics = await _topicRepository.GetListAsync(t => t.CategoryId == id);
var topicIds = topics.Select(t => t.Id).ToList();
if (topicIds.Any())
{
await _postRepository.DeleteAsync(p => topicIds.Contains(p.TopicId));
await _topicRepository.DeleteAsync(t => t.CategoryId == id);
}
await _categoryRepository.DeleteAsync(id);
}
// Topic management
public async Task<PagedResultDto<ForumTopicDto>> GetTopicsAsync(GetTopicsInput input)
{
var queryable = await _topicRepository.GetQueryableAsync();
if (input.CategoryId.HasValue)
{
queryable = queryable.Where(t => t.CategoryId == input.CategoryId.Value);
}
if (input.IsPinned.HasValue)
{
queryable = queryable.Where(t => t.IsPinned == input.IsPinned.Value);
}
if (input.IsSolved.HasValue)
{
queryable = queryable.Where(t => t.IsSolved == input.IsSolved.Value);
}
if (!string.IsNullOrWhiteSpace(input.Search))
{
var search = input.Search.ToLower();
queryable = queryable.Where(t =>
t.Title.ToLower().Contains(search) ||
t.Content.ToLower().Contains(search));
}
queryable = queryable.OrderByDescending(t => t.IsPinned)
.ThenByDescending(t => t.CreationTime);
var totalCount = await AsyncExecuter.CountAsync(queryable);
var topics = await AsyncExecuter.ToListAsync(
queryable.Skip(input.SkipCount).Take(input.MaxResultCount)
);
return new PagedResultDto<ForumTopicDto>(
totalCount,
ObjectMapper.Map<List<ForumTopic>, List<ForumTopicDto>>(topics)
);
}
public async Task<ForumTopicDto> GetTopicAsync(Guid id)
{
var topic = await _topicRepository.GetAsync(id);
topic.ViewCount++;
await _topicRepository.UpdateAsync(topic);
return ObjectMapper.Map<ForumTopic, ForumTopicDto>(topic);
}
public async Task<ForumTopicDto> CreateTopicAsync(CreateForumTopicDto input)
{
var topic = new ForumTopic(
GuidGenerator.Create(),
input.Title,
input.Content,
input.CategoryId,
CurrentUser.Id.Value,
CurrentUser.Name,
input.TenantId
)
{
IsPinned = input.IsPinned,
IsLocked = input.IsLocked
};
await _topicRepository.InsertAsync(topic);
// Update category topic count
var category = await _categoryRepository.GetAsync(input.CategoryId);
category.TopicCount++;
await _categoryRepository.UpdateAsync(category);
return ObjectMapper.Map<ForumTopic, ForumTopicDto>(topic);
}
public async Task<ForumTopicDto> UpdateTopicAsync(Guid id, UpdateForumTopicDto input)
{
var topic = await _topicRepository.GetAsync(id);
topic.Title = input.Title;
topic.Content = input.Content;
topic.IsPinned = input.IsPinned;
topic.IsLocked = input.IsLocked;
topic.IsSolved = input.IsSolved;
await _topicRepository.UpdateAsync(topic);
return ObjectMapper.Map<ForumTopic, ForumTopicDto>(topic);
}
public async Task DeleteTopicAsync(Guid id)
{
var topic = await _topicRepository.GetAsync(id);
// Delete all posts in this topic
await _postRepository.DeleteAsync(p => p.TopicId == id);
// Update category counts
var category = await _categoryRepository.GetAsync(topic.CategoryId);
category.TopicCount = Math.Max(0, category.TopicCount ?? 0 - 1);
var postCount = await _postRepository.CountAsync(p => p.TopicId == id);
category.PostCount = Math.Max(0, category.PostCount ?? 0 - postCount);
await _categoryRepository.UpdateAsync(category);
await _topicRepository.DeleteAsync(id);
}
// Post management
public async Task<PagedResultDto<ForumPostDto>> GetPostsAsync(GetPostsInput input)
{
var queryable = await _postRepository.GetQueryableAsync();
if (input.TopicId.HasValue)
{
queryable = queryable.Where(p => p.TopicId == input.TopicId.Value);
// Increment view count
var topic = await _topicRepository.GetAsync(input.TopicId.Value);
}
if (input.IsAcceptedAnswer.HasValue)
{
queryable = queryable.Where(p => p.IsAcceptedAnswer == input.IsAcceptedAnswer.Value);
}
if (!string.IsNullOrWhiteSpace(input.Search))
{
var search = input.Search.ToLower();
queryable = queryable.Where(p => p.Content.ToLower().Contains(search));
}
queryable = queryable.OrderBy(p => p.CreationTime);
var totalCount = await AsyncExecuter.CountAsync(queryable);
var posts = await AsyncExecuter.ToListAsync(
queryable.Skip(input.SkipCount).Take(input.MaxResultCount)
);
return new PagedResultDto<ForumPostDto>(
totalCount,
ObjectMapper.Map<List<ForumPost>, List<ForumPostDto>>(posts)
);
}
public async Task<ForumPostDto> GetPostAsync(Guid id)
{
var post = await _postRepository.GetAsync(id);
return ObjectMapper.Map<ForumPost, ForumPostDto>(post);
}
public async Task<ForumPostDto> CreatePostAsync(CreateForumPostDto input)
{
var post = new ForumPost(
GuidGenerator.Create(),
input.TopicId,
input.Content,
CurrentUser.Id.Value,
CurrentUser.Name,
input.ParentPostId,
input.TenantId
);
await _postRepository.InsertAsync(post, autoSave: true);
// 🔽 Update topic
var topic = await _topicRepository.GetAsync(input.TopicId);
topic.ReplyCount++;
topic.LastPostId = post.Id;
topic.LastPostDate = post.CreationTime;
topic.LastPostUserId = post.AuthorId;
topic.LastPostUserName = post.AuthorName;
await _topicRepository.UpdateAsync(topic);
// 🔽 Update category
var category = await _categoryRepository.GetAsync(topic.CategoryId);
category.PostCount++;
category.LastPostId = post.Id;
category.LastPostDate = post.CreationTime;
category.LastPostUserId = post.AuthorId;
category.LastPostUserName = post.AuthorName;
await _categoryRepository.UpdateAsync(category);
return ObjectMapper.Map<ForumPost, ForumPostDto>(post);
}
public async Task<ForumPostDto> UpdatePostAsync(Guid id, UpdateForumPostDto input)
{
var post = await _postRepository.GetAsync(id);
// Check if user can edit this post
if (post.AuthorId != CurrentUser.Id && !await AuthorizationService.IsGrantedAsync("Forum.Posts.Edit"))
{
throw new AbpAuthorizationException();
}
post.Content = input.Content;
post.IsAcceptedAnswer = input.IsAcceptedAnswer;
await _postRepository.UpdateAsync(post);
return ObjectMapper.Map<ForumPost, ForumPostDto>(post);
}
public async Task DeletePostAsync(Guid id)
{
var post = await _postRepository.GetAsync(id);
var topic = await _topicRepository.GetAsync(post.TopicId);
var category = await _categoryRepository.GetAsync(topic.CategoryId);
await _postRepository.DeleteAsync(id);
topic.ReplyCount = Math.Max(0, topic.ReplyCount - 1);
category.PostCount = Math.Max(0, category.PostCount ?? 0 - 1);
// 🔁 Last post değişti mi kontrol et
var latestPost = await _postRepository
.GetQueryableAsync()
.ContinueWith(q => q.Result
.Where(p => p.TopicId == topic.Id)
.OrderByDescending(p => p.CreationTime)
.FirstOrDefault()
);
if (latestPost != null)
{
topic.LastPostId = latestPost.Id;
topic.LastPostDate = latestPost.CreationTime;
topic.LastPostUserId = latestPost.AuthorId;
topic.LastPostUserName = latestPost.AuthorName;
category.LastPostId = latestPost.Id;
category.LastPostDate = latestPost.CreationTime;
category.LastPostUserId = latestPost.AuthorId;
category.LastPostUserName = latestPost.AuthorName;
}
else
{
// Tüm postlar silindiyse
topic.LastPostId = null;
topic.LastPostDate = null;
topic.LastPostUserId = null;
topic.LastPostUserName = null;
category.LastPostId = null;
category.LastPostDate = null;
category.LastPostUserId = null;
category.LastPostUserName = null;
}
await _topicRepository.UpdateAsync(topic);
await _categoryRepository.UpdateAsync(category);
}
public async Task<ForumPostDto> LikePostAsync(Guid id)
{
var post = await _postRepository.GetAsync(id);
post.LikeCount++;
await _postRepository.UpdateAsync(post);
var topic = await _topicRepository.GetAsync(post.TopicId);
var postsInTopic = await _postRepository.GetListAsync(p => p.TopicId == topic.Id);
topic.LikeCount = postsInTopic.Sum(p => p.LikeCount ?? 0);
await _topicRepository.UpdateAsync(topic);
return ObjectMapper.Map<ForumPost, ForumPostDto>(post);
}
public async Task<ForumPostDto> UnlikePostAsync(Guid id)
{
var post = await _postRepository.GetAsync(id);
post.LikeCount = Math.Max(0, post.LikeCount ?? 0 - 1);
await _postRepository.UpdateAsync(post);
// 🔽 Topic'in toplam beğeni sayısını güncelle
var topic = await _topicRepository.GetAsync(post.TopicId);
var postsInTopic = await _postRepository.GetListAsync(p => p.TopicId == topic.Id);
topic.LikeCount = postsInTopic.Sum(p => p.LikeCount ?? 0);
await _topicRepository.UpdateAsync(topic);
return ObjectMapper.Map<ForumPost, ForumPostDto>(post);
}
public async Task<ForumPostDto> MarkPostAsync(Guid id)
{
var post = await _postRepository.GetAsync(id);
post.IsAcceptedAnswer = true;
await _postRepository.UpdateAsync(post);
return ObjectMapper.Map<ForumPost, ForumPostDto>(post);
}
public async Task<ForumPostDto> UnmarkPostAsync(Guid id)
{
var post = await _postRepository.GetAsync(id);
post.IsAcceptedAnswer = false;
await _postRepository.UpdateAsync(post);
return ObjectMapper.Map<ForumPost, ForumPostDto>(post);
}
// Like/Unlike topic
public async Task<ForumTopicDto> LikeTopicAsync(Guid id)
{
var topic = await _topicRepository.GetAsync(id);
topic.LikeCount++;
await _topicRepository.UpdateAsync(topic);
return ObjectMapper.Map<ForumTopic, ForumTopicDto>(topic);
}
public async Task<ForumTopicDto> UnlikeTopicAsync(Guid id)
{
var topic = await _topicRepository.GetAsync(id);
topic.LikeCount = Math.Max(0, topic.LikeCount - 1);
await _topicRepository.UpdateAsync(topic);
return ObjectMapper.Map<ForumTopic, ForumTopicDto>(topic);
}
public async Task<ForumTopicDto> PinTopicAsync(Guid id)
{
var topic = await _topicRepository.GetAsync(id);
topic.IsPinned = true;
await _topicRepository.UpdateAsync(topic);
return ObjectMapper.Map<ForumTopic, ForumTopicDto>(topic);
}
public async Task<ForumTopicDto> UnpinTopicAsync(Guid id)
{
var topic = await _topicRepository.GetAsync(id);
topic.IsPinned = false;
await _topicRepository.UpdateAsync(topic);
return ObjectMapper.Map<ForumTopic, ForumTopicDto>(topic);
}
public async Task<ForumTopicDto> LockTopicAsync(Guid id)
{
var topic = await _topicRepository.GetAsync(id);
topic.IsLocked = true;
await _topicRepository.UpdateAsync(topic);
return ObjectMapper.Map<ForumTopic, ForumTopicDto>(topic);
}
public async Task<ForumTopicDto> UnlockTopicAsync(Guid id)
{
var topic = await _topicRepository.GetAsync(id);
topic.IsLocked = false;
await _topicRepository.UpdateAsync(topic);
return ObjectMapper.Map<ForumTopic, ForumTopicDto>(topic);
}
public async Task<ForumTopicDto> SolvedTopicAsync(Guid id)
{
var topic = await _topicRepository.GetAsync(id);
topic.IsSolved = true;
await _topicRepository.UpdateAsync(topic);
return ObjectMapper.Map<ForumTopic, ForumTopicDto>(topic);
}
public async Task<ForumTopicDto> UnsolvedTopicAsync(Guid id)
{
var topic = await _topicRepository.GetAsync(id);
topic.IsSolved = false;
await _topicRepository.UpdateAsync(topic);
return ObjectMapper.Map<ForumTopic, ForumTopicDto>(topic);
}
// Statistics
public async Task<ForumStatsDto> GetForumStatsAsync()
{
var totalCategories = await _categoryRepository.CountAsync();
var totalTopics = await _topicRepository.CountAsync();
var totalPosts = await _postRepository.CountAsync();
var totalUsers = await _identityUserRepository.GetCountAsync();
return new ForumStatsDto
{
TotalCategories = totalCategories,
TotalTopics = totalTopics,
TotalPosts = totalPosts,
TotalUsers = totalUsers,
ActiveUsers = totalUsers
};
}
}