using DocumentFormat.OpenXml.Vml.Spreadsheet; using IRaCIS.Core.Application.Filter; using IRaCIS.Core.Application.Service.WorkLoad.DTO; using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Infra.EFCore.Common; using Microsoft.AspNetCore.Mvc; using System.Text.RegularExpressions; namespace IRaCIS.Core.Application.Service { [ApiExplorerSettings(GroupName = "Enroll")] public class EnrollService(IRepository _trialRepository, IRepository _trialDetailRepository, IRepository _TrialPaymentPriceRepository, IRepository _enrollRepository, IRepository _doctorRepository, IRepository _enrollDetailRepository, IRepository _workloadRepository, IRepository _userRoleRepository, IRepository _trialUserRepository, IRepository _ReadingQuestionCriterionTrialRepository, IRepository _trialExperienceRepository, IRepository _dictionaryRepository, IRepository _trialExperienceCriteriaRepository, IMailVerificationService _mailVerificationService, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IEnrollService { /// /// 添加或更新项目医生项目价格 /// [HttpPost] public async Task AddOrUpdateEnroll(EnrollCommand addOrUpdateModel) { var trialDoctoritem = await _enrollRepository.FirstOrDefaultAsync(u => u.Id == addOrUpdateModel.Id); if (trialDoctoritem == null)//insert { await _enrollRepository.InsertFromDTOAsync(addOrUpdateModel); } else//update { await _enrollRepository.UpdateFromDTOAsync(addOrUpdateModel); } var success = await _enrollRepository.SaveChangesAsync(); return ResponseOutput.Result(success); } /// /// 获取医生项目列表 /// /// /// [HttpPost] public async Task> GetTrialDoctorList(EnrollGetQuery challengeQuery) { var costStatisticsQueryable = from enroll in _enrollRepository.Where(t => t.TrialId == challengeQuery.TrialId) join dociorc in _doctorRepository.Where() on enroll.DoctorId equals dociorc.Id join price in _TrialPaymentPriceRepository.Where() on enroll.TrialId equals price.TrialId select new EnrollViewModel() { ChineseName = dociorc.ChineseName, AdjustmentMultiple = enroll.AdjustmentMultiple, FirstName = dociorc.FirstName, LastName = dociorc.LastName, DoctorId = dociorc.Id, TrialId = challengeQuery.TrialId, Id = enroll.Id, Training = enroll.Training, RefresherTraining = enroll.RefresherTraining, Timepoint = enroll.Timepoint, Timepoint48H = enroll.Timepoint48H, Timepoint24H = enroll.Timepoint24H, Adjudication = enroll.Adjudication, Adjudication48H = enroll.Adjudication48H, Adjudication24H = enroll.Adjudication24H, Global = enroll.Global, Code = dociorc.Code, ReviewerCode = dociorc.ReviewerCode, Downtime = enroll.Downtime, }; return await costStatisticsQueryable.ToPagedListAsync(challengeQuery); //var query2 = _repository.Where(x => x.TrialId == challengeQuery.TrialId) // .WhereIf(challengeQuery.TrialId != null, t => t.TrialId == challengeQuery.TrialId) // .WhereIf(challengeQuery.DoctorId != null, t => t.DoctorId == challengeQuery.DoctorId) // .ProjectTo(_mapper.ConfigurationProvider); //var pageList = await query2.ToListAsync(); // return new PageOutput(challengeQuery.pageIndex, challengeQuery.pageSize, //costStatisticsQueryable); } #region Reviewer 入组审核流程(select-submit-approve-confirm) /// 为项目筛选医生 提交 【select】 /// 项目Id /// 医生Id数组 /// [HttpPost("{trialId:guid}")] [TrialGlobalLimit( "AfterStopCannNotOpt" )] //[Authorize(Policy = IRaCISPolicy.PM_APM_SPM_CPM_SMM_CMM)] public async Task SelectReviewers(Guid trialId, Guid[] doctorIdArray) { var trial = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId); if (trial == null) return Null404NotFound(trial); //_trialRepository.Attach(trial); //更新项目状态 trial.TrialEnrollStatus = (int)TrialEnrollStatus.HasApplyDownLoadResume; //_trialRepository.Update(trial); //添加项目状态变化记录 var trialDetail = new TrialStatusDetail() { TrialId = trial.Id, TrialStatus = (int)TrialEnrollStatus.HasApplyDownLoadResume, }; await _trialDetailRepository.AddAsync(trialDetail); // 入组表 入组状态跟踪表 foreach (var doctorId in doctorIdArray) { await _enrollRepository.AddAsync(new Enroll() { DoctorId = doctorId, TrialId = trialId, EnrollStatus = EnrollStatus.HasApplyDownloadResume, DoctorUserId = _userRoleRepository.Where(t => t.DoctorId == doctorId).Select(t => t.Id).FirstOrDefault() }); ; await _enrollDetailRepository.AddAsync(new EnrollDetail() { DoctorId = doctorId, TrialId = trialId, EnrollStatus = EnrollStatus.HasApplyDownloadResume, OptUserType = (int)SystemUserType.AdminUser, }); } return ResponseOutput.Result(await _enrollRepository.SaveChangesAsync()); } /// /// 入组流程-向CRO提交医生[Submit] /// [HttpPost("{trialId:guid}/{commitState:int}")] [TrialGlobalLimit( "AfterStopCannNotOpt" )] //[Authorize(Policy = IRaCISPolicy.PM_APM_SPM_CPM_SMM_CMM)] public async Task SubmitReviewer(Guid trialId, Guid[] doctorIdArray, int commitState) { var trial = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId); var hasSPMOrCPM = await _trialUserRepository.Where(t => t.TrialId == trialId).AnyAsync(t => t.User.UserTypeEnum == UserTypeEnum.SPM || t.User.UserTypeEnum == UserTypeEnum.CPM); var isSPMjoin= trial.IsSPMJoinReviewerSelect && hasSPMOrCPM; if (trial != null) { if (commitState == 1) //确认提交CRO { //更新项目状态 trial.TrialEnrollStatus = isSPMjoin ? (int)TrialEnrollStatus.HasCommitCRO : (int)TrialEnrollStatus.HasConfirmedDoctorNames; //添加项目详细记录 var trialDetail = new TrialStatusDetail() { TrialId = trial.Id, TrialStatus = (int)TrialEnrollStatus.HasCommitCRO }; await _trialDetailRepository.AddAsync(trialDetail); //更新入组表 跟踪了 所以不用下面的_enrollRepository.Update(intoGroupItem); var intoGroupList = await _enrollRepository.Where(t => t.TrialId == trialId, true).ToListAsync(); foreach (var intoGroupItem in intoGroupList) { if (doctorIdArray.Contains(intoGroupItem.DoctorId)) { intoGroupItem.EnrollStatus = isSPMjoin ? EnrollStatus.HasCommittedToCRO : EnrollStatus.InviteIntoGroup; await _enrollDetailRepository.AddAsync(new EnrollDetail() { TrialDetailId = trialDetail.Id, DoctorId = intoGroupItem.DoctorId, TrialId = trialId, EnrollStatus = isSPMjoin ? EnrollStatus.HasCommittedToCRO : EnrollStatus.InviteIntoGroup, OptUserType = (int)SystemUserType.AdminUser, //后台用户 }); if (!isSPMjoin) { await _enrollDetailRepository.AddAsync(new EnrollDetail() { TrialDetailId = trialDetail.Id, DoctorId = intoGroupItem.DoctorId, TrialId = trialId, EnrollStatus = EnrollStatus.HasCommittedToCRO, OptUserType = (int)SystemUserType.AdminUser, //后台用户 }); } } } } else if (commitState == 0)//回退上一步 { //更新入组表 var intoGroupList = await _enrollRepository.Where(t => t.TrialId == trialId, true).ToListAsync(); foreach (var intoGroupItem in intoGroupList) { if (doctorIdArray.Contains(intoGroupItem.DoctorId)) { intoGroupItem.EnrollStatus = EnrollStatus.HasApplyDownloadResume; //_enrollRepository.Update(intoGroupItem); var deleteItem = await _enrollDetailRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.DoctorId == intoGroupItem.DoctorId && t.EnrollStatus == EnrollStatus.HasCommittedToCRO); await _enrollDetailRepository.DeleteAsync(deleteItem); } } } return ResponseOutput.Result(await _enrollRepository.SaveChangesAsync(), new { IsHaveSPMOrCPM = isSPMjoin }); } //$"Cannot find trial {trialId}" return ResponseOutput.NotOk(_localizer["Enroll_NotFound", trialId]); } /// /// 入组流程-CRO确定医生名单 [ Approve] /// [HttpPost("{trialId:guid}/{auditState:int}")] [TrialGlobalLimit( "AfterStopCannNotOpt" )] //[Authorize(Policy = IRaCISPolicy.PM_APM_SPM_CPM_SMM_CMM)] public async Task ApproveReviewer(Guid trialId, Guid[] doctorIdArray, int auditState) { var trial = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId); if (trial == null) return Null404NotFound(trial); if (auditState == 1) //确认入组 { //var existItem = _trialRepository.FindSingleOrDefault(u => u.Id == trialId && u.TrialStatus >= (int)TrialEnrollStatus.HasConfirmedDoctorNames); //if (existItem == null) //{ // trial.TrialStatus = (int)TrialEnrollStatus.HasConfirmedDoctorNames; // trial.TrialStatusStr = "Approved"; //} //_trialRepository.Update(trial); //更新项目状态 trial.TrialEnrollStatus = (int)TrialEnrollStatus.HasConfirmedDoctorNames; //添加项目详细记录 var trialDetail = new TrialStatusDetail() { TrialId = trialId, TrialStatus = (int)TrialEnrollStatus.HasConfirmedDoctorNames }; await _trialDetailRepository.AddAsync(trialDetail); //更新入组表 跟踪方式,不用下面的_enrollRepository.Update(intoGroupItem); var intoGroupList = _enrollRepository.Where(t => t.TrialId == trialId, true).ToList(); foreach (var intoGroupItem in intoGroupList) { if (doctorIdArray.Contains(intoGroupItem.DoctorId)) { intoGroupItem.EnrollStatus = EnrollStatus.InviteIntoGroup; //_enrollRepository.Update(intoGroupItem); await _enrollDetailRepository.AddAsync(new EnrollDetail() { DoctorId = intoGroupItem.DoctorId, TrialId = trialId, EnrollStatus = EnrollStatus.InviteIntoGroup, OptUserType = (int)SystemUserType.AdminUser, //后台用户 }); } } } else if (auditState == 0)//回退上一步 { //更新入组表 var intoGroupList = _enrollRepository.Where(t => t.TrialId == trialId, true).ToList(); foreach (var intoGroupItem in intoGroupList) { if (doctorIdArray.Contains(intoGroupItem.DoctorId)) { intoGroupItem.EnrollStatus = EnrollStatus.HasCommittedToCRO; //_enrollRepository.Update(intoGroupItem); var deleteItem = await _enrollDetailRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.DoctorId == intoGroupItem.DoctorId && t.EnrollStatus == EnrollStatus.InviteIntoGroup); await _enrollDetailRepository.DeleteAsync(deleteItem); } } } return ResponseOutput.Result(await _enrollRepository.SaveChangesAsync()); } /// /// 入组流程-后台确认医生入组[Confirm] /// [HttpPost] [TrialGlobalLimit( "AfterStopCannNotOpt" )] //[Authorize(Policy = IRaCISPolicy.PM_APM_SPM_CPM_SMM_CMM)] [UnitOfWork] public async Task ConfirmReviewer(ConfirmReviewerCommand confirmReviewerCommand, [FromServices] IRepository _trialUserRepository, [FromServices] IRepository _taskAllocationRuleRepository) { //var trial = _trialRepository.FirstOrDefault(t => t.Id == trialId); //var existItem = _trialRepository.FindSingleOrDefault(u => u.Id == trialId && u.TrialStatus >= (int)TrialEnrollStatus.HasConfirmedDoctorNames); //trial.TrialStatusStr = "Reading"; ////trial.TrialStatus = (int)TrialStatus.HasConfirmedDoctorNames; //_trialRepository.Update(trial); var trialId = confirmReviewerCommand.TrialId; var trial = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId); if (trial == null) return Null404NotFound(trial); //更新入组表 var intoGroupList = await _enrollRepository.Where(t => t.TrialId == trialId, true).ToListAsync(); //验证邮件 var emaiList = await _doctorRepository.Where(t => intoGroupList.Select(t => t.DoctorId).Contains(t.Id)) .Select(t => new { t.EMail, t.FirstName, t.LastName }).ToListAsync(); var errorList = emaiList.Where(t => !Regex.IsMatch(t.EMail, @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")) .ToList(); if (errorList.Count() > 0) { // errorList.Select(c => c.LastName+" / "+c.FirstName)) +"邮箱格式存在问题" return ResponseOutput.NotOk(string.Join(',', _localizer["Enroll_EmailFormat"], errorList.Select(c => c.LastName + " / " + c.FirstName))); } var readingQuestionCriterionTrial = await _ReadingQuestionCriterionTrialRepository.Where(x => x.TrialId == confirmReviewerCommand.TrialId && x.IsConfirm).ToListAsync(); List trialExperienceList = new List(); // 获取标准字典 var criterionTypeList = await _dictionaryRepository.Where(x => x.Parent.Code == "CriterionType").ToListAsync(); if (confirmReviewerCommand.ConfirmState == 1) //确认入组 { foreach (var intoGroupItem in intoGroupList) { if (confirmReviewerCommand.DoctorIdArray.Contains(intoGroupItem.DoctorId)) { //当邮件发送没有问题的时候,才修改状态 如果有问题,就当前不做处理 try { var userId = await _mailVerificationService.DoctorJoinTrialEmail(trialId, intoGroupItem.DoctorId, confirmReviewerCommand.BaseUrl, confirmReviewerCommand.RouteUrl); if (!await _trialUserRepository.AnyAsync(t => t.TrialId == trialId && t.UserId == userId, true)) { await _trialUserRepository.AddAsync(new TrialUser() { TrialId = trialId, UserId = userId, JoinTime = DateTime.Now }); } await _enrollRepository.BatchUpdateNoTrackingAsync(t => t.Id == intoGroupItem.Id, u => new Enroll() { DoctorUserId = userId }); if (!await _taskAllocationRuleRepository.AnyAsync(t => t.TrialId == trialId && t.DoctorUserId == userId && t.EnrollId == intoGroupItem.Id, true)) { await _taskAllocationRuleRepository.AddAsync(new TaskAllocationRule() { TrialId = trialId, DoctorUserId = userId, EnrollId = intoGroupItem.Id }); } await _enrollDetailRepository.AddAsync(new EnrollDetail() { DoctorId = intoGroupItem.DoctorId, TrialId = trialId, EnrollStatus = EnrollStatus.ConfirmIntoGroup, OptUserType = (int)SystemUserType.AdminUser, //后台用户 }); intoGroupItem.EnrollStatus = EnrollStatus.ConfirmIntoGroup; intoGroupItem.EnrollTime = DateTime.Now; } catch (Exception) { intoGroupItem.EnrollStatus = EnrollStatus.ConfirmIntoGroupFailed; } } } List dataTypes = new List() { ExperienceDataType.TrialAuto, ExperienceDataType.SystemAuto, }; // 添加临床试验经历 if (trial.TrialType == TrialType.OfficialTrial) { foreach (var doctorId in confirmReviewerCommand.DoctorIdArray) { foreach (var item in readingQuestionCriterionTrial) { foreach (var dataType in dataTypes) { trialExperienceList.Add(new TrialExperience() { CriterionType = item.CriterionType, DoctorId = doctorId, PhaseId = trial.PhaseId, StartTime = DateTime.Now, IndicationEnum = trial.IndicationEnum, IndicationTypeId= trial.IndicationTypeId, ExperienceDataType = dataType, TrialId = trial.Id, OtherCriterion= item.CriterionType.GetEnumInt()=="0"? item.CriterionName:string.Empty, VisitReadingCount = 0, ExperienceCriteriaList = new List() { new TrialExperienceCriteria() { DoctorId=doctorId, EvaluationCriteriaId=criterionTypeList.Where(x=>x.Code==item.CriterionType.GetEnumInt()).Select(x=>x.Id).FirstOrDefault() } } }); } } } await _trialExperienceRepository.AddRangeAsync(trialExperienceList); await _trialExperienceRepository.SaveChangesAsync(); } } else if (confirmReviewerCommand.ConfirmState == 0)//回退上一步 { foreach (var intoGroupItem in intoGroupList) { if (confirmReviewerCommand.DoctorIdArray.Contains(intoGroupItem.DoctorId)) { intoGroupItem.EnrollStatus = EnrollStatus.InviteIntoGroup; intoGroupItem.EnrollTime = null; var deleteItem = await _enrollDetailRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.DoctorId == intoGroupItem.DoctorId && t.EnrollStatus == EnrollStatus.ConfirmIntoGroup); await _enrollDetailRepository.DeleteAsync(deleteItem); } } } await _enrollRepository.SaveChangesAsync(); return ResponseOutput.Ok(); } /// /// optType 0表示入组,列表没这条数据了, 1表示出组,需要填写出组时间 废弃 /// /// /// /// /// /// [HttpPost("{trialId:guid}/{doctorId:guid}/{optType:int}")] [TrialGlobalLimit( "AfterStopCannNotOpt" )] //[Authorize(Policy = IRaCISPolicy.PM_APM_SPM_CPM)] [Obsolete] public async Task EnrollBackOrOut(Guid trialId, Guid doctorId, int optType, DateTime? outEnrollTime) { var intoGroupItem = await _enrollRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.DoctorId == doctorId); if (optType == 0) { var sum = _workloadRepository.Where(t => t.TrialId == trialId && t.DoctorId == doctorId && t.DataFrom == (int)WorkLoadFromStatus.FinalConfirm) .Sum(u => u.Adjudication + u.AdjudicationIn24H + u.AdjudicationIn48H + u.Timepoint + u.TimepointIn24H + u.TimepointIn48H + u.RefresherTraining + u.Training + u.Global + u.Downtime); if (sum != 0) { //---Reviewers with workload cannot go back return ResponseOutput.NotOk(_localizer["Enroll_CannotRollback"]); } intoGroupItem.EnrollStatus = EnrollStatus.InviteIntoGroup; intoGroupItem.EnrollTime = null; var deleteItem = await _enrollDetailRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.DoctorId == intoGroupItem.DoctorId && t.EnrollStatus == EnrollStatus.ConfirmIntoGroup); await _enrollDetailRepository.DeleteAsync(deleteItem); } else if (optType == 1) { intoGroupItem.OutEnrollTime = outEnrollTime; } return ResponseOutput.Result(await _enrollRepository.SaveChangesAsync()); } #endregion } }