Addedd the tenant ID in employee profile cache
This commit is contained in:
parent
bd421d45aa
commit
ef84ba34da
@ -20,18 +20,21 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
var mongoDB = client.GetDatabase(mongoUrl.DatabaseName); // Your MongoDB Database name
|
||||
_collection = mongoDB.GetCollection<EmployeePermissionMongoDB>("EmployeeProfile");
|
||||
}
|
||||
public async Task<bool> AddApplicationRoleToCache(Guid employeeId, List<string> newRoleIds, List<string> newPermissionIds)
|
||||
public async Task<bool> AddApplicationRoleToCache(Guid employeeId, List<string> newRoleIds, List<string> newPermissionIds, Guid tenantId)
|
||||
{
|
||||
|
||||
// 2. Perform database queries concurrently for better performance.
|
||||
var employeeIdString = employeeId.ToString();
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
// 5. Build a single, efficient update operation.
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeIdString);
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var update = Builders<EmployeePermissionMongoDB>.Update
|
||||
.AddToSetEach(e => e.ApplicationRoleIds, newRoleIds)
|
||||
.Set(r => r.ExpireAt, DateTime.UtcNow.Date.AddDays(1))
|
||||
.Set(r => r.TenantId, tenantIdString)
|
||||
.AddToSetEach(e => e.PermissionIds, newPermissionIds);
|
||||
|
||||
var options = new UpdateOptions { IsUpsert = true };
|
||||
@ -44,14 +47,17 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
// The operation is successful if an existing document was modified OR a new one was created.
|
||||
return result.IsAcknowledged && (result.ModifiedCount > 0 || result.UpsertedId != null);
|
||||
}
|
||||
public async Task<bool> AddProjectsToCache(Guid employeeId, List<Guid> projectIds)
|
||||
public async Task<bool> AddProjectsToCache(Guid employeeId, List<Guid> projectIds, Guid tenantId)
|
||||
{
|
||||
var newprojectIds = projectIds.Select(p => p.ToString()).ToList();
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeId.ToString());
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var update = Builders<EmployeePermissionMongoDB>.Update
|
||||
.Set(r => r.ExpireAt, DateTime.UtcNow.Date.AddDays(1))
|
||||
.Set(r => r.TenantId, tenantIdString)
|
||||
.AddToSetEach(e => e.ProjectIds, newprojectIds);
|
||||
|
||||
var result = await _collection.UpdateOneAsync(filter, update, new UpdateOptions { IsUpsert = true });
|
||||
@ -62,10 +68,12 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
await InitializeCollectionAsync();
|
||||
return true;
|
||||
}
|
||||
public async Task<List<Guid>> GetProjectsFromCache(Guid employeeId)
|
||||
public async Task<List<Guid>> GetProjectsFromCache(Guid employeeId, Guid tenantId)
|
||||
{
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeId.ToString());
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeId.ToString());
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var result = await _collection
|
||||
.Find(filter)
|
||||
@ -79,10 +87,12 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
|
||||
return projectIds;
|
||||
}
|
||||
public async Task<List<Guid>> GetPermissionsFromCache(Guid employeeId)
|
||||
public async Task<List<Guid>> GetPermissionsFromCache(Guid employeeId, Guid tenantId)
|
||||
{
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeId.ToString());
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.Id, employeeId.ToString());
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var result = await _collection
|
||||
.Find(filter)
|
||||
@ -96,10 +106,13 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
|
||||
return permissionIds;
|
||||
}
|
||||
public async Task<bool> ClearAllProjectIdsFromCache(Guid employeeId)
|
||||
public async Task<bool> ClearAllProjectIdsFromCache(Guid employeeId, Guid tenantId)
|
||||
{
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter
|
||||
.Eq(e => e.Id, employeeId.ToString());
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var update = Builders<EmployeePermissionMongoDB>.Update
|
||||
.Set(e => e.ProjectIds, new List<string>());
|
||||
@ -125,18 +138,25 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
|
||||
return true;
|
||||
}
|
||||
public async Task<bool> ClearAllProjectIdsByPermissionIdFromCache(Guid permissionId)
|
||||
public async Task<bool> ClearAllProjectIdsByPermissionIdFromCache(Guid permissionId, Guid tenantId)
|
||||
{
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.AnyEq(e => e.PermissionIds, permissionId.ToString());
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var update = Builders<EmployeePermissionMongoDB>.Update.Set(e => e.ProjectIds, new List<string>());
|
||||
|
||||
var result = await _collection.UpdateManyAsync(filter, update).ConfigureAwait(false);
|
||||
return result.IsAcknowledged && result.ModifiedCount > 0;
|
||||
}
|
||||
public async Task<bool> RemoveRoleIdFromCache(Guid employeeId, Guid roleId)
|
||||
public async Task<bool> RemoveRoleIdFromCache(Guid employeeId, Guid roleId, Guid tenantId)
|
||||
{
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter
|
||||
.Eq(e => e.Id, employeeId.ToString());
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var update = Builders<EmployeePermissionMongoDB>.Update
|
||||
.Pull(e => e.ApplicationRoleIds, roleId.ToString());
|
||||
@ -151,10 +171,13 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
|
||||
return true;
|
||||
}
|
||||
public async Task<bool> ClearAllPermissionIdsByEmployeeIDFromCache(Guid employeeId)
|
||||
public async Task<bool> ClearAllPermissionIdsByEmployeeIDFromCache(Guid employeeId, Guid tenantId)
|
||||
{
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter
|
||||
.Eq(e => e.Id, employeeId.ToString());
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var update = Builders<EmployeePermissionMongoDB>.Update
|
||||
.Set(e => e.PermissionIds, new List<string>());
|
||||
@ -189,11 +212,14 @@ namespace Marco.Pms.Helpers.CacheHelper
|
||||
|
||||
return true;
|
||||
}
|
||||
public async Task<bool> ClearAllEmployeesFromCacheByEmployeeIds(List<string> employeeIds)
|
||||
public async Task<bool> ClearAllEmployeesFromCacheByEmployeeIds(List<string> employeeIds, Guid tenantId)
|
||||
{
|
||||
var tenantIdString = tenantId.ToString();
|
||||
|
||||
try
|
||||
{
|
||||
var filter = Builders<EmployeePermissionMongoDB>.Filter.In(x => x.Id, employeeIds);
|
||||
filter &= Builders<EmployeePermissionMongoDB>.Filter.Eq(e => e.TenantId, tenantIdString);
|
||||
|
||||
var result = await _collection.DeleteManyAsync(filter);
|
||||
|
||||
|
@ -10,5 +10,6 @@ namespace Marco.Pms.Model.MongoDBModels.Employees
|
||||
public List<string> PermissionIds { get; set; } = new List<string>();
|
||||
public List<string> ProjectIds { get; set; } = new List<string>();
|
||||
public DateTime ExpireAt { get; set; } = DateTime.UtcNow.Date.AddDays(1);
|
||||
public string TenantId { get; set; } = string.Empty; // Tenant ID
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ using Marco.Pms.Model.Master;
|
||||
using Marco.Pms.Model.TenantModels;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.Tenant;
|
||||
using Marco.Pms.Services.Helpers;
|
||||
using MarcoBMS.Services.Service;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -130,5 +131,65 @@ namespace Marco.Pms.Services.Controllers
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("An error occurred while fetching subscription plans."));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("get/project/report/{projectId}")]
|
||||
public async Task<IActionResult> GetProjectReport(Guid projectId)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _reportHelper = scope.ServiceProvider.GetRequiredService<ReportHelper>();
|
||||
var _logger = scope.ServiceProvider.GetRequiredService<ILoggingService>();
|
||||
|
||||
var resonse = await _reportHelper.GetDailyProjectReportWithOutTenant(projectId);
|
||||
|
||||
if (resonse == null)
|
||||
{
|
||||
_logger.LogWarning("Project report not found");
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Project report not found", "Project report not found", 404));
|
||||
}
|
||||
_logger.LogInfo("Report for the project fetched successfully");
|
||||
return Ok(ApiResponse<object>.SuccessResponse(resonse, "Report for the project fetched successfully", 200));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a daily project report by its unique identifier.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The GUID of the project for which to generate the report.</param>
|
||||
/// <returns>An IActionResult containing the project report or an appropriate error response.</returns>
|
||||
[HttpGet("{projectId}/report")]
|
||||
public async Task<IActionResult> GetProjectReportAsync(Guid projectId)
|
||||
{
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _reportHelper = scope.ServiceProvider.GetRequiredService<ReportHelper>();
|
||||
var _logger = scope.ServiceProvider.GetRequiredService<ILoggingService>();
|
||||
|
||||
// Use structured logging to include the projectId for better traceability.
|
||||
_logger.LogInfo("Attempting to fetch report for ProjectId: {ProjectId}", projectId);
|
||||
|
||||
try
|
||||
{
|
||||
// Call the helper service, which is now available as a class member.
|
||||
var response = await _reportHelper.GetDailyProjectReportWithOutTenant(projectId);
|
||||
|
||||
// Check if the report data was found.
|
||||
if (response == null)
|
||||
{
|
||||
_logger.LogWarning("Project report not found for ProjectId: {ProjectId}", projectId);
|
||||
return NotFound(ApiResponse<object>.ErrorResponse("Project report not found.", 404));
|
||||
}
|
||||
|
||||
// Log success and return the report.
|
||||
_logger.LogInfo("Successfully fetched report for ProjectId: {ProjectId}", projectId);
|
||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Report for the project fetched successfully.", 200));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log the full exception if anything goes wrong during the process.
|
||||
_logger.LogError(ex, "An error occurred while generating the report for ProjectId: {ProjectId}", projectId);
|
||||
|
||||
// Return a standardized 500 Internal Server Error response to the client.
|
||||
return StatusCode(500, ApiResponse<object>.ErrorResponse("An internal server error occurred while processing the report.", 500));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -424,7 +424,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
var LoggedEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||
var employees = await _context.Employees.Where(e => employeesIds.Contains(e.Id)).ToListAsync();
|
||||
|
||||
Guid TenantId = GetTenantId();
|
||||
Guid tenantId = GetTenantId();
|
||||
try
|
||||
{
|
||||
foreach (EmployeeRoleDot role in employeeRoleDots)
|
||||
@ -435,7 +435,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
_logger.LogWarning("Employee with ID {LoggedEmployeeId} tries to assign or remove the application role to System-defined employee with ID {EmployeeId}", LoggedEmployee.Id, employee.Id);
|
||||
return BadRequest(ApiResponse<object>.ErrorResponse("System-defined employee cannot have application roles assigned or removed.", "System-defined employee cannot have application roles assigned or removed.", 400));
|
||||
}
|
||||
EmployeeRoleMapping mapping = role.ToEmployeeRoleMappingFromEmployeeRoleDot(TenantId);
|
||||
EmployeeRoleMapping mapping = role.ToEmployeeRoleMappingFromEmployeeRoleDot(tenantId);
|
||||
|
||||
var existingItem = await _context.EmployeeRoleMappings.AsNoTracking().SingleOrDefaultAsync(c => c.Id == mapping.Id);
|
||||
|
||||
@ -444,16 +444,16 @@ namespace MarcoBMS.Services.Controllers
|
||||
if (role.IsEnabled == true)
|
||||
{
|
||||
_context.EmployeeRoleMappings.Add(mapping);
|
||||
await _cache.AddApplicationRole(role.EmployeeId, [mapping.RoleId]);
|
||||
await _cache.AddApplicationRole(role.EmployeeId, [mapping.RoleId], tenantId);
|
||||
}
|
||||
}
|
||||
else if (role.IsEnabled == false)
|
||||
{
|
||||
_context.EmployeeRoleMappings.Remove(existingItem);
|
||||
await _cache.RemoveRoleId(existingItem.EmployeeId, existingItem.RoleId);
|
||||
await _cache.ClearAllPermissionIdsByEmployeeID(existingItem.EmployeeId);
|
||||
await _cache.RemoveRoleId(existingItem.EmployeeId, existingItem.RoleId, tenantId);
|
||||
await _cache.ClearAllPermissionIdsByEmployeeID(existingItem.EmployeeId, tenantId);
|
||||
}
|
||||
await _cache.ClearAllProjectIds(role.EmployeeId);
|
||||
await _cache.ClearAllProjectIds(role.EmployeeId, tenantId);
|
||||
|
||||
}
|
||||
await _context.SaveChangesAsync();
|
||||
|
@ -1776,7 +1776,7 @@ namespace Marco.Pms.Services.Controllers
|
||||
var _cacheLogger = scope.ServiceProvider.GetRequiredService<ILoggingService>();
|
||||
|
||||
var employeeIds = await _context.Employees.Where(e => e.TenantId == tenantId).Select(e => e.Id).ToListAsync();
|
||||
await _cache.ClearAllEmployeesFromCacheByEmployeeIds(employeeIds);
|
||||
await _cache.ClearAllEmployeesFromCacheByEmployeeIds(employeeIds, tenantId);
|
||||
_cacheLogger.LogInfo("{EmployeeCount} number of employee deleted", employeeIds.Count);
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
private readonly ILoggingService _logger;
|
||||
private readonly IProjectServices _projectServices;
|
||||
private readonly RolesHelper _rolesHelper;
|
||||
private readonly Guid tenantId;
|
||||
|
||||
public UserController(EmployeeHelper employeeHelper, UserManager<ApplicationUser> userManager, ILoggingService logger, IProjectServices projectServices, UserHelper userHelper, RolesHelper rolesHelper)
|
||||
{
|
||||
@ -35,8 +36,9 @@ namespace MarcoBMS.Services.Controllers
|
||||
_employeeHelper = employeeHelper;
|
||||
_projectServices = projectServices;
|
||||
_rolesHelper = rolesHelper;
|
||||
|
||||
tenantId = userHelper.GetTenantId();
|
||||
}
|
||||
|
||||
[HttpGet("profile")]
|
||||
public async Task<IActionResult> GetUserProfileFromJwt()
|
||||
{
|
||||
@ -57,7 +59,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
emp = await _employeeHelper.GetEmployeeByApplicationUserID(user.Id);
|
||||
}
|
||||
|
||||
List<FeaturePermission> featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeId(emp.Id);
|
||||
List<FeaturePermission> featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeId(emp.Id, tenantId);
|
||||
string[] projectsId = [];
|
||||
|
||||
/* User with permission manage project can see all projects */
|
||||
@ -75,6 +77,8 @@ namespace MarcoBMS.Services.Controllers
|
||||
if (featurePermission != null)
|
||||
{
|
||||
EmployeeVM employeeVM = EmployeeMapper.ToEmployeeVMFromEmployee(emp);
|
||||
employeeVM.TenantId = tenantId;
|
||||
|
||||
profile = new EmployeeProfile()
|
||||
{
|
||||
EmployeeInfo = employeeVM,
|
||||
|
@ -708,7 +708,7 @@ namespace Marco.Pms.Services.Helpers
|
||||
|
||||
#region ======================================================= Employee Profile Cache =======================================================
|
||||
|
||||
public async Task AddApplicationRole(Guid employeeId, List<Guid> roleIds)
|
||||
public async Task AddApplicationRole(Guid employeeId, List<Guid> roleIds, Guid tenantId)
|
||||
{
|
||||
// 1. Guard Clause: Avoid unnecessary database work if there are no roles to add.
|
||||
if (roleIds == null || !roleIds.Any())
|
||||
@ -733,18 +733,18 @@ namespace Marco.Pms.Services.Helpers
|
||||
var newPermissionIds = await getPermissionIdsTask;
|
||||
try
|
||||
{
|
||||
var response = await _employeeCache.AddApplicationRoleToCache(employeeId, newRoleIds, newPermissionIds);
|
||||
var response = await _employeeCache.AddApplicationRoleToCache(employeeId, newRoleIds, newPermissionIds, tenantId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning("Error occured while adding Application roleIds to Cache to employee {Employee}: {Error}", employeeId, ex.Message);
|
||||
}
|
||||
}
|
||||
public async Task<bool> AddProjects(Guid employeeId, List<Guid> projectIds)
|
||||
public async Task<bool> AddProjects(Guid employeeId, List<Guid> projectIds, Guid tenantId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _employeeCache.AddProjectsToCache(employeeId, projectIds);
|
||||
var response = await _employeeCache.AddProjectsToCache(employeeId, projectIds, tenantId);
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -753,11 +753,11 @@ namespace Marco.Pms.Services.Helpers
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public async Task<List<Guid>?> GetProjects(Guid employeeId)
|
||||
public async Task<List<Guid>?> GetProjects(Guid employeeId, Guid tenantId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _employeeCache.GetProjectsFromCache(employeeId);
|
||||
var response = await _employeeCache.GetProjectsFromCache(employeeId, tenantId);
|
||||
if (response.Count > 0)
|
||||
{
|
||||
return response;
|
||||
@ -770,11 +770,11 @@ namespace Marco.Pms.Services.Helpers
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public async Task<List<Guid>?> GetPermissions(Guid employeeId)
|
||||
public async Task<List<Guid>?> GetPermissions(Guid employeeId, Guid tenantId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _employeeCache.GetPermissionsFromCache(employeeId);
|
||||
var response = await _employeeCache.GetPermissionsFromCache(employeeId, tenantId);
|
||||
if (response.Count > 0)
|
||||
{
|
||||
return response;
|
||||
@ -787,11 +787,11 @@ namespace Marco.Pms.Services.Helpers
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public async Task ClearAllProjectIds(Guid employeeId)
|
||||
public async Task ClearAllProjectIds(Guid employeeId, Guid tenantId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _employeeCache.ClearAllProjectIdsFromCache(employeeId);
|
||||
var response = await _employeeCache.ClearAllProjectIdsFromCache(employeeId, tenantId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -809,22 +809,22 @@ namespace Marco.Pms.Services.Helpers
|
||||
_logger.LogWarning("Error occured while deleting projectIds from Cache for Application Role {RoleId}: {Error}", roleId, ex.Message);
|
||||
}
|
||||
}
|
||||
public async Task ClearAllProjectIdsByPermissionId(Guid permissionId)
|
||||
public async Task ClearAllProjectIdsByPermissionId(Guid permissionId, Guid tenantId)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _employeeCache.ClearAllProjectIdsByPermissionIdFromCache(permissionId);
|
||||
await _employeeCache.ClearAllProjectIdsByPermissionIdFromCache(permissionId, tenantId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning("Error occured while deleting projectIds from Cache for Permission {PermissionId}: {Error}", permissionId, ex.Message);
|
||||
}
|
||||
}
|
||||
public async Task ClearAllPermissionIdsByEmployeeID(Guid employeeId)
|
||||
public async Task ClearAllPermissionIdsByEmployeeID(Guid employeeId, Guid tenantId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _employeeCache.ClearAllPermissionIdsByEmployeeIDFromCache(employeeId);
|
||||
var response = await _employeeCache.ClearAllPermissionIdsByEmployeeIDFromCache(employeeId, tenantId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -842,23 +842,23 @@ namespace Marco.Pms.Services.Helpers
|
||||
_logger.LogWarning("Error occured while deleting permissionIds from Cache for Application role {RoleId}: {Error}", roleId, ex.Message);
|
||||
}
|
||||
}
|
||||
public async Task RemoveRoleId(Guid employeeId, Guid roleId)
|
||||
public async Task RemoveRoleId(Guid employeeId, Guid roleId, Guid tenantId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _employeeCache.RemoveRoleIdFromCache(employeeId, roleId);
|
||||
var response = await _employeeCache.RemoveRoleIdFromCache(employeeId, roleId, tenantId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning("Error occured while deleting Application role {RoleId} from Cache for employee {EmployeeId}: {Error}", roleId, employeeId, ex.Message);
|
||||
}
|
||||
}
|
||||
public async Task ClearAllEmployeesFromCacheByEmployeeIds(List<Guid> employeeIds)
|
||||
public async Task ClearAllEmployeesFromCacheByEmployeeIds(List<Guid> employeeIds, Guid tenantId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var stringEmployeeIds = employeeIds.Select(e => e.ToString()).ToList();
|
||||
var response = await _employeeCache.ClearAllEmployeesFromCacheByEmployeeIds(stringEmployeeIds);
|
||||
var response = await _employeeCache.ClearAllEmployeesFromCacheByEmployeeIds(stringEmployeeIds, tenantId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -24,6 +24,262 @@ namespace Marco.Pms.Services.Helpers
|
||||
_logger = logger;
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
public async Task<ProjectStatisticReport?> GetDailyProjectReportWithOutTenant(Guid projectId)
|
||||
{
|
||||
// await _cache.GetBuildingAndFloorByWorkAreaId();
|
||||
DateTime reportDate = DateTime.UtcNow.AddDays(-1).Date;
|
||||
var project = await _cache.GetProjectDetailsWithBuildings(projectId);
|
||||
if (project == null)
|
||||
{
|
||||
var projectSQL = await _context.Projects
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(p => p.Id == projectId);
|
||||
if (projectSQL != null)
|
||||
{
|
||||
project = new ProjectMongoDB
|
||||
{
|
||||
Id = projectSQL.Id.ToString(),
|
||||
Name = projectSQL.Name,
|
||||
ShortName = projectSQL.ShortName,
|
||||
ProjectAddress = projectSQL.ProjectAddress,
|
||||
ContactPerson = projectSQL.ContactPerson
|
||||
};
|
||||
await _cache.AddProjectDetails(projectSQL);
|
||||
}
|
||||
}
|
||||
if (project != null)
|
||||
{
|
||||
|
||||
var statisticReport = new ProjectStatisticReport
|
||||
{
|
||||
Date = reportDate,
|
||||
ProjectName = project.Name ?? "",
|
||||
TimeStamp = DateTime.Now.ToString("dd-MMM-yyyy HH:mm:ss", CultureInfo.InvariantCulture)
|
||||
};
|
||||
|
||||
// Preload relevant data
|
||||
var projectAllocations = await _context.ProjectAllocations
|
||||
.Include(p => p.Employee)
|
||||
.Where(p => p.ProjectId == projectId && p.IsActive)
|
||||
.ToListAsync();
|
||||
|
||||
var assignedEmployeeIds = projectAllocations.Select(p => p.EmployeeId).ToHashSet();
|
||||
|
||||
var attendances = await _context.Attendes
|
||||
.AsNoTracking()
|
||||
.Where(a => a.ProjectID == projectId && a.InTime != null && a.InTime.Value.Date == reportDate)
|
||||
.ToListAsync();
|
||||
|
||||
var checkedInEmployeeIds = attendances.Select(a => a.EmployeeID).Distinct().ToHashSet();
|
||||
var checkoutPendingIds = attendances.Where(a => a.OutTime == null).Select(a => a.EmployeeID).Distinct().ToHashSet();
|
||||
var regularizationIds = attendances
|
||||
.Where(a => a.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE)
|
||||
.Select(a => a.EmployeeID).Distinct().ToHashSet();
|
||||
|
||||
// Preload buildings, floors, areas
|
||||
List<BuildingMongoDBVM>? buildings = null;
|
||||
List<FloorMongoDBVM>? floors = null;
|
||||
List<WorkAreaMongoDB>? areas = null;
|
||||
List<WorkItemMongoDB>? workItems = null;
|
||||
|
||||
// Fetch Buildings
|
||||
buildings = project.Buildings
|
||||
.Select(b => new BuildingMongoDBVM
|
||||
{
|
||||
Id = b.Id,
|
||||
ProjectId = b.ProjectId,
|
||||
BuildingName = b.BuildingName,
|
||||
Description = b.Description
|
||||
}).ToList();
|
||||
if (!buildings.Any())
|
||||
{
|
||||
buildings = await _context.Buildings
|
||||
.Where(b => b.ProjectId == projectId)
|
||||
.Select(b => new BuildingMongoDBVM
|
||||
{
|
||||
Id = b.Id.ToString(),
|
||||
ProjectId = b.ProjectId.ToString(),
|
||||
BuildingName = b.Name,
|
||||
Description = b.Description
|
||||
})
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
// fetch Floors
|
||||
floors = project.Buildings
|
||||
.SelectMany(b => b.Floors.Select(f => new FloorMongoDBVM
|
||||
{
|
||||
Id = f.Id.ToString(),
|
||||
BuildingId = f.BuildingId,
|
||||
FloorName = f.FloorName
|
||||
})).ToList();
|
||||
if (!floors.Any())
|
||||
{
|
||||
var buildingIds = buildings.Select(b => Guid.Parse(b.Id)).ToList();
|
||||
floors = await _context.Floor
|
||||
.Where(f => buildingIds.Contains(f.BuildingId))
|
||||
.Select(f => new FloorMongoDBVM
|
||||
{
|
||||
Id = f.Id.ToString(),
|
||||
BuildingId = f.BuildingId.ToString(),
|
||||
FloorName = f.FloorName
|
||||
})
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
// fetch Work Areas
|
||||
areas = project.Buildings
|
||||
.SelectMany(b => b.Floors)
|
||||
.SelectMany(f => f.WorkAreas).ToList();
|
||||
if (!areas.Any())
|
||||
{
|
||||
var floorIds = floors.Select(f => Guid.Parse(f.Id)).ToList();
|
||||
areas = await _context.WorkAreas
|
||||
.Where(a => floorIds.Contains(a.FloorId))
|
||||
.Select(wa => new WorkAreaMongoDB
|
||||
{
|
||||
Id = wa.Id.ToString(),
|
||||
FloorId = wa.FloorId.ToString(),
|
||||
AreaName = wa.AreaName,
|
||||
})
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
var areaIds = areas.Select(a => Guid.Parse(a.Id)).ToList();
|
||||
|
||||
// fetch Work Items
|
||||
workItems = await _cache.GetWorkItemsByWorkAreaIds(areaIds, new List<Guid>());
|
||||
if (workItems == null || !workItems.Any())
|
||||
{
|
||||
workItems = await _context.WorkItems
|
||||
.Include(w => w.ActivityMaster)
|
||||
.Where(w => areaIds.Contains(w.WorkAreaId))
|
||||
.Select(wi => new WorkItemMongoDB
|
||||
{
|
||||
Id = wi.Id.ToString(),
|
||||
WorkAreaId = wi.WorkAreaId.ToString(),
|
||||
PlannedWork = wi.PlannedWork,
|
||||
CompletedWork = wi.CompletedWork,
|
||||
Description = wi.Description,
|
||||
TaskDate = wi.TaskDate,
|
||||
ActivityMaster = new ActivityMasterMongoDB
|
||||
{
|
||||
ActivityName = wi.ActivityMaster != null ? wi.ActivityMaster.ActivityName : null,
|
||||
UnitOfMeasurement = wi.ActivityMaster != null ? wi.ActivityMaster.UnitOfMeasurement : null
|
||||
}
|
||||
})
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
var itemIds = workItems.Select(i => Guid.Parse(i.Id)).ToList();
|
||||
|
||||
var tasks = await _context.TaskAllocations
|
||||
.Where(t => itemIds.Contains(t.WorkItemId))
|
||||
.ToListAsync();
|
||||
|
||||
var taskIds = tasks.Select(t => t.Id).ToList();
|
||||
|
||||
var taskMembers = await _context.TaskMembers
|
||||
.Include(m => m.Employee)
|
||||
.Where(m => taskIds.Contains(m.TaskAllocationId))
|
||||
.ToListAsync();
|
||||
|
||||
// Aggregate data
|
||||
double totalPlannedWork = workItems.Sum(w => w.PlannedWork);
|
||||
double totalCompletedWork = workItems.Sum(w => w.CompletedWork);
|
||||
|
||||
var todayAssignedTasks = tasks.Where(t => t.AssignmentDate.Date == reportDate).ToList();
|
||||
var reportPending = tasks.Where(t => t.ReportedDate == null).ToList();
|
||||
|
||||
double totalPlannedTask = todayAssignedTasks.Sum(t => t.PlannedTask);
|
||||
double totalCompletedTask = todayAssignedTasks.Sum(t => t.CompletedTask);
|
||||
var jobRoleIds = projectAllocations.Select(pa => pa.JobRoleId).ToList();
|
||||
var jobRoles = await _context.JobRoles
|
||||
.Where(j => jobRoleIds.Contains(j.Id))
|
||||
.ToListAsync();
|
||||
|
||||
// Team on site
|
||||
var teamOnSite = jobRoles
|
||||
.Select(role =>
|
||||
{
|
||||
var count = projectAllocations.Count(p => p.JobRoleId == role.Id && checkedInEmployeeIds.Contains(p.EmployeeId));
|
||||
return new TeamOnSite { RoleName = role.Name, NumberofEmployees = count };
|
||||
})
|
||||
.OrderByDescending(t => t.NumberofEmployees)
|
||||
.ToList();
|
||||
|
||||
// Task details
|
||||
var performedTasks = todayAssignedTasks.Select(task =>
|
||||
{
|
||||
var workItem = workItems.FirstOrDefault(w => w.Id == task.WorkItemId.ToString());
|
||||
var area = areas.FirstOrDefault(a => a.Id == workItem?.WorkAreaId);
|
||||
var floor = floors.FirstOrDefault(f => f.Id == area?.FloorId);
|
||||
var building = buildings.FirstOrDefault(b => b.Id == floor?.BuildingId);
|
||||
|
||||
string activityName = workItem?.ActivityMaster?.ActivityName ?? "";
|
||||
string location = $"{building?.BuildingName} > {floor?.FloorName}</span><br/><span style=\"color: gray; font-size: small; padding-left: 10px;\"> {floor?.FloorName}-{area?.AreaName}";
|
||||
double pending = (workItem?.PlannedWork ?? 0) - (workItem?.CompletedWork ?? 0);
|
||||
|
||||
var taskTeam = taskMembers
|
||||
.Where(m => m.TaskAllocationId == task.Id)
|
||||
.Select(m =>
|
||||
{
|
||||
string name = $"{m.Employee?.FirstName ?? ""} {m.Employee?.LastName ?? ""}";
|
||||
var role = jobRoles.FirstOrDefault(r => r.Id == m.Employee?.JobRoleId);
|
||||
return new TaskTeam { Name = name, RoleName = role?.Name ?? "" };
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return new PerformedTask
|
||||
{
|
||||
Activity = activityName,
|
||||
Location = location,
|
||||
AssignedToday = task.PlannedTask,
|
||||
CompletedToday = task.CompletedTask,
|
||||
Pending = pending,
|
||||
Comment = task.Description,
|
||||
Team = taskTeam
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
// Attendance details
|
||||
var performedAttendance = attendances.Select(att =>
|
||||
{
|
||||
var alloc = projectAllocations.FirstOrDefault(p => p.EmployeeId == att.EmployeeID);
|
||||
var role = jobRoles.FirstOrDefault(r => r.Id == alloc?.JobRoleId);
|
||||
string name = $"{alloc?.Employee?.FirstName ?? ""} {alloc?.Employee?.LastName ?? ""}";
|
||||
|
||||
return new PerformedAttendance
|
||||
{
|
||||
Name = name,
|
||||
RoleName = role?.Name ?? "",
|
||||
InTime = att.InTime ?? DateTime.UtcNow,
|
||||
OutTime = att.OutTime,
|
||||
Comment = att.Comment
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
// Fill report
|
||||
statisticReport.TodaysAttendances = checkedInEmployeeIds.Count;
|
||||
statisticReport.TotalEmployees = assignedEmployeeIds.Count;
|
||||
statisticReport.RegularizationPending = regularizationIds.Count;
|
||||
statisticReport.CheckoutPending = checkoutPendingIds.Count;
|
||||
statisticReport.TotalPlannedWork = totalPlannedWork;
|
||||
statisticReport.TotalCompletedWork = totalCompletedWork;
|
||||
statisticReport.TotalPlannedTask = totalPlannedTask;
|
||||
statisticReport.TotalCompletedTask = totalCompletedTask;
|
||||
statisticReport.CompletionStatus = totalPlannedWork > 0 ? totalCompletedWork / totalPlannedWork : 0;
|
||||
statisticReport.TodaysAssignTasks = todayAssignedTasks.Count;
|
||||
statisticReport.ReportPending = reportPending.Count;
|
||||
statisticReport.TeamOnSite = teamOnSite;
|
||||
statisticReport.PerformedTasks = performedTasks;
|
||||
statisticReport.PerformedAttendance = performedAttendance;
|
||||
return statisticReport;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<ProjectStatisticReport?> GetDailyProjectReport(Guid projectId, Guid tenantId)
|
||||
{
|
||||
// await _cache.GetBuildingAndFloorByWorkAreaId();
|
||||
|
@ -28,7 +28,7 @@ namespace MarcoBMS.Services.Helpers
|
||||
/// </summary>
|
||||
/// <param name="EmployeeId">The ID of the employee.</param>
|
||||
/// <returns>A distinct list of FeaturePermission objects the employee is granted.</returns>
|
||||
public async Task<List<FeaturePermission>> GetFeaturePermissionByEmployeeId(Guid EmployeeId)
|
||||
public async Task<List<FeaturePermission>> GetFeaturePermissionByEmployeeId(Guid EmployeeId, Guid tenantId)
|
||||
{
|
||||
_logger.LogInfo("Fetching feature permissions for EmployeeId: {EmployeeId}", EmployeeId);
|
||||
|
||||
@ -58,7 +58,7 @@ namespace MarcoBMS.Services.Helpers
|
||||
{
|
||||
// The cache service might also need its own context, or you can pass the data directly.
|
||||
// Assuming AddApplicationRole takes the data, not a context.
|
||||
await _cache.AddApplicationRole(EmployeeId, roleIds);
|
||||
await _cache.AddApplicationRole(EmployeeId, roleIds, tenantId);
|
||||
_logger.LogInfo("Successfully queued cache update for EmployeeId: {EmployeeId}", EmployeeId);
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,14 @@ namespace Marco.Pms.Services.Service
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly RolesHelper _rolesHelper;
|
||||
private readonly CacheUpdateHelper _cache;
|
||||
public PermissionServices(ApplicationDbContext context, RolesHelper rolesHelper, CacheUpdateHelper cache)
|
||||
private readonly Guid tenantId;
|
||||
|
||||
public PermissionServices(ApplicationDbContext context, RolesHelper rolesHelper, CacheUpdateHelper cache, UserHelper userHelper)
|
||||
{
|
||||
_context = context;
|
||||
_rolesHelper = rolesHelper;
|
||||
_cache = cache;
|
||||
tenantId = userHelper.GetTenantId();
|
||||
}
|
||||
|
||||
//public async Task<bool> HasPermission(Guid featurePermissionId, Guid employeeId, Guid? projectId = null)
|
||||
@ -62,12 +65,12 @@ namespace Marco.Pms.Services.Service
|
||||
public async Task<bool> HasPermission(Guid featurePermissionId, Guid employeeId, Guid? projectId = null)
|
||||
{
|
||||
// 1. Try fetching permissions from cache (fast-path lookup).
|
||||
var featurePermissionIds = await _cache.GetPermissions(employeeId);
|
||||
var featurePermissionIds = await _cache.GetPermissions(employeeId, tenantId);
|
||||
|
||||
// If not found in cache, fallback to database (slower).
|
||||
if (featurePermissionIds == null)
|
||||
{
|
||||
var featurePermissions = await _rolesHelper.GetFeaturePermissionByEmployeeId(employeeId);
|
||||
var featurePermissions = await _rolesHelper.GetFeaturePermissionByEmployeeId(employeeId, tenantId);
|
||||
featurePermissionIds = featurePermissions.Select(fp => fp.Id).ToList();
|
||||
}
|
||||
|
||||
@ -115,10 +118,10 @@ namespace Marco.Pms.Services.Service
|
||||
}
|
||||
public async Task<bool> HasPermissionAny(List<Guid> featurePermissionIds, Guid employeeId)
|
||||
{
|
||||
var allFeaturePermissionIds = await _cache.GetPermissions(employeeId);
|
||||
var allFeaturePermissionIds = await _cache.GetPermissions(employeeId, tenantId);
|
||||
if (allFeaturePermissionIds == null)
|
||||
{
|
||||
List<FeaturePermission> featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeId(employeeId);
|
||||
List<FeaturePermission> featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeId(employeeId, tenantId);
|
||||
allFeaturePermissionIds = featurePermission.Select(fp => fp.Id).ToList();
|
||||
}
|
||||
var hasPermission = featurePermissionIds.Any(f => allFeaturePermissionIds.Contains(f));
|
||||
@ -128,7 +131,7 @@ namespace Marco.Pms.Services.Service
|
||||
public async Task<bool> HasProjectPermission(Employee LoggedInEmployee, Guid projectId)
|
||||
{
|
||||
var employeeId = LoggedInEmployee.Id;
|
||||
var projectIds = await _cache.GetProjects(employeeId);
|
||||
var projectIds = await _cache.GetProjects(employeeId, tenantId);
|
||||
|
||||
if (projectIds == null)
|
||||
{
|
||||
@ -147,7 +150,7 @@ namespace Marco.Pms.Services.Service
|
||||
}
|
||||
projectIds = allocation.Select(c => c.ProjectId).Distinct().ToList();
|
||||
}
|
||||
await _cache.AddProjects(LoggedInEmployee.Id, projectIds);
|
||||
await _cache.AddProjects(LoggedInEmployee.Id, projectIds, tenantId);
|
||||
}
|
||||
return projectIds.Contains(projectId);
|
||||
}
|
||||
|
@ -380,7 +380,7 @@ namespace Marco.Pms.Services.Service
|
||||
{
|
||||
// These operations do not depend on each other, so they can run in parallel.
|
||||
Task cacheAddDetailsTask = _cache.AddProjectDetails(project);
|
||||
Task cacheClearListTask = _cache.ClearAllProjectIdsByPermissionId(PermissionsMaster.ManageProject);
|
||||
Task cacheClearListTask = _cache.ClearAllProjectIdsByPermissionId(PermissionsMaster.ManageProject, tenantId);
|
||||
|
||||
// Await all side-effect tasks to complete in parallel
|
||||
await Task.WhenAll(cacheAddDetailsTask, cacheClearListTask);
|
||||
@ -767,7 +767,7 @@ namespace Marco.Pms.Services.Service
|
||||
}
|
||||
try
|
||||
{
|
||||
await _cache.ClearAllProjectIds(dto.EmployeeId);
|
||||
await _cache.ClearAllProjectIds(dto.EmployeeId, tenantId);
|
||||
_logger.LogInfo("Successfully completed cache invalidation for employee {EmployeeId}.", dto.EmployeeId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -986,7 +986,7 @@ namespace Marco.Pms.Services.Service
|
||||
// --- Step 5: Invalidate Cache ONCE after successful save ---
|
||||
try
|
||||
{
|
||||
await _cache.ClearAllProjectIds(employeeId);
|
||||
await _cache.ClearAllProjectIds(employeeId, tenantId);
|
||||
_logger.LogInfo("Successfully queued cache invalidation for employee {EmployeeId}.", employeeId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -2320,7 +2320,7 @@ namespace Marco.Pms.Services.Service
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||
|
||||
var projectIds = await _cache.GetProjects(LoggedInEmployee.Id);
|
||||
var projectIds = await _cache.GetProjects(LoggedInEmployee.Id, tenantId);
|
||||
|
||||
if (projectIds == null)
|
||||
{
|
||||
@ -2339,7 +2339,7 @@ namespace Marco.Pms.Services.Service
|
||||
}
|
||||
projectIds = allocation.Select(c => c.ProjectId).Distinct().ToList();
|
||||
}
|
||||
await _cache.AddProjects(LoggedInEmployee.Id, projectIds);
|
||||
await _cache.AddProjects(LoggedInEmployee.Id, projectIds, tenantId);
|
||||
}
|
||||
return projectIds;
|
||||
}
|
||||
@ -2350,7 +2350,7 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
// 1. Attempt to retrieve the list of project IDs from the cache first.
|
||||
// This is the "happy path" and should be as fast as possible.
|
||||
List<Guid>? projectIds = await _cache.GetProjects(loggedInEmployee.Id);
|
||||
List<Guid>? projectIds = await _cache.GetProjects(loggedInEmployee.Id, tenantId);
|
||||
|
||||
if (projectIds != null)
|
||||
{
|
||||
@ -2389,7 +2389,7 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
// 4. Populate the cache with the newly fetched list (even if it's empty).
|
||||
// This prevents repeated database queries for employees with no projects.
|
||||
await _cache.AddProjects(loggedInEmployee.Id, newProjectIds);
|
||||
await _cache.AddProjects(loggedInEmployee.Id, newProjectIds, tenantId);
|
||||
|
||||
return newProjectIds;
|
||||
}
|
||||
@ -2757,12 +2757,12 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
var _rolesHelper = scope.ServiceProvider.GetRequiredService<RolesHelper>();
|
||||
// 1. Try fetching permissions from cache (fast-path lookup).
|
||||
var featurePermissionIds = await _cache.GetPermissions(EmployeeId);
|
||||
var featurePermissionIds = await _cache.GetPermissions(EmployeeId, tenantId);
|
||||
|
||||
// If not found in cache, fallback to database (slower).
|
||||
if (featurePermissionIds == null)
|
||||
{
|
||||
var featurePermissions = await _rolesHelper.GetFeaturePermissionByEmployeeId(EmployeeId);
|
||||
var featurePermissions = await _rolesHelper.GetFeaturePermissionByEmployeeId(EmployeeId, tenantId);
|
||||
featurePermissionIds = featurePermissions.Select(fp => fp.Id).ToList();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user