Merge branch 'master' of https://github.com/samsyntax24/OriginalGozareshgir
This commit is contained in:
2
.github/workflows/dotnet-developPublish.yml
vendored
2
.github/workflows/dotnet-developPublish.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
& "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" `
|
||||
-verb:sync `
|
||||
-source:contentPath="$publishFolder" `
|
||||
-dest:contentPath="dadmehrg",computerName="https://171.22.24.15:8172/msdeploy.axd?site=dadmehrg",userName="Administrator",password="R2rNpdnetP3j>q5b18",authType="Basic" `
|
||||
-dest:contentPath="dadmehrg",computerName="https://171.22.24.15:8172/msdeploy.axd?site=dadmehrg",userName="Administrator",password="R",authType="Basic" `
|
||||
-allowUntrusted `
|
||||
-enableRule:AppOffline
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
using GozareshgirProgramManager.Application._Common.Interfaces;
|
||||
using GozareshgirProgramManager.Application._Common.Models;
|
||||
using GozareshgirProgramManager.Domain._Common;
|
||||
using GozareshgirProgramManager.Domain.ProjectAgg.Enums;
|
||||
using GozareshgirProgramManager.Domain.ProjectAgg.Repositories;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace GozareshgirProgramManager.Application.Modules.Projects.Commands.AutoStopOverTimeTaskSections;
|
||||
|
||||
public record AutoStopOverTimeTaskSectionsCommand : IBaseCommand;
|
||||
|
||||
public class AutoStopOverTimeTaskSectionsCommandHandler : IBaseCommandHandler<AutoStopOverTimeTaskSectionsCommand>
|
||||
{
|
||||
private readonly ITaskSectionRepository _taskSectionRepository;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
|
||||
public AutoStopOverTimeTaskSectionsCommandHandler(ITaskSectionRepository taskSectionRepository,
|
||||
IUnitOfWork unitOfWork)
|
||||
{
|
||||
_taskSectionRepository = taskSectionRepository;
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public async Task<OperationResult> Handle(AutoStopOverTimeTaskSectionsCommand request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
// دریافت تمام تسکهای در حال انجام
|
||||
var taskSections = await _taskSectionRepository.GetActiveSectionsIncludeAllAsync(cancellationToken);
|
||||
|
||||
|
||||
foreach (var taskSection in taskSections)
|
||||
{
|
||||
// استفاده از متد Domain برای بررسی و متوقف کردن خودکار
|
||||
taskSection.AutoStopIfOverTime();
|
||||
}
|
||||
|
||||
// ذخیره تغییرات در دیتابیس
|
||||
await _unitOfWork.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return OperationResult.Success();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return OperationResult.Failure($"خطا در ناتمام کردن تسکهای overtime: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Globalization;
|
||||
using GozareshgirProgramManager.Application._Common.Interfaces;
|
||||
using GozareshgirProgramManager.Application._Common.Models;
|
||||
using GozareshgirProgramManager.Domain._Common;
|
||||
@@ -7,21 +8,23 @@ namespace GozareshgirProgramManager.Application.Modules.Projects.Queries.Project
|
||||
|
||||
public record ProjectBoardDetailQuery(Guid SectionId) : IBaseQuery<ProjectBoardDetailResponse>;
|
||||
|
||||
public record ProjectBoardDetailResponse(List<ProjectBoardDetailUserResponse> Users, string TotalTime);
|
||||
public record ProjectBoardDetailResponse(List<ProjectBoardDetailUserResponse> Users, string TotalTime,string RemainingTime );
|
||||
|
||||
public record ProjectBoardDetailUserResponse
|
||||
{
|
||||
public List<ProjectBoardDetailUserHistoryResponse> Histories { get; set; } = new();
|
||||
public string UserFullName { get; set; }
|
||||
public long UserId { get; set; }
|
||||
public List<ProjectBoardDetailUserHistoryResponse> Histories { get; init; }
|
||||
public string UserFullName { get; init; }
|
||||
public string TotalTime { get; init; }
|
||||
public string SpentTime { get; init; }
|
||||
public long UserId { get; init; }
|
||||
}
|
||||
|
||||
public class ProjectBoardDetailUserHistoryResponse
|
||||
public record ProjectBoardDetailUserHistoryResponse
|
||||
{
|
||||
public string Date { get; set; }
|
||||
public string startTime { get; set; }
|
||||
public string EndTime { get; set; }
|
||||
public string TotalTime { get; set; }
|
||||
public string Date { get; init; }
|
||||
public string startTime { get; init; }
|
||||
public string EndTime { get; init; }
|
||||
public string TotalTime { get; init; }
|
||||
}
|
||||
|
||||
public class ProjectBoardDetailQueryHandler : IBaseQueryHandler<ProjectBoardDetailQuery, ProjectBoardDetailResponse>
|
||||
@@ -38,6 +41,7 @@ public class ProjectBoardDetailQueryHandler : IBaseQueryHandler<ProjectBoardDeta
|
||||
{
|
||||
var section = await _programManagerDbContext.TaskSections
|
||||
.Include(x => x.Activities)
|
||||
.Include(x=>x.AdditionalTimes)
|
||||
.FirstOrDefaultAsync(x => x.Id == request.SectionId, cancellationToken: cancellationToken);
|
||||
|
||||
if (section == null)
|
||||
@@ -49,16 +53,22 @@ public class ProjectBoardDetailQueryHandler : IBaseQueryHandler<ProjectBoardDeta
|
||||
.Where(x => userIds.Contains(x.Id))
|
||||
.ToDictionaryAsync(x => x.Id, x => x.FullName, cancellationToken);
|
||||
|
||||
var totalTimeSpan = section.Activities
|
||||
var totalActivityTimeSpan = section.Activities
|
||||
.Select(x => x.GetTimeSpent())
|
||||
.Aggregate(TimeSpan.Zero, (sum, next) => sum.Add(next));
|
||||
|
||||
var finalTime = section.FinalEstimatedHours;
|
||||
|
||||
var remainingTimeSpan = finalTime >= totalActivityTimeSpan
|
||||
? TimeSpan.FromTicks(finalTime.Ticks - totalActivityTimeSpan.Ticks)
|
||||
: TimeSpan.Zero;
|
||||
var users = section.Activities.GroupBy(x => x.UserId).Select(x =>
|
||||
{
|
||||
return new ProjectBoardDetailUserResponse()
|
||||
{
|
||||
UserId = x.Key,
|
||||
UserFullName = usersDict[x.Key],
|
||||
TotalTime = TimeSpan.FromTicks(x.Sum(h=>h.GetTimeSpent().Ticks)).TotalHours.ToString(CultureInfo.InvariantCulture),
|
||||
Histories = x.Select(h => new ProjectBoardDetailUserHistoryResponse()
|
||||
{
|
||||
Date = h.StartDate.ToFarsi(),
|
||||
@@ -68,7 +78,8 @@ public class ProjectBoardDetailQueryHandler : IBaseQueryHandler<ProjectBoardDeta
|
||||
}).ToList()
|
||||
};
|
||||
}).ToList();
|
||||
var response = new ProjectBoardDetailResponse(users, $"{(int)totalTimeSpan.TotalHours}:{totalTimeSpan.Minutes:D2}");
|
||||
var response = new ProjectBoardDetailResponse(users, $"{(int)totalActivityTimeSpan.TotalHours}:{totalActivityTimeSpan.Minutes:D2}",
|
||||
$"{(int)remainingTimeSpan.TotalHours}:{remainingTimeSpan.Minutes:D2}");
|
||||
return OperationResult<ProjectBoardDetailResponse>.Success(response);
|
||||
}
|
||||
}
|
||||
@@ -217,4 +217,39 @@ public class TaskSection : EntityBase<Guid>
|
||||
var finalEstimate = FinalEstimatedHours;
|
||||
return totalSpent < finalEstimate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// اگر زمان کار شده بیش از تایم تعیین شده باشد، تسک را متوقف میکند
|
||||
/// و EndDate را به طوری تنظیم میکند که کل زمان برابر با FinalEstimatedHours شود
|
||||
/// </summary>
|
||||
public void AutoStopIfOverTime()
|
||||
{
|
||||
if (Status != TaskSectionStatus.InProgress)
|
||||
return;
|
||||
|
||||
var activeActivity = _activities.FirstOrDefault(a => a.IsActive);
|
||||
if (activeActivity == null)
|
||||
return;
|
||||
|
||||
// محاسبه کل زمان صرف شده تا کنون (بدون فعالیت فعال)
|
||||
var totalTimeSpentExcludingActive = _activities.Where(a => !a.IsActive).Sum(a => a.GetTimeSpent().Ticks);
|
||||
var totalTimeSpentTimeSpan = TimeSpan.FromTicks(totalTimeSpentExcludingActive);
|
||||
var finalEstimate = FinalEstimatedHours;
|
||||
|
||||
// اگر زمان صرف شده (بدون فعالیت فعال) + فعالیت فعال > تایم تعیین شده
|
||||
var activeTimeSpent = activeActivity.GetTimeSpent();
|
||||
if (totalTimeSpentTimeSpan + activeTimeSpent > finalEstimate)
|
||||
{
|
||||
// محاسبه مدت زمانی که این فعالیت باید برای رسیدن به FinalEstimatedHours داشته باشد
|
||||
var remainingTime = finalEstimate - totalTimeSpentTimeSpan;
|
||||
|
||||
// EndDate = StartDate + remainingTime
|
||||
var adjustedEndDate = activeActivity.StartDate.Add(remainingTime);
|
||||
|
||||
// متوقف کردن فعالیت با EndDate دقیق شده
|
||||
activeActivity.StopWorkWithSpecificTime(adjustedEndDate, "متوقف خودکار - بیش از تایم تعیین شده");
|
||||
|
||||
UpdateStatus(TaskSectionStatus.Incomplete);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,22 @@ public class TaskSectionActivity : EntityBase<Guid>
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// متوقف کردن فعالیت با مشخص کردن EndDate دقیق
|
||||
/// </summary>
|
||||
public void StopWorkWithSpecificTime(DateTime endDate, string? endNotes = null)
|
||||
{
|
||||
if (!IsActive)
|
||||
throw new InvalidOperationException("این فعالیت قبلاً متوقف شده است.");
|
||||
|
||||
if (endDate < StartDate)
|
||||
throw new InvalidOperationException("تاریخ پایان نمیتواند قبل از تاریخ شروع باشد.");
|
||||
|
||||
EndDate = endDate;
|
||||
EndNotes = endNotes;
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
public TimeSpan GetTimeSpent()
|
||||
{
|
||||
if (IsActive)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections;
|
||||
using GozareshgirProgramManager.Domain._Common;
|
||||
using GozareshgirProgramManager.Domain.ProjectAgg.Entities;
|
||||
|
||||
@@ -11,4 +12,5 @@ public interface ITaskSectionRepository: IRepository<Guid,TaskSection>
|
||||
Task<TaskSection?> GetByIdWithFullDataAsync(Guid id, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<List<TaskSection>> GetAssignedToUserAsync(long userId);
|
||||
Task<List<TaskSection>> GetActiveSectionsIncludeAllAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using GozareshgirProgramManager.Domain.ProjectAgg.Entities;
|
||||
using GozareshgirProgramManager.Domain.ProjectAgg.Enums;
|
||||
using GozareshgirProgramManager.Domain.ProjectAgg.Repositories;
|
||||
using GozareshgirProgramManager.Infrastructure.Persistence._Common;
|
||||
using GozareshgirProgramManager.Infrastructure.Persistence.Context;
|
||||
@@ -35,4 +36,13 @@ public class TaskSectionRepository:RepositoryBase<Guid,TaskSection>,ITaskSection
|
||||
.Where(x => x.CurrentAssignedUserId == userId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public Task<List<TaskSection>> GetActiveSectionsIncludeAllAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return _context.TaskSections
|
||||
.Where(x => x.Status == TaskSectionStatus.InProgress)
|
||||
.Include(x => x.Activities)
|
||||
.Include(x => x.AdditionalTimes)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using GozareshgirProgramManager.Application._Common.Models;
|
||||
using GozareshgirProgramManager.Application.Modules.Projects.Commands.AssignProject;
|
||||
using GozareshgirProgramManager.Application.Modules.Projects.Commands.AutoStopOverTimeTaskSections;
|
||||
using GozareshgirProgramManager.Application.Modules.Projects.Commands.ChangeStatusSection;
|
||||
using GozareshgirProgramManager.Application.Modules.Projects.Commands.CreateProject;
|
||||
using GozareshgirProgramManager.Application.Modules.Projects.Commands.DeleteProject;
|
||||
@@ -98,6 +99,9 @@ public class ProjectController : ProgramManagerBaseController
|
||||
[HttpGet("board")]
|
||||
public async Task<ActionResult<OperationResult<List<ProjectBoardListResponse>>>> GetProjectBoard([FromQuery] ProjectBoardListQuery query)
|
||||
{
|
||||
// اجرای Command برای متوقف کردن تسکهای overtime قبل از نمایش
|
||||
await _mediator.Send(new AutoStopOverTimeTaskSectionsCommand());
|
||||
|
||||
var res = await _mediator.Send(query);
|
||||
return res;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user