using IRaCIS.Application.Interfaces; using IRaCIS.Application.Contracts; using IRaCIS.Core.Domain.Share; using EasyCaching.Core; using IRaCIS.Core.Application.Filter; using Microsoft.AspNetCore.Mvc; using Panda.DynamicWebApi.Attributes; using IRaCIS.Core.Infrastructure; using Microsoft.Extensions.Options; using static IRaCIS.Core.Domain.Share.StaticData; using Microsoft.AspNetCore.Authorization; using System.Linq.Expressions; using System.Linq; namespace IRaCIS.Application.Services { [ApiExplorerSettings(GroupName = "Trial")] public class TrialService : BaseService, ITrialService { private readonly IEasyCachingProvider _provider; private readonly IRepository<Trial> _trialRepository; private readonly IRepository<TrialUser> _trialUserRepository; private readonly IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig; private readonly IOptionsMonitor<SystemEmailSendConfig> _systemEmailSendConfig; public bool TrialExpeditedChange { get; set; } = false; public TrialService(IEasyCachingProvider provider, IRepository<Trial> trialRepository, IRepository<TrialUser> trialUserRepository, IOptionsMonitor<ServiceVerifyConfigOption> verifyConfig , IOptionsMonitor<SystemEmailSendConfig> systemEmailSendConfig ) { _systemEmailSendConfig = systemEmailSendConfig; _verifyConfig = verifyConfig; _provider = provider; _trialRepository = trialRepository; this._trialUserRepository = trialUserRepository; } /// <summary> /// 分页获取临床项目列表 默认后台加急状态为3 查所有的 /// </summary> /// <param name="searchParam"></param> /// <returns></returns> [HttpPost] public async Task<PageOutput<TrialDetailDTO>> GetTrialList(TrialQueryDTO searchParam) { var multiModalityIdSelectCount = searchParam.ModalityIds.Count; var multiCriteriaSelectCount = searchParam.CriterionIds.Count; var multiReviewTypeSelectCount = searchParam.ReviewTypeIds.Count; Expression<Func<Trial, bool>> trialDeclarationTypeExpression = x => true; foreach (var item in searchParam.DeclarationTypeEnumList) { trialDeclarationTypeExpression = trialDeclarationTypeExpression.And(t => t.DeclarationTypes.Contains($"|{item}|")); } Expression<Func<Trial, bool>> trialAttendedReviewerTypeExpression = x => true; foreach (var item in searchParam.AttendedReviewerTypeEnumList) { trialAttendedReviewerTypeExpression = trialAttendedReviewerTypeExpression.And(t => t.AttendedReviewerTypes.Contains($"|{item}|")); } var query = _trialRepository.AsQueryable().IgnoreQueryFilters() .WhereIf(!string.IsNullOrEmpty(searchParam.TrialStatusStr), o => o.TrialStatusStr.Contains(searchParam.TrialStatusStr)) .WhereIf(searchParam.SponsorId != null, o => o.SponsorId == searchParam.SponsorId) .WhereIf(searchParam.Expedited != null, o => o.Expedited == searchParam.Expedited) .WhereIf(!string.IsNullOrEmpty(searchParam.Code), o => o.TrialCode.Contains(searchParam.Code)) .WhereIf(!string.IsNullOrWhiteSpace(searchParam.Indication), o => o.Indication.Contains(searchParam.Indication)) .WhereIf(!string.IsNullOrEmpty(searchParam.ResearchProgramNo), o => o.ResearchProgramNo.Contains(searchParam.ResearchProgramNo)) .WhereIf(!string.IsNullOrWhiteSpace(searchParam.ExperimentName), o => o.ExperimentName.Contains(searchParam.ExperimentName)) .WhereIf(searchParam.PhaseId != null, o => o.PhaseId == searchParam.PhaseId) .WhereIf(searchParam.DeclarationTypeEnumList.Count > 0, trialDeclarationTypeExpression) .WhereIf(searchParam.AttendedReviewerTypeEnumList.Count > 0, trialAttendedReviewerTypeExpression) //.WhereIf(searchParam.AttendedReviewerType != null, o => o.AttendedReviewerType == searchParam.AttendedReviewerType) .WhereIf(searchParam.IndicationTypeId != null, o => o.IndicationTypeId == searchParam.IndicationTypeId) .WhereIf(searchParam.CROId != null, o => o.CROId == searchParam.CROId) .WhereIf(searchParam.BeginDate != null, o => o.CreateTime >= searchParam.BeginDate) .WhereIf(searchParam.EndDate != null, o => o.CreateTime <= searchParam.EndDate) .WhereIf(multiModalityIdSelectCount > 0, t => t.TrialDicList.Count(t => t.KeyName == StaticData.Modality) == multiModalityIdSelectCount) .WhereIf(multiCriteriaSelectCount > 0, t => t.TrialDicList.Count(t => t.KeyName == StaticData.Criterion) == multiCriteriaSelectCount) .WhereIf(multiReviewTypeSelectCount > 0, t => t.TrialDicList.Count(t => t.KeyName == StaticData.ReviewType) == multiReviewTypeSelectCount) .WhereIf(_userInfo.UserTypeEnumInt != (int)UserTypeEnum.SuperAdmin, t => t.TrialUserList.Any(t => t.UserId == _userInfo.Id && t.IsDeleted == false) && t.IsDeleted == false) .ProjectTo<TrialDetailDTO>(_mapper.ConfigurationProvider, new { userTypeEnumInt = _userInfo.UserTypeEnumInt, userId = _userInfo.Id , isEn_Us= _userInfo.IsEn_Us }); return await query.ToPagedListAsync(searchParam.PageIndex, searchParam.PageSize, string.IsNullOrWhiteSpace(searchParam.SortField) ? "CreateTime" : searchParam.SortField, searchParam.Asc); } //过滤废除的项目 public async Task<List<TrialSelectDTO>> GetTrialSelect() { return await _trialRepository.AsQueryable().IgnoreQueryFilters() .WhereIf(_userInfo.UserTypeEnumInt != (int)UserTypeEnum.SuperAdmin, t => t.TrialUserList.Any(t => t.UserId == _userInfo.Id) && t.IsDeleted == false) .ProjectTo<TrialSelectDTO>(_mapper.ConfigurationProvider).ToListAsync(); } /// <summary> /// 获取项目基本信息 /// </summary> /// <param name="projectId"></param> /// <returns></returns> [HttpGet("{projectId:guid}")] public async Task<TrialDetailDTO> GetTrialInfoAndLockState(Guid projectId) { return (await _trialRepository.Where(o => o.Id == projectId).IgnoreQueryFilters().ProjectTo<TrialDetailDTO>(_mapper.ConfigurationProvider, new {isEn_Us = _userInfo.IsEn_Us }).FirstOrDefaultAsync()).IfNullThrowException(); } [HttpGet("{trialId:guid}")] public async Task<TrialAndTrialStateVieModel> GetTrialInfoAndMaxTrialState(Guid trialId) { return new TrialAndTrialStateVieModel() { TrialView = await GetTrialInfoAndLockState(trialId), }; } [HttpGet("{trialId:guid}")] public async Task<int> GetTrialExpeditedState(Guid trialId) { var trial = (await _trialRepository.FirstOrDefaultAsync(u => u.Id == trialId)).IfNullThrowException(); return trial.Expedited; } /// <summary> /// 添加项目 /// </summary> /// <param name="trialAddModel"></param> /// <returns></returns> [NonDynamicMethod] public virtual async Task<IResponseOutput<Trial>> AddOrUpdateTrial(TrialCommand trialAddModel) { //测试用户 只能创建非正式项目 if (_userInfo.IsTestUser && trialAddModel.TrialType != TrialType.NoneOfficial) { //---测试用户 只能创建非正式项目 throw new BusinessValidationFailedException(_localizer["Trial_TestUserCreateOnlyNonFormal"]); } if (trialAddModel.Id == Guid.Empty || trialAddModel.Id == null) { if (await _trialRepository.AnyAsync(u => u.TrialCode == trialAddModel.TrialCode)) { //---已经存在相同的项目编号。 throw new BusinessValidationFailedException(_localizer["Trial_DuplicateProjectNumber"]); } var dbMaxCode = await _trialRepository.Where(t => t.CreateTime.Year == DateTime.Now.Year && t.TrialType == trialAddModel.TrialType).Select(t => t.Code).DefaultIfEmpty().MaxAsync(); var currentYearMaxCodeNext = dbMaxCode + 1; //var test = _trialRepository.Where(t => t.CreateTime.Year == DateTime.Now.Year + 1).Select(t => t.Code).DefaultIfEmpty(1).ToList(); var trial = _mapper.Map<Trial>(trialAddModel); //trial.Id = NewId.NextGuid(); var yearStr = DateTime.Now.Year.ToString(); trial.Code = currentYearMaxCodeNext; trial.TrialCode = (trial.TrialType == TrialType.NoneOfficial ? "T0" : yearStr.Substring(yearStr.Length - 2)) + trial.TrialCode + currentYearMaxCodeNext.ToString("D3"); trial.DeclarationTypes = $"|{string.Join('|', trialAddModel.DeclarationTypeEnumList.Select(x => ((int)x).ToString()).ToList())}|"; trial.AttendedReviewerTypes = $"|{string.Join('|', trialAddModel.AttendedReviewerTypeEnumList.Select(x => ((int)x).ToString()).ToList())}|"; //多选信息 trialAddModel.ModalityIds.ForEach(modalityId => trial.TrialDicList.Add(new TrialDictionary() { DictionaryId = modalityId, KeyName = StaticData.Modality, TrialId = trial.Id })); trialAddModel.CriterionIds.ForEach(criterionId => trial.TrialDicList.Add(new TrialDictionary() { DictionaryId = criterionId, KeyName = StaticData.Criterion, TrialId = trial.Id })); trialAddModel.ReviewTypeIds.ForEach(ReviewTypeId => trial.TrialDicList.Add(new TrialDictionary() { DictionaryId = ReviewTypeId, KeyName = StaticData.ReviewType, TrialId = trial.Id })); //添加项目后 项目状态变更为申请下载简历 trial.TrialEnrollStatus = (int)TrialEnrollStatus.ChooseDoctor; //trial.TrialStatusStr = StaticData.TrialInitializing; //状态变更详细表 trial.ClinicalTrialProjectDetails.Add(new TrialStatusDetail() { TrialId = trial.Id, TrialStatus = (int)TrialEnrollStatus.ChooseDoctor }); trial = await _trialRepository.AddAsync(trial); //如果是PM, 则需要将该人员添加到 运维人员表 //添加运维人员PM await _repository.AddAsync(new TrialUser() { TrialId = trial.Id, UserId = _userInfo.Id, JoinTime = DateTime.Now }); //添加访视 await _repository.AddAsync(new VisitStage { TrialId = trial.Id, VisitNum = 0, BlindName = "B" + 0.ToString("D3"), VisitDay = 0, VisitName = "Baseline", IsBaseLine = true }); await _repository.AddAsync(new VisitStage { TrialId = trial.Id, VisitNum = 1, BlindName = "B" + 10.ToString("D3"), VisitDay = 30, VisitName = "Visit 1" }); //默认采用系统邮件 trial.EmailAuthorizationCode = _systemEmailSendConfig.CurrentValue.AuthorizationCode; trial.EmailFromEmail = _systemEmailSendConfig.CurrentValue.FromEmail; trial.EmailFromName = _systemEmailSendConfig.CurrentValue.FromName; trial.EmailSMTPServerAddress = _systemEmailSendConfig.CurrentValue.Host; trial.EmailSMTPServerPort = _systemEmailSendConfig.CurrentValue.Port; trial.IsConfigureEmail = true; var success = await _repository.SaveChangesAsync(); _provider.Set(trial.Id.ToString(), StaticData.TrialState.TrialInitializing, TimeSpan.FromDays(7)); return ResponseOutput.Ok(trial); } else { var updateModel = trialAddModel; if (!await _repository.AnyAsync<Trial>(u => u.Id == trialAddModel.Id && (u.TrialStatusStr == StaticData.TrialState.TrialInitializing || u.TrialStatusStr == StaticData.TrialState.TrialOngoing))) { //---项目状态只有处于:初始化或者进行中时,才允许操作。 throw new BusinessValidationFailedException(_localizer["Trial_InvalidProjectStatus"]); } // 判断项目Id 是否已经存在 if (await _repository.AnyAsync<Trial>(u => u.TrialCode == updateModel.TrialCode && u.Id != updateModel.Id)) { //---已经存在相同的项目编号。 throw new BusinessValidationFailedException(_localizer["Trial_DuplicateProjectNumber"]); } var trial = await _repository.FirstOrDefaultAsync<Trial>(t => t.Id == updateModel.Id); //删除中间表 Title对应的记录 await _repository.BatchDeleteAsync<TrialDictionary>(t => t.TrialId == updateModel.Id); //重新插入新的 Title记录 updateModel.ModalityIds.ForEach(modalityId => trial.TrialDicList.Add(new TrialDictionary() { DictionaryId = modalityId, KeyName = StaticData.Modality, TrialId = trial.Id })); updateModel.CriterionIds.ForEach(criterionId => trial.TrialDicList.Add(new TrialDictionary() { DictionaryId = criterionId, KeyName = StaticData.Criterion, TrialId = trial.Id })); updateModel.ReviewTypeIds.ForEach(ReviewTypeId => trial.TrialDicList.Add(new TrialDictionary() { DictionaryId = ReviewTypeId, KeyName = StaticData.ReviewType, TrialId = trial.Id })); if (updateModel.Expedited != trial.Expedited && updateModel.Expedited != null) { TrialExpeditedChange = true; } _mapper.Map(updateModel, trial); trial.DeclarationTypes = $"|{string.Join('|', updateModel.DeclarationTypeEnumList.Select(x => ((int)x).ToString()).ToList())}|"; trial.AttendedReviewerTypes = $"|{string.Join('|', updateModel.AttendedReviewerTypeEnumList.Select(x => ((int)x).ToString()).ToList())}|"; var success = await _repository.SaveChangesAsync(); return ResponseOutput.Ok(trial); } } /// <summary> 真删除项目 方便清理测试数据 </summary> /// <param name="trialId">临床试验项目Id</param> [AllowAnonymous] [HttpDelete, Route("{trialId:guid}")] public async Task<IResponseOutput> DeleteTrial(Guid trialId) { if (_verifyConfig.CurrentValue.OpenTrialRelationDelete) { await _repository.BatchDeleteAsync<CheckChallengeDialog>(o => o.SubjectVisit.TrialId == trialId); await _repository.BatchDeleteAsync<ClinicalDataTrialSet>(o => o.TrialId == trialId); await _repository.BatchDeleteAsync<ClinicalQuestionAnswer>(o => o.ClinicalForm.TrialId == trialId); await _repository.BatchDeleteAsync<ClinicalQuestionAnswer>(o => o.ClinicalForm.TrialId == trialId); await _repository.BatchDeleteAsync<ClinicalForm>(o => o.TrialId == trialId); await _repository.BatchDeleteAsync<UserUploadFile>(o => o.TrialId == trialId); await _repository.BatchDeleteAsync<CriterionNidusSystem>(o => o.TrialReadingCriterion.TrialId == trialId); await _repository.BatchDeleteAsync<CriterionNidusTrial>(o => o.TrialReadingCriterion.TrialId == trialId); await _repository.BatchDeleteAsync<DataInspection>(o => o.TrialId == trialId); await _repository.BatchDeleteAsync<DicomStudy>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<StudyMonitor>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<DicomSeries>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<DicomInstance>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<NoneDicomStudyFile>(t => t.NoneDicomStudy.TrialId == trialId); await _repository.BatchDeleteAsync<NoneDicomStudy>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<OrganTrialInfo>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<PreviousHistory>(t => t.SubjectVisit.TrialId == trialId); await _repository.BatchDeleteAsync<PreviousOther>(t => t.SubjectVisit.TrialId == trialId); await _repository.BatchDeleteAsync<PreviousPDF>(t => t.SubjectVisit.TrialId == trialId); await _repository.BatchDeleteAsync<QCChallengeDialog>(t => t.QCChallenge.TrialId == trialId); await _repository.BatchDeleteAsync<QCChallenge>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingClinicalDataPDF>(t => t.ReadingClinicalData.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingClinicalData>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingGlobalTaskInfo>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingJudgeInfo>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingMedicalReviewDialog>(t => t.TaskMedicalReview.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingMedicineQuestionAnswer>(t => t.TaskMedicalReview.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingMedicineTrialQuestion>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingOncologyTaskInfo>(t => t.VisitTask.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingPeriodPlan>(t => t.ReadingPeriodSet.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingPeriodSet>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingQuestionTrial>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingTableAnswerRowInfo>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingTableQuestionAnswer>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingTableQuestionTrial>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingTaskQuestionAnswer>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingTaskRelation>(t => t.VisitTask.TrialId == trialId); await _repository.BatchDeleteAsync<ReadModule>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingTaskQuestionAnswer>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<Subject>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<SubjectVisit>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TaskInfluence>(t => t.InfluenceTask.TrialId == trialId); await _repository.BatchDeleteAsync<TaskMedicalReview>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TaskMedicalReviewRule>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TrialStateChange>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<Trial>(o => o.Id == trialId); await _repository.BatchDeleteAsync<TrialClinicalDataSetCriterion>(t => t.TrialReadingCriterion.TrialId == trialId); await _repository.BatchDeleteAsync<TrialCriterionDictionaryCode>(t => t.TrialReadingCriterion.TrialId == trialId); await _repository.BatchDeleteAsync<ReadingQuestionCriterionTrial>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TrialDictionary>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TrialDocConfirmedUser>(t => t.TrialDocument.TrialId == trialId); await _repository.BatchDeleteAsync<TrialDocument>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TrialDocNeedConfirmedUserType>(t => t.TrialDocument.TrialId == trialId); await _repository.BatchDeleteAsync<TrialEmailNoticeUser>(t => t.TrialEmailNoticeConfig.TrialId == trialId); //await _repository.BatchDeleteAsync<TrialExperience>(t => t.TrialId == trialId) ; await _repository.BatchDeleteAsync<TrialEmailNoticeUser>(t => t.TrialEmailNoticeConfig.TrialId == trialId); await _repository.BatchDeleteAsync<TrialEmailNoticeConfig>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TrialExternalUser>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TrialQCQuestion>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TrialQCQuestionAnswer>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TrialSign>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TrialSite>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TrialSiteSurvey>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TrialSiteUserSurvey>(t => t.TrialSiteSurvey.TrialId == trialId); await _repository.BatchDeleteAsync<TrialSiteEquipmentSurvey>(t => t.TrialSiteSurvey.TrialId == trialId); await _repository.BatchDeleteAsync<TrialUser>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TrialSiteUser>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<VisitStage>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<VisitPlanInfluenceStat>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<VisitPlanInfluenceStudy>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<VisitTaskReReading>(t => t.OriginalReReadingTask.TrialId == trialId); await _repository.BatchDeleteAsync<VisitTask>(t => t.TrialId == trialId); await _repository.BatchDeleteAsync<TrialStateChange>(t => t.TrialId == trialId) ; return ResponseOutput.Ok(); } else { //---当前运行环境下,不允许删除项目数据。 return ResponseOutput.NotOk(_localizer["Trial_CannotDeleteProject"]); } } #region 医生用户接口 #endregion } }