351 lines
13 KiB
C#
351 lines
13 KiB
C#
|
|
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.Dto;
|
|||
|
|
using Sozsoft.Platform.Extensions;
|
|||
|
|
using Volo.Abp.Domain.Repositories;
|
|||
|
|
using Volo.Abp.Identity;
|
|||
|
|
using Volo.Abp.MultiTenancy;
|
|||
|
|
using Volo.Abp.Uow;
|
|||
|
|
|
|||
|
|
namespace Sozsoft.Platform.Intranet;
|
|||
|
|
|
|||
|
|
[Authorize]
|
|||
|
|
public class IntranetAppService : PlatformAppService, IIntranetAppService
|
|||
|
|
{
|
|||
|
|
private readonly ICurrentTenant _currentTenant;
|
|||
|
|
private readonly BlobManager _blobContainer;
|
|||
|
|
private readonly IConfiguration _configuration;
|
|||
|
|
|
|||
|
|
private readonly IIdentityUserAppService _identityUserAppService;
|
|||
|
|
private readonly IIdentityUserRepository _identityUserRepository;
|
|||
|
|
private readonly IRepository<Department, Guid> _departmentRepository;
|
|||
|
|
private readonly IRepository<JobPosition, Guid> _jobPositionRepository;
|
|||
|
|
private readonly IRepository<Announcement, Guid> _announcementRepository;
|
|||
|
|
private readonly IRepository<Survey, Guid> _surveyRepository;
|
|||
|
|
private readonly IRepository<SocialPost, Guid> _socialPostRepository;
|
|||
|
|
|
|||
|
|
public IntranetAppService(
|
|||
|
|
ICurrentTenant currentTenant,
|
|||
|
|
BlobManager blobContainer,
|
|||
|
|
IConfiguration configuration,
|
|||
|
|
|
|||
|
|
IIdentityUserAppService identityUserAppService,
|
|||
|
|
IIdentityUserRepository identityUserRepository,
|
|||
|
|
IRepository<Department, Guid> departmentRepository,
|
|||
|
|
IRepository<JobPosition, Guid> jobPositionRepository,
|
|||
|
|
IRepository<Announcement, Guid> announcementRepository,
|
|||
|
|
IRepository<Survey, Guid> surveyRepository,
|
|||
|
|
IRepository<SocialPost, Guid> socialPostRepository
|
|||
|
|
)
|
|||
|
|
{
|
|||
|
|
_currentTenant = currentTenant;
|
|||
|
|
_blobContainer = blobContainer;
|
|||
|
|
_configuration = configuration;
|
|||
|
|
_identityUserAppService = identityUserAppService;
|
|||
|
|
_identityUserRepository = identityUserRepository;
|
|||
|
|
_departmentRepository = departmentRepository;
|
|||
|
|
_jobPositionRepository = jobPositionRepository;
|
|||
|
|
_announcementRepository = announcementRepository;
|
|||
|
|
_surveyRepository = surveyRepository;
|
|||
|
|
_socialPostRepository = socialPostRepository;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[UnitOfWork]
|
|||
|
|
public async Task<IntranetDashboardDto> GetIntranetDashboardAsync()
|
|||
|
|
{
|
|||
|
|
return new IntranetDashboardDto
|
|||
|
|
{
|
|||
|
|
Birthdays = await GetBirthdaysAsync(), //1
|
|||
|
|
Documents = await GetIntranetDocumentsAsync(BlobContainerNames.Intranet), //2
|
|||
|
|
Announcements = await GetAnnouncementsAsync(), //3
|
|||
|
|
Surveys = await GetSurveysAsync(), //4
|
|||
|
|
SocialPosts = await GetSocialPostsAsync(), //5
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task<List<UserInfoViewModel>> 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 allDepartments = await _departmentRepository.GetListAsync();
|
|||
|
|
var departmentDict = allDepartments.ToDictionary(d => d.Id, d => d.Name);
|
|||
|
|
|
|||
|
|
var allJobPositions = await _jobPositionRepository.GetListAsync();
|
|||
|
|
var jobPositionDict = allJobPositions.ToDictionary(j => j.Id, j => j);
|
|||
|
|
|
|||
|
|
var result = ObjectMapper.Map<List<IdentityUser>, List<UserInfoViewModel>>(userList);
|
|||
|
|
|
|||
|
|
for (var i = 0; i < userList.Count; i++)
|
|||
|
|
{
|
|||
|
|
var user = userList[i];
|
|||
|
|
|
|||
|
|
result[i].BirthDate = user.GetBirthDate();
|
|||
|
|
result[i].WorkHour = user.GetWorkHour();
|
|||
|
|
result[i].Nationality = user.GetNationality();
|
|||
|
|
|
|||
|
|
var deptId = user.GetDepartmentId();
|
|||
|
|
if (deptId != Guid.Empty && departmentDict.TryGetValue(deptId, out var deptName))
|
|||
|
|
{
|
|||
|
|
result[i].DepartmentId = deptId;
|
|||
|
|
result[i].Departments =
|
|||
|
|
[
|
|||
|
|
new AssignedDepartmentViewModel { Id = deptId, Name = deptName, IsAssigned = true }
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var jobPosId = user.GetJobPositionId();
|
|||
|
|
if (jobPosId != Guid.Empty && jobPositionDict.TryGetValue(jobPosId, out var jobPosition))
|
|||
|
|
{
|
|||
|
|
result[i].JobPositionId = jobPosId;
|
|||
|
|
result[i].JobPositions =
|
|||
|
|
[
|
|||
|
|
new AssignedJobPoisitionViewModel { Id = jobPosId, Name = jobPosition.Name, DepartmentId = jobPosition.DepartmentId, IsAssigned = true }
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task<List<AnnouncementDto>> GetAnnouncementsAsync()
|
|||
|
|
{
|
|||
|
|
var announcements = await _announcementRepository.GetListAsync();
|
|||
|
|
var announcementDtos = new List<AnnouncementDto>();
|
|||
|
|
|
|||
|
|
var allDepartments = await _departmentRepository.GetListAsync();
|
|||
|
|
var departmentDict = allDepartments.ToDictionary(d => d.Id, d => d.Name);
|
|||
|
|
|
|||
|
|
var allJobPositions = await _jobPositionRepository.GetListAsync();
|
|||
|
|
var jobPositionDict = allJobPositions.ToDictionary(j => j.Id, j => j);
|
|||
|
|
|
|||
|
|
foreach (var announcement in announcements)
|
|||
|
|
{
|
|||
|
|
var dto = ObjectMapper.Map<Announcement, AnnouncementDto>(announcement);
|
|||
|
|
|
|||
|
|
var user = await _identityUserRepository.FindAsync(announcement.UserId ?? Guid.Empty);
|
|||
|
|
if (user != null)
|
|||
|
|
{
|
|||
|
|
var userVm = ObjectMapper.Map<IdentityUser, UserInfoViewModel>(user);
|
|||
|
|
|
|||
|
|
userVm.BirthDate = user.GetBirthDate();
|
|||
|
|
userVm.WorkHour = user.GetWorkHour();
|
|||
|
|
userVm.Nationality = user.GetNationality();
|
|||
|
|
|
|||
|
|
var deptId = user.GetDepartmentId();
|
|||
|
|
if (deptId != Guid.Empty && departmentDict.TryGetValue(deptId, out var deptName))
|
|||
|
|
{
|
|||
|
|
userVm.DepartmentId = deptId;
|
|||
|
|
userVm.Departments =
|
|||
|
|
[
|
|||
|
|
new AssignedDepartmentViewModel { Id = deptId, Name = deptName, IsAssigned = true }
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var jobPosId = user.GetJobPositionId();
|
|||
|
|
if (jobPosId != Guid.Empty && jobPositionDict.TryGetValue(jobPosId, out var jobPosition))
|
|||
|
|
{
|
|||
|
|
userVm.JobPositionId = jobPosId;
|
|||
|
|
userVm.JobPositions =
|
|||
|
|
[
|
|||
|
|
new AssignedJobPoisitionViewModel { Id = jobPosId, Name = jobPosition.Name, DepartmentId = jobPosition.DepartmentId, IsAssigned = true }
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dto.User = userVm;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
announcementDtos.Add(dto);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return announcementDtos;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task<List<SurveyDto>> GetSurveysAsync()
|
|||
|
|
{
|
|||
|
|
var queryable = await _surveyRepository.GetQueryableAsync();
|
|||
|
|
|
|||
|
|
var surveys = await AsyncExecuter.ToListAsync(
|
|||
|
|
queryable
|
|||
|
|
.Where(s => s.Status == "active")
|
|||
|
|
.Include(s => s.Questions)
|
|||
|
|
.ThenInclude(q => q.Options)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
return ObjectMapper.Map<List<Survey>, List<SurveyDto>>(surveys);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private async Task<List<SocialPostDto>> GetSocialPostsAsync()
|
|||
|
|
{
|
|||
|
|
var queryable = await _socialPostRepository
|
|||
|
|
.WithDetailsAsync(e => e.Location, e => e.Media, e => e.Comments, e => e.Likes);
|
|||
|
|
|
|||
|
|
var socialPosts = await AsyncExecuter.ToListAsync(queryable);
|
|||
|
|
|
|||
|
|
var dtos = ObjectMapper.Map<List<SocialPost>, List<SocialPostDto>>(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 allDepartments = await _departmentRepository.GetListAsync();
|
|||
|
|
var departmentDict = allDepartments.ToDictionary(d => d.Id, d => d.Name);
|
|||
|
|
|
|||
|
|
var allJobPositions = await _jobPositionRepository.GetListAsync();
|
|||
|
|
var jobPositionDict = allJobPositions.ToDictionary(j => j.Id, j => j);
|
|||
|
|
|
|||
|
|
var users = await _identityUserRepository.GetListAsync();
|
|||
|
|
var userMap = users
|
|||
|
|
.Where(u => userIds.Contains(u.Id))
|
|||
|
|
.ToDictionary(u => u.Id, u =>
|
|||
|
|
{
|
|||
|
|
var vm = ObjectMapper.Map<IdentityUser, UserInfoViewModel>(u);
|
|||
|
|
vm.BirthDate = u.GetBirthDate();
|
|||
|
|
vm.WorkHour = u.GetWorkHour();
|
|||
|
|
vm.Nationality = u.GetNationality();
|
|||
|
|
|
|||
|
|
var deptId = u.GetDepartmentId();
|
|||
|
|
if (deptId != Guid.Empty && departmentDict.TryGetValue(deptId, out var deptName))
|
|||
|
|
{
|
|||
|
|
vm.DepartmentId = deptId;
|
|||
|
|
vm.Departments =
|
|||
|
|
[
|
|||
|
|
new AssignedDepartmentViewModel { Id = deptId, Name = deptName, IsAssigned = true }
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var jobPosId = u.GetJobPositionId();
|
|||
|
|
if (jobPosId != Guid.Empty && jobPositionDict.TryGetValue(jobPosId, out var jobPosition))
|
|||
|
|
{
|
|||
|
|
vm.JobPositionId = jobPosId;
|
|||
|
|
vm.JobPositions =
|
|||
|
|
[
|
|||
|
|
new AssignedJobPoisitionViewModel { Id = jobPosId, Name = jobPosition.Name, DepartmentId = jobPosition.DepartmentId, IsAssigned = true }
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return vm;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return dtos;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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,
|
|||
|
|
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"
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|