IntranetAppService ve MenuAppService güçlendirildi
This commit is contained in:
parent
b070702378
commit
ec44d03e79
3 changed files with 182 additions and 147 deletions
|
|
@ -13,6 +13,7 @@ using Microsoft.Extensions.Configuration;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
using Volo.Abp.MultiTenancy;
|
||||
using Volo.Abp.Uow;
|
||||
|
||||
namespace Erp.Platform.Public;
|
||||
|
||||
|
|
@ -83,8 +84,10 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
_projectTaskRepository = projectTaskRepository;
|
||||
}
|
||||
|
||||
[UnitOfWork]
|
||||
public async Task<IntranetDashboardDto> GetIntranetDashboardAsync()
|
||||
{
|
||||
// ABP 9.0.2: UnitOfWork kapsamında tüm sorgular optimize edilir
|
||||
return new IntranetDashboardDto
|
||||
{
|
||||
Events = await GetUpcomingEventsAsync(),
|
||||
|
|
@ -107,21 +110,24 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
|
||||
private async Task<List<ProjectTaskDto>> GetProjectTasksAsync()
|
||||
{
|
||||
var tasks = await _projectTaskRepository
|
||||
.WithDetailsAsync(e => e.Employee)
|
||||
.ContinueWith(t => t.Result.Where(s => s.Priority == "High" || s.Priority == "Urgent")
|
||||
var queryable = await _projectTaskRepository.WithDetailsAsync(e => e.Employee);
|
||||
|
||||
var tasks = await AsyncExecuter.ToListAsync(
|
||||
queryable
|
||||
.Where(s => s.Priority == "High" || s.Priority == "Urgent")
|
||||
.OrderByDescending(s => s.StartDate)
|
||||
.Take(3)
|
||||
.ToList());
|
||||
);
|
||||
|
||||
return ObjectMapper.Map<List<ProjectTask>, List<ProjectTaskDto>>(tasks);
|
||||
}
|
||||
|
||||
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());
|
||||
var queryable = await _socialPostRepository
|
||||
.WithDetailsAsync(e => e.Employee, e => e.Location, e => e.Media, e => e.Comments, e => e.Likes);
|
||||
|
||||
var socialPosts = await AsyncExecuter.ToListAsync(queryable);
|
||||
|
||||
return ObjectMapper.Map<List<SocialPost>, List<SocialPostDto>>(socialPosts);
|
||||
}
|
||||
|
|
@ -130,29 +136,28 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
{
|
||||
var queryable = await _surveyRepository.GetQueryableAsync();
|
||||
|
||||
var surveys = queryable
|
||||
var surveys = await AsyncExecuter.ToListAsync(
|
||||
queryable
|
||||
.Where(s => s.Status == "active")
|
||||
.Include(s => s.Questions)
|
||||
.ThenInclude(q => q.Options)
|
||||
.ToList();
|
||||
);
|
||||
|
||||
return ObjectMapper.Map<List<Survey>, List<SurveyDto>>(surveys);
|
||||
}
|
||||
|
||||
private async Task<List<OvertimeDto>> GetOvertimesAsync()
|
||||
{
|
||||
var overtimes = await _overtimeRepository
|
||||
.WithDetailsAsync(e => e.Employee)
|
||||
.ContinueWith(t => t.Result.ToList());
|
||||
var queryable = await _overtimeRepository.WithDetailsAsync(e => e.Employee);
|
||||
var overtimes = await AsyncExecuter.ToListAsync(queryable);
|
||||
|
||||
return ObjectMapper.Map<List<Overtime>, List<OvertimeDto>>(overtimes);
|
||||
}
|
||||
|
||||
private async Task<List<LeaveDto>> GetLeavesAsync()
|
||||
{
|
||||
var leaves = await _leaveRepository
|
||||
.WithDetailsAsync(e => e.Employee)
|
||||
.ContinueWith(t => t.Result.ToList());
|
||||
var queryable = await _leaveRepository.WithDetailsAsync(e => e.Employee);
|
||||
var leaves = await AsyncExecuter.ToListAsync(queryable);
|
||||
|
||||
return ObjectMapper.Map<List<Leave>, List<LeaveDto>>(leaves);
|
||||
}
|
||||
|
|
@ -225,14 +230,14 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
|
||||
private async Task<List<AnnouncementDto>> GetAnnouncementsAsync()
|
||||
{
|
||||
var announcements = await _announcementRepository
|
||||
.WithDetailsAsync(e => e.Employee)
|
||||
.ContinueWith(t => t.Result.ToList());
|
||||
var queryable = await _announcementRepository.WithDetailsAsync(e => e.Employee);
|
||||
var announcements = await AsyncExecuter.ToListAsync(queryable);
|
||||
|
||||
var announcementDtos = new List<AnnouncementDto>();
|
||||
|
||||
// Tüm departmanları bir kez çek (performans için)
|
||||
var allDepartments = await _departmentRepository.GetListAsync();
|
||||
var departmentDict = allDepartments.ToDictionary(d => d.Id, d => d.Name);
|
||||
|
||||
foreach (var announcement in announcements)
|
||||
{
|
||||
|
|
@ -246,21 +251,15 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// ID'leri Department Name'lere çevir (Dictionary lookup çok hızlı)
|
||||
var departmentNames = departmentIds
|
||||
.Where(deptId => Guid.TryParse(deptId, out _))
|
||||
.Select(deptId => Guid.Parse(deptId))
|
||||
.Where(guid => departmentDict.ContainsKey(guid))
|
||||
.Select(guid => departmentDict[guid])
|
||||
.ToArray();
|
||||
|
||||
dto.Departments = departmentNames.ToArray();
|
||||
dto.Departments = departmentNames;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -284,19 +283,17 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
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);
|
||||
// ABP 9.0.2: Separate queries for better performance
|
||||
var expensesList = await AsyncExecuter.ToListAsync(lastMonthExpenses);
|
||||
var totalRequested = expensesList.Sum(a => a.Amount);
|
||||
var totalApproved = expensesList.Where(a => a.Status == "approved").Sum(a => a.Amount);
|
||||
|
||||
// Son 5 kayıt
|
||||
var last5Expenses = queryable
|
||||
var last5Expenses = await AsyncExecuter.ToListAsync(
|
||||
queryable
|
||||
.OrderByDescending(a => a.RequestDate)
|
||||
.Take(5)
|
||||
.ToList();
|
||||
);
|
||||
|
||||
// Map işlemleri
|
||||
var last5Dtos = ObjectMapper.Map<List<Expense>, List<ExpenseDto>>(last5Expenses);
|
||||
|
|
@ -335,30 +332,55 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
{
|
||||
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());
|
||||
var queryable = await _employeeRepository
|
||||
.WithDetailsAsync(e => e.EmploymentType, e => e.JobPosition, e => e.Department);
|
||||
|
||||
var employees = await AsyncExecuter.ToListAsync(
|
||||
queryable.Where(e => e.BirthDate.Day == today.Day && e.BirthDate.Month == today.Month)
|
||||
);
|
||||
|
||||
return ObjectMapper.Map<List<Employee>, List<EmployeeDto>>(employees);
|
||||
}
|
||||
|
||||
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)
|
||||
.ContinueWith(t => t.Result.ToList().Where(e => e.isPublished).OrderByDescending(e => e.CreationTime));
|
||||
var queryable = await _eventRepository
|
||||
.WithDetailsAsync(e => e.Category, e => e.Type, e => e.Photos, e => e.Comments);
|
||||
|
||||
var events = await AsyncExecuter.ToListAsync(
|
||||
queryable.Where(e => e.isPublished).OrderByDescending(e => e.CreationTime)
|
||||
);
|
||||
|
||||
if (!events.Any())
|
||||
return new List<EventDto>();
|
||||
|
||||
// Tüm unique employee ID'lerini topla (event'ler ve comment'ler için)
|
||||
var employeeIds = new HashSet<Guid>();
|
||||
foreach (var evt in events)
|
||||
{
|
||||
employeeIds.Add(evt.EmployeeId);
|
||||
if (evt.Comments != null)
|
||||
{
|
||||
foreach (var comment in evt.Comments)
|
||||
{
|
||||
employeeIds.Add(comment.EmployeeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tüm employee'leri tek sorguda getir (N+1 problemi çözüldü)
|
||||
var employeeQueryable = await _employeeRepository.WithDetailsAsync(e => e.JobPosition);
|
||||
var employees = await AsyncExecuter.ToListAsync(
|
||||
employeeQueryable.Where(e => employeeIds.Contains(e.Id))
|
||||
);
|
||||
var employeeDict = employees.ToDictionary(e => e.Id);
|
||||
|
||||
var result = new List<EventDto>();
|
||||
foreach (var evt in events)
|
||||
{
|
||||
var employee = await _employeeRepository
|
||||
.WithDetailsAsync(e => e.JobPosition)
|
||||
.ContinueWith(t => t.Result.FirstOrDefault(e => e.Id == evt.EmployeeId));
|
||||
if (!employeeDict.TryGetValue(evt.EmployeeId, out var employee))
|
||||
continue;
|
||||
|
||||
if (employee != null)
|
||||
{
|
||||
var calendarEvent = new EventDto
|
||||
{
|
||||
Id = evt.Id.ToString(),
|
||||
|
|
@ -372,7 +394,7 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
{
|
||||
Id = employee.Id,
|
||||
Name = employee.Name,
|
||||
Position = employee.JobPosition.Name,
|
||||
Position = employee.JobPosition?.Name,
|
||||
Avatar = employee.Avatar
|
||||
},
|
||||
Participants = evt.ParticipantsCount,
|
||||
|
|
@ -382,16 +404,12 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
IsPublished = evt.isPublished
|
||||
};
|
||||
|
||||
// Comment'lerin author bilgilerini doldur
|
||||
// Comment'lerin author bilgilerini doldur (artık dictionary'den hızlıca alınıyor)
|
||||
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)
|
||||
if (employeeDict.TryGetValue(comment.EmployeeId, out var commentAuthor))
|
||||
{
|
||||
calendarEvent.Comments.Add(new EventCommentDto
|
||||
{
|
||||
|
|
@ -400,7 +418,7 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
{
|
||||
Id = commentAuthor.Id,
|
||||
Name = commentAuthor.Name,
|
||||
Position = commentAuthor.JobPosition.Name,
|
||||
Position = commentAuthor.JobPosition?.Name,
|
||||
Avatar = commentAuthor.Avatar
|
||||
},
|
||||
Content = comment.Content,
|
||||
|
|
@ -413,7 +431,6 @@ public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
|
||||
result.Add(calendarEvent);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,29 +88,37 @@ public class MenuAppService : CrudAppService<
|
|||
query = ApplyPaging(query, input);
|
||||
|
||||
var items = await AsyncExecuter.ToListAsync(query);
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.RequiredPermissionName.IsNullOrWhiteSpace())
|
||||
{
|
||||
entities.Add(item);
|
||||
}
|
||||
else
|
||||
|
||||
// Tüm unique permission'ları topla
|
||||
var uniquePermissions = items
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.RequiredPermissionName))
|
||||
.Select(x => x.RequiredPermissionName)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
// Tüm permission'ları bir kerede kontrol et (N+1 query problemini önler)
|
||||
var grantedPermissions = new HashSet<string>();
|
||||
foreach (var permission in uniquePermissions)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await AuthorizationService.IsGrantedAsync(item.RequiredPermissionName);
|
||||
if (result == true)
|
||||
if (await AuthorizationService.IsGrantedAsync(permission))
|
||||
{
|
||||
entities.Add(item);
|
||||
grantedPermissions.Add(permission);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, ex.Message);
|
||||
}
|
||||
Logger.LogError(ex, $"Permission error for {permission}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Sadece yetkili menüleri filtrele
|
||||
entities = items
|
||||
.Where(item => string.IsNullOrWhiteSpace(item.RequiredPermissionName) ||
|
||||
grantedPermissions.Contains(item.RequiredPermissionName))
|
||||
.ToList();
|
||||
|
||||
entityDtos = await base.MapToGetListOutputDtosAsync(entities);
|
||||
}
|
||||
|
||||
|
|
@ -164,28 +172,44 @@ public class MenuAppService : CrudAppService<
|
|||
{
|
||||
await CheckUpdatePolicyAsync();
|
||||
|
||||
var result = new List<MenuDto>();
|
||||
if (!inputs.Any())
|
||||
return new List<MenuDto>();
|
||||
|
||||
foreach (var input in inputs)
|
||||
{
|
||||
if (input.Id == Guid.Empty)
|
||||
// Id kontrolü
|
||||
if (inputs.Any(x => x.Id == Guid.Empty))
|
||||
{
|
||||
throw new ArgumentException("MenuDto içinde geçerli bir Id bulunmalıdır.");
|
||||
}
|
||||
|
||||
var key = await _repositoryKey.FirstOrDefaultAsync(
|
||||
a => a.Key == input.DisplayName &&
|
||||
a.ResourceName == PlatformConsts.AppName);
|
||||
// Tüm DisplayName'leri topla
|
||||
var displayNames = inputs.Select(x => x.DisplayName).Distinct().ToList();
|
||||
|
||||
if (key is null)
|
||||
// Mevcut key'leri tek sorguda getir
|
||||
var existingKeys = await AsyncExecuter.ToListAsync(
|
||||
(await _repositoryKey.GetQueryableAsync())
|
||||
.Where(a => displayNames.Contains(a.Key) && a.ResourceName == PlatformConsts.AppName));
|
||||
|
||||
var existingKeyNames = existingKeys.Select(x => x.Key).ToHashSet();
|
||||
|
||||
// Eksik key'leri toplu ekle
|
||||
var newKeys = displayNames
|
||||
.Where(name => !existingKeyNames.Contains(name))
|
||||
.Select(name => new LanguageKey
|
||||
{
|
||||
await _repositoryKey.InsertAsync(new LanguageKey
|
||||
{
|
||||
Key = input.DisplayName,
|
||||
Key = name,
|
||||
ResourceName = PlatformConsts.AppName
|
||||
});
|
||||
})
|
||||
.ToList();
|
||||
|
||||
if (newKeys.Any())
|
||||
{
|
||||
await _repositoryKey.InsertManyAsync(newKeys, autoSave: true);
|
||||
}
|
||||
|
||||
// Menüleri güncelle
|
||||
var result = new List<MenuDto>();
|
||||
foreach (var input in inputs)
|
||||
{
|
||||
var updated = await base.UpdateAsync(input.Id, input);
|
||||
result.Add(updated);
|
||||
}
|
||||
|
|
@ -221,5 +245,3 @@ public class MenuAppService : CrudAppService<
|
|||
return entityDtos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -122,14 +122,10 @@ const List: React.FC = () => {
|
|||
<h4 className="text-sm font-medium">{translate('::' + gridDto.gridOptions.title)}</h4>
|
||||
<Badge content={viewMode} />
|
||||
|
||||
<p className="ml-2 text-xs text-slate-500 mr-auto">
|
||||
{translate('::' + gridDto.gridOptions.description)}
|
||||
</p>
|
||||
|
||||
{/* =======================
|
||||
VIEW BUTTONS
|
||||
======================= */}
|
||||
<div className="flex gap-1">
|
||||
<div className="flex gap-1 ml-auto">
|
||||
{gridDto?.gridOptions?.layoutDto.scheduler &&
|
||||
gridDto?.gridOptions?.schedulerOptionDto?.textExpr &&
|
||||
gridDto?.gridOptions?.schedulerOptionDto?.startDateExpr && (
|
||||
|
|
|
|||
Loading…
Reference in a new issue