diff --git a/IRaCIS.Core.Application/Service/Allocation/DTO/TaskConsistentRuleViewModel.cs b/IRaCIS.Core.Application/Service/Allocation/DTO/TaskConsistentRuleViewModel.cs index 7f478ad6e..e2c1b3ef3 100644 --- a/IRaCIS.Core.Application/Service/Allocation/DTO/TaskConsistentRuleViewModel.cs +++ b/IRaCIS.Core.Application/Service/Allocation/DTO/TaskConsistentRuleViewModel.cs @@ -24,7 +24,9 @@ namespace IRaCIS.Core.Application.ViewModel public int? GeneratedSubjectCount { get; set; } + public int? MatchSubejctCount { get; set; } + public int? CanGeneratedSubejctCount => MatchSubejctCount - GeneratedSubjectCount; } @@ -34,6 +36,13 @@ namespace IRaCIS.Core.Application.ViewModel } + public class SubjectGeneratedTask + { + public Guid SubjectId { get; set; } + + public bool IsHaveGeneratedTask { get; set; } + } + public class ConsistentQuery : PageInput { [NotDefault] @@ -46,6 +55,15 @@ namespace IRaCIS.Core.Application.ViewModel } + public class GroupConsistentSimpleQuery + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + + public Guid TrialReadingCriterionId { get; set; } + } public class GroupConsistentQuery : PageInput { [NotDefault] @@ -56,13 +74,21 @@ namespace IRaCIS.Core.Application.ViewModel public Guid TrialReadingCriterionId { get; set; } } - public class ConsistentConfirmGenerateCommand + public class SelfConsistentSimpleQuery { + [NotDefault] public Guid TaskConsistentRuleId { get; set; } [NotDefault] public Guid DoctorUserId { get; set; } + } + + public class ConsistentConfirmGenerateCommand: SelfConsistentSimpleQuery + { + public List SubejctIdList { get; set; } + + public bool IsAutoAllocateGenerateTask { get; set; } } @@ -70,7 +96,13 @@ namespace IRaCIS.Core.Application.ViewModel { [NotDefault] public Guid TrialId { get; set; } + + [NotDefault] + + public Guid TrialReadingCriterionId { get; set; } public List SubejctIdList { get; set; } + + public bool IsAutoAllocateGenerateTask { get; set; } } public class DoctorSelfConsistentSubjectView: ConsistentCommonView @@ -105,6 +137,8 @@ namespace IRaCIS.Core.Application.ViewModel public List SubjectTaskVisitList => VisitTaskList.GroupBy(t => new { t.SubjectId, t.VisitTaskNum }).Where(g => g.Count() == 2).Select(g => g.First()).OrderBy(t=>t.VisitTaskNum).ToList(); public List VisitTaskList { get; set; } = new List(); + + public List DoctorUserList { get; set; }=new List(); } public class VisitTaskGroupSimpleDTO diff --git a/IRaCIS.Core.Application/Service/Allocation/TaskConsistentRuleService.cs b/IRaCIS.Core.Application/Service/Allocation/TaskConsistentRuleService.cs index bda6e6ddf..dfeb6e08b 100644 --- a/IRaCIS.Core.Application/Service/Allocation/TaskConsistentRuleService.cs +++ b/IRaCIS.Core.Application/Service/Allocation/TaskConsistentRuleService.cs @@ -16,6 +16,9 @@ using System.Linq; using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.Filter; using Medallion.Threading; +using IRaCIS.Core.Infrastructure.Extention; +using System; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace IRaCIS.Core.Application.Service { @@ -37,10 +40,11 @@ namespace IRaCIS.Core.Application.Service private readonly IRepository _trialVirtualSiteCodeUpdateRepository; + private readonly IVisitTaskHelpeService _visitTaskCommonService; public TaskConsistentRuleService(IRepository visitTaskRepository, IRepository enrollRepository, IRepository taskConsistentRuleRepository, IRepository subjectUserRepository, IRepository subjectRepository, IDistributedLockProvider distributedLockProvider, - IRepository trialRepository, IRepository trialSiteRepository, IRepository trialVirtualSiteCodeUpdateRepository) + IRepository trialRepository, IRepository trialSiteRepository, IRepository trialVirtualSiteCodeUpdateRepository, IVisitTaskHelpeService visitTaskCommonService) { _taskConsistentRuleRepository = taskConsistentRuleRepository; _visitTaskRepository = visitTaskRepository; @@ -54,6 +58,8 @@ namespace IRaCIS.Core.Application.Service _trialSiteRepository = trialSiteRepository; _trialVirtualSiteCodeUpdateRepository = trialVirtualSiteCodeUpdateRepository; + + _visitTaskCommonService = visitTaskCommonService; } /// @@ -136,13 +142,12 @@ namespace IRaCIS.Core.Application.Service /// 确认生成自身一致性分析任务 /// /// - /// /// [HttpPost] [UnitOfWork] //[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] - public async Task ConfirmGenerateSelfConsistentTask(ConsistentConfirmGenerateCommand inCommand, [FromServices] IVisitTaskHelpeService _visitTaskCommonService) + public async Task ConfirmGenerateSelfConsistentTask(ConsistentConfirmGenerateCommand inCommand) { @@ -150,6 +155,20 @@ namespace IRaCIS.Core.Application.Service var doctorUserId = inCommand.DoctorUserId; var trialReadingCriterionId = filterObj.TrialReadingCriterionId; + //随机分配 + if (inCommand.IsAutoAllocateGenerateTask) + { + var subjectList = (await GetSelfConsistentRuleMatchSubjectIdListAsync(new SelfConsistentSimpleQuery() { TaskConsistentRuleId = inCommand.TaskConsistentRuleId, DoctorUserId = inCommand.DoctorUserId })).ToList(); + + if (subjectList.Any(t => t.IsHaveGeneratedTask)) + { + //已手动分配,不允许自动分配 + throw new BusinessValidationFailedException(_localizer["TaskConsistent_NotAllowedGenerate"]); + } + + inCommand.SubejctIdList = GetRandomSubjectIdList(subjectList.Select(t => t.SubjectId).ToList(), filterObj.PlanSubjectCount); + } + var list = await GetIQueryableDoctorSelfConsistentSubjectView(filterObj, doctorUserId, inCommand.SubejctIdList).ToListAsync(); //var (group, query) = GetIQueryableDoctorSelfConsistentRuleSubjectView(filterObj, inCommand.SubejctIdList); @@ -284,20 +303,34 @@ namespace IRaCIS.Core.Application.Service /// 确认生成组间一致性分析任务 /// /// - /// /// [HttpPost] [UnitOfWork] [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] - public async Task ConfirmGenerateGroupConsistentTask(GroupConsistentConfirmGenrateCommand inCommand, [FromServices] IVisitTaskHelpeService _visitTaskCommonService) + public async Task ConfirmGenerateGroupConsistentTask(GroupConsistentConfirmGenrateCommand inCommand) { var trialId = inCommand.TrialId; - var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.IsSelfAnalysis == false); + var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.TrialReadingCriterionId == inCommand.TrialReadingCriterionId && t.IsSelfAnalysis == false); var trialReadingCriterionId = filterObj.TrialReadingCriterionId; + //随机分配 + if (inCommand.IsAutoAllocateGenerateTask) + { + var subjectSelectList = (await GetGroupConsistentRuleMatchSubjectIdListAsync(new GroupConsistentSimpleQuery() { TrialId = inCommand.TrialId, TrialReadingCriterionId = inCommand.TrialReadingCriterionId })).ToList(); + + if (subjectSelectList.Any(t => t.IsHaveGeneratedTask)) + { + //已手动分配过,不允许自动分配 + throw new BusinessValidationFailedException(_localizer["TaskConsistent_NotAllowedGenerate"]); + } + + inCommand.SubejctIdList = GetRandomSubjectIdList(subjectSelectList.Select(t => t.SubjectId).ToList(), filterObj.PlanSubjectCount); + } + + var query = await GetGroupConsistentQueryAsync(filterObj, inCommand.SubejctIdList); @@ -578,7 +611,12 @@ namespace IRaCIS.Core.Application.Service - + /// + /// 组间一致性分析 满足条件的subject 筛选 + /// + /// + /// + /// private async Task> GetGroupConsistentQueryAsync(TaskConsistentRule filterObj, List? subejctIdList = null) { @@ -638,6 +676,13 @@ namespace IRaCIS.Core.Application.Service IsHaveGeneratedTask = t.SubjectVisitTaskList.Any(c => c.IsSelfAnalysis == false && c.TrialReadingCriterionId == trialReadingCriterionId), + DoctorUserList = t.SubjectDoctorList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsConfirmed).Select(t => new UserSimpleInfo() + { + UserId = t.Id, + FullName = t.DoctorUser.FullName, + UserCode = t.DoctorUser.UserCode, + UserName = t.DoctorUser.UserName + }).ToList(), ValidVisitCount = t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter).GroupBy(t => new { t.SubjectId, t.VisitTaskNum }).Where(g => g.Count() == 2).Count(), @@ -686,11 +731,21 @@ namespace IRaCIS.Core.Application.Service /// /// [HttpPost] - public async Task> GetSelfConsistentDoctorStatList(TaskConsistentRuleQuery inQuery) + public async Task>> GetSelfConsistentDoctorStatList(TaskConsistentRuleQuery inQuery) { + + var trialId = inQuery.TrialId; - var taskConsistentRuleQueryable = from enroll in _repository.Where(t => t.TrialId == trialId && t.EnrollStatus == EnrollStatus.ConfirmIntoGroup) + Expression> comonTaskFilter = u => u.TrialId == trialId && u.IsAnalysisCreate == false && u.TaskState == TaskState.Effect && u.ReadingTaskState == ReadingTaskState.HaveSigned && (u.ReReadingApplyState == ReReadingApplyState.Default || u.ReReadingApplyState == ReReadingApplyState.Reject); + + //&& u.TrialReadingCriterionId == trialReadingCriterionId &&u.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < DateTime.Now && u.DoctorUserId == doctorUserId; + + Expression> visitTaskFilter = comonTaskFilter.And(t => t.ReadingCategory == ReadingCategory.Visit); + + + var taskConsistentRuleQueryable = from enroll in _repository.Where(t => t.TrialId == trialId && t.EnrollStatus == EnrollStatus.ConfirmIntoGroup + && t.EnrollReadingCriteriaList.Any(c => c.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && c.IsJoinAnalysis)) join user in _repository.Where() on enroll.DoctorId equals user.DoctorId join taskConsistentRule in _repository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.IsSelfAnalysis) on enroll.TrialId equals taskConsistentRule.TrialId select new TaskConsistentRuleView() @@ -711,7 +766,26 @@ namespace IRaCIS.Core.Application.Service IsHaveReadingPeriod = taskConsistentRule.IsHaveReadingPeriod, PlanVisitCount = taskConsistentRule.PlanVisitCount, - GeneratedSubjectCount = taskConsistentRule.Trial.VisitTaskList.Where(t => t.IsAnalysisCreate && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.IsSelfAnalysis == true && t.DoctorUserId == user.Id).Select(t => t.SubjectId).Distinct().Count(), + GeneratedSubjectCount = taskConsistentRule.TrialReadingCriterion.VisitTaskList.Where(t => t.IsAnalysisCreate && t.IsSelfAnalysis == true && t.DoctorUserId == user.Id).Select(t => t.SubjectId).Distinct().Count(), + + //MatchSubejctCount = taskConsistentRule.Trial.SubjectList.Where(t => t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter) + //.Where(t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.SignTime!.Value.AddDays(taskConsistentRule.IntervalWeeks * 7) < DateTime.Now && t.DoctorUserId == user.DoctorId) + //.Count() >= taskConsistentRule.PlanVisitCount) + //.WhereIf(taskConsistentRule.IsHaveReadingPeriod == true, u => u.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter) + //.Where(t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.SignTime!.Value.AddDays(taskConsistentRule.IntervalWeeks * 7) < DateTime.Now && t.DoctorUserId == user.DoctorId) + //.Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).OrderBy(t => t.VisitTaskNum).Take(taskConsistentRule.PlanVisitCount + 1).Any(t => t.ReadingCategory == ReadingCategory.Global)) + //.Count(), + + MatchSubejctCount = taskConsistentRule.Trial.SubjectList.AsQueryable() + .Where(t => taskConsistentRule.IsHaveReadingPeriod == false ? t.SubjectVisitTaskList.AsQueryable() + .Where(visitTaskFilter).Where(t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.SignTime!.Value.AddDays(taskConsistentRule.IntervalWeeks * 7) < DateTime.Now && t.DoctorUserId == user.DoctorId) + .Count() >= taskConsistentRule.PlanVisitCount : + t.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter) + .Where(t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.SignTime!.Value.AddDays(taskConsistentRule.IntervalWeeks * 7) < DateTime.Now && t.DoctorUserId == user.DoctorId) + .Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).OrderBy(t => t.VisitTaskNum).Take(taskConsistentRule.PlanVisitCount + 1).Any(t => t.ReadingCategory == ReadingCategory.Global)) + .Count(), + + AnalysisDoctorUser = new UserSimpleInfo() { @@ -738,7 +812,10 @@ namespace IRaCIS.Core.Application.Service //var taskConsistentRuleQueryable = _taskConsistentRuleRepository.Where(t => t.TrialId == inQuery.TrialId) // .ProjectTo(_mapper.ConfigurationProvider); - return await taskConsistentRuleQueryable.ToListAsync(); + var list= await taskConsistentRuleQueryable.ToListAsync(); + + var rule= await _taskConsistentRuleRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsSelfAnalysis == inQuery.IsSelfAnalysis && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); + return ResponseOutput.Ok(list, rule); } [HttpPost] @@ -831,8 +908,65 @@ namespace IRaCIS.Core.Application.Service } + /// + /// 获取自身一致性分析 符合条件的subject 数组 + /// + /// + /// + public async Task> GetSelfConsistentRuleMatchSubjectIdListAsync(SelfConsistentSimpleQuery inQuery) + { + var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.Id == inQuery.TaskConsistentRuleId); + + var subjectList = await GetIQueryableDoctorSelfConsistentSubjectView(filterObj, inQuery.DoctorUserId).Select(t => new SubjectGeneratedTask() { SubjectId = t.SubjectId, IsHaveGeneratedTask = t.IsHaveGeneratedTask }).ToListAsync(); + + return subjectList; + } + + /// + /// 获取组件一致性分析符合条件的subject 数组 + /// + /// + /// + public async Task> GetGroupConsistentRuleMatchSubjectIdListAsync(GroupConsistentSimpleQuery inQuery) + { + var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.TrialId == inQuery.TrialId && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.IsSelfAnalysis == false); + + var subjectList = await (await GetGroupConsistentQueryAsync(filterObj)).Select(t => new SubjectGeneratedTask() { SubjectId = t.SubjectId, IsHaveGeneratedTask = t.IsHaveGeneratedTask }).ToListAsync(); + + return subjectList; + } + /// + /// 随机算法,选择指定数量的 subject + /// + /// + /// + /// + public List GetRandomSubjectIdList(List matchSubjectIdList, int countToSelect) + { + + // 使用 Fisher-Yates 随机置换算法来选择指定数量的 GUID + Random random = new Random(); + + for (int i = 0; i < countToSelect; i++) + { + // 生成一个随机索引 + int randomIndex = random.Next(i, matchSubjectIdList.Count); + + // 将选中的元素与当前元素交换位置 + Guid temp = matchSubjectIdList[randomIndex]; + matchSubjectIdList[randomIndex] = matchSubjectIdList[i]; + matchSubjectIdList[i] = temp; + } + + return matchSubjectIdList.Take(countToSelect).ToList(); + + // 使用洗牌算法来随机选择指定数量的GUID + //Random random = new Random(); + //return matchSubjectIdList.OrderBy(g => random.Next()).Take(countToSelect).ToList(); + } + } } diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/PersonalWorkstationViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/PersonalWorkstationViewModel.cs index f38043e7d..e65a83799 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/PersonalWorkstationViewModel.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/PersonalWorkstationViewModel.cs @@ -440,7 +440,7 @@ namespace IRaCIS.Core.Application.Contracts #endregion - public int? TotoalCount => TrialWaitSignDocCount + SysWaitSignDocCount + public int? TotalCount => TrialWaitSignDocCount + SysWaitSignDocCount + PM_SiteSurveryCount + PM_CheckCount + PM_ReviewerSelectCount + PM_ReReadingApprovalCount + PM_ClinicalDataCount + CRC_ImageSubmitCount + CRC_ImageQuestionCount + CRC_CheckQuestionCount + CRC_ImageReUploadCount + CRC_ClinicalDataTobeDoneCount + CRC_ClinialDataTobeConfirmCount + SPM_SiteSurveryCount + SPM_ReviewerApprovalCount + SPM_ReReadingApprovalCount diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs index cbe137ae4..e418f26e6 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs @@ -715,7 +715,7 @@ namespace IRaCIS.Core.Application .Where(c => c.TrialSite.CRCUserList.Any(u => u.UserId == _userInfo.Id)) .Where(u => u.SubmitState == SubmitStateEnum.ToSubmit).Count(), - }).Where(x => x.ToBeDealedCount > 0); ; + }).Where(x => x.ToBeDealedCount > 0); diff --git a/IRaCIS.Core.Domain/Trial/Trial.cs b/IRaCIS.Core.Domain/Trial/Trial.cs index 2a3812f28..fa45f6f95 100644 --- a/IRaCIS.Core.Domain/Trial/Trial.cs +++ b/IRaCIS.Core.Domain/Trial/Trial.cs @@ -359,7 +359,7 @@ namespace IRaCIS.Core.Domain.Models public int IndicationEnum { get; set; } = -1; - public string VitrualSiteCode { get; set; } + public string VitrualSiteCode { get; set; }=string.Empty; #region 邮件配置 ///