using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using ClosedXML; using Erp.Platform.BlobStoring; using Erp.Platform.Entities; using Erp.Platform.FileManagement; using Erp.Platform.Intranet; using Microsoft.AspNetCore.Authorization; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Volo.Abp.Domain.Repositories; using Volo.Abp.MultiTenancy; namespace Erp.Platform.Public; [Authorize] public class IntranetAppService : PlatformAppService, IIntranetAppService { private readonly ICurrentTenant _currentTenant; private readonly BlobManager _blobContainer; private readonly IConfiguration _configuration; private readonly IRepository _eventRepository; private readonly IRepository _employeeRepository; private readonly IRepository _visitorRepository; private readonly IRepository _reservationRepository; private readonly IRepository _trainingRepository; private readonly IRepository _expenseRepository; private readonly IRepository _announcementRepository; private readonly IRepository _departmentRepository; private readonly IRepository _shuttleRouteRepository; private readonly IRepository _mealRepository; private readonly IRepository _leaveRepository; private readonly IRepository _overtimeRepository; private readonly IRepository _surveyRepository; private readonly IRepository _socialPostRepository; public IntranetAppService( ICurrentTenant currentTenant, BlobManager blobContainer, IConfiguration configuration, IRepository eventRepository, IRepository employeeRepository, IRepository visitorRepository, IRepository reservationRepository, IRepository trainingRepository, IRepository expenseRepository, IRepository announcementRepository, IRepository departmentRepository, IRepository shuttleRouteRepository, IRepository mealRepository, IRepository leaveRepository, IRepository overtimeRepository, IRepository surveyRepository, IRepository socialPostRepository ) { _currentTenant = currentTenant; _blobContainer = blobContainer; _configuration = configuration; _eventRepository = eventRepository; _employeeRepository = employeeRepository; _visitorRepository = visitorRepository; _reservationRepository = reservationRepository; _trainingRepository = trainingRepository; _expenseRepository = expenseRepository; _announcementRepository = announcementRepository; _departmentRepository = departmentRepository; _shuttleRouteRepository = shuttleRouteRepository; _mealRepository = mealRepository; _leaveRepository = leaveRepository; _overtimeRepository = overtimeRepository; _surveyRepository = surveyRepository; _socialPostRepository = socialPostRepository; } public async Task GetIntranetDashboardAsync() { return new IntranetDashboardDto { Events = await GetUpcomingEventsAsync(), Birthdays = await GetBirthdaysAsync(), Visitors = await GetVisitorsAsync(), Reservations = await GetReservationsAsync(), Trainings = await GetTrainingsAsync(), Expenses = await GetExpensesAsync(), Documents = await GetIntranetDocumentsAsync(BlobContainerNames.Intranet), Announcements = await GetAnnouncementsAsync(), ShuttleRoutes = await GetShuttleRoutesAsync(), Meals = await GetMealsAsync(), Leaves = await GetLeavesAsync(), Overtimes = await GetOvertimesAsync(), Surveys = await GetSurveysAsync(), SocialPosts = await GetSocialPostsAsync() }; } private async Task> GetSocialPostsAsync() { var socialPosts = await _socialPostRepository .WithDetailsAsync(e => e.Employee, e => e.Location, e => e.Media, e => e.Comments, e => e.Likes) .ContinueWith(t => t.Result.ToList()); return ObjectMapper.Map, List>(socialPosts); } private async Task> GetSurveysAsync() { var queryable = await _surveyRepository.GetQueryableAsync(); var surveys = queryable .Where(s => s.Status == "active") .Include(s => s.Questions) .ThenInclude(q => q.Options) .ToList(); return ObjectMapper.Map, List>(surveys); } private async Task> GetOvertimesAsync() { var today = DateTime.Now; var overtimes = await _overtimeRepository .WithDetailsAsync(e => e.Employee) .ContinueWith(t => t.Result.ToList()); return ObjectMapper.Map, List>(overtimes); } private async Task> GetLeavesAsync() { var today = DateTime.Now; var leaves = await _leaveRepository .WithDetailsAsync(e => e.Employee) .ContinueWith(t => t.Result.ToList()); return ObjectMapper.Map, List>(leaves); } private async Task> GetMealsAsync() { // Bu haftanın başlangıç ve bitiş tarihlerini hesapla var today = DateTime.Now.Date; var dayOfWeek = (int)today.DayOfWeek; // Pazartesi'yi haftanın başı olarak kabul ediyoruz (ISO 8601) var weekStart = today.AddDays(-(dayOfWeek == 0 ? 6 : dayOfWeek - 1)); var weekEnd = weekStart.AddDays(6); // Sadece bu haftanın yemeklerini getir var meals = await _mealRepository.GetListAsync(m => m.Date >= weekStart && m.Date <= weekEnd); var mealDtos = new List(); foreach (var meal in meals) { var dto = ObjectMapper.Map(meal); if (!string.IsNullOrEmpty(meal.Materials)) { dto.Materials = meal.Materials .Split('|', StringSplitOptions.RemoveEmptyEntries) .Select(r => r.Trim()) .ToArray(); } else { dto.Materials = []; } mealDtos.Add(dto); } return mealDtos; } private async Task> GetShuttleRoutesAsync() { var shuttleRoutes = await _shuttleRouteRepository.GetListAsync(); var shuttleRouteDtos = new List(); foreach (var shuttleRoute in shuttleRoutes) { var dto = ObjectMapper.Map(shuttleRoute); // Route string'ini array'e çevir (pipe ile ayrılmış) if (!string.IsNullOrEmpty(shuttleRoute.Route)) { dto.Route = shuttleRoute.Route .Split('|', StringSplitOptions.RemoveEmptyEntries) .Select(r => r.Trim()) .ToArray(); } else { dto.Route = []; } shuttleRouteDtos.Add(dto); } return shuttleRouteDtos; } private async Task> GetAnnouncementsAsync() { var announcements = await _announcementRepository .WithDetailsAsync(e => e.Employee) .ContinueWith(t => t.Result.ToList()); var announcementDtos = new List(); // Tüm departmanları bir kez çek (performans için) var allDepartments = await _departmentRepository.GetListAsync(); foreach (var announcement in announcements) { var dto = ObjectMapper.Map(announcement); // Departments string'ini array'e çevir (pipe ile ayrılmış ID'ler) if (!string.IsNullOrEmpty(announcement.Departments)) { var departmentIds = announcement.Departments .Split('|', StringSplitOptions.RemoveEmptyEntries) .Select(d => d.Trim()) .ToArray(); // ID'leri Department Name'lere çevir var departmentNames = new List(); foreach (var deptId in departmentIds) { if (Guid.TryParse(deptId, out var guid)) { var department = allDepartments.FirstOrDefault(d => d.Id == guid); if (department != null) { departmentNames.Add(department.Name); } } } dto.Departments = departmentNames.ToArray(); } else { dto.Departments = []; } announcementDtos.Add(dto); } return announcementDtos; } private async Task GetExpensesAsync() { var queryable = await _expenseRepository.GetQueryableAsync(); var today = DateTime.Today; var oneMonthAgo = today.AddMonths(-1); // Son 1 ay içerisindeki kayıtlar var lastMonthExpenses = queryable .Where(a => a.RequestDate >= oneMonthAgo && a.RequestDate <= today); // Son 1 aydaki toplam talep miktarı var totalRequested = lastMonthExpenses.Sum(a => a.Amount); // Son 1 aydaki onaylanan harcamaların toplamı var totalApproved = lastMonthExpenses .Where(a => a.Status == "approved") .Sum(a => a.Amount); // Son 5 kayıt var last5Expenses = queryable .OrderByDescending(a => a.RequestDate) .Take(5) .ToList(); // Map işlemleri var last5Dtos = ObjectMapper.Map, List>(last5Expenses); // Dönüş DTO'su return new ExpensesDto { TotalRequested = totalRequested, TotalApproved = totalApproved, Last5Expenses = last5Dtos }; } private async Task> GetTrainingsAsync() { var trainings = await _trainingRepository.GetListAsync(a => a.StartDate >= DateTime.Today); return ObjectMapper.Map, List>(trainings); } private async Task> GetReservationsAsync() { var reservations = await _reservationRepository.GetListAsync(a => a.StartDate >= DateTime.Today); return ObjectMapper.Map, List>(reservations); } private async Task> GetVisitorsAsync() { var visitors = await _visitorRepository.GetListAsync(a => a.VisitDate >= DateTime.Today); return ObjectMapper.Map, List>(visitors); } private async Task> GetBirthdaysAsync() { var today = DateTime.Now; var employees = await _employeeRepository .WithDetailsAsync(e => e.EmploymentType, e => e.JobPosition, e => e.Department) .ContinueWith(t => t.Result .Where(e => e.BirthDate.Day == today.Day && e.BirthDate.Month == today.Month) .ToList()); return ObjectMapper.Map, List>(employees); } private async Task> GetUpcomingEventsAsync() { var events = await _eventRepository .WithDetailsAsync(e => e.Category, e => e.Type, e => e.Category, e => e.Photos, e => e.Comments) .ContinueWith(t => t.Result.ToList().Where(e => e.isPublished).OrderByDescending(e => e.CreationTime)); var result = new List(); foreach (var evt in events) { var employee = await _employeeRepository .WithDetailsAsync(e => e.JobPosition) .ContinueWith(t => t.Result.FirstOrDefault(e => e.Id == evt.EmployeeId)); if (employee != null) { var calendarEvent = new EventDto { Id = evt.Id.ToString(), Name = evt.Name, Description = evt.Description, TypeName = evt.Type?.Name, CategoryName = evt.Category?.Name, Date = evt.Date, Place = evt.Place, Organizer = new EventOrganizerDto { Id = employee.Id, Name = employee.Name, Position = employee.JobPosition.Name, Avatar = employee.Avatar }, Participants = evt.ParticipantsCount, Photos = [], Comments = [], Likes = evt.Likes, IsPublished = evt.isPublished }; // Comment'lerin author bilgilerini doldur if (evt.Comments != null && evt.Comments.Any()) { foreach (var comment in evt.Comments) { var commentAuthor = await _employeeRepository .WithDetailsAsync(e => e.JobPosition) .ContinueWith(t => t.Result.FirstOrDefault(e => e.Id == comment.EmployeeId)); if (commentAuthor != null) { calendarEvent.Comments.Add(new EventCommentDto { Id = comment.Id.ToString(), Employee = new EventOrganizerDto { Id = commentAuthor.Id, Name = commentAuthor.Name, Position = commentAuthor.JobPosition.Name, Avatar = commentAuthor.Avatar }, Content = comment.Content, CreationTime = comment.CreationTime, Likes = comment.Likes }); } } } result.Add(calendarEvent); } } return result; } 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 }); } 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" }; } }