Ashutosh_Refactor #107
@ -33,6 +33,8 @@ namespace Marco.Pms.CacheHelper
|
||||
|
||||
var result = await _collection.UpdateOneAsync(filter, update, options);
|
||||
|
||||
await InitializeCollectionAsync();
|
||||
|
||||
// 6. Return a more accurate result indicating success for both updates and upserts.
|
||||
// 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);
|
||||
@ -51,6 +53,7 @@ namespace Marco.Pms.CacheHelper
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await InitializeCollectionAsync();
|
||||
return true;
|
||||
}
|
||||
public async Task<List<Guid>> GetProjectsFromCache(Guid employeeId)
|
||||
@ -177,5 +180,22 @@ namespace Marco.Pms.CacheHelper
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// A private method to handle the one-time setup of the collection's indexes.
|
||||
private async Task InitializeCollectionAsync()
|
||||
{
|
||||
// 1. Define the TTL (Time-To-Live) index on the 'ExpireAt' field.
|
||||
var indexKeys = Builders<EmployeePermissionMongoDB>.IndexKeys.Ascending(x => x.ExpireAt);
|
||||
var indexOptions = new CreateIndexOptions
|
||||
{
|
||||
// This tells MongoDB to automatically delete documents when their 'ExpireAt' time is reached.
|
||||
ExpireAfter = TimeSpan.FromSeconds(0)
|
||||
};
|
||||
var indexModel = new CreateIndexModel<EmployeePermissionMongoDB>(indexKeys, indexOptions);
|
||||
|
||||
// 2. Create the index. This is an idempotent operation if the index already exists.
|
||||
// Use CreateOneAsync since we are only creating a single index.
|
||||
await _collection.Indexes.CreateOneAsync(indexModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,27 +11,59 @@ namespace Marco.Pms.CacheHelper
|
||||
{
|
||||
public class ProjectCache
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly IMongoCollection<ProjectMongoDB> _projetCollection;
|
||||
private readonly IMongoCollection<ProjectMongoDB> _projectCollection;
|
||||
private readonly IMongoCollection<WorkItemMongoDB> _taskCollection;
|
||||
public ProjectCache(ApplicationDbContext context, IConfiguration configuration)
|
||||
{
|
||||
var connectionString = configuration["MongoDB:ConnectionString"];
|
||||
_context = context;
|
||||
var mongoUrl = new MongoUrl(connectionString);
|
||||
var client = new MongoClient(mongoUrl); // Your MongoDB connection string
|
||||
var mongoDB = client.GetDatabase(mongoUrl.DatabaseName); // Your MongoDB Database name
|
||||
_projetCollection = mongoDB.GetCollection<ProjectMongoDB>("ProjectDetails");
|
||||
_projectCollection = mongoDB.GetCollection<ProjectMongoDB>("ProjectDetails");
|
||||
_taskCollection = mongoDB.GetCollection<WorkItemMongoDB>("WorkItemDetails");
|
||||
}
|
||||
|
||||
public async Task AddProjectDetailsToCache(ProjectMongoDB projectDetails)
|
||||
{
|
||||
await _projetCollection.InsertOneAsync(projectDetails);
|
||||
await _projectCollection.InsertOneAsync(projectDetails);
|
||||
|
||||
var indexKeys = Builders<ProjectMongoDB>.IndexKeys.Ascending(x => x.ExpireAt);
|
||||
var indexOptions = new CreateIndexOptions
|
||||
{
|
||||
ExpireAfter = TimeSpan.Zero // required for fixed expiration time
|
||||
};
|
||||
var indexModel = new CreateIndexModel<ProjectMongoDB>(indexKeys, indexOptions);
|
||||
await _projectCollection.Indexes.CreateOneAsync(indexModel);
|
||||
|
||||
}
|
||||
// The method should focus only on inserting data.
|
||||
public async Task AddProjectDetailsListToCache(List<ProjectMongoDB> projectDetailsList)
|
||||
{
|
||||
await _projetCollection.InsertManyAsync(projectDetailsList);
|
||||
// 1. Add a guard clause to avoid an unnecessary database call for an empty list.
|
||||
if (projectDetailsList == null || !projectDetailsList.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Perform the insert operation. This is the only responsibility of this method.
|
||||
await _projectCollection.InsertManyAsync(projectDetailsList);
|
||||
await InitializeCollectionAsync();
|
||||
}
|
||||
// A private method to handle the one-time setup of the collection's indexes.
|
||||
private async Task InitializeCollectionAsync()
|
||||
{
|
||||
// 1. Define the TTL (Time-To-Live) index on the 'ExpireAt' field.
|
||||
var indexKeys = Builders<ProjectMongoDB>.IndexKeys.Ascending(x => x.ExpireAt);
|
||||
var indexOptions = new CreateIndexOptions
|
||||
{
|
||||
// This tells MongoDB to automatically delete documents when their 'ExpireAt' time is reached.
|
||||
ExpireAfter = TimeSpan.FromSeconds(0)
|
||||
};
|
||||
var indexModel = new CreateIndexModel<ProjectMongoDB>(indexKeys, indexOptions);
|
||||
|
||||
// 2. Create the index. This is an idempotent operation if the index already exists.
|
||||
// Use CreateOneAsync since we are only creating a single index.
|
||||
await _projectCollection.Indexes.CreateOneAsync(indexModel);
|
||||
}
|
||||
public async Task<bool> UpdateProjectDetailsOnlyToCache(Project project, StatusMaster projectStatus)
|
||||
{
|
||||
@ -51,7 +83,7 @@ namespace Marco.Pms.CacheHelper
|
||||
);
|
||||
|
||||
// Perform the update
|
||||
var result = await _projetCollection.UpdateOneAsync(
|
||||
var result = await _projectCollection.UpdateOneAsync(
|
||||
filter: r => r.Id == project.Id.ToString(),
|
||||
update: updates
|
||||
);
|
||||
@ -71,7 +103,7 @@ namespace Marco.Pms.CacheHelper
|
||||
var projection = Builders<ProjectMongoDB>.Projection.Exclude(p => p.Buildings);
|
||||
|
||||
// Perform query
|
||||
var project = await _projetCollection
|
||||
var project = await _projectCollection
|
||||
.Find(filter)
|
||||
.Project<ProjectMongoDB>(projection)
|
||||
.FirstOrDefaultAsync();
|
||||
@ -83,7 +115,7 @@ namespace Marco.Pms.CacheHelper
|
||||
List<string> stringProjectIds = projectIds.Select(p => p.ToString()).ToList();
|
||||
var filter = Builders<ProjectMongoDB>.Filter.In(p => p.Id, stringProjectIds);
|
||||
var projection = Builders<ProjectMongoDB>.Projection.Exclude(p => p.Buildings);
|
||||
var projects = await _projetCollection
|
||||
var projects = await _projectCollection
|
||||
.Find(filter)
|
||||
.Project<ProjectMongoDB>(projection)
|
||||
.ToListAsync();
|
||||
@ -92,14 +124,14 @@ namespace Marco.Pms.CacheHelper
|
||||
public async Task<bool> DeleteProjectByIdFromCacheAsync(Guid projectId)
|
||||
{
|
||||
var filter = Builders<ProjectMongoDB>.Filter.Eq(e => e.Id, projectId.ToString());
|
||||
var result = await _projetCollection.DeleteOneAsync(filter);
|
||||
var result = await _projectCollection.DeleteOneAsync(filter);
|
||||
return result.DeletedCount > 0;
|
||||
}
|
||||
public async Task<bool> RemoveProjectsFromCacheAsync(List<Guid> projectIds)
|
||||
{
|
||||
var stringIds = projectIds.Select(id => id.ToString()).ToList();
|
||||
var filter = Builders<ProjectMongoDB>.Filter.In(p => p.Id, stringIds);
|
||||
var result = await _projetCollection.DeleteManyAsync(filter);
|
||||
var result = await _projectCollection.DeleteManyAsync(filter);
|
||||
return result.DeletedCount > 0;
|
||||
}
|
||||
|
||||
@ -125,7 +157,7 @@ namespace Marco.Pms.CacheHelper
|
||||
var filter = Builders<ProjectMongoDB>.Filter.Eq(p => p.Id, stringProjectId);
|
||||
var update = Builders<ProjectMongoDB>.Update.Push("Buildings", buildingMongo);
|
||||
|
||||
var result = await _projetCollection.UpdateOneAsync(filter, update);
|
||||
var result = await _projectCollection.UpdateOneAsync(filter, update);
|
||||
|
||||
if (result.MatchedCount == 0)
|
||||
{
|
||||
@ -155,7 +187,7 @@ namespace Marco.Pms.CacheHelper
|
||||
);
|
||||
|
||||
var update = Builders<ProjectMongoDB>.Update.Push("Buildings.$.Floors", floorMongo);
|
||||
var result = await _projetCollection.UpdateOneAsync(filter, update);
|
||||
var result = await _projectCollection.UpdateOneAsync(filter, update);
|
||||
|
||||
if (result.MatchedCount == 0)
|
||||
{
|
||||
@ -189,7 +221,7 @@ namespace Marco.Pms.CacheHelper
|
||||
var update = Builders<ProjectMongoDB>.Update.Push("Buildings.$[b].Floors.$[f].WorkAreas", workAreaMongo);
|
||||
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
|
||||
|
||||
var result = await _projetCollection.UpdateOneAsync(filter, update, updateOptions);
|
||||
var result = await _projectCollection.UpdateOneAsync(filter, update, updateOptions);
|
||||
|
||||
if (result.MatchedCount == 0)
|
||||
{
|
||||
@ -221,7 +253,7 @@ namespace Marco.Pms.CacheHelper
|
||||
Builders<ProjectMongoDB>.Update.Set("Buildings.$.Description", building.Description)
|
||||
);
|
||||
|
||||
var result = await _projetCollection.UpdateOneAsync(filter, update);
|
||||
var result = await _projectCollection.UpdateOneAsync(filter, update);
|
||||
|
||||
if (result.MatchedCount == 0)
|
||||
{
|
||||
@ -246,7 +278,7 @@ namespace Marco.Pms.CacheHelper
|
||||
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
|
||||
var filter = Builders<ProjectMongoDB>.Filter.Eq(p => p.Id, stringProjectId);
|
||||
|
||||
var result = await _projetCollection.UpdateOneAsync(filter, update, updateOptions);
|
||||
var result = await _projectCollection.UpdateOneAsync(filter, update, updateOptions);
|
||||
|
||||
if (result.MatchedCount == 0)
|
||||
{
|
||||
@ -272,7 +304,7 @@ namespace Marco.Pms.CacheHelper
|
||||
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
|
||||
var filter = Builders<ProjectMongoDB>.Filter.Eq(p => p.Id, stringProjectId);
|
||||
|
||||
var result = await _projetCollection.UpdateOneAsync(filter, update, updateOptions);
|
||||
var result = await _projectCollection.UpdateOneAsync(filter, update, updateOptions);
|
||||
|
||||
if (result.MatchedCount == 0)
|
||||
{
|
||||
@ -296,7 +328,7 @@ namespace Marco.Pms.CacheHelper
|
||||
var filter = Builders<ProjectMongoDB>.Filter.Eq(p => p.Id, projectId.ToString());
|
||||
|
||||
// Project only the "Buildings" field from the document
|
||||
var buildings = await _projetCollection
|
||||
var buildings = await _projectCollection
|
||||
.Find(filter)
|
||||
.Project(p => p.Buildings)
|
||||
.FirstOrDefaultAsync();
|
||||
@ -315,7 +347,7 @@ namespace Marco.Pms.CacheHelper
|
||||
public async Task UpdatePlannedAndCompleteWorksInBuildingFromCache(Guid workAreaId, double plannedWork, double completedWork)
|
||||
{
|
||||
var filter = Builders<ProjectMongoDB>.Filter.Eq("Buildings.Floors.WorkAreas._id", workAreaId.ToString());
|
||||
var project = await _projetCollection.Find(filter).FirstOrDefaultAsync();
|
||||
var project = await _projectCollection.Find(filter).FirstOrDefaultAsync();
|
||||
|
||||
string? selectedBuildingId = null;
|
||||
string? selectedFloorId = null;
|
||||
@ -353,7 +385,7 @@ namespace Marco.Pms.CacheHelper
|
||||
.Inc("Buildings.$[b].CompletedWork", completedWork)
|
||||
.Inc("PlannedWork", plannedWork)
|
||||
.Inc("CompletedWork", completedWork);
|
||||
var result = await _projetCollection.UpdateOneAsync(filter, update, updateOptions);
|
||||
var result = await _projectCollection.UpdateOneAsync(filter, update, updateOptions);
|
||||
|
||||
}
|
||||
public async Task<WorkAreaInfoMongoDB?> GetBuildingAndFloorByWorkAreaIdFromCache(Guid workAreaId)
|
||||
@ -393,7 +425,7 @@ namespace Marco.Pms.CacheHelper
|
||||
{ "WorkArea", "$Buildings.Floors.WorkAreas" }
|
||||
})
|
||||
};
|
||||
var result = await _projetCollection.Aggregate<WorkAreaInfoMongoDB>(pipeline).FirstOrDefaultAsync();
|
||||
var result = await _projectCollection.Aggregate<WorkAreaInfoMongoDB>(pipeline).FirstOrDefaultAsync();
|
||||
if (result == null)
|
||||
return null;
|
||||
return result;
|
||||
|
@ -9,5 +9,6 @@ namespace Marco.Pms.Model.MongoDBModels
|
||||
public List<string> ApplicationRoleIds { get; set; } = new List<string>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -14,5 +14,6 @@
|
||||
public int TeamSize { get; set; }
|
||||
public double CompletedWork { get; set; }
|
||||
public double PlannedWork { get; set; }
|
||||
public DateTime ExpireAt { get; set; } = DateTime.UtcNow.Date.AddDays(1);
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.AttendanceVM;
|
||||
using Marco.Pms.Services.Hubs;
|
||||
using Marco.Pms.Services.Service;
|
||||
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||
using MarcoBMS.Services.Helpers;
|
||||
using MarcoBMS.Services.Service;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -28,7 +29,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly EmployeeHelper _employeeHelper;
|
||||
private readonly ProjectsHelper _projectsHelper;
|
||||
private readonly IProjectServices _projectServices;
|
||||
private readonly UserHelper _userHelper;
|
||||
private readonly S3UploadService _s3Service;
|
||||
private readonly PermissionServices _permission;
|
||||
@ -37,11 +38,11 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
|
||||
public AttendanceController(
|
||||
ApplicationDbContext context, EmployeeHelper employeeHelper, ProjectsHelper projectsHelper, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permission, IHubContext<MarcoHub> signalR)
|
||||
ApplicationDbContext context, EmployeeHelper employeeHelper, IProjectServices projectServices, UserHelper userHelper, S3UploadService s3Service, ILoggingService logger, PermissionServices permission, IHubContext<MarcoHub> signalR)
|
||||
{
|
||||
_context = context;
|
||||
_employeeHelper = employeeHelper;
|
||||
_projectsHelper = projectsHelper;
|
||||
_projectServices = projectServices;
|
||||
_userHelper = userHelper;
|
||||
_s3Service = s3Service;
|
||||
_logger = logger;
|
||||
@ -188,7 +189,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date >= fromDate.Date && c.AttendanceDate.Date <= toDate.Date && c.TenantId == TenantId).ToListAsync();
|
||||
|
||||
|
||||
List<ProjectAllocation> projectteam = await _projectsHelper.GetTeamByProject(TenantId, projectId, true);
|
||||
List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(TenantId, projectId, true);
|
||||
var jobRole = await _context.JobRoles.ToListAsync();
|
||||
foreach (Attendance? attendance in lstAttendance)
|
||||
{
|
||||
@ -295,7 +296,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date == forDate && c.TenantId == TenantId).ToListAsync();
|
||||
|
||||
|
||||
List<ProjectAllocation> projectteam = await _projectsHelper.GetTeamByProject(TenantId, projectId, IncludeInActive);
|
||||
List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(TenantId, projectId, IncludeInActive);
|
||||
var idList = projectteam.Select(p => p.EmployeeId).ToList();
|
||||
//var emp = await _context.Employees.Where(e => idList.Contains(e.Id)).Include(e => e.JobRole).ToListAsync();
|
||||
var jobRole = await _context.JobRoles.ToListAsync();
|
||||
@ -378,7 +379,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
|
||||
List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE && c.TenantId == TenantId).ToListAsync();
|
||||
|
||||
List<ProjectAllocation> projectteam = await _projectsHelper.GetTeamByProject(TenantId, projectId, true);
|
||||
List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(TenantId, projectId, true);
|
||||
var idList = projectteam.Select(p => p.EmployeeId).ToList();
|
||||
var jobRole = await _context.JobRoles.ToListAsync();
|
||||
|
||||
|
@ -9,6 +9,7 @@ using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.Employee;
|
||||
using Marco.Pms.Services.Hubs;
|
||||
using Marco.Pms.Services.Service;
|
||||
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||
using MarcoBMS.Services.Helpers;
|
||||
using MarcoBMS.Services.Service;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -37,13 +38,13 @@ namespace MarcoBMS.Services.Controllers
|
||||
private readonly ILoggingService _logger;
|
||||
private readonly IHubContext<MarcoHub> _signalR;
|
||||
private readonly PermissionServices _permission;
|
||||
private readonly ProjectsHelper _projectsHelper;
|
||||
private readonly IProjectServices _projectServices;
|
||||
private readonly Guid tenantId;
|
||||
|
||||
|
||||
public EmployeeController(UserManager<ApplicationUser> userManager, IEmailSender emailSender,
|
||||
ApplicationDbContext context, EmployeeHelper employeeHelper, UserHelper userHelper, IConfiguration configuration, ILoggingService logger,
|
||||
IHubContext<MarcoHub> signalR, PermissionServices permission, ProjectsHelper projectsHelper)
|
||||
IHubContext<MarcoHub> signalR, PermissionServices permission, IProjectServices projectServices)
|
||||
{
|
||||
_context = context;
|
||||
_userManager = userManager;
|
||||
@ -54,7 +55,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
_logger = logger;
|
||||
_signalR = signalR;
|
||||
_permission = permission;
|
||||
_projectsHelper = projectsHelper;
|
||||
_projectServices = projectServices;
|
||||
tenantId = _userHelper.GetTenantId();
|
||||
}
|
||||
|
||||
@ -119,7 +120,7 @@ namespace MarcoBMS.Services.Controllers
|
||||
loggedInEmployee.Id, projectid ?? Guid.Empty, ShowInactive);
|
||||
|
||||
// Step 3: Fetch project access and permissions
|
||||
var projectIds = await _projectsHelper.GetMyProjects(tenantId, loggedInEmployee);
|
||||
var projectIds = await _projectServices.GetMyProjectIdsAsync(tenantId, loggedInEmployee);
|
||||
|
||||
var hasViewAllEmployeesPermission = await _permission.HasPermission(PermissionsMaster.ViewAllEmployees, loggedInEmployee.Id);
|
||||
var hasViewTeamMembersPermission = await _permission.HasPermission(PermissionsMaster.ViewTeamMembers, loggedInEmployee.Id);
|
||||
|
@ -4,6 +4,7 @@ using Marco.Pms.Model.Mapper;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.Employee;
|
||||
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||
using MarcoBMS.Services.Helpers;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -19,14 +20,14 @@ namespace MarcoBMS.Services.Controllers
|
||||
private readonly UserHelper _userHelper;
|
||||
private readonly EmployeeHelper _employeeHelper;
|
||||
|
||||
private readonly ProjectsHelper _projectsHelper;
|
||||
private readonly IProjectServices _projectServices;
|
||||
private readonly RolesHelper _rolesHelper;
|
||||
|
||||
public UserController(EmployeeHelper employeeHelper, ProjectsHelper projectsHelper, UserHelper userHelper, RolesHelper rolesHelper)
|
||||
public UserController(EmployeeHelper employeeHelper, IProjectServices projectServices, UserHelper userHelper, RolesHelper rolesHelper)
|
||||
{
|
||||
_userHelper = userHelper;
|
||||
_employeeHelper = employeeHelper;
|
||||
_projectsHelper = projectsHelper;
|
||||
_projectServices = projectServices;
|
||||
_rolesHelper = rolesHelper;
|
||||
|
||||
}
|
||||
@ -56,12 +57,12 @@ namespace MarcoBMS.Services.Controllers
|
||||
/* User with permission manage project can see all projects */
|
||||
if (featurePermission != null && featurePermission.Exists(c => c.Id.ToString() == "172fc9b6-755b-4f62-ab26-55c34a330614"))
|
||||
{
|
||||
List<Project> projects = await _projectsHelper.GetAllProjectByTanentID(emp.TenantId);
|
||||
List<Project> projects = await _projectServices.GetAllProjectByTanentID(emp.TenantId);
|
||||
projectsId = projects.Select(c => c.Id.ToString()).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
List<ProjectAllocation> allocation = await _projectsHelper.GetProjectByEmployeeID(emp.Id);
|
||||
List<ProjectAllocation> allocation = await _projectServices.GetProjectByEmployeeID(emp.Id);
|
||||
projectsId = allocation.Select(c => c.ProjectId.ToString()).ToArray();
|
||||
}
|
||||
EmployeeProfile profile = new EmployeeProfile() { };
|
||||
|
@ -1,37 +0,0 @@
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
|
||||
namespace ModelServices.Helpers
|
||||
{
|
||||
public class ProjectHelper
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
public ProjectHelper(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<List<ProjectAllocation>> GetTeamByProject(Guid TenantId, Guid ProjectId, bool IncludeInactive)
|
||||
{
|
||||
if (IncludeInactive)
|
||||
{
|
||||
|
||||
var employees = await _context.ProjectAllocations.Where(c => c.TenantId == TenantId && c.ProjectId == ProjectId).Include(e => e.Employee).ToListAsync();
|
||||
|
||||
return employees;
|
||||
}
|
||||
else
|
||||
{
|
||||
var employees = await _context.ProjectAllocations.Where(c => c.TenantId == TenantId && c.ProjectId == ProjectId && c.IsActive == true).Include(e => e.Employee).ToListAsync();
|
||||
|
||||
return employees;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
using Marco.Pms.DataAccess.Data;
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Entitlements;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Marco.Pms.Services.Helpers;
|
||||
using Marco.Pms.Services.Service;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace MarcoBMS.Services.Helpers
|
||||
{
|
||||
public class ProjectsHelper
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly CacheUpdateHelper _cache;
|
||||
private readonly PermissionServices _permission;
|
||||
|
||||
public ProjectsHelper(ApplicationDbContext context, CacheUpdateHelper cache, PermissionServices permission)
|
||||
{
|
||||
_context = context;
|
||||
_cache = cache;
|
||||
_permission = permission;
|
||||
}
|
||||
|
||||
public async Task<List<Project>> GetAllProjectByTanentID(Guid tanentID)
|
||||
{
|
||||
List<Project> alloc = await _context.Projects.Where(c => c.TenantId == tanentID).ToListAsync();
|
||||
return alloc;
|
||||
}
|
||||
|
||||
public async Task<List<ProjectAllocation>> GetProjectByEmployeeID(Guid employeeID)
|
||||
{
|
||||
List<ProjectAllocation> alloc = await _context.ProjectAllocations.Where(c => c.EmployeeId == employeeID && c.IsActive == true).Include(c => c.Project).ToListAsync();
|
||||
return alloc;
|
||||
}
|
||||
|
||||
public async Task<List<ProjectAllocation>> GetTeamByProject(Guid TenantId, Guid ProjectId, bool IncludeInactive)
|
||||
{
|
||||
if (IncludeInactive)
|
||||
{
|
||||
|
||||
var employees = await _context.ProjectAllocations.Where(c => c.TenantId == TenantId && c.ProjectId == ProjectId).Include(e => e.Employee).ToListAsync();
|
||||
|
||||
return employees;
|
||||
}
|
||||
else
|
||||
{
|
||||
var employees = await _context.ProjectAllocations.Where(c => c.TenantId == TenantId && c.ProjectId == ProjectId && c.IsActive == true).Include(e => e.Employee).ToListAsync();
|
||||
|
||||
return employees;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<Guid>> GetMyProjects(Guid tenantId, Employee LoggedInEmployee)
|
||||
{
|
||||
var projectIds = await _cache.GetProjects(LoggedInEmployee.Id);
|
||||
|
||||
if (projectIds == null)
|
||||
{
|
||||
var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageProject, LoggedInEmployee.Id);
|
||||
if (hasPermission)
|
||||
{
|
||||
var projects = await _context.Projects.Where(c => c.TenantId == tenantId).ToListAsync();
|
||||
projectIds = projects.Select(p => p.Id).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
var allocation = await GetProjectByEmployeeID(LoggedInEmployee.Id);
|
||||
if (!allocation.Any())
|
||||
{
|
||||
return new List<Guid>();
|
||||
}
|
||||
projectIds = allocation.Select(c => c.ProjectId).Distinct().ToList();
|
||||
}
|
||||
await _cache.AddProjects(LoggedInEmployee.Id, projectIds);
|
||||
}
|
||||
|
||||
return projectIds;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -167,7 +167,6 @@ builder.Services.AddScoped<GeneralHelper>();
|
||||
builder.Services.AddScoped<UserHelper>();
|
||||
builder.Services.AddScoped<RolesHelper>();
|
||||
builder.Services.AddScoped<EmployeeHelper>();
|
||||
builder.Services.AddScoped<ProjectsHelper>();
|
||||
builder.Services.AddScoped<DirectoryHelper>();
|
||||
builder.Services.AddScoped<MasterHelper>();
|
||||
builder.Services.AddScoped<ReportHelper>();
|
||||
|
@ -12,7 +12,6 @@ using Marco.Pms.Model.ViewModels.Employee;
|
||||
using Marco.Pms.Model.ViewModels.Projects;
|
||||
using Marco.Pms.Services.Helpers;
|
||||
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||
using MarcoBMS.Services.Helpers;
|
||||
using MarcoBMS.Services.Service;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -25,7 +24,6 @@ namespace Marco.Pms.Services.Service
|
||||
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
|
||||
private readonly ApplicationDbContext _context; // Keeping this for direct scoped context use where appropriate
|
||||
private readonly ILoggingService _logger;
|
||||
private readonly ProjectsHelper _projectsHelper;
|
||||
private readonly PermissionServices _permission;
|
||||
private readonly CacheUpdateHelper _cache;
|
||||
private readonly IMapper _mapper;
|
||||
@ -34,7 +32,6 @@ namespace Marco.Pms.Services.Service
|
||||
IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
||||
ApplicationDbContext context,
|
||||
ILoggingService logger,
|
||||
ProjectsHelper projectsHelper,
|
||||
PermissionServices permission,
|
||||
CacheUpdateHelper cache,
|
||||
IMapper mapper,
|
||||
@ -43,7 +40,6 @@ namespace Marco.Pms.Services.Service
|
||||
_dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
|
||||
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_projectsHelper = projectsHelper ?? throw new ArgumentNullException(nameof(projectsHelper));
|
||||
_permission = permission ?? throw new ArgumentNullException(nameof(permission));
|
||||
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
|
||||
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
|
||||
@ -64,7 +60,7 @@ namespace Marco.Pms.Services.Service
|
||||
_logger.LogInfo("Basic project list requested by EmployeeId {EmployeeId}", loggedInEmployee.Id);
|
||||
|
||||
// Step 2: Get the list of project IDs the user has access to
|
||||
List<Guid> accessibleProjectIds = await _projectsHelper.GetMyProjects(tenantId, loggedInEmployee);
|
||||
List<Guid> accessibleProjectIds = await GetMyProjects(tenantId, loggedInEmployee);
|
||||
|
||||
if (accessibleProjectIds == null || !accessibleProjectIds.Any())
|
||||
{
|
||||
@ -94,7 +90,7 @@ namespace Marco.Pms.Services.Service
|
||||
_logger.LogInfo("Starting GetAllProjects for TenantId: {TenantId}, User: {UserId}", tenantId, loggedInEmployee.Id);
|
||||
|
||||
// --- Step 1: Get a list of project IDs the user can access ---
|
||||
List<Guid> projectIds = await _projectsHelper.GetMyProjects(tenantId, loggedInEmployee);
|
||||
List<Guid> projectIds = await GetMyProjects(tenantId, loggedInEmployee);
|
||||
if (!projectIds.Any())
|
||||
{
|
||||
_logger.LogInfo("User has no assigned projects. Returning empty list.");
|
||||
@ -743,7 +739,7 @@ namespace Marco.Pms.Services.Service
|
||||
// This is a placeholder for your actual, more specific permission logic.
|
||||
// It should also handle the case where a user is requesting their own projects (employeeId == loggedInEmployee.Id).
|
||||
var hasPermission = await _permission.HasPermission(PermissionsMaster.ViewProject, loggedInEmployee.Id);
|
||||
var projectIds = await _projectsHelper.GetMyProjects(tenantId, loggedInEmployee);
|
||||
var projectIds = await GetMyProjects(tenantId, loggedInEmployee);
|
||||
if (!hasPermission)
|
||||
{
|
||||
_logger.LogWarning("Access DENIED for user {UserId} trying to view projects for employee {TargetEmployeeId}.", loggedInEmployee.Id, employeeId);
|
||||
@ -1329,6 +1325,110 @@ namespace Marco.Pms.Services.Service
|
||||
|
||||
#region =================================================================== Helper Functions ===================================================================
|
||||
|
||||
public async Task<List<Project>> GetAllProjectByTanentID(Guid tanentId)
|
||||
{
|
||||
List<Project> alloc = await _context.Projects.Where(c => c.TenantId == tanentId).ToListAsync();
|
||||
return alloc;
|
||||
}
|
||||
|
||||
public async Task<List<ProjectAllocation>> GetProjectByEmployeeID(Guid employeeId)
|
||||
{
|
||||
List<ProjectAllocation> alloc = await _context.ProjectAllocations.Where(c => c.EmployeeId == employeeId && c.IsActive == true).Include(c => c.Project).ToListAsync();
|
||||
return alloc;
|
||||
}
|
||||
|
||||
public async Task<List<ProjectAllocation>> GetTeamByProject(Guid TenantId, Guid ProjectId, bool IncludeInactive)
|
||||
{
|
||||
if (IncludeInactive)
|
||||
{
|
||||
|
||||
var employees = await _context.ProjectAllocations.Where(c => c.TenantId == TenantId && c.ProjectId == ProjectId).Include(e => e.Employee).ToListAsync();
|
||||
|
||||
return employees;
|
||||
}
|
||||
else
|
||||
{
|
||||
var employees = await _context.ProjectAllocations.Where(c => c.TenantId == TenantId && c.ProjectId == ProjectId && c.IsActive == true).Include(e => e.Employee).ToListAsync();
|
||||
|
||||
return employees;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<Guid>> GetMyProjects(Guid tenantId, Employee LoggedInEmployee)
|
||||
{
|
||||
var projectIds = await _cache.GetProjects(LoggedInEmployee.Id);
|
||||
|
||||
if (projectIds == null)
|
||||
{
|
||||
var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageProject, LoggedInEmployee.Id);
|
||||
if (hasPermission)
|
||||
{
|
||||
var projects = await _context.Projects.Where(c => c.TenantId == tenantId).ToListAsync();
|
||||
projectIds = projects.Select(p => p.Id).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
var allocation = await GetProjectByEmployeeID(LoggedInEmployee.Id);
|
||||
if (!allocation.Any())
|
||||
{
|
||||
return new List<Guid>();
|
||||
}
|
||||
projectIds = allocation.Select(c => c.ProjectId).Distinct().ToList();
|
||||
}
|
||||
await _cache.AddProjects(LoggedInEmployee.Id, projectIds);
|
||||
}
|
||||
return projectIds;
|
||||
}
|
||||
|
||||
public async Task<List<Guid>> GetMyProjectIdsAsync(Guid tenantId, Employee loggedInEmployee)
|
||||
{
|
||||
// 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);
|
||||
|
||||
if (projectIds != null)
|
||||
{
|
||||
// Cache Hit: Return the cached list immediately.
|
||||
return projectIds;
|
||||
}
|
||||
|
||||
// 2. Cache Miss: The list was not in the cache, so we must fetch it from the database.
|
||||
List<Guid> newProjectIds;
|
||||
|
||||
// Check for the specific permission.
|
||||
var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageProject, loggedInEmployee.Id);
|
||||
|
||||
if (hasPermission)
|
||||
{
|
||||
// 3a. OPTIMIZATION: User has permission to see all projects.
|
||||
// Fetch *only* the Ids directly from the database. This is far more efficient
|
||||
// than fetching full Project objects and then selecting the Ids in memory.
|
||||
newProjectIds = await _context.Projects
|
||||
.Where(p => p.TenantId == tenantId)
|
||||
.Select(p => p.Id) // This translates to `SELECT Id FROM Projects...` in SQL.
|
||||
.ToListAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 3b. OPTIMIZATION: User can only see projects they are allocated to.
|
||||
// We go directly to the source (ProjectAllocations) and ask the database
|
||||
// for a distinct list of ProjectIds. This is much better than calling a
|
||||
// helper function that might return full allocation objects.
|
||||
newProjectIds = await _context.ProjectAllocations
|
||||
.Where(a => a.EmployeeId == loggedInEmployee.Id && a.ProjectId != Guid.Empty)
|
||||
.Select(a => a.ProjectId)
|
||||
.Distinct() // Pushes the DISTINCT operation to the database.
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
return newProjectIds;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of ProjectInfoVMs by their IDs, using an efficient partial cache-hit strategy.
|
||||
/// It fetches what it can from the cache (as ProjectMongoDB), gets the rest from the
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Marco.Pms.Model.Dtos.Project;
|
||||
using Marco.Pms.Model.Employees;
|
||||
using Marco.Pms.Model.Projects;
|
||||
using Marco.Pms.Model.Utilities;
|
||||
using Marco.Pms.Model.ViewModels.Projects;
|
||||
|
||||
@ -25,5 +26,10 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
|
||||
Task<ApiResponse<List<WorkItemVM>>> CreateProjectTaskAsync(List<WorkItemDto> workItemDtos, Guid tenantId, Employee loggedInEmployee);
|
||||
Task<ServiceResponse> DeleteProjectTaskAsync(Guid id, Guid tenantId, Employee loggedInEmployee);
|
||||
|
||||
Task<List<Project>> GetAllProjectByTanentID(Guid tanentId);
|
||||
Task<List<ProjectAllocation>> GetProjectByEmployeeID(Guid employeeId);
|
||||
Task<List<ProjectAllocation>> GetTeamByProject(Guid TenantId, Guid ProjectId, bool IncludeInactive);
|
||||
Task<List<Guid>> GetMyProjectIdsAsync(Guid tenantId, Employee LoggedInEmployee);
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user