Teannt porfile API Optimized
This commit is contained in:
parent
c210e6e3f2
commit
9ef7946d89
@ -57,13 +57,13 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
UserHelper userHelper,
|
UserHelper userHelper,
|
||||||
FeatureDetailsHelper featureDetailsHelper)
|
FeatureDetailsHelper featureDetailsHelper)
|
||||||
{
|
{
|
||||||
_dbContextFactory = dbContextFactory;
|
_dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory)); ;
|
||||||
_serviceScopeFactory = serviceScopeFactory;
|
_serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); ;
|
||||||
_logger = logger;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); ;
|
||||||
_userManager = userManager;
|
_userManager = userManager ?? throw new ArgumentNullException(nameof(userManager)); ;
|
||||||
_mapper = mapper;
|
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); ;
|
||||||
_userHelper = userHelper;
|
_userHelper = userHelper ?? throw new ArgumentNullException(nameof(userHelper)); ;
|
||||||
_featureDetailsHelper = featureDetailsHelper;
|
_featureDetailsHelper = featureDetailsHelper ?? throw new ArgumentNullException(nameof(featureDetailsHelper)); ;
|
||||||
}
|
}
|
||||||
#region =================================================================== Tenant APIs ===================================================================
|
#region =================================================================== Tenant APIs ===================================================================
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
|
|
||||||
// GET api/<TenantController>/5
|
// GET api/<TenantController>/5
|
||||||
[HttpGet("details/{id}")]
|
[HttpGet("details/{id}")]
|
||||||
public async Task<IActionResult> GetDetails(Guid id)
|
private async Task<IActionResult> GetTenantDetails(Guid id)
|
||||||
{
|
{
|
||||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
|
|
||||||
@ -325,6 +325,148 @@ namespace Marco.Pms.Services.Controllers
|
|||||||
return Ok(ApiResponse<object>.SuccessResponse(response, "Tenant profile fetched successfully", 200));
|
return Ok(ApiResponse<object>.SuccessResponse(response, "Tenant profile fetched successfully", 200));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("details/{id}")]
|
||||||
|
public async Task<IActionResult> GetTenantDetailsAsync(Guid id)
|
||||||
|
{
|
||||||
|
_logger.LogInfo("GetTenantDetails started for TenantId: {TenantId}", id);
|
||||||
|
|
||||||
|
// Get currently logged-in employee info
|
||||||
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
|
if (loggedInEmployee == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("No logged-in employee found for the request.");
|
||||||
|
return Unauthorized(ApiResponse<object>.ErrorResponse("Unauthorized", "User must be logged in.", 401));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check permissions using a single service scope to avoid overhead
|
||||||
|
bool hasManagePermission, hasModifyPermission, hasViewPermission;
|
||||||
|
using (var scope = _serviceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
|
||||||
|
|
||||||
|
var manageTask = permissionService.HasPermission(PermissionsMaster.ManageTenants, loggedInEmployee.Id);
|
||||||
|
var modifyTask = permissionService.HasPermission(PermissionsMaster.ModifyTenant, loggedInEmployee.Id);
|
||||||
|
var viewTask = permissionService.HasPermission(PermissionsMaster.ViewTenant, loggedInEmployee.Id);
|
||||||
|
|
||||||
|
await Task.WhenAll(manageTask, modifyTask, viewTask);
|
||||||
|
|
||||||
|
hasManagePermission = manageTask.Result;
|
||||||
|
hasModifyPermission = modifyTask.Result;
|
||||||
|
hasViewPermission = viewTask.Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasManagePermission && !hasModifyPermission && !hasViewPermission)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Permission denied: User {EmployeeId} attempted to access tenant details without the required permissions.", loggedInEmployee.Id);
|
||||||
|
return StatusCode(403,
|
||||||
|
ApiResponse<object>.ErrorResponse("Access denied", "User does not have the required permissions for this action.", 403));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a single DbContext for main tenant fetch and related data requests
|
||||||
|
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
|
||||||
|
// Fetch tenant details with related Industry and TenantStatus in a single query
|
||||||
|
var tenant = await _context.Tenants
|
||||||
|
.Include(t => t.Industry)
|
||||||
|
.Include(t => t.TenantStatus)
|
||||||
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(t => t.Id == id);
|
||||||
|
|
||||||
|
if (tenant == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Tenant {TenantId} not found in database", id);
|
||||||
|
return NotFound(ApiResponse<object>.ErrorResponse("Tenant not found", "Tenant not found", 404));
|
||||||
|
}
|
||||||
|
_logger.LogInfo("Tenant {TenantId} found.", tenant.Id);
|
||||||
|
|
||||||
|
// Fetch dependent data in parallel to improve performance
|
||||||
|
var employeesTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Employees
|
||||||
|
.Include(e => e.ApplicationUser)
|
||||||
|
.Where(e => e.TenantId == tenant.Id)
|
||||||
|
.AsNoTracking()
|
||||||
|
.ToListAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
var createdByTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Employees
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(e => e.Id == tenant.CreatedById)
|
||||||
|
.Select(e => _mapper.Map<BasicEmployeeVM>(e))
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
var plansTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.TenantSubscriptions
|
||||||
|
.Include(sp => sp.CreatedBy)
|
||||||
|
.ThenInclude(e => e!.JobRole)
|
||||||
|
.Include(sp => sp.UpdatedBy)
|
||||||
|
.ThenInclude(e => e!.JobRole)
|
||||||
|
.Include(sp => sp.Currency)
|
||||||
|
.Include(ts => ts.Plan).ThenInclude(sp => sp.Plan)
|
||||||
|
.Where(ts => ts.TenantId == tenant.Id && ts.Plan != null)
|
||||||
|
.AsNoTracking()
|
||||||
|
.OrderBy(ts => ts.CreatedBy)
|
||||||
|
.ToListAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
var projectsTask = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await using var dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
return await dbContext.Projects
|
||||||
|
.Include(p => p.ProjectStatus)
|
||||||
|
.Where(p => p.TenantId == tenant.Id)
|
||||||
|
.AsNoTracking()
|
||||||
|
.ToListAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
await Task.WhenAll(employeesTask, createdByTask, plansTask, projectsTask);
|
||||||
|
|
||||||
|
var employees = employeesTask.Result;
|
||||||
|
var createdBy = createdByTask.Result;
|
||||||
|
var plans = plansTask.Result;
|
||||||
|
var projects = projectsTask.Result;
|
||||||
|
|
||||||
|
// Calculate active/inactive employees count
|
||||||
|
var activeEmployeesCount = employees.Count(e => e.IsActive);
|
||||||
|
var inActiveEmployeesCount = employees.Count - activeEmployeesCount;
|
||||||
|
|
||||||
|
// Filter current active (non-cancelled) subscription plan
|
||||||
|
var currentPlan = plans.FirstOrDefault(ts => !ts.IsCancelled);
|
||||||
|
var expiryDate = currentPlan?.EndDate;
|
||||||
|
var nextBillingDate = currentPlan?.NextBillingDate;
|
||||||
|
|
||||||
|
// Map Tenant entity to TenantDetailsVM response model
|
||||||
|
var response = _mapper.Map<TenantDetailsVM>(tenant);
|
||||||
|
response.ActiveEmployees = activeEmployeesCount;
|
||||||
|
response.InActiveEmployees = inActiveEmployeesCount;
|
||||||
|
|
||||||
|
// Count projects by status
|
||||||
|
response.ActiveProjects = projects.Count(p => p.ProjectStatusId == projectActiveStatus);
|
||||||
|
response.InProgressProjects = projects.Count(p => p.ProjectStatusId == projectInProgressStatus);
|
||||||
|
response.OnHoldProjects = projects.Count(p => p.ProjectStatusId == projectOnHoldStatus);
|
||||||
|
response.InActiveProjects = projects.Count(p => p.ProjectStatusId == projectInActiveStatus);
|
||||||
|
response.CompletedProjects = projects.Count(p => p.ProjectStatusId == projectCompletedStatus);
|
||||||
|
|
||||||
|
response.ExpiryDate = expiryDate;
|
||||||
|
response.NextBillingDate = nextBillingDate;
|
||||||
|
response.CreatedBy = createdBy;
|
||||||
|
|
||||||
|
// Map subscription history plans to DTO
|
||||||
|
response.SubscriptionHistery = _mapper.Map<List<SubscriptionPlanDetailsVM>>(plans);
|
||||||
|
|
||||||
|
_logger.LogInfo("Tenant details fetched successfully for TenantId: {TenantId}", tenant.Id);
|
||||||
|
|
||||||
|
return Ok(ApiResponse<object>.SuccessResponse(response, "Tenant profile fetched successfully", 200));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// POST api/<TenantController>
|
// POST api/<TenantController>
|
||||||
[HttpPost("create")]
|
[HttpPost("create")]
|
||||||
public async Task<IActionResult> CreateTenant([FromBody] CreateTenantDto model)
|
public async Task<IActionResult> CreateTenant([FromBody] CreateTenantDto model)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user