using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Sozsoft.Platform.BlobStoring; using Sozsoft.Platform.Entities; using Sozsoft.Platform.FileManagement; using Sozsoft.Platform.Identity; using Sozsoft.Platform.Identity.Dto; using Sozsoft.Platform.Extensions; using Volo.Abp.Domain.Repositories; using Volo.Abp.Identity; using Volo.Abp.MultiTenancy; using Volo.Abp.Uow; using Microsoft.AspNetCore.Mvc; namespace Sozsoft.Platform.Intranet; [Authorize] public class IntranetAppService : PlatformAppService, IIntranetAppService { private readonly ICurrentTenant _currentTenant; private readonly BlobManager _blobContainer; private readonly IConfiguration _configuration; private readonly IRepository _eventRepository; private readonly IIdentityUserAppService _identityUserAppService; private readonly IIdentityUserRepository _identityUserRepository; private readonly IRepository _departmentRepository; private readonly IRepository _jobPositionRepository; private readonly IRepository _announcementRepository; private readonly IRepository _surveyRepository; private readonly IRepository _surveyResponseRepository; private readonly IRepository _surveyAnswerRepository; private readonly IRepository _socialPostRepository; private readonly IRepository _socialCommentRepository; private readonly IRepository _socialLikeRepository; private readonly IRepository _socialMediaRepository; private readonly IRepository _socialPollOptionRepository; private readonly IRepository _eventCommentRepository; private readonly IRepository _eventLikeRepository; public IntranetAppService( ICurrentTenant currentTenant, BlobManager blobContainer, IConfiguration configuration, IRepository eventRepository, IIdentityUserAppService identityUserAppService, IIdentityUserRepository identityUserRepository, IRepository departmentRepository, IRepository jobPositionRepository, IRepository announcementRepository, IRepository surveyRepository, IRepository surveyResponseRepository, IRepository surveyAnswerRepository, IRepository socialPostRepository, IRepository socialCommentRepository, IRepository socialLikeRepository, IRepository socialMediaRepository, IRepository socialPollOptionRepository, IRepository eventCommentRepository, IRepository eventLikeRepository ) { _currentTenant = currentTenant; _blobContainer = blobContainer; _configuration = configuration; _eventRepository = eventRepository; _identityUserAppService = identityUserAppService; _identityUserRepository = identityUserRepository; _departmentRepository = departmentRepository; _jobPositionRepository = jobPositionRepository; _announcementRepository = announcementRepository; _surveyRepository = surveyRepository; _surveyResponseRepository = surveyResponseRepository; _surveyAnswerRepository = surveyAnswerRepository; _socialPostRepository = socialPostRepository; _socialCommentRepository = socialCommentRepository; _socialLikeRepository = socialLikeRepository; _socialMediaRepository = socialMediaRepository; _socialPollOptionRepository = socialPollOptionRepository; _eventCommentRepository = eventCommentRepository; _eventLikeRepository = eventLikeRepository; } [UnitOfWork] public async Task GetIntranetDashboardAsync() { return new IntranetDashboardDto { Birthdays = await GetBirthdaysAsync(), //1 Documents = await GetIntranetDocumentsAsync(BlobContainerNames.Intranet), //2 Announcements = await GetAnnouncementsAsync(), //3 Surveys = await GetSurveysAsync(), //4 Events = await GetUpcomingEventsAsync(), //5 }; } private async Task<(Dictionary DepartmentDict, Dictionary JobPositionDict)> GetUserLookupDictionariesAsync() { var departments = await _departmentRepository.GetListAsync(); var jobPositions = await _jobPositionRepository.GetListAsync(); return ( departments.ToDictionary(d => d.Id, d => d.Name), jobPositions.ToDictionary(j => j.Id, j => j) ); } private UserInfoViewModel MapUserInfoViewModel( IdentityUser user, IReadOnlyDictionary departmentDict, IReadOnlyDictionary jobPositionDict) { return ObjectMapper .Map(user) .MapDepartmentAndJobPositionAssignments(departmentDict, jobPositionDict); } private async Task GetDashboardFallbackUserAsync( IReadOnlyDictionary departmentDict, IReadOnlyDictionary jobPositionDict) { var normalizedAdmin = PlatformConsts.AbpIdentity.User.AdminEmailDefaultValue; var user = await _identityUserRepository.FindByNormalizedUserNameAsync(normalizedAdmin) ?? await _identityUserRepository.FindByNormalizedEmailAsync(normalizedAdmin); if (user == null && CurrentUser.Id.HasValue) { user = await _identityUserRepository.FindAsync(CurrentUser.Id.Value); } return user != null ? MapUserInfoViewModel(user, departmentDict, jobPositionDict) : null; } private async Task> GetUpcomingEventsAsync() { var queryable = await _eventRepository .WithDetailsAsync(e => e.Category, e => e.Type); var events = await AsyncExecuter.ToListAsync( queryable.Where(e => e.isPublished).OrderByDescending(e => e.CreationTime) ); if (events.Count == 0) return []; var eventIds = events.Select(e => e.Id).ToList(); // Load all comments for these events var commentsQueryable = await _eventCommentRepository.GetQueryableAsync(); var allComments = await AsyncExecuter.ToListAsync( commentsQueryable.Where(c => eventIds.Contains(c.EventId)).OrderBy(c => c.CreationTime) ); // Load all likes for these events var likesQueryable = await _eventLikeRepository.GetQueryableAsync(); var allLikes = await AsyncExecuter.ToListAsync( likesQueryable.Where(l => eventIds.Contains(l.EventId)) ); var likedEventIds = allLikes .Where(l => l.UserId == CurrentUser.Id) .Select(l => l.EventId) .ToHashSet(); // Collect all unique user IDs var userIds = new HashSet(); foreach (var evt in events) { if (evt.UserId.HasValue) userIds.Add(evt.UserId.Value); } foreach (var comment in allComments) { userIds.Add(comment.UserId); } var (departmentDict, jobPositionDict) = await GetUserLookupDictionariesAsync(); var fallbackUser = await GetDashboardFallbackUserAsync(departmentDict, jobPositionDict); var users = await _identityUserRepository.GetListAsync(); var userDict = users .Where(u => userIds.Contains(u.Id)) .ToDictionary(u => u.Id, u => MapUserInfoViewModel(u, departmentDict, jobPositionDict)); var commentsByEvent = allComments.GroupBy(c => c.EventId) .ToDictionary(g => g.Key, g => g.ToList()); var result = new List(); foreach (var evt in events) { var user = evt.UserId.HasValue && userDict.TryGetValue(evt.UserId.Value, out var eventUser) ? eventUser : fallbackUser; if (user == null) continue; var commentDtos = new List(); if (commentsByEvent.TryGetValue(evt.Id, out var eventComments)) { foreach (var c in eventComments) { commentDtos.Add(new EventCommentDto { Id = c.Id, EventId = c.EventId, UserId = c.UserId, User = userDict.TryGetValue(c.UserId, out var commentUser) ? commentUser : null, Content = c.Content, Likes = c.Likes, CreationTime = c.CreationTime }); } } var calendarEvent = new EventDto { Id = evt.Id, Name = evt.Name, Description = evt.Description, TypeName = evt.Type?.Name, CategoryName = evt.Category?.Name, Date = evt.Date, Place = evt.Place, User = user, ParticipantsCount = evt.ParticipantsCount, Likes = evt.Likes, IsLiked = likedEventIds.Contains(evt.Id), IsPublished = evt.isPublished, Photos = evt.Photos, Comments = commentDtos }; result.Add(calendarEvent); } return result; } public async Task> GetEventCommentsAsync(Guid eventId) { var commentsQueryable = await _eventCommentRepository.GetQueryableAsync(); var comments = await AsyncExecuter.ToListAsync( commentsQueryable.Where(c => c.EventId == eventId).OrderBy(c => c.CreationTime) ); if (comments.Count == 0) return []; var userIds = comments.Select(c => c.UserId).Distinct().ToList(); var (departmentDict, jobPositionDict) = await GetUserLookupDictionariesAsync(); var users = await _identityUserRepository.GetListAsync(); var userDict = users .Where(u => userIds.Contains(u.Id)) .ToDictionary(u => u.Id, u => MapUserInfoViewModel(u, departmentDict, jobPositionDict)); return comments.Select(c => new EventCommentDto { Id = c.Id, EventId = c.EventId, UserId = c.UserId, User = userDict.TryGetValue(c.UserId, out var u) ? u : null, Content = c.Content, Likes = c.Likes, CreationTime = c.CreationTime }).ToList(); } public async Task CreateEventCommentAsync(Guid eventId, string content) { var comment = new EventComment { EventId = eventId, UserId = CurrentUser.Id ?? Guid.Empty, Content = content, Likes = 0 }; comment = await _eventCommentRepository.InsertAsync(comment, autoSave: true); var (departmentDict, jobPositionDict) = await GetUserLookupDictionariesAsync(); var user = await _identityUserRepository.FindAsync(comment.UserId); var userViewModel = user != null ? MapUserInfoViewModel(user, departmentDict, jobPositionDict) : null; return new EventCommentDto { Id = comment.Id, EventId = comment.EventId, UserId = comment.UserId, User = userViewModel, Content = comment.Content, Likes = comment.Likes, CreationTime = comment.CreationTime }; } [HttpPost("api/app/intranet/like-event")] public async Task LikeEventAsync(Guid id) { var evt = await _eventRepository.GetAsync(id); var likeQueryable = await _eventLikeRepository.GetQueryableAsync(); var existingLike = await AsyncExecuter.FirstOrDefaultAsync( likeQueryable.Where(l => l.EventId == id && l.UserId == CurrentUser.Id)); bool isNowLiked; if (existingLike != null) { await _eventLikeRepository.DeleteAsync(existingLike.Id); evt.Likes = Math.Max(0, evt.Likes - 1); isNowLiked = false; } else { await _eventLikeRepository.InsertAsync(new EventLike(Guid.NewGuid()) { EventId = id, UserId = CurrentUser.Id, }); evt.Likes++; isNowLiked = true; } await _eventRepository.UpdateAsync(evt, autoSave: true); return new EventDto { Id = evt.Id, Likes = evt.Likes, IsLiked = isNowLiked, }; } private async Task> GetBirthdaysAsync() { var today = DateTime.Now; var users = await _identityUserRepository.GetListAsync(); var userList = users .Where(u => { var birthDate = u.GetBirthDate(); return birthDate.HasValue && birthDate.Value.Day == today.Day && birthDate.Value.Month == today.Month; }) .ToList(); var (departmentDict, jobPositionDict) = await GetUserLookupDictionariesAsync(); return userList .Select(user => MapUserInfoViewModel(user, departmentDict, jobPositionDict)) .ToList(); } private async Task> GetAnnouncementsAsync() { var announcements = await _announcementRepository.GetListAsync(); var announcementDtos = new List(); var (departmentDict, jobPositionDict) = await GetUserLookupDictionariesAsync(); var fallbackUser = await GetDashboardFallbackUserAsync(departmentDict, jobPositionDict); foreach (var announcement in announcements) { var dto = ObjectMapper.Map(announcement); if (announcement.UserId.HasValue) { var user = await _identityUserRepository.FindAsync(announcement.UserId.Value); if (user != null) { dto.User = MapUserInfoViewModel(user, departmentDict, jobPositionDict); } } dto.User ??= fallbackUser; announcementDtos.Add(dto); } return announcementDtos; } private async Task> GetSurveysAsync() { var queryable = await _surveyRepository.GetQueryableAsync(); var surveys = await AsyncExecuter.ToListAsync( queryable .AsNoTracking() .Where(s => s.Status == "active") .Include(s => s.Questions) .ThenInclude(q => q.Options) ); var dtos = ObjectMapper.Map, List>(surveys); // Tüm anketler için mevcut kullanıcının cevabını çek (anonim dahil — CreatorId ile) if (CurrentUser.IsAuthenticated) { var allSurveyIds = surveys.Select(s => s.Id).ToList(); if (allSurveyIds.Any()) { var rq = await _surveyResponseRepository.GetQueryableAsync(); var myResponses = await AsyncExecuter.ToListAsync( rq.AsNoTracking() .Where(r => allSurveyIds.Contains(r.SurveyId) && (r.UserId == CurrentUser.Id || r.CreatorId == CurrentUser.Id)) .Include(r => r.Answers) ); var responseMap = myResponses.ToDictionary(r => r.SurveyId); for (var i = 0; i < surveys.Count; i++) { if (responseMap.TryGetValue(surveys[i].Id, out var myResponse)) dtos[i].MyResponse = ObjectMapper.Map(myResponse); } } } return dtos; } [UnitOfWork] public async Task> GetIntranetSocialPostsAsync(int skipCount, int maxResultCount) { // Önce sadece ID'leri sayfalayarak çek (collection include'lar olmadan) var baseQueryable = await _socialPostRepository.GetQueryableAsync(); var pagedIds = await AsyncExecuter.ToListAsync( baseQueryable .OrderByDescending(p => p.CreationTime) .Skip(skipCount) .Take(maxResultCount) .Select(p => p.Id)); if (pagedIds.Count == 0) return []; // Sonra sadece bu ID'ler için detayları yükle var queryable = await _socialPostRepository .WithDetailsAsync(e => e.Location, e => e.Media, e => e.Comments, e => e.Likes); var socialPosts = await AsyncExecuter.ToListAsync( queryable .Where(p => pagedIds.Contains(p.Id)) .OrderByDescending(p => p.CreationTime)); var dtos = ObjectMapper.Map, List>(socialPosts); // Collect all unique user IDs to resolve in a single query var userIds = dtos .Select(p => p.UserId) .Union(dtos.SelectMany(p => (p.Comments ?? []).Select(c => c.UserId))) .Union(dtos.SelectMany(p => (p.Likes ?? []).Select(l => l.UserId))) .Where(id => id.HasValue) .Select(id => id!.Value) .Distinct() .ToList(); if (userIds.Count > 0) { var (departmentDict, jobPositionDict) = await GetUserLookupDictionariesAsync(); var users = await _identityUserRepository.GetListAsync(); var userMap = users .Where(u => userIds.Contains(u.Id)) .ToDictionary(u => u.Id, u => MapUserInfoViewModel(u, departmentDict, jobPositionDict)); foreach (var dto in dtos) { if (dto.UserId.HasValue && userMap.TryGetValue(dto.UserId.Value, out var postUser)) dto.User = postUser; foreach (var comment in dto.Comments ?? []) if (comment.UserId.HasValue && userMap.TryGetValue(comment.UserId.Value, out var commentUser)) comment.User = commentUser; foreach (var like in dto.Likes ?? []) if (like.UserId.HasValue && userMap.TryGetValue(like.UserId.Value, out var likeUser)) like.User = likeUser; } } foreach (var dto in dtos) { dto.IsOwnPost = dto.UserId == CurrentUser.Id; dto.IsLiked = dto.Likes?.Any(l => l.UserId == CurrentUser.Id) == true; } await EnrichPollOptionsAsync(dtos); return dtos; } private async Task EnrichPollOptionsAsync(IEnumerable dtos) { var pollMediaIds = dtos .Where(d => d.Media?.Type == "poll") .Select(d => d.Media!.Id) .ToList(); if (pollMediaIds.Count == 0) return; var optionsQueryable = await _socialPollOptionRepository.GetQueryableAsync(); var allOptions = await AsyncExecuter.ToListAsync( optionsQueryable.Where(o => pollMediaIds.Contains(o.SocialMediaId) && !o.IsDeleted)); var optionsByMedia = allOptions .GroupBy(o => o.SocialMediaId) .ToDictionary(g => g.Key, g => g.ToList()); foreach (var dto in dtos) { if (dto.Media?.Type == "poll" && optionsByMedia.TryGetValue(dto.Media.Id, out var opts)) dto.Media.PollOptions = ObjectMapper.Map, List>(opts); } } public async Task> GetIntranetDocumentsAsync(string folderPath) { var items = new List(); var cdnBasePath = _configuration["App:CdnPath"]; if (string.IsNullOrEmpty(cdnBasePath)) { Logger.LogWarning("CDN path is not configured"); return items; } var tenantId = _currentTenant.Id?.ToString() ?? "host"; var fullPath = Path.Combine(cdnBasePath, tenantId); if (!string.IsNullOrEmpty(folderPath)) { fullPath = Path.Combine(fullPath, folderPath); } if (!Directory.Exists(fullPath)) { Logger.LogWarning($"Directory not found: {fullPath}"); return items; } var files = Directory.GetFiles(fullPath); foreach (var file in files) { var fileInfo = new FileInfo(file); var relativePath = string.IsNullOrEmpty(folderPath) ? fileInfo.Name : $"{folderPath}/{fileInfo.Name}"; items.Add(new FileItemDto { Id = Guid.NewGuid().ToString(), Name = fileInfo.Name, Type = "file", Size = fileInfo.Length, Extension = fileInfo.Extension, MimeType = GetMimeType(fileInfo.Extension), CreatedAt = fileInfo.CreationTime, ModifiedAt = fileInfo.LastWriteTime, Path = relativePath, ParentId = string.Empty, IsReadOnly = false, ChildCount = 0, TenantId = _currentTenant.Id?.ToString() }); } return items.OrderBy(x => x.Name).ToList(); } private string GetMimeType(string extension) { return extension.ToLowerInvariant() switch { ".pdf" => "application/pdf", ".doc" => "application/msword", ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".xls" => "application/vnd.ms-excel", ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ".ppt" => "application/vnd.ms-powerpoint", ".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation", ".jpg" or ".jpeg" => "image/jpeg", ".png" => "image/png", ".gif" => "image/gif", ".txt" => "text/plain", ".zip" => "application/zip", ".rar" => "application/x-rar-compressed", _ => "application/octet-stream" }; } public async Task CreateSurveyResponseAsync(SubmitSurveyInput input) { var survey = await _surveyRepository.GetAsync(input.SurveyId); SurveyResponse? response = null; if (CurrentUser.IsAuthenticated) { var responseQueryable = await _surveyResponseRepository.GetQueryableAsync(); response = await AsyncExecuter.FirstOrDefaultAsync( responseQueryable.Where(r => r.SurveyId == input.SurveyId && (r.UserId == CurrentUser.Id || r.CreatorId == CurrentUser.Id)) ); } if (response != null) { var answerQueryable = await _surveyAnswerRepository.GetQueryableAsync(); var existingAnswers = await AsyncExecuter.ToListAsync( answerQueryable.Where(a => a.ResponseId == response.Id) ); var existingAnswerMap = existingAnswers.ToDictionary(x => x.QuestionId); foreach (var inputAnswer in input.Answers) { if (existingAnswerMap.TryGetValue(inputAnswer.QuestionId, out var existingAnswer)) { existingAnswer.Value = inputAnswer.Value ?? string.Empty; existingAnswer.QuestionType = inputAnswer.QuestionType; await _surveyAnswerRepository.UpdateAsync(existingAnswer); } else { await _surveyAnswerRepository.InsertAsync(new SurveyAnswer(Guid.NewGuid()) { ResponseId = response.Id, QuestionId = inputAnswer.QuestionId, QuestionType = inputAnswer.QuestionType, Value = inputAnswer.Value ?? string.Empty }); } } response.SubmissionTime = Clock.Now; await _surveyResponseRepository.UpdateAsync(response); return; } var newResponse = new SurveyResponse(Guid.NewGuid()) { SurveyId = input.SurveyId, UserId = survey.IsAnonymous ? null : CurrentUser.Id, SubmissionTime = Clock.Now, Answers = input.Answers.Select(a => new SurveyAnswer(Guid.NewGuid()) { QuestionId = a.QuestionId, QuestionType = a.QuestionType, Value = a.Value ?? string.Empty }).ToList() }; await _surveyResponseRepository.InsertAsync(newResponse); survey.Responses++; await _surveyRepository.UpdateAsync(survey); } public async Task CreateSocialPostAsync(CreateSocialPostInput input) { var post = new SocialPost(Guid.NewGuid()) { UserId = CurrentUser.Id, Content = input.Content, }; if (!string.IsNullOrWhiteSpace(input.LocationJson)) { var locData = System.Text.Json.JsonSerializer.Deserialize(input.LocationJson); post.Location = new SocialLocation(Guid.NewGuid()) { SocialPostId = post.Id, Name = locData.TryGetProperty("name", out var nameProp) ? nameProp.GetString() ?? string.Empty : string.Empty, Address = locData.TryGetProperty("address", out var addrProp) ? addrProp.GetString() : null, Lat = locData.TryGetProperty("lat", out var latProp) && latProp.TryGetDouble(out var latVal) ? latVal : null, Lng = locData.TryGetProperty("lng", out var lngProp) && lngProp.TryGetDouble(out var lngVal) ? lngVal : null, PlaceId = locData.TryGetProperty("placeId", out var placeIdProp) ? placeIdProp.GetString() : null, }; } if (input.Media != null) { var media = new SocialMedia(Guid.NewGuid()) { SocialPostId = post.Id, Type = input.Media.Type, Urls = input.Media.Urls ?? [], PollQuestion = input.Media.PollQuestion, }; if (input.Media.PollOptions is { Count: > 0 }) { media.PollOptions = input.Media.PollOptions .Select(o => new SocialPollOption(Guid.NewGuid()) { SocialMediaId = media.Id, Text = o.Text, Votes = 0, }) .ToList(); } post.Media = media; } await _socialPostRepository.InsertAsync(post, autoSave: true); // Reload with full navigation properties for mapping var queryable = await _socialPostRepository .WithDetailsAsync(e => e.Location, e => e.Media, e => e.Comments, e => e.Likes); var savedPost = await AsyncExecuter.FirstOrDefaultAsync(queryable.Where(p => p.Id == post.Id)); var dto = ObjectMapper.Map(savedPost!); dto.IsOwnPost = true; if (CurrentUser.Id.HasValue) { var user = await _identityUserRepository.FindAsync(CurrentUser.Id.Value); if (user != null) { var (departmentDict, jobPositionDict) = await GetUserLookupDictionariesAsync(); dto.User = MapUserInfoViewModel(user, departmentDict, jobPositionDict); } } await EnrichPollOptionsAsync([dto]); return dto; } public async Task DeleteSocialPostAsync(Guid id) { var post = await _socialPostRepository.GetAsync(id); if (post.UserId != CurrentUser.Id) throw new Volo.Abp.Authorization.AbpAuthorizationException("You can only delete your own posts."); await _socialPostRepository.DeleteAsync(id); } [HttpPost("api/app/intranet/like-social-post")] public async Task LikeSocialPostAsync(Guid id) { var post = await _socialPostRepository.GetAsync(id); var likeQueryable = await _socialLikeRepository.GetQueryableAsync(); var existingLike = await AsyncExecuter.FirstOrDefaultAsync( likeQueryable.Where(l => l.SocialPostId == id && l.UserId == CurrentUser.Id)); bool isNowLiked; if (existingLike != null) { await _socialLikeRepository.DeleteAsync(existingLike.Id); post.LikeCount = Math.Max(0, post.LikeCount - 1); isNowLiked = false; } else { await _socialLikeRepository.InsertAsync(new SocialLike(Guid.NewGuid()) { SocialPostId = id, UserId = CurrentUser.Id, }); post.LikeCount++; isNowLiked = true; } post.IsLiked = isNowLiked; await _socialPostRepository.UpdateAsync(post, autoSave: true); var queryable = await _socialPostRepository .WithDetailsAsync(e => e.Location, e => e.Media, e => e.Comments, e => e.Likes); var updated = await AsyncExecuter.FirstOrDefaultAsync(queryable.Where(p => p.Id == id)); var dto = ObjectMapper.Map(updated!); // Resolve user info var userIds = new List { dto.UserId } .Union(dto.Comments.Select(c => c.UserId)) .Union(dto.Likes.Select(l => l.UserId)) .Where(uid => uid.HasValue) .Select(uid => uid!.Value) .Distinct() .ToList(); if (userIds.Count > 0) { var (departmentDict, jobPositionDict) = await GetUserLookupDictionariesAsync(); var users = await _identityUserRepository.GetListAsync(); var userMap = users .Where(u => userIds.Contains(u.Id)) .ToDictionary(u => u.Id, u => MapUserInfoViewModel(u, departmentDict, jobPositionDict)); if (dto.UserId.HasValue && userMap.TryGetValue(dto.UserId.Value, out var postUser)) dto.User = postUser; foreach (var comment in dto.Comments) if (comment.UserId.HasValue && userMap.TryGetValue(comment.UserId.Value, out var commentUser)) comment.User = commentUser; foreach (var like in dto.Likes) if (like.UserId.HasValue && userMap.TryGetValue(like.UserId.Value, out var likeUser)) like.User = likeUser; } await EnrichPollOptionsAsync([dto]); dto.IsLiked = isNowLiked; dto.IsOwnPost = dto.UserId == CurrentUser.Id; return dto; } [HttpPost("api/app/intranet/comment-social-post")] public async Task CommentSocialPostAsync(Guid id, string content) { var comment = new SocialComment(Guid.NewGuid()) { SocialPostId = id, UserId = CurrentUser.Id, Content = content, }; await _socialCommentRepository.InsertAsync(comment, autoSave: true); var dto = ObjectMapper.Map(comment); if (CurrentUser.Id.HasValue) { var user = await _identityUserRepository.FindAsync(CurrentUser.Id.Value); if (user != null) { var (departmentDict, jobPositionDict) = await GetUserLookupDictionariesAsync(); dto.User = MapUserInfoViewModel(user, departmentDict, jobPositionDict); } } return dto; } [HttpPost("api/app/intranet/vote-social-poll")] public async Task VoteSocialPollAsync(Guid postId, Guid optionId) { var post = await _socialPostRepository.GetAsync(postId); var mediaQueryable = await _socialMediaRepository.GetQueryableAsync(); var media = await AsyncExecuter.FirstOrDefaultAsync( mediaQueryable.Where(m => m.SocialPostId == postId)); if (media == null) return; var option = await _socialPollOptionRepository.GetAsync(optionId); option.Votes++; await _socialPollOptionRepository.UpdateAsync(option, autoSave: true); media.PollTotalVotes = (media.PollTotalVotes ?? 0) + 1; media.PollUserVoteId = optionId.ToString(); await _socialMediaRepository.UpdateAsync(media, autoSave: true); } [HttpPost("api/app/intranet/{id}/announcement-view")] public async Task IncrementAnnouncementViewCountAsync(Guid id) { var announcement = await _announcementRepository.GetAsync(id); announcement.ViewCount++; await _announcementRepository.UpdateAsync(announcement, autoSave: true); } }