erp-platform/api/src/Kurs.Platform.Application/Intranet/IntranetAppService.cs

481 lines
17 KiB
C#
Raw Normal View History

2025-10-28 18:03:15 +00:00
using System;
using System.Collections.Generic;
2025-10-29 10:20:21 +00:00
using System.IO;
2025-10-28 18:03:15 +00:00
using System.Linq;
using System.Threading.Tasks;
2025-10-29 13:11:03 +00:00
using ClosedXML;
2025-10-29 10:20:21 +00:00
using Kurs.Platform.BlobStoring;
2025-10-28 18:03:15 +00:00
using Kurs.Platform.Entities;
2025-10-29 10:20:21 +00:00
using Kurs.Platform.FileManagement;
using Kurs.Platform.Intranet;
2025-10-28 18:03:15 +00:00
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
2025-10-29 10:20:21 +00:00
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
2025-10-28 18:03:15 +00:00
using Volo.Abp.Domain.Repositories;
2025-10-29 10:20:21 +00:00
using Volo.Abp.MultiTenancy;
2025-10-28 18:03:15 +00:00
namespace Kurs.Platform.Public;
[Authorize]
public class IntranetAppService : PlatformAppService, IIntranetAppService
{
2025-10-29 10:20:21 +00:00
private readonly ICurrentTenant _currentTenant;
private readonly BlobManager _blobContainer;
private readonly IConfiguration _configuration;
2025-10-28 18:03:15 +00:00
private readonly IRepository<Event, Guid> _eventRepository;
private readonly IRepository<Employee, Guid> _employeeRepository;
2025-10-29 10:20:21 +00:00
private readonly IRepository<Visitor, Guid> _visitorRepository;
private readonly IRepository<Reservation, Guid> _reservationRepository;
private readonly IRepository<Training, Guid> _trainingRepository;
private readonly IRepository<Expense, Guid> _expenseRepository;
2025-10-29 11:57:08 +00:00
private readonly IRepository<Announcement, Guid> _announcementRepository;
private readonly IRepository<Department, Guid> _departmentRepository;
2025-10-29 13:11:03 +00:00
private readonly IRepository<ShuttleRoute, Guid> _shuttleRouteRepository;
private readonly IRepository<Meal, Guid> _mealRepository;
2025-10-29 13:45:58 +00:00
private readonly IRepository<Leave, Guid> _leaveRepository;
private readonly IRepository<Overtime, Guid> _overtimeRepository;
private readonly IRepository<Survey, Guid> _surveyRepository;
private readonly IRepository<SocialPost, Guid> _socialPostRepository;
2025-10-28 18:03:15 +00:00
public IntranetAppService(
2025-10-29 10:20:21 +00:00
ICurrentTenant currentTenant,
BlobManager blobContainer,
IConfiguration configuration,
2025-10-28 18:03:15 +00:00
IRepository<Event, Guid> eventRepository,
2025-10-29 10:20:21 +00:00
IRepository<Employee, Guid> employeeRepository,
IRepository<Visitor, Guid> visitorRepository,
IRepository<Reservation, Guid> reservationRepository,
IRepository<Training, Guid> trainingRepository,
2025-10-29 11:57:08 +00:00
IRepository<Expense, Guid> expenseRepository,
IRepository<Announcement, Guid> announcementRepository,
2025-10-29 13:11:03 +00:00
IRepository<Department, Guid> departmentRepository,
IRepository<ShuttleRoute, Guid> shuttleRouteRepository,
2025-10-29 13:45:58 +00:00
IRepository<Meal, Guid> mealRepository,
IRepository<Leave, Guid> leaveRepository,
IRepository<Overtime, Guid> overtimeRepository,
IRepository<Survey, Guid> surveyRepository,
IRepository<SocialPost, Guid> socialPostRepository
2025-10-29 11:57:08 +00:00
)
2025-10-28 18:03:15 +00:00
{
2025-10-29 10:20:21 +00:00
_currentTenant = currentTenant;
_blobContainer = blobContainer;
_configuration = configuration;
2025-10-28 18:03:15 +00:00
_eventRepository = eventRepository;
_employeeRepository = employeeRepository;
2025-10-29 10:20:21 +00:00
_visitorRepository = visitorRepository;
_reservationRepository = reservationRepository;
_trainingRepository = trainingRepository;
_expenseRepository = expenseRepository;
2025-10-29 11:57:08 +00:00
_announcementRepository = announcementRepository;
_departmentRepository = departmentRepository;
2025-10-29 13:11:03 +00:00
_shuttleRouteRepository = shuttleRouteRepository;
_mealRepository = mealRepository;
2025-10-29 13:45:58 +00:00
_leaveRepository = leaveRepository;
_overtimeRepository = overtimeRepository;
_surveyRepository = surveyRepository;
_socialPostRepository = socialPostRepository;
2025-10-28 18:03:15 +00:00
}
public async Task<IntranetDashboardDto> GetIntranetDashboardAsync()
{
return new IntranetDashboardDto
{
2025-10-29 10:20:21 +00:00
Events = await GetUpcomingEventsAsync(),
Birthdays = await GetBirthdaysAsync(),
Visitors = await GetVisitorsAsync(),
Reservations = await GetReservationsAsync(),
Trainings = await GetTrainingsAsync(),
Expenses = await GetExpensesAsync(),
2025-10-29 11:57:08 +00:00
Documents = await GetIntranetDocumentsAsync(BlobContainerNames.Intranet),
2025-10-29 13:11:03 +00:00
Announcements = await GetAnnouncementsAsync(),
ShuttleRoutes = await GetShuttleRoutesAsync(),
2025-10-29 13:45:58 +00:00
Meals = await GetMealsAsync(),
Leaves = await GetLeavesAsync(),
Overtimes = await GetOvertimesAsync(),
Surveys = await GetSurveysAsync(),
SocialPosts = await GetSocialPostsAsync()
2025-10-29 10:20:21 +00:00
};
}
private async Task<List<SocialPostDto>> 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<SocialPost>, List<SocialPostDto>>(socialPosts);
}
private async Task<List<SurveyDto>> 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<Survey>, List<SurveyDto>>(surveys);
}
2025-10-29 13:45:58 +00:00
private async Task<List<OvertimeDto>> GetOvertimesAsync()
{
var today = DateTime.Now;
var overtimes = await _overtimeRepository
.WithDetailsAsync(e => e.Employee)
.ContinueWith(t => t.Result.ToList());
return ObjectMapper.Map<List<Overtime>, List<OvertimeDto>>(overtimes);
}
private async Task<List<LeaveDto>> GetLeavesAsync()
{
var today = DateTime.Now;
var leaves = await _leaveRepository
.WithDetailsAsync(e => e.Employee)
.ContinueWith(t => t.Result.ToList());
return ObjectMapper.Map<List<Leave>, List<LeaveDto>>(leaves);
}
2025-10-29 13:11:03 +00:00
private async Task<List<MealDto>> 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
2025-10-29 13:45:58 +00:00
var meals = await _mealRepository.GetListAsync(m =>
2025-10-29 13:11:03 +00:00
m.Date >= weekStart && m.Date <= weekEnd);
var mealDtos = new List<MealDto>();
foreach (var meal in meals)
{
var dto = ObjectMapper.Map<Meal, MealDto>(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;
}
2025-10-29 13:45:58 +00:00
2025-10-29 13:11:03 +00:00
private async Task<List<ShuttleRouteDto>> GetShuttleRoutesAsync()
{
var shuttleRoutes = await _shuttleRouteRepository.GetListAsync();
var shuttleRouteDtos = new List<ShuttleRouteDto>();
foreach (var shuttleRoute in shuttleRoutes)
{
var dto = ObjectMapper.Map<ShuttleRoute, ShuttleRouteDto>(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;
}
2025-10-29 11:57:08 +00:00
private async Task<List<AnnouncementDto>> GetAnnouncementsAsync()
2025-10-29 10:20:21 +00:00
{
2025-10-29 11:57:08 +00:00
var announcements = await _announcementRepository
.WithDetailsAsync(e => e.Employee)
.ContinueWith(t => t.Result.ToList());
2025-10-29 10:20:21 +00:00
2025-10-29 11:57:08 +00:00
var announcementDtos = new List<AnnouncementDto>();
2025-10-29 10:20:21 +00:00
2025-10-29 11:57:08 +00:00
// Tüm departmanları bir kez çek (performans için)
var allDepartments = await _departmentRepository.GetListAsync();
2025-10-29 10:20:21 +00:00
2025-10-29 11:57:08 +00:00
foreach (var announcement in announcements)
2025-10-29 10:20:21 +00:00
{
2025-10-29 11:57:08 +00:00
var dto = ObjectMapper.Map<Announcement, AnnouncementDto>(announcement);
2025-10-29 10:20:21 +00:00
2025-10-29 11:57:08 +00:00
// 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<string>();
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);
}
}
}
2025-10-29 10:20:21 +00:00
2025-10-29 11:57:08 +00:00
dto.Departments = departmentNames.ToArray();
}
else
2025-10-29 10:20:21 +00:00
{
2025-10-29 11:57:08 +00:00
dto.Departments = [];
}
announcementDtos.Add(dto);
2025-10-29 10:20:21 +00:00
}
2025-10-29 11:57:08 +00:00
return announcementDtos;
2025-10-29 10:20:21 +00:00
}
private async Task<ExpensesDto> 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<Expense>, List<ExpenseDto>>(last5Expenses);
// Dönüş DTO'su
return new ExpensesDto
{
TotalRequested = totalRequested,
TotalApproved = totalApproved,
Last5Expenses = last5Dtos
2025-10-28 18:03:15 +00:00
};
}
2025-10-29 10:20:21 +00:00
private async Task<List<TrainingDto>> GetTrainingsAsync()
{
var trainings = await _trainingRepository.GetListAsync(a => a.StartDate >= DateTime.Today);
return ObjectMapper.Map<List<Training>, List<TrainingDto>>(trainings);
}
private async Task<List<ReservationDto>> GetReservationsAsync()
{
var reservations = await _reservationRepository.GetListAsync(a => a.StartDate >= DateTime.Today);
return ObjectMapper.Map<List<Reservation>, List<ReservationDto>>(reservations);
}
private async Task<List<VisitorDto>> GetVisitorsAsync()
{
var visitors = await _visitorRepository.GetListAsync(a => a.VisitDate >= DateTime.Today);
return ObjectMapper.Map<List<Visitor>, List<VisitorDto>>(visitors);
}
private async Task<List<EmployeeDto>> 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<Employee>, List<EmployeeDto>>(employees);
}
2025-10-28 18:03:15 +00:00
private async Task<List<EventDto>> GetUpcomingEventsAsync()
{
var events = await _eventRepository
.WithDetailsAsync(e => e.Category, e => e.Type, e => e.Category, e => e.Photos, e => e.Comments)
2025-10-28 18:03:15 +00:00
.ContinueWith(t => t.Result.ToList().Where(e => e.isPublished).OrderByDescending(e => e.CreationTime));
var result = new List<EventDto>();
foreach (var evt in events)
{
var employee = await _employeeRepository
.WithDetailsAsync(e => e.JobPosition)
2025-10-28 18:59:07 +00:00
.ContinueWith(t => t.Result.FirstOrDefault(e => e.Id == evt.EmployeeId));
2025-10-28 18:03:15 +00:00
if (employee != null)
{
var calendarEvent = new EventDto
{
Id = evt.Id.ToString(),
Name = evt.Name,
2025-10-28 18:03:15 +00:00
Description = evt.Description,
TypeName = evt.Type?.Name,
CategoryName = evt.Category?.Name,
Date = evt.Date,
Place = evt.Place,
2025-10-28 18:03:15 +00:00
Organizer = new EventOrganizerDto
{
Id = employee.Id,
Name = employee.FullName,
Position = employee.JobPosition.Name,
Avatar = employee.Avatar
},
Participants = evt.ParticipantsCount,
2025-10-29 10:20:21 +00:00
Photos = [],
2025-10-28 18:03:15 +00:00
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)
2025-10-28 18:59:07 +00:00
.ContinueWith(t => t.Result.FirstOrDefault(e => e.Id == comment.EmployeeId));
2025-10-28 18:03:15 +00:00
if (commentAuthor != null)
{
calendarEvent.Comments.Add(new EventCommentDto
{
Id = comment.Id.ToString(),
Author = new EventOrganizerDto
{
Id = commentAuthor.Id,
Name = commentAuthor.FullName,
Position = commentAuthor.JobPosition.Name,
Avatar = commentAuthor.Avatar
},
2025-10-28 18:59:07 +00:00
Content = comment.Content,
2025-10-28 18:03:15 +00:00
CreationTime = comment.CreationTime,
Likes = comment.Likes
});
}
}
}
result.Add(calendarEvent);
}
}
return result;
}
2025-10-29 11:57:08 +00:00
public async Task<List<FileItemDto>> GetIntranetDocumentsAsync(string folderPath)
{
var items = new List<FileItemDto>();
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"
};
}
2025-10-28 18:03:15 +00:00
}