erp-platform/api/src/Erp.Platform.Application/Forum/ForumAppService.cs

619 lines
21 KiB
C#
Raw Normal View History

2025-11-11 19:49:52 +00:00
using System;
2025-06-23 14:58:13 +00:00
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;
2025-11-11 19:49:52 +00:00
namespace Erp.Platform.Forum;
2025-06-23 14:58:13 +00:00
[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)
{
2025-10-07 06:28:16 +00:00
var category = new ForumCategory
2025-06-23 14:58:13 +00:00
{
2025-10-07 06:28:16 +00:00
Name = input.Name,
Slug = input.Slug,
Description = input.Description,
Icon = input.Icon,
DisplayOrder = input.DisplayOrder,
2025-06-23 14:58:13 +00:00
IsActive = input.IsActive,
IsLocked = input.IsLocked
2025-10-07 06:28:16 +00:00
2025-06-23 14:58:13 +00:00
};
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);
}
2025-06-23 21:22:11 +00:00
[Authorize("App.ForumManagement.Update")]
public async Task<ForumCategoryDto> UpdateCategoryLockAsync(Guid id)
{
var category = await _categoryRepository.GetAsync(id);
category.IsLocked = !category.IsLocked;
2025-06-24 07:09:33 +00:00
2025-06-23 21:22:11 +00:00
await _categoryRepository.UpdateAsync(category);
return ObjectMapper.Map<ForumCategory, ForumCategoryDto>(category);
}
2025-06-24 07:09:33 +00:00
[Authorize("App.ForumManagement.Update")]
2025-06-23 21:22:11 +00:00
public async Task<ForumCategoryDto> UpdateCategoryActiveAsync(Guid id)
{
var category = await _categoryRepository.GetAsync(id);
category.IsActive = !category.IsActive;
2025-06-24 07:09:33 +00:00
2025-06-23 21:22:11 +00:00
await _categoryRepository.UpdateAsync(category);
return ObjectMapper.Map<ForumCategory, ForumCategoryDto>(category);
}
2025-06-23 14:58:13 +00:00
[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,
2025-06-24 07:09:33 +00:00
CurrentUser.Name,
input.TenantId
2025-06-23 14:58:13 +00:00
)
{
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);
2025-08-20 19:26:08 +00:00
category.TopicCount = Math.Max(0, category.TopicCount ?? 0 - 1);
2025-06-23 14:58:13 +00:00
var postCount = await _postRepository.CountAsync(p => p.TopicId == id);
2025-08-20 19:26:08 +00:00
category.PostCount = Math.Max(0, category.PostCount ?? 0 - postCount);
2025-06-23 14:58:13 +00:00
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,
2025-06-24 07:09:33 +00:00
input.ParentPostId,
input.TenantId
2025-06-23 14:58:13 +00:00
);
await _postRepository.InsertAsync(post, autoSave: true);
2025-11-11 19:49:52 +00:00
// 🔽 Update topic
2025-06-23 14:58:13 +00:00
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);
2025-11-11 19:49:52 +00:00
// 🔽 Update category
2025-06-23 14:58:13 +00:00
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);
2025-08-20 19:26:08 +00:00
category.PostCount = Math.Max(0, category.PostCount ?? 0 - 1);
2025-06-23 14:58:13 +00:00
2025-11-12 12:59:31 +00:00
// 🔁 Last post değişti mi kontrol et
2025-06-23 14:58:13 +00:00
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
{
2025-11-12 12:59:31 +00:00
// Tüm postlar silindiyse
2025-06-23 14:58:13 +00:00
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);
2025-08-20 19:26:08 +00:00
topic.LikeCount = postsInTopic.Sum(p => p.LikeCount ?? 0);
2025-06-23 14:58:13 +00:00
await _topicRepository.UpdateAsync(topic);
return ObjectMapper.Map<ForumPost, ForumPostDto>(post);
}
public async Task<ForumPostDto> UnlikePostAsync(Guid id)
{
var post = await _postRepository.GetAsync(id);
2025-08-20 19:26:08 +00:00
post.LikeCount = Math.Max(0, post.LikeCount ?? 0 - 1);
2025-06-23 14:58:13 +00:00
await _postRepository.UpdateAsync(post);
2025-11-12 12:59:31 +00:00
// 🔽 Topic'in toplam beğeni sayısını güncelle
2025-06-23 14:58:13 +00:00
var topic = await _topicRepository.GetAsync(post.TopicId);
var postsInTopic = await _postRepository.GetListAsync(p => p.TopicId == topic.Id);
2025-08-20 19:26:08 +00:00
topic.LikeCount = postsInTopic.Sum(p => p.LikeCount ?? 0);
2025-06-23 14:58:13 +00:00
await _topicRepository.UpdateAsync(topic);
return ObjectMapper.Map<ForumPost, ForumPostDto>(post);
}
2025-06-24 07:09:33 +00:00
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);
}
2025-06-23 14:58:13 +00:00
// 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
2025-06-23 14:58:13 +00:00
};
}
}
2025-11-11 19:49:52 +00:00