using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.Contracts.DTO; using IRaCIS.Core.Application.Filter; using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.Service.Inspection.DTO; using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Infrastructure; using Medallion.Threading; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; using System.Data; namespace IRaCIS.Core.Application.Image.QA { [ApiExplorerSettings(GroupName = "Image")] public class QCOperationService(IRepository _subjectVisitRepository, IRepository _qcChallengeRepository, IRepository _dicomStudyRepository, IRepository _dicomSeriesrepository, IRepository _subjectRepository, IRepository _readingClinicalDataRepository, IRepository _clinicalDataTrialSetRepository, IRepository _qCChallengeDialogrepository, IRepository _checkChallengeDialogRepository, IRepository _trialRepository, IRepository _dicomSeriesRepository, IRepository _visitTaskRepository, IRepository _scpPatientRepository, IRepository _scpStudyRepository, IRepository _trialUserRoleRepository, IRepository _readModuleRepository, IRepository _dicomInstanceRepository, IRepository _trialQCQuestionAnswerRepository, IRepository _readingQuestionCriterionTrialRepository, IDistributedLockProvider _distributedLockProvider, IReadingClinicalDataService _readingClinicalDataService, IOSSService _oSSService, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IWebHostEnvironment _hostEnvironment) : BaseService, IQCOperationService { #region QC质疑 以及回复 关闭 [HttpGet("{trialId:guid}/{subjectVisitId:guid}/{currentQCType:int}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.IQC)] public async Task VerifyQCCanAddChallenge(Guid subjectVisitId, [FromRoute] CurrentQC currentQCType) { await VerifyIsCanQCAsync(null, subjectVisitId); if (!await _trialQCQuestionAnswerRepository.AnyAsync(t => t.SubjectVisitId == subjectVisitId && t.CurrentQCEnum == currentQCType)) { //---请先核查图像,并保存审核问题,然后再发质疑。 return ResponseOutput.NotOk(_localizer["QCOperation_CheckFirst"]); } return ResponseOutput.Ok(); } /// /// 添加和更新质疑 /// /// /// /// /// /// [HttpPost("{trialId:guid}/{trialQCProcess:int}/{currentQCType:int}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.IQC)] public async Task AddOrUpdateQCChallenge(QCChallengeCommand qaQuestionCommand, Guid trialId, [FromRoute] TrialQCProcess trialQCProcess, [FromRoute] CurrentQC currentQCType) { await VerifyIsCanQCAsync(null, qaQuestionCommand.SubjectVisitId); if (qaQuestionCommand.Id == null) { if (await _qcChallengeRepository.AnyAsync(t => t.IsClosed == false && t.SubjectVisitId == qaQuestionCommand.SubjectVisitId && t.ReuploadEnum == QCChanllengeReuploadEnum.QCAgreeUpload)) { //---当前访视未关闭的质疑已设置了同意CRC重传影像。请在CRC完成影像重传后,先关闭原质疑,再添加新的质疑。 throw new BusinessValidationFailedException(_localizer["QCOperation_QuestionExist"]); } var trialConfig = (await _trialRepository.Where(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification }).FirstOrDefaultAsync()).IfNullThrowException(); var @lock = _distributedLockProvider.CreateLock($"QCChallengeCode"); using (await @lock.AcquireAsync()) { //获取编号 var code = _qcChallengeRepository.Where(t => t.TrialId == trialId).Select(t => t.Code).DefaultIfEmpty().Max(); var qcChallenge = _mapper.Map(qaQuestionCommand); qcChallenge.QCProcessEnum = trialConfig.QCProcessEnum; qcChallenge.CurrentQCEnum = currentQCType; qcChallenge.TrialId = trialId; qcChallenge.Code = code + 1; qcChallenge.UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt; qcChallenge.ChallengeCode = AppSettings.GetCodeStr(qcChallenge.Code, nameof(QCChallenge)); qcChallenge = await _qcChallengeRepository.AddAsync(qcChallenge, true); return ResponseOutput.Ok(qcChallenge.Id); } } else { await _qcChallengeRepository.UpdateFromDTOAsync(qaQuestionCommand, true); return ResponseOutput.Ok(); } } /// /// 关闭质疑,什么情况下允许? /// /// [HttpPut] [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] //[Authorize(Policy = IRaCISPolicy.IQC)] public async Task CloseQCChallenge(CloseQCChallengeInDto input) { if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.IQC) { await VerifyIsCanQCAsync(null, input.subjectVisitId); } var dbQCChallenge = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == input.qcChallengeId)).IfNullThrowException(); if (dbQCChallenge.ReuploadEnum == QCChanllengeReuploadEnum.CRCRequestReupload || dbQCChallenge.ReuploadEnum == QCChanllengeReuploadEnum.QCAgreeUpload) { //---CRC已申请重传或者QC同意重传,不允许关闭该质疑。请在QC拒绝重传申请或者CRC设置重传影像后,再关闭质疑。 throw new BusinessValidationFailedException(_localizer["QCOperation_QuestionCannotClose"]); } #region 之前 dbQCChallenge.CloseResonEnum = input.closeEnum; dbQCChallenge.IsClosed = true; dbQCChallenge.ClosedTime = DateTime.Now; dbQCChallenge.DialogList.Add(new QCChallengeDialog() { SubjectVisitId = dbQCChallenge.SubjectVisitId, UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, QCChallengeId = dbQCChallenge.Id, //---关闭原因: TalkContent = _localizer["QCOperation_CloseReason"] + input.closeReason }); var success = await _qcChallengeRepository.SaveChangesAsync(); #endregion return ResponseOutput.Result(success); } /// /// 删除QC质疑记录 /// /// [HttpDelete("{trialId:guid}/{subjectVisitId:guid}/{qcChallengeId:guid}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.IQC)] public async Task DeleteQCChallenge(Guid qcChallengeId) { if (await _qCChallengeDialogrepository.AnyAsync(t => t.QCChallengeId == qcChallengeId)) { //---当前QC质疑已经回复。 ResponseOutput.NotOk(_localizer["QCOperation_QuestionReplied"]); } var qaRecord = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == qcChallengeId)).IfNullThrowException(); await _qcChallengeRepository.DeleteAsync(qaRecord); var success1 = await _qcChallengeRepository.SaveChangesAsync(); return ResponseOutput.Result(success1 /*|| success2 || success3*/); } /// /// 针对 某条QC质疑 添加回复 /// /// /// [HttpPost("{trialId:guid}")] [TrialGlobalLimit("AfterStopCannNotOpt")] // [Authorize(Policy = IRaCISPolicy.CRC_IQC)] public async Task AddQCChallengeReply(QADialogCommand qaDialogCommand) { var qaReply = _mapper.Map(qaDialogCommand); await _qCChallengeDialogrepository.AddAsync(qaReply); qaReply.UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt; var dbQCChallenge = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == qaDialogCommand.QCChallengeId)).IfNullThrowException(); dbQCChallenge.LatestMsgTime = DateTime.Now; dbQCChallenge.LatestReplyUserId = _userInfo.UserRoleId; var success = await _qCChallengeDialogrepository.SaveChangesAsync(); return ResponseOutput.Result(success, qaReply); } #endregion #region 一致性核查 /// /// 一致性核查 质疑的添加/回复 /// /// /// [HttpPost("{trialId:guid}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.PM_APM_CRC)] public async Task AddCheckChallengeReply(CheckChallengeDialogCommand checkDialogCommand) { //修改一致性核查 质疑状态 var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == checkDialogCommand.SubjectVisitId)).IfNullThrowException(); var qaReply = _mapper.Map(checkDialogCommand); qaReply.UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt; await _checkChallengeDialogRepository.AddAsync(qaReply); if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator) { sv.CheckChallengeState = CheckChanllengeTypeEnum.CRCWaitPMReply; } else if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.APM) { sv.CheckChallengeState = CheckChanllengeTypeEnum.PMWaitCRCReply; } else { //---您没有权限回复一致性核查对话。 throw new BusinessValidationFailedException(_localizer["QCOperation_NoPermissionReply"]); } var success = await _subjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Result(success, qaReply.Id); } /// /// 关闭 一致性核查质疑 /// /// [HttpPut("{trialId:guid}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.PM_APM)] public async Task CloseCheckChallenge(CloseCheckChallengeDto input) { var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == input.subjectVisitId)).IfNullThrowException(); if (sv.RequestBackState == RequestBackStateEnum.CRC_RequestBack) { //---当前访视处于申请回退状态, 不允许关闭质疑。 ResponseOutput.NotOk(_localizer["QCOperation_CannotClose"]); } sv.CloseTheReason = input.CloseCheckChallenge; sv.CheckChallengeState = CheckChanllengeTypeEnum.Closed; await _checkChallengeDialogRepository.AddAsync(new CheckChallengeDialog() { SubjectVisitId = input.subjectVisitId, //---原因: TalkContent = _localizer["QCOperation_Reason"] + input.CloseCheckChallenge, UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, }); await _subjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Ok(sv); } /// /// 手动设置一致性核查通过 /// /// [HttpPut("{trialId:guid}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.PM_APM)] [UnitOfWork] public async Task SetCheckPass(SetCheckPassDt data) { //if (_userInfo.UserTypeEnumInt != (int)UserTypeEnum.ProjectManager && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.APM) //{ // ResponseOutput.NotOk("您不是PM/APM,不允许设置一致性核查通过"); //} var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == data.Id)).IfNullThrowException(); if (sv.RequestBackState == RequestBackStateEnum.PM_AgressBack) { //---当前访视处于回退状态,不允许设置一致性核查通过 ResponseOutput.NotOk(_localizer["QCOperation_CannotPass"]); } if (sv.CheckChallengeState != CheckChanllengeTypeEnum.Closed && sv.AuditState == AuditStateEnum.QCPassed) { //---请先关闭一致性核查质疑后,再设置一致性核查通过。 ResponseOutput.NotOk(_localizer["QCOperation_PassAfterClose"]); } sv.CheckUserId = _userInfo.UserRoleId; sv.CheckState = CheckStateEnum.CVPassed; sv.ReadingStatus = ReadingStatusEnum.TaskAllocate; sv.ForwardState = ForwardStateEnum.ToForward; sv.ManualPassReason = data.ManualPassReason; sv.CheckPassedTime = DateTime.Now; await _subjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Result(true); } /// /// CRC 请求回退 /// /// /// [HttpPut("{trialId:guid}/{subjectVisitId:guid}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.CRC)] public async Task CRCRequstCheckBack(Guid subjectVisitId) { var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); if (sv.CheckState == CheckStateEnum.CVPassed) { //---当前访视已通过一致性核查,不允许申请回退 return ResponseOutput.NotOk(_localizer["QCOperation_Cannotback"]); } var QCChallengeId = await _qcChallengeRepository.Where(x => x.SubjectVisitId == subjectVisitId).Select(x => x.Id).FirstOrDefaultAsync(); await _checkChallengeDialogRepository.AddAsync(new CheckChallengeDialog() { SubjectVisitId = subjectVisitId, UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, //---CRC申请回退 TalkContent = _localizer["QCOperation_CRCRollback"] }); if (sv.RequestBackState == RequestBackStateEnum.NotRequest || sv.RequestBackState == RequestBackStateEnum.PM_NotAgree) { sv.RequestBackState = RequestBackStateEnum.CRC_RequestBack; await _subjectVisitRepository.SaveChangesAsync(); } else { //---其他CRC已申请处理,请刷新页面 return ResponseOutput.NotOk(_localizer["QCOperation_OtherCRC"]); } return ResponseOutput.Ok(); } [HttpPut("{trialId:guid}/{subjectVisitId:guid}")] //[Authorize(Policy = IRaCISPolicy.PM_APM)] [TrialGlobalLimit("AfterStopCannNotOpt")] public async Task RejectCheckBack(Guid subjectVisitId) { //if (_userInfo.UserTypeEnumInt != (int)UserTypeEnum.ProjectManager && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.APM) //{ // return ResponseOutput.NotOk(" 只有PM/APM具有操作权限!"); //} var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); if (sv.RequestBackState == RequestBackStateEnum.CRC_RequestBack) { sv.RequestBackState = RequestBackStateEnum.PM_NotAgree; //---PM/APM拒绝一致性核查回退 await _checkChallengeDialogRepository.AddAsync(new CheckChallengeDialog() { SubjectVisitId = subjectVisitId, TalkContent = _localizer["QCOperation_PMReject"], UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt }); await _subjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Ok(); } else { //---您不具备操作权限。 return ResponseOutput.NotOk(_localizer["QCOperation_NoPermission"]); } } /// /// 一致性核查 回退 对话记录不清除 只允许PM回退 [签名 不需要对] /// /// [HttpPut("{trialId:guid}/{subjectVisitId:guid}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.PM_APM)] [UnitOfWork] public async Task CheckBack(Guid subjectVisitId) { //if (_userInfo.UserTypeEnumInt != (int)UserTypeEnum.ProjectManager && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.APM) //{ // return ResponseOutput.NotOk("只有PM/APM具有操作权限!"); //} var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); if (sv.CheckState == CheckStateEnum.CVPassed || sv.CheckState == CheckStateEnum.ToCheck) { //---当前访视还未进行核查或者核查已通过,不允许设置回退。 return ResponseOutput.NotOk(_localizer["QCOperation_CannotRollback"]); } //sv.CheckChallengeState = (int)CheckChanllengeTypeEnum.None; //sv.CheckState = CheckStateEnum.None; //sv.ChallengeState = (int)ChallengeStateEnum.No; sv.AuditState = AuditStateEnum.None; sv.SubmitState = SubmitStateEnum.ToSubmit; sv.ReadingStatus = ReadingStatusEnum.ImageNotSubmit; //回退后,回退状态恢复 sv.RequestBackState = RequestBackStateEnum.NotRequest; sv.IsCheckBack = true; sv.CheckBackTime = DateTime.Now; sv.CheckState = CheckStateEnum.None; sv.CheckChallengeState = CheckChanllengeTypeEnum.None; sv.SVENDTC = null; sv.SVSTDTC = null; sv.PreliminaryAuditTime = null; sv.SubmitTime = null; sv.ReviewAuditTime = null; sv.CurrentActionUserExpireTime = null; sv.IsTake = false; sv.CurrentActionUserId = null; sv.PreliminaryAuditUserId = null; sv.ReviewAuditUserId = null; //var success1 = _studyRepository.Delete(t => t.SubjectVisitId == subjectVisitId); //var succeess2 = _instanceRepository.Delete(t => t.SubjectVisitId == subjectVisitId); //var success3 = _seriesRepository.Delete(t => t.SubjectVisitId == subjectVisitId); //_qcChallengeRepository.Delete(t => t.SubjectVisitId == subjectVisitId); //_qcChallengeDialogRepository.Delete(t => t.SubjectVisitId == subjectVisitId); //_checkChallengeDialogRepository.Delete(t => t.SubjectVisitId == subjectVisitId); //---PM/APM同意一致性核查回退。 await _checkChallengeDialogRepository.AddAsync(new CheckChallengeDialog() { SubjectVisitId = subjectVisitId, TalkContent = _localizer["QCOperation_PMAPMConsistency"], UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt }); await _trialQCQuestionAnswerRepository.BatchDeleteNoTrackingAsync(t => t.SubjectVisitId == subjectVisitId); await _dicomInstanceRepository.BatchDeleteNoTrackingAsync(t => t.DicomSerie.IsDeleted); await _dicomSeriesRepository.BatchDeleteNoTrackingAsync(t => t.IsDeleted); var success = await _subjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Result(success); } #endregion #region QC 核对问题 操作检查各种操作 /// /// 添加或者更新 QC核对问题列表 两个人不能同时操作,就算意外进去了,提交数据,也不会覆盖前一个人数据, 后台已经做好判断 /// [HttpPost("{trialId:guid}/{subjectVisitId:guid}/{trialQCProcess:int}/{currentQCType:int}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.IQC)] public async Task AddOrUpdateQCQuestionAnswerList(QCQuestionAnswerCommand[] qcQuestionAnswerCommands, Guid trialId, Guid subjectVisitId, [FromRoute] TrialQCProcess trialQCProcess, [FromRoute] CurrentQC currentQCType) { //验证是否能操作 await VerifyIsCanQCAsync(null, subjectVisitId); //更新 if (qcQuestionAnswerCommands.Any(t => t.Id != null)) { #region 先删除再添加 //await _repository.DeleteFromQueryAsync(t => t.SubjectVisitId == subjectVisitId && t.QCProcessEnum == trialQCProcess && t.CurrentQCEnum == currentQCType); //var addlist = _mapper.Map>(qcQuestionAnswerCommands); //addlist.ForEach(t => { t.TrialId = trialId; t.SubjectVisitId = subjectVisitId; t.CurrentQCEnum = currentQCType; t.QCProcessEnum = trialQCProcess; }); //await _repository.AddRangeAsync(addlist); #endregion #region 先查询再更新 var questionAnswerList = await _trialQCQuestionAnswerRepository.Where(t => t.SubjectVisitId == subjectVisitId && t.QCProcessEnum == trialQCProcess && t.CurrentQCEnum == currentQCType, true).ToListAsync(); qcQuestionAnswerCommands.ToList().ForEach(t => { var temp = questionAnswerList.FirstOrDefault(u => u.Id == t.Id); if (temp != null) { temp.Answer = t.Answer; //temp.ChildAnswer = t.ChildAnswer; } }); //Automapper 映射有问题 automapper 会把Guid? 类型的null 值转为 guid.Empty 导致映射错误 //_mapper.Map(qcQuestionAnswerCommands, questionAnswerList); #endregion return ResponseOutput.Ok(await _trialQCQuestionAnswerRepository.SaveChangesAsync()); } else { var addlist = _mapper.Map>(qcQuestionAnswerCommands); addlist.ForEach(t => { t.TrialId = trialId; t.SubjectVisitId = subjectVisitId; t.CurrentQCEnum = currentQCType; t.QCProcessEnum = trialQCProcess; }); await _trialQCQuestionAnswerRepository.AddRangeAsync(addlist); return ResponseOutput.Result(await _trialQCQuestionAnswerRepository.SaveChangesAsync()); } } /// /// 1、设置为不读片,2 设置为读片(取消 先前设置为不读片) 4 设置为删除(数据库记录软删除) 5 恢复为未删除 /// /// /// /// /// /// [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{studyId:guid}/{seriesId:guid}/{state:int}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.IQC)] public async Task SetSeriesState(Guid subjectVisitId, Guid studyId, Guid seriesId, int state) { await VerifyIsCanQCAsync(null, subjectVisitId); var series = (await _dicomSeriesRepository.Where(t => t.Id == seriesId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException(); if (state == 1) { series.IsReading = false; } else if (state == 2) { series.IsReading = true; } else if (state == 4) { series.IsDeleted = true; var study = (await _dicomStudyRepository.Where(t => t.Id == studyId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException(); var instanceIdList = await _dicomInstanceRepository.Where(t => t.SeriesId == seriesId).Select(t => t.Id).ToListAsync(); //instanceIdList.ForEach(t => //{ // var path = _dicomFileStoreHelper.GetInstanceFilePath(study, seriesId, t.ToString()); // if (System.IO.File.Exists(path)) // { // File.Delete(path); // } //}); study.InstanceCount = study.InstanceCount - instanceIdList.Count; study.SeriesCount = study.SeriesCount - 1; study.IsDeleted = study.SeriesCount == 0; } else if (state == 5) { series.IsDeleted = false; var study = (await _dicomStudyRepository.Where(t => t.Id == studyId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException(); var instanceIdList = await _dicomInstanceRepository.Where(t => t.SeriesId == seriesId).Select(t => t.Id).ToListAsync(); study.InstanceCount = study.InstanceCount + instanceIdList.Count; study.SeriesCount = study.SeriesCount + 1; study.IsDeleted = study.SeriesCount == 0; } return ResponseOutput.Ok(await _dicomStudyRepository.SaveChangesAsync()); } /// /// 1、设置为不读片,2 设置为读片(取消 先前设置为不读片) 4 设置为删除(数据库记录软删除) 5 恢复为未删除 /// /// /// /// /// /// [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{seriesId:guid}/{instanceId:guid}/{state:int}")] [TrialGlobalLimit("AfterStopCannNotOpt")] public async Task SetInstanceState(Guid subjectVisitId, Guid seriesId, Guid instanceId, int state) { await VerifyIsCanQCAsync(null, subjectVisitId); var instance = (await _dicomInstanceRepository.Where(t => t.Id == instanceId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException(); if (state == 1) { instance.IsReading = false; } else if (state == 2) { instance.IsReading = true; } else if (state == 4) { instance.IsDeleted = true; var series = (await _dicomSeriesRepository.Where(t => t.Id == seriesId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException(); series.InstanceCount = series.InstanceCount - 1; var study = (await _dicomStudyRepository.Where(t => t.Id == series.StudyId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException(); study.InstanceCount = study.InstanceCount - 1; ////删除到最后一个instance //if (series.InstanceCount == 0) //{ // series.IsDeleted = true; //} } else if (state == 5) { instance.IsDeleted = false; var series = (await _dicomSeriesRepository.Where(t => t.Id == seriesId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException(); series.InstanceCount = series.InstanceCount + 1; var study = (await _dicomStudyRepository.Where(t => t.Id == series.StudyId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException(); study.InstanceCount = study.InstanceCount + 1; } return ResponseOutput.Ok(await _dicomStudyRepository.SaveChangesAsync()); } /// ///type 1 :study 2: series 3:非dicom QC修改检查部位和 拍片类型 /// /// [HttpPost("{trialId:guid}")] [TrialGlobalLimit("AfterStopCannNotOpt")] // [Authorize(Policy = IRaCISPolicy.CRC_IQC)] public async Task UpdateModality(UpdateModalityCommand updateModalityCommand) { if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.IQC) { await VerifyIsCanQCAsync(null, updateModalityCommand.SubjectVisitId); } else { if (await _subjectVisitRepository.AnyAsync(t => t.Id == updateModalityCommand.SubjectVisitId && t.SubmitState == SubmitStateEnum.Submitted && !t.QCChallengeList.Any(c => c.ReuploadEnum == QCChanllengeReuploadEnum.QCAgreeUpload))) { //---提交之后,不允许修改! throw new BusinessValidationFailedException(_localizer["QCOperation_NoModifyAfterSubmit"]); } } DateTime time = DateTime.Now.AddMilliseconds(500); if (updateModalityCommand.Type == 1) { var studyId = updateModalityCommand.Id; var study = (await _dicomStudyRepository.FirstOrDefaultAsync(t => t.Id == studyId)).IfNullThrowException(); study.BodyPartForEdit = updateModalityCommand.BodyPart; study.ModalityForEdit = updateModalityCommand.Modality; await _dicomSeriesRepository.BatchUpdateNoTrackingAsync(t => t.StudyId == studyId, r => new DicomSeries() { BodyPartForEdit = updateModalityCommand.BodyPart }); } else if (updateModalityCommand.Type == 2) { var seriesId = updateModalityCommand.Id; var series = (await _dicomSeriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId)).IfNullThrowException(); series.BodyPartForEdit = updateModalityCommand.BodyPart; } else if (updateModalityCommand.Type == 3) { } await _subjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Ok(); } /// /// 验证是否质疑都关闭了 可以审核通过和不通过 /// /// /// [HttpGet("{trialId:guid}/{subjectVisitId:guid}")] public async Task VerifyCanQCPassedOrFailed(Guid subjectVisitId) { await VerifyIsCanQCAsync(null, subjectVisitId); if (await _qcChallengeRepository.AnyAsync(t => t.SubjectVisitId == subjectVisitId && t.IsClosed == false)) { //---当前访视有质疑未关闭,不允许该操作 return ResponseOutput.NotOk(_localizer["QCOperation_UnresolvedQCQuery"]); } return ResponseOutput.Ok(); } /// /// 删除检查列表 不能用delete方法 传递数组 /// /// /// /// /// SeriesCount [HttpPost, Route("{trialId:guid}/{subjectVisitId:guid}")] [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] // [Authorize(Policy = IRaCISPolicy.CRC_IQC)] public async Task DeleteStudyList(Guid[] ids, Guid subjectVisitId, Guid trialId) { //提交了 但是IQC同意的时候 是可以删除的 | 普通提交后也不能删除 if (await _subjectVisitRepository.AnyAsync(t => t.Id == subjectVisitId && t.SubmitState == SubmitStateEnum.Submitted && (!t.QCChallengeList.Any(u => u.ReuploadEnum == QCChanllengeReuploadEnum.QCAgreeUpload)))) { //---CRC已经提交,不允许删除。 return ResponseOutput.NotOk(_localizer["QCOperation_CrcNoDelete"]); } var waitDeleteStudyList = await _dicomStudyRepository.Where(x => ids.Contains(x.Id)).ToListAsync(); foreach (var study in waitDeleteStudyList) { var id = study.Id; await _dicomStudyRepository.DeleteAsync(study); var fisrtPath = await _dicomInstanceRepository.Where(t => t.StudyId == id).Select(t => t.Path).FirstOrDefaultAsync(); var succeess2 = await _dicomInstanceRepository.BatchDeleteNoTrackingAsync(t => t.StudyId == id); var success3 = await _dicomSeriesrepository.BatchDeleteNoTrackingAsync(t => t.StudyId == id); await _scpStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == id, u => new SCPStudy() { SubjectVisitId = null }); var prefix = fisrtPath.Substring(1, fisrtPath.LastIndexOf('/') - 1); await _oSSService.DeleteFromPrefix(prefix, true); //var success3 = await _dicomSeriesrepository.DeleteFromQueryAsync(t => t.StudyId == id, true); //var success4 = await _repository.BatchDeleteAsync(t => t.StudyId == id); //删除 物理文件 //var instancePathList = await _dicomInstanceRepository.Where(t => t.StudyId == id) // .Select(t => t.Path).ToListAsync(); //instancePathList.ForEach(path => //{ // var physicalPath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, path); // if (System.IO.File.Exists(physicalPath)) // { // File.Delete(physicalPath); // } //}); } var subjectId = waitDeleteStudyList.Select(t => t.SubjectId).FirstOrDefault(); var patientList = _scpPatientRepository.Where(t => t.SubjectId == subjectId).Select(t => t.Id).ToList(); foreach (var patientId in patientList) { if (_scpPatientRepository.Where(t => t.Id == patientId).Any(t => t.SCPStudyList.Count() == t.SCPStudyList.Where(t => t.SubjectVisitId == null).Count())) { await _scpPatientRepository.BatchUpdateNoTrackingAsync(t => t.Id == patientId, u => new SCPPatient() { SubjectId = null }); } } await _subjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Ok(); } #endregion [HttpGet] public async Task IsQCCanOpt([FromQuery, Required] Guid subjectVisitId) { await VerifyIsCanQCAsync(null, subjectVisitId); return ResponseOutput.Ok(); } private async Task VerifyIsCanQCAsync(SubjectVisit? subjectVisit, Guid? sujectVisitId = null) { if (sujectVisitId != null) { subjectVisit = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == sujectVisitId)).IfNullThrowException(); } if (subjectVisit!.CurrentActionUserId != _userInfo.UserRoleId) { //---您不是该质控任务当前领取人,没有操作权限! throw new BusinessValidationFailedException(_localizer["QCOperation_NoRecipient"]); } } #region 临床数据签名 领取、 设置紧急、RequestToQC QC通过、不通过 /// 替换当前领取人 [HttpPut("{trialId:guid}/{subjectVisitId:guid}")] //[Authorize(Policy = IRaCISPolicy.IQC)] [TrialGlobalLimit("AfterStopCannNotOpt")] public async Task ReplaceQCTaskActionUser(Guid trialId, Guid subjectVisitId) { var dbSubjectVisit = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); if (dbSubjectVisit.CurrentActionUserId == null && dbSubjectVisit.IsTake == false) { //---当前访视的影像质控任务已被原领取人释放。您可以通过“领取”获得 return ResponseOutput.NotOk(_localizer["QCOperation_QCImageTaskAlreadyReleased"], ApiResponseCodeEnum.NeedTips); } dbSubjectVisit.IsTake = true; dbSubjectVisit.CurrentActionUserId = _userInfo.UserRoleId; dbSubjectVisit.CurrentActionUserExpireTime = DateTime.Now.AddHours(1); var success = await _subjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Result(success); } /// /// 领取下一个质控任务 /// /// /// [HttpPost] public async Task CollectNextIQCQuality(GetNextIQCQualityInDto inDto) { var nextIQCQuality = await this.GetNextIQCQuality(inDto); if (nextIQCQuality.VisitId != null && nextIQCQuality.IsReceived == false) { var visit = await _subjectVisitRepository.Where(x => x.Id == nextIQCQuality.VisitId).FirstNotNullAsync(); if (!visit.IsTake) { await ObtainOrCancelQCTask(inDto.TrialId, nextIQCQuality.VisitId.Value, true); } } return nextIQCQuality; } /// /// 获取下一个质控任务 /// /// /// [HttpPost] public async Task GetNextIQCQuality(GetNextIQCQualityInDto inDto) { var trialConfig = await _trialRepository .Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification }) .FirstOrDefaultAsync(t => t.TrialId == inDto.TrialId) .IfNullThrowException(); var currentActionList = await _subjectVisitRepository.Where(x => x.TrialId == inDto.TrialId && x.CurrentActionUserId == _userInfo.UserRoleId).OrderByDescending(x => x.IsUrgent) .ThenBy(x => x.Subject.Code).ThenBy(x => x.VisitNum).ToListAsync(); if (currentActionList.Count() > 0) { return new GetNextIQCQualityOutDto() { IsReceived = true, SubjectId = currentActionList[0].SubjectId, VisitId = currentActionList[0].Id, }; } SubjectVisit? subjectVisit = null; List? visitList = null; switch (trialConfig.QCProcessEnum) { case TrialQCProcess.NotAudit: return new GetNextIQCQualityOutDto() { }; break; case TrialQCProcess.SingleAudit: visitList = await _subjectVisitRepository.Where(x => x.SubmitState == SubmitStateEnum.Submitted && x.TrialId == inDto.TrialId && x.PreliminaryAuditUserId != _userInfo.UserRoleId && (x.CurrentActionUserId == _userInfo.UserRoleId || (x.AuditState != AuditStateEnum.PrimaryQCPassed && !x.IsTake))) .Where(x => x.QCChallengeList.Count() == 0 || x.QCChallengeList.Where(y => !y.IsClosed).OrderByDescending(x => x.CreateTime).FirstOrDefault().CreateUserId != _userInfo.UserRoleId) .Include(x => x.Subject).ToListAsync(); subjectVisit = visitList.Where(x => x.SubjectId == inDto.SubjectId) .OrderBy(x => x.VisitNum).FirstOrDefault(); if (subjectVisit != null) { return new GetNextIQCQualityOutDto() { SubjectId = subjectVisit.SubjectId, VisitId = subjectVisit.Id }; } subjectVisit = visitList.OrderBy(x => x.Subject.Code).ThenBy(x => x.VisitNum).FirstOrDefault(); if (subjectVisit != null) { return new GetNextIQCQualityOutDto() { SubjectId = subjectVisit.SubjectId, VisitId = subjectVisit.Id }; } else { return new GetNextIQCQualityOutDto() { }; } break; case TrialQCProcess.DoubleAudit: visitList = await _subjectVisitRepository.Where(x => x.SubmitState == SubmitStateEnum.Submitted && x.TrialId == inDto.TrialId && ((x.CurrentActionUserId == _userInfo.UserRoleId) || (!x.IsTake && x.AuditState != AuditStateEnum.QCPassed && (x.PreliminaryAuditUserId != _userInfo.UserRoleId))) ) .Where(x => x.QCChallengeList.Count() == 0 || x.QCChallengeList.Where(y => !y.IsClosed).OrderByDescending(x => x.CreateTime).FirstOrDefault().CreateUserId != _userInfo.UserRoleId) .Include(x => x.Subject).ToListAsync(); if (subjectVisit != null) { return new GetNextIQCQualityOutDto() { SubjectId = subjectVisit.SubjectId, VisitId = subjectVisit.Id }; } subjectVisit = visitList.OrderBy(x => x.Subject.Code).ThenBy(x => x.VisitNum).FirstOrDefault(); if (subjectVisit != null) { return new GetNextIQCQualityOutDto() { SubjectId = subjectVisit.SubjectId, VisitId = subjectVisit.Id }; } else { return new GetNextIQCQualityOutDto() { }; } break; default: return new GetNextIQCQualityOutDto() { }; } } /// /// 手动领取 或者取消 QC任务 /// /// /// /// true 获取 false是取消领取 /// [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{obtaionOrCancel:bool}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.IQC)] public async Task ObtainOrCancelQCTask(Guid trialId, Guid subjectVisitId, bool obtaionOrCancel) { var dbSubjectVisit = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); var trialConfig = await _trialRepository .Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification }) .FirstOrDefaultAsync(t => t.TrialId == trialId) .IfNullThrowException(); if (obtaionOrCancel) { if (dbSubjectVisit.CurrentActionUserId != null && dbSubjectVisit.IsTake) { //throw new BusinessValidationFailedException("当前访视已被领取,不允许领取"); //---当前访视的影像质控任务已被其他QC领取,不允许领取 return ResponseOutput.NotOk(_localizer["QCOperation_QCTaskNoAccess"], ApiResponseCodeEnum.NeedTips); } if (await _subjectVisitRepository.AnyAsync(t => t.Trial.QCQuestionConfirmedUserId == null && t.Id == subjectVisitId)) { //---请先配置影像质控审核问题,再领取影像质控任务 return ResponseOutput.NotOk(_localizer["QCOperation_QCConfigFirst"], ApiResponseCodeEnum.NeedTips); } //if (await _subjectVisitRepository.AnyAsync(t => t.IsTake && // t.SubjectId != dbSubjectVisit.SubjectId && // t.CurrentActionUserId == _userInfo.Id && // t.TrialId == dbSubjectVisit.TrialId // )) //{ // return ResponseOutput.NotOk("您已经领取了其他受试者,完成后才允许领取新的受试者"); //} #region 处理验证 if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit) { //---项目配置为不审,没有领取QC Task return ResponseOutput.NotOk(_localizer["QCOperation_NoQC"]); } else if (trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit) { if (dbSubjectVisit.PreliminaryAuditUserId == _userInfo.UserRoleId) { //---初审已通过,不能继续领取 return ResponseOutput.NotOk(_localizer["QCOperation_InitialAuditPassed"]); } if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.ToAudit) { dbSubjectVisit.AuditState = AuditStateEnum.InPrimaryQC; } if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC) { //单审QC 释放后 也是可以领取的 } else { // ---项目配置为单审,不满足Submmit State:已提交 或者 Audit State:待审核/审核中, 不允许领取,请刷新界面 return ResponseOutput.NotOk(_localizer["QCOperation_NoSingleAudit"]); } } else if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit) { if (dbSubjectVisit.PreliminaryAuditUserId == _userInfo.UserRoleId) { //---复审不能和初审是同一个人 return ResponseOutput.NotOk(_localizer["QCOperation_NoSameReviewer"]); } //提交 并且初审通过 那么领取后进入 复审中 if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.PrimaryQCPassed) { dbSubjectVisit.AuditState = AuditStateEnum.InSecondaryQC; } else if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.ToAudit) { dbSubjectVisit.AuditState = AuditStateEnum.InPrimaryQC; } else if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && (dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC || dbSubjectVisit.AuditState == AuditStateEnum.InSecondaryQC)) { //初审中 复审中 领取也是ok的 其他人接着做 } else { //---项目配置为复审,不满足提交状态:已提交 或者 审核状态:待审核/QC中, 不允许领取,请刷新界面 return ResponseOutput.NotOk(_localizer["QCOperation_NoSecondaryAudit"]); } } #endregion dbSubjectVisit.IsTake = true; dbSubjectVisit.CurrentActionUserId = _userInfo.UserRoleId; dbSubjectVisit.CurrentActionUserExpireTime = DateTime.Now.AddHours(1); //启动定时任务 1h后处理 //BackgroundJob.Schedule(t => t.CancelQCObtaion(subjectVisitId, DateTime.Now), TimeSpan.FromHours(1)); } else { if (dbSubjectVisit!.CurrentActionUserId != _userInfo.UserRoleId) { //---您不是该质控任务当前领取人,没有操作权限! return ResponseOutput.NotOk(_localizer["QCOperation_NoRecipient"], ApiResponseCodeEnum.NeedTips); } if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit) { //---项目配置影像质控为不审,不需要取消任务功能 return ResponseOutput.NotOk(_localizer["QCOperation_NoQCFunction"]); } else if (trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit) { if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC) { dbSubjectVisit.AuditState = AuditStateEnum.ToAudit; } else { //---当前访视影像质控任务没有当前领取人,不能释放。 return ResponseOutput.NotOk(_localizer["QCOperation_NoTaskOwner"]); } } else if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit) { //提交 并且初审通过 那么领取后进入 复审中 if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.InSecondaryQC) { dbSubjectVisit.AuditState = AuditStateEnum.PrimaryQCPassed; } else if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC) { dbSubjectVisit.AuditState = AuditStateEnum.ToAudit; } else { //---当前访视影像质控任务没有当前领取人,不能释放。 return ResponseOutput.NotOk(_localizer["QCOperation_NoTaskOwner"]); } } dbSubjectVisit.IsTake = false; dbSubjectVisit.CurrentActionUserId = null; dbSubjectVisit.CurrentActionUserExpireTime = null; } var success = await _subjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Result(success); } [HttpPost] public async Task VerifyCRCRequestToQC(CRCRequestToQCCommand cRCRequestToQCCommand) { var dbSubjectVisitList = await _subjectVisitRepository.Where(t => cRCRequestToQCCommand.SubjectVisitIds.Contains(t.Id), true).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); var trialinfo = await _trialRepository.Where(x => x.Id == dbSubjectVisitList[0].TrialId).FirstNotNullAsync(); //普通提交 if (dbSubjectVisitList.Count == 1) { var sv = dbSubjectVisitList[0]; var nameList = await _subjectVisitRepository.Where(t => t.SubjectId == sv.SubjectId && t.SubmitState != SubmitStateEnum.Submitted && t.VisitNum < sv.VisitNum && t.IsLostVisit == false).Select(t => t.VisitName).ToListAsync() ?? new List(); if (sv.PDState == PDStateEnum.PDProgress) { if (nameList.Count > 0) { //$"当前访视要求进行疾病进展确认。请在提交当前访视前,先处理未提交的前序访视:{string.Join('、', nameList)}。" return ResponseOutput.NotOk(_localizer["QCOperation_DiseaseProgressConfirmation", string.Join('、', nameList)], 1, ApiResponseCodeEnum.NeedTips); } } else { if (nameList.Count > 0) { //$"在提交当前访视后,请尽快处理尚未提交的前序访视:{string.Join('、', nameList)}。" return ResponseOutput.NotOk(_localizer["QCOperation_UnsubmittedVisits", string.Join('、', nameList)], 0, ApiResponseCodeEnum.NeedTips); } } } //同时要根据项目有没有配置Subject 级别临床数据 if (dbSubjectVisitList.Any(t => t.IsBaseLine && !t.IsHaveClinicalData) && trialinfo.ClinicalInformationTransmissionEnum != 0) { //---基线没有临床数据,确认提交? return ResponseOutput.NotOk(_localizer["QCOperation_NoBaselineData"], 0, ApiResponseCodeEnum.NeedTips); } return ResponseOutput.Ok(); } /// /// CRC RequestToQC 批量提交 [需要签名 不需要对] /// /// [HttpPost] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.CRC)] public async Task CRCRequestToQC(CRCRequestToQCCommand cRCRequestToQCCommand) { List modalitieTypess = new List() { "PT、CT", "CT、PT", "PET-CT" }; // 验证临床数据 是否有Pet类型 #region 验证临床数据 是否有Pet类型 if (await _clinicalDataTrialSetRepository.AnyAsync(x => x.TrialId == cRCRequestToQCCommand.TrialId && x.IsConfirm && x.ClinicalDataLevel == ClinicalLevel.Study)) { foreach (var item in cRCRequestToQCCommand.SubjectVisitIds) { if (await _dicomStudyRepository.AnyAsync(x => x.SubjectVisitId == item && modalitieTypess.Contains(x.Modalities))) { var visit = await _subjectVisitRepository.Where(x => x.Id == item).FirstNotNullAsync(); var clinicalData = await _readingClinicalDataService.GetStudyClinicalData(new Service.Reading.Dto.GetStudyClinicalDataInDto() { SubjectVisitId = item, }); if (!clinicalData.Any(x => x.ClinicalDataLevel == ClinicalLevel.Study && x.ClinicalUploadType == ClinicalUploadType.PDF && x.PDFFileList.Count() > 0)) { throw new BusinessValidationFailedException(_localizer["QCOperation_NeedStudyClinical"]); } } } } #endregion var trialId = cRCRequestToQCCommand.TrialId; var trialConfig = (await _trialRepository .Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification, t.IsUrgent, t.IsHaveFirstGiveMedicineDate, t.ClinicalInformationTransmissionEnum }) .FirstOrDefaultAsync(t => t.TrialId == cRCRequestToQCCommand.TrialId)).IfNullThrowException(); //找到符合配置的标准 确认的并且签名的、双重,有序,阅片期仲裁 var trialReadingCriterionIdList = _readingQuestionCriterionTrialRepository.Where(t => t.TrialId == cRCRequestToQCCommand.TrialId && t.IsConfirm && t.ReadingInfoSignTime != null //&& t.ReadingType == ReadingMethod.Double && t.ArbitrationRule == ArbitrationRule.Reading && t.IsReadingTaskViewInOrder == ReadingOrder.InOrder && t.IsReadingPeriod).Select(t => t.Id).ToList(); var trialInOrderCriterionIdList = _readingQuestionCriterionTrialRepository.Where(t => t.TrialId == cRCRequestToQCCommand.TrialId && t.IsConfirm && t.ReadingInfoSignTime != null //&& t.ReadingType == ReadingMethod.Double && t.ArbitrationRule == ArbitrationRule.Reading && t.IsReadingTaskViewInOrder == ReadingOrder.InOrder).Select(t => t.Id).ToList(); var dbSubjectVisitList = await _subjectVisitRepository.Where(t => cRCRequestToQCCommand.SubjectVisitIds.Contains(t.Id), true).Include(t => t.Subject).ToListAsync(); if (dbSubjectVisitList.Any(t => t.SubmitState == SubmitStateEnum.None)) { //---有访视未上传任何Dicom/非Dicom影像数据,不允许提交 return ResponseOutput.NotOk(_localizer["QCOperation_NoImageData"]); } //单个提交提示信息 if (dbSubjectVisitList.Count == 1 && dbSubjectVisitList.First().SubmitState == SubmitStateEnum.Submitted) { //---当前访视的影像数据,已经由其他CRC提交。 return ResponseOutput.NotOk(_localizer["QCOperation_ImagesSubmitted"], 3, ApiResponseCodeEnum.NeedTips); } else if (dbSubjectVisitList.Any(t => t.SubmitState == SubmitStateEnum.Submitted)) { //---当前批量提交访视的影像数据,其中部分已由其他CRC提交。 return ResponseOutput.NotOk(_localizer["QCOperation_BatchImagesSubmitted"], 3, ApiResponseCodeEnum.NeedTips); } //获取确认的临床数据配置 var clinicalDataConfirmList = _clinicalDataTrialSetRepository.Where(t => t.TrialId == trialId && t.IsConfirm).ToList(); // 别人未提交的 foreach (var dbSubjectVisit in dbSubjectVisitList) { //基线不验证 if (trialConfig.IsHaveFirstGiveMedicineDate && !dbSubjectVisit.IsBaseLine && dbSubjectVisit.Subject.FirstGiveMedicineTime == null) { //---项目配置了需要填写访视基准日期。但是受试者没有填写访视基准日期,不允许提交 return ResponseOutput.NotOk(_localizer["QCOperation_MissingBaselineDate"]); } if (_subjectVisitRepository.Where(t => t.Id == dbSubjectVisit.Id).Any(t => (t.StudyList.Count(t => t.ModalityForEdit == "") > 0 || t.NoneDicomStudyList.Count(t => t.Modality == "") > 0) && t.Trial.TrialReadingCriterionList.Count(t => t.IsImageFilter == true) > 0)) { //有标准配置了影像筛选,该受试者有检查未设置检查类型,不允许提交 return ResponseOutput.NotOk(_localizer["QCOperation_MissingModality"]); } //已确认临床数据完整性 dbSubjectVisit.IsConfirmedClinicalData = true; // CRC 上传的基线数据签名 List clinicalneedSign = new List() { ClinicalLevel.Subject, ClinicalLevel.SubjectVisit, ClinicalLevel.Study, }; await _readingClinicalDataRepository.UpdatePartialFromQueryAsync(x => clinicalneedSign.Contains(x.ClinicalDataTrialSet.ClinicalDataLevel) && x.ClinicalDataTrialSet.UploadRole == UploadRole.CRC && x.ReadingId == dbSubjectVisit.Id && x.IsSign == false, x => new ReadingClinicalData() { IsSign = true, ReadingClinicalDataState = ReadingClinicalDataStatus.HaveSigned }); var maxVisit = await _subjectVisitRepository.Where(t => t.SubjectId == dbSubjectVisit.SubjectId && t.SubmitState == SubmitStateEnum.Submitted) .OrderByDescending(t => t.VisitNum).Select(t => new { t.Id, t.VisitNum }).FirstOrDefaultAsync(); //修改受试者最新访视 dbSubjectVisit.Subject.LatestSubjectVisitId = maxVisit == null ? dbSubjectVisit.Id : maxVisit.VisitNum < dbSubjectVisit.VisitNum ? dbSubjectVisit.Id : maxVisit.Id; //var maxVisitNum = maxVisit == null ? dbSubjectVisit.VisitNum : maxVisit.VisitNum < dbSubjectVisit.VisitNum ? dbSubjectVisit.VisitNum : maxVisit.VisitNum; ////判断是否有缺失影像 //dbSubjectVisit.Subject.IsMissingImages = await _subjectVisitRepository.AnyAsync(t => (t.VisitNum < maxVisitNum && t.SubmitState != SubmitStateEnum.Submitted && t.IsLostVisit == false)); //项目或者Subject IsUrgent 提交时 访视也设置为紧急 if (trialConfig.IsUrgent || dbSubjectVisit.Subject.IsUrgent || (dbSubjectVisit.PDState == PDStateEnum.PDProgress && !dbSubjectVisit.IsBaseLine) || (dbSubjectVisit.IsEnrollmentConfirm && dbSubjectVisit.IsBaseLine)) { if (dbSubjectVisit.PDState == PDStateEnum.PDProgress) { await _subjectVisitRepository.BatchUpdateNoTrackingAsync(x => x.SubjectId == dbSubjectVisit.SubjectId && x.VisitNum <= dbSubjectVisit.VisitNum, x => new SubjectVisit() { IsUrgent = true }); await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => trialInOrderCriterionIdList.Contains(x.TrialReadingCriterionId) && x.SubjectId == dbSubjectVisit.SubjectId && dbSubjectVisit.VisitNum <= x.VisitTaskNum && x.VisitTaskNum < Math.Ceiling(dbSubjectVisit.VisitNum + 0.01m) // 当前的访视 全局 裁判 及之前 全都加急 && x.ReadingTaskState != ReadingTaskState.HaveSigned && x.TaskState == TaskState.Effect, x => new VisitTask() { IsUrgent = true, TaskUrgentType = TaskUrgentType.PDProgress, IsCanEditUrgentState = false, }); await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => trialInOrderCriterionIdList.Contains(x.TrialReadingCriterionId) && x.SubjectId == dbSubjectVisit.SubjectId && x.VisitTaskNum < dbSubjectVisit.VisitNum // 当前的访视 全局 裁判 及之前 全都加急 && x.ReadingTaskState != ReadingTaskState.HaveSigned && x.TaskState == TaskState.Effect, x => new VisitTask() { IsUrgent = true, TaskUrgentType = TaskUrgentType.Other, TaskUrgentRemake = "后续访视设为pd", IsCanEditUrgentState = false, }); } else if (dbSubjectVisit.IsEnrollmentConfirm) { await _subjectVisitRepository.BatchUpdateNoTrackingAsync(x => x.Id == dbSubjectVisit.Id, x => new SubjectVisit() { IsUrgent = true }); await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => trialInOrderCriterionIdList.Contains(x.TrialReadingCriterionId) && x.VisitTaskNum >= dbSubjectVisit.VisitNum && x.VisitTaskNum < Math.Ceiling(dbSubjectVisit.VisitNum + 0.01m) // 当前的访视 全局 裁判 全都加急 && x.ReadingTaskState != ReadingTaskState.HaveSigned && x.TaskState == TaskState.Effect, x => new VisitTask() { IsUrgent = true, TaskUrgentType = TaskUrgentType.EnrollmentConfirm, IsCanEditUrgentState = false, }); } else { await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => trialInOrderCriterionIdList.Contains(x.TrialReadingCriterionId) && x.VisitTaskNum >= dbSubjectVisit.VisitNum && x.VisitTaskNum < Math.Ceiling(dbSubjectVisit.VisitNum + 0.01m) // 当前的访视 全局 裁判 全都加急 && x.ReadingTaskState != ReadingTaskState.HaveSigned && x.TaskState == TaskState.Effect, x => new VisitTask() { IsUrgent = true, TaskUrgentType = TaskUrgentType.VisitUrgent, }); } dbSubjectVisit.IsUrgent = true; } if (dbSubjectVisit.SubmitState == SubmitStateEnum.ToSubmit) { dbSubjectVisit.SubmitState = SubmitStateEnum.Submitted; dbSubjectVisit.SubmitTime = DateTime.Now; dbSubjectVisit.SubmitUserId = _userInfo.UserRoleId; //维护统一状态 dbSubjectVisit.ReadingStatus = ReadingStatusEnum.ImageQuality; } //不审 直接QC通过 可能一致性核查 也可能不一致性核查 if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit) { dbSubjectVisit.AuditState = AuditStateEnum.QCPassed; //维护统一状态 dbSubjectVisit.ReadingStatus = ReadingStatusEnum.ConsistencyCheck; // 不一致性核查 就CVPassed ToForward 否则就是待核查 dbSubjectVisit.CheckState = trialConfig.IsImageConsistencyVerification ? CheckStateEnum.ToCheck : CheckStateEnum.CVPassed; //维护统一状态 dbSubjectVisit.ReadingStatus = trialConfig.IsImageConsistencyVerification ? ReadingStatusEnum.ConsistencyCheck : ReadingStatusEnum.TaskAllocate; if (dbSubjectVisit.CheckState == CheckStateEnum.CVPassed) { dbSubjectVisit.CheckPassedTime = DateTime.Now; } dbSubjectVisit.ForwardState = trialConfig.IsImageConsistencyVerification ? ForwardStateEnum.None : ForwardStateEnum.ToForward; } else if (trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit) { dbSubjectVisit.AuditState = AuditStateEnum.ToAudit; } else if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit) { dbSubjectVisit.AuditState = AuditStateEnum.ToAudit; } //非基线设置为PD的话 或者设置为末次访视 根据配置自动生成阅片期 if (!dbSubjectVisit.IsBaseLine && (dbSubjectVisit.PDState == PDStateEnum.PDProgress || dbSubjectVisit.IsFinalVisit)) { //该标准需要添加阅片期 foreach (var trialReadingCriterionId in trialReadingCriterionIdList) { //不存在该阅片期 (重传、回退都会重新提交) if (!await _readModuleRepository.Where(t => t.SubjectVisitId == dbSubjectVisit.Id && t.ReadingSetType == ReadingSetType.ImageReading && t.TrialReadingCriterionId == trialReadingCriterionId).AnyAsync()) { var newReadModule = await _readModuleRepository.AddAsync(new ReadModule() { TrialReadingCriterionId = trialReadingCriterionId, ReadingSetType = ReadingSetType.ImageReading, SubjectVisitId = dbSubjectVisit.Id, ModuleName = $"G-{dbSubjectVisit.VisitName}", ModuleType = ModuleTypeEnum.Global, IsUrgent = dbSubjectVisit.IsUrgent, TrialId = dbSubjectVisit.TrialId, SubjectId = dbSubjectVisit.SubjectId }); } } } } var success = await _subjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Ok(success); } /// /// 设置QC 通过或者不通过 7:QC failed 8:QC passed [签名 不需要对] /// /// /// /// /// [HttpPost("{trialId:guid}/{subjectVisitId:guid}/{auditState:int}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.IQC)] [UnitOfWork] public async Task QCPassedOrFailed(Guid trialId, Guid subjectVisitId, [FromRoute] AuditStateEnum auditState) { if (!await _trialUserRoleRepository.AnyAsync(t => t.TrialId == trialId && t.UserId == _userInfo.UserRoleId)) { //---您已经被移出项目,没有操作权限。 return ResponseOutput.NotOk(_localizer["QCOperation_RemoveItem"]); } if (auditState == AuditStateEnum.QCPassed) { //判断质疑是否都关闭了 if (await _qcChallengeRepository.AnyAsync(t => t.SubjectVisitId == subjectVisitId && t.IsClosed == false)) { //---当前访视有影像质控质疑未关闭,不能进行此操作。 return ResponseOutput.NotOk(_localizer["QCOperation_QCNotClosed"]); } } var trialConfig = await _trialRepository .Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification }) .FirstOrDefaultAsync(t => t.TrialId == trialId) .IfNullThrowException(); var dbSubjectVisit = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); await VerifyIsCanQCAsync(dbSubjectVisit); //删除 软删除的物理文件 var instancePathList = await _dicomInstanceRepository.Where(t => (t.DicomSerie.IsDeleted || t.IsDeleted) && t.SubjectVisitId == subjectVisitId, false, true) .Select(t => t.Path).ToListAsync(); //维护统一状态 dbSubjectVisit.ReadingStatus = ReadingStatusEnum.ConsistencyCheck; //有人QC Passed if (auditState == AuditStateEnum.QCPassed) { //判断 QC流程 不审 单审 双审 if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit) { //---项目配置影像质控为不审,不需要设置为影像质控通过。 return ResponseOutput.NotOk(_localizer["QCOperation_NoQCNeeded"]); } else if (trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit) { if (dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC) { if (!await _trialQCQuestionAnswerRepository.AnyAsync(t => t.SubjectVisitId == subjectVisitId && t.CurrentQCEnum == CurrentQC.First)) { //---影像质控审核问题没有保存,不能进行此操作。 return ResponseOutput.NotOk(_localizer["QCOperation_QCNotSaved"]); } // 单审 dbSubjectVisit.AuditState = AuditStateEnum.QCPassed; dbSubjectVisit.CheckState = trialConfig.IsImageConsistencyVerification ? CheckStateEnum.ToCheck : CheckStateEnum.CVPassed; dbSubjectVisit.ForwardState = trialConfig.IsImageConsistencyVerification ? ForwardStateEnum.None : ForwardStateEnum.ToForward; dbSubjectVisit.PreliminaryAuditUserId = _userInfo.UserRoleId; dbSubjectVisit.PreliminaryAuditTime = DateTime.Now; //维护统一状态 dbSubjectVisit.ReadingStatus = trialConfig.IsImageConsistencyVerification ? ReadingStatusEnum.ConsistencyCheck : ReadingStatusEnum.TaskAllocate; //删除影像 await _dicomInstanceRepository.BatchDeleteNoTrackingAsync(t => (t.DicomSerie.IsDeleted || t.IsDeleted) && t.SubjectVisitId == subjectVisitId); await _oSSService.DeleteObjects(instancePathList.Select(t => t.TrimStart('/')).ToList()); } else { //---项目配置影像质控为单审,当前访视影像质控任务不能从当前审核状态变更到 审核通过。 return ResponseOutput.NotOk(_localizer["QCOperation_NotChangePass"]); } } else if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit) { // 双审 如果当前db 状态是 InPrimaryQC 当前操作为 QCPassed 那么设置为 PrimaryQCPassed if (dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC) { if (!await _trialQCQuestionAnswerRepository.AnyAsync(t => t.SubjectVisitId == subjectVisitId && t.CurrentQCEnum == CurrentQC.First)) { //---影像质控审核问题没有保存,不能进行此操作。 return ResponseOutput.NotOk(_localizer["QCOperation_QCNotSaved"]); } dbSubjectVisit.AuditState = AuditStateEnum.PrimaryQCPassed; dbSubjectVisit.PreliminaryAuditUserId = _userInfo.UserRoleId; dbSubjectVisit.PreliminaryAuditTime = DateTime.Now; } else if (dbSubjectVisit.AuditState == AuditStateEnum.InSecondaryQC) { if (!await _trialQCQuestionAnswerRepository.AnyAsync(t => t.SubjectVisitId == subjectVisitId && t.CurrentQCEnum == CurrentQC.Second)) { //---影像质控审核问题没有保存,不能进行此操作。 return ResponseOutput.NotOk(_localizer["QCOperation_QCNotSaved"]); } dbSubjectVisit.AuditState = AuditStateEnum.QCPassed; dbSubjectVisit.CheckState = trialConfig.IsImageConsistencyVerification ? CheckStateEnum.ToCheck : CheckStateEnum.CVPassed; dbSubjectVisit.ForwardState = trialConfig.IsImageConsistencyVerification ? ForwardStateEnum.None : ForwardStateEnum.ToForward; dbSubjectVisit.ReviewAuditUserId = _userInfo.UserRoleId; dbSubjectVisit.ReviewAuditTime = DateTime.Now; //维护统一状态 dbSubjectVisit.ReadingStatus = trialConfig.IsImageConsistencyVerification ? ReadingStatusEnum.ConsistencyCheck : ReadingStatusEnum.TaskAllocate; //删除影像 await _dicomInstanceRepository.BatchDeleteNoTrackingAsync(t => (t.DicomSerie.IsDeleted || t.IsDeleted) && t.SubjectVisitId == subjectVisitId); await _oSSService.DeleteObjects(instancePathList.Select(t => t.TrimStart('/')).ToList()); } else { //$"项目配置影像质控为双审。当前审核状态为 {dbSubjectVisit.AuditState},不能变更到 审核通过。" return ResponseOutput.NotOk(_localizer["QCOperation_CannotChangeToPass", dbSubjectVisit.AuditState]); } } } else if (auditState == AuditStateEnum.QCFailed) { //判断 QC流程 不审 单审 双审 if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit) { //---项目配置影像质控为不审,不允许设置影像质控终止。 return ResponseOutput.NotOk(_localizer["QCOperation_NoQCNeededNotEnd"]); } else if (trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit) { // 单审 if (dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC) { dbSubjectVisit.AuditState = AuditStateEnum.QCFailed; dbSubjectVisit.PreliminaryAuditUserId = _userInfo.UserRoleId; } else { // 项目配置影像质控为单审,当前审核状态不为 InPrimaryQC,不能变更到 审核终止 return ResponseOutput.NotOk(_localizer["QCOperation_CannotChangeToStop"]); } } else if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit) { // 双审 if (dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC) { dbSubjectVisit.AuditState = AuditStateEnum.QCFailed; dbSubjectVisit.PreliminaryAuditUserId = _userInfo.UserRoleId; } else if (dbSubjectVisit.AuditState == AuditStateEnum.InSecondaryQC) { dbSubjectVisit.AuditState = AuditStateEnum.QCFailed; dbSubjectVisit.ReviewAuditUserId = _userInfo.UserRoleId; } else { //$"项目配置影像质控为双审,当前审核状态为 {dbSubjectVisit.AuditState},不能变更到 审核终止" return ResponseOutput.NotOk(_localizer["QCOperation_CannotChangeStop", dbSubjectVisit.AuditState]); } } await _qcChallengeRepository.UpdatePartialFromQueryAsync(t => t.IsClosed == false && t.SubjectVisitId == dbSubjectVisit.Id, u => new QCChallenge() { IsClosed = true, ClosedTime = DateTime.Now, CloseResonEnum = QCChallengeCloseEnum.Unresolvable }); } dbSubjectVisit.Auditor = _userInfo.UserRoleId; dbSubjectVisit.IsTake = false; dbSubjectVisit.CurrentActionUserId = null; dbSubjectVisit.CurrentActionUserExpireTime = null; await _subjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Result(true); } /// /// 设置、取消 访视紧急 /// /// /// /// /// [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{setOrCancel:bool}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.IQC)] public async Task SetVisitUrgent(Guid trialId, Guid subjectVisitId, bool setOrCancel) { var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException(); sv.IsUrgent = setOrCancel; var success = await _subjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Ok(); } #endregion #region 重传、重传完设置 /// 验证质疑对话页面 是否可以 跳转到重传页面 进行重传 [HttpGet("{trialId:guid}/{qcChallengeId:guid}")] public async Task VerifyReuploadIsCanJump(Guid trialId, Guid qcChallengeId) { var qcChallenge = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == qcChallengeId)).IfNullThrowException(); if (qcChallenge.ReuploadEnum != QCChanllengeReuploadEnum.QCAgreeUpload) { //---当前重传状态不为QC同意重传,不允许重传 throw new BusinessValidationFailedException(_localizer["QCOperation_InvalidResend"]); } return ResponseOutput.Ok(); } /// /// QA设置 同意重传 /// /// /// /// [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{qcChallengeId:guid}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.IQC)] public async Task SetNeedReupload(Guid trialId, Guid qcChallengeId) { //获取项目配置 var trialConfig = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification }) .FirstOrDefaultAsync().IfNullThrowException(); if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit) { //---不审操作,不会有需要重传的操作! return ResponseOutput.NotOk(_localizer["QCOperation_NoResendNeeded"]); } var qcChallenge = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == qcChallengeId)).IfNullThrowException(); var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == qcChallenge.SubjectVisitId)).IfNullThrowException(); await VerifyIsCanQCAsync(sv); if (qcChallenge.ReuploadEnum != QCChanllengeReuploadEnum.CRCRequestReupload) { //---当前重传状态不为CRC申请重传,不允许设置同意重传 throw new BusinessValidationFailedException(_localizer["QCOperation_InvalidAgreeResend"]); } if (await _qcChallengeRepository.CountAsync(t => t.ReuploadEnum == QCChanllengeReuploadEnum.QCAgreeUpload && t.SubjectVisitId == qcChallenge.SubjectVisitId && t.IsClosed == false) >= 1) { //---当前访视,有一个未关闭的质疑 QC设置了同意重传,CRC还未完成上传,当前不允许再次设置 return ResponseOutput.NotOk(_localizer["QCOperation_CannotAgreeResend"]); } qcChallenge.ReuploadEnum = QCChanllengeReuploadEnum.QCAgreeUpload; qcChallenge.LatestMsgTime = DateTime.Now; qcChallenge.LatestReplyUserId = _userInfo.UserRoleId; await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == qcChallenge.SubjectVisitId, c => new SubjectVisit() { IsQCConfirmedReupload = true }); qcChallenge.DialogList.Add(new QCChallengeDialog() { SubjectVisitId = qcChallenge.SubjectVisitId, UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, QCChallengeId = qcChallenge.Id, //---QC同意重传 TalkContent = _localizer["QCOperation_AgreeResend"] }); //双审 并且是2QC 那么需要回退到1QC 讲1QC数据清除 if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit && await _qcChallengeRepository.AnyAsync(t => t.Id == qcChallengeId && t.SubjectVisit.AuditState == AuditStateEnum.InSecondaryQC)) { // //一致性核查质疑状态 // sv.CheckChallengeState = CheckChanllengeTypeEnum.None; //// 一致性核查状态 // sv.CheckState = CheckStateEnum.None; // 审核状态 sv.AuditState = AuditStateEnum.InPrimaryQC; sv.CurrentActionUserExpireTime = DateTime.Now.AddHours(1); sv.CurrentActionUserId = _userInfo.UserRoleId; //BackgroundJob.Schedule(t => t.CancelQCObtaion(qcChallenge.SubjectVisitId, DateTime.Now), TimeSpan.FromHours(1)); sv.IsTake = true; sv.PreliminaryAuditUserId = null; sv.ReviewAuditUserId = null; //删除1QC 填写的问题答案 await _trialQCQuestionAnswerRepository.BatchDeleteNoTrackingAsync(t => t.SubjectVisitId == qcChallenge.SubjectVisitId && t.CurrentQCEnum == CurrentQC.First); //2QC 数据变为1QC await _qcChallengeRepository.BatchUpdateNoTrackingAsync(t => t.SubjectVisitId == qcChallenge.SubjectVisitId && t.CurrentQCEnum == CurrentQC.Second, k => new QCChallenge() { CurrentQCEnum = CurrentQC.First }); await _trialQCQuestionAnswerRepository.BatchUpdateNoTrackingAsync(t => t.SubjectVisitId == qcChallenge.SubjectVisitId && t.CurrentQCEnum == CurrentQC.Second, k => new TrialQCQuestionAnswer() { CurrentQCEnum = CurrentQC.First }); } var success = await _qcChallengeRepository.SaveChangesAsync(); return ResponseOutput.Result(success); } /// /// CRC 设置已经重传完成 [需要签名 不需要对] /// /// [HttpPost] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.CRC)] public async Task SetReuploadFinished(CRCReuploadFinishedCommand cRCReuploadFinishedCommand) { var qcChallenge = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == cRCReuploadFinishedCommand.QCChallengeId)).IfNullThrowException(); if (qcChallenge.ReuploadEnum != QCChanllengeReuploadEnum.QCAgreeUpload) { //---当前重传状态不为QC同意重传,不允许设置重传完成 throw new BusinessValidationFailedException(_localizer["QCOperation_InvalidCompleteResend"]); } List modalitieTypess = new List() { "PT、CT", "CT、PT", "PET-CT" }; // 验证临床数据 是否有Pet类型 #region 验证临床数据 是否有Pet类型 if (await _clinicalDataTrialSetRepository.AnyAsync(x => x.TrialId == cRCReuploadFinishedCommand.TrialId && x.IsConfirm && x.ClinicalDataLevel == ClinicalLevel.Study)) { var item = qcChallenge.SubjectVisitId; if (await _dicomStudyRepository.AnyAsync(x => x.SubjectVisitId == item && modalitieTypess.Contains(x.Modalities))) { var visit = await _subjectVisitRepository.Where(x => x.Id == item).FirstNotNullAsync(); var clinicalData = await _readingClinicalDataService.GetStudyClinicalData(new Service.Reading.Dto.GetStudyClinicalDataInDto() { SubjectVisitId = item, }); if (!clinicalData.Any(x => x.ClinicalDataLevel == ClinicalLevel.Study && x.ClinicalUploadType == ClinicalUploadType.PDF && x.PDFFileList.Count() > 0)) { throw new BusinessValidationFailedException(_localizer["QCOperation_NeedStudyClinical"]); } } } #endregion var subjectVisitId = qcChallenge.SubjectVisitId; if (await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).SelectMany(t => t.StudyList).CountAsync() == 0 && await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).SelectMany(t => t.NoneDicomStudyList).SelectMany(u => u.NoneDicomFileList).CountAsync() == 0) { //---当前没有影像,不允许设置重传完成 throw new BusinessValidationFailedException(_localizer["QCOperation_NoImageNotAllowed"]); } var trialConfig = await _trialRepository .Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification, t.IsUrgent, t.IsHaveFirstGiveMedicineDate, t.ClinicalInformationTransmissionEnum }) .Where(t => t.TrialId == cRCReuploadFinishedCommand.TrialId).FirstOrDefaultAsync(); qcChallenge.ReuploadEnum = QCChanllengeReuploadEnum.CRCReuploaded; qcChallenge.ReUploadedTime = DateTime.Now; qcChallenge.ReUploadUserId = _userInfo.UserRoleId; qcChallenge.ReUploader = _userInfo.FullName; qcChallenge.LatestMsgTime = DateTime.Now; qcChallenge.LatestReplyUserId = _userInfo.UserRoleId; var dbSubjectVisit = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == qcChallenge.SubjectVisitId).IfNullThrowException(); await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == qcChallenge.SubjectVisitId, c => new SubjectVisit() { IsQCConfirmedReupload = false }); qcChallenge.DialogList.Add(new QCChallengeDialog() { SubjectVisitId = qcChallenge.SubjectVisitId, UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, QCChallengeId = qcChallenge.Id, //---CRC已重传完成 TalkContent = _localizer["QCOperation_CRCTransferCompleted"] }); //基线 且配置了临床数据 if (trialConfig.ClinicalInformationTransmissionEnum != 0 && dbSubjectVisit.IsBaseLine) { //已确认临床数据完整性 dbSubjectVisit.IsConfirmedClinicalData = true; await _readingClinicalDataRepository.UpdatePartialFromQueryAsync(x => (x.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.Subject || x.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.SubjectVisit) && x.ClinicalDataTrialSet.UploadRole == UploadRole.CRC && x.ReadingId == dbSubjectVisit.Id && x.IsSign == false, x => new ReadingClinicalData() { IsSign = true, ReadingClinicalDataState = ReadingClinicalDataStatus.HaveSigned }); //var signSuccess = await _repository.BatchUpdateAsync(t => t.Id == cRCReuploadFinishedCommand.SignId, u => new TrialSign() { IsCompleted = true }); } var success = await _subjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Result(success); } [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{qcChallengeId:guid}")] [TrialGlobalLimit("AfterStopCannNotOpt")] //[Authorize(Policy = IRaCISPolicy.CRC)] public async Task CRCRequestReUpload(Guid qcChallengeId) { var qcChallenge = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == qcChallengeId)).IfNullThrowException(); if (qcChallenge.ReuploadEnum != QCChanllengeReuploadEnum.None && qcChallenge.ReuploadEnum != QCChanllengeReuploadEnum.CRCReuploaded) { //---当前质疑重传状态不为初始状态|CRC重传完成状态,不允许申请重传 throw new BusinessValidationFailedException(_localizer["QCOperation_InvalidTransferStatus"]); } if (qcChallenge.ReuploadEnum == QCChanllengeReuploadEnum.CRCReuploaded) { qcChallenge.ReUploadedTime = null; } qcChallenge.LatestMsgTime = DateTime.Now; qcChallenge.LatestReplyUserId = _userInfo.UserRoleId; qcChallenge.ReuploadEnum = QCChanllengeReuploadEnum.CRCRequestReupload; qcChallenge.DialogList.Add(new QCChallengeDialog() { SubjectVisitId = qcChallenge.SubjectVisitId, UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt, QCChallengeId = qcChallenge.Id, //---CRC申请重传/上传影像 TalkContent = _localizer["QCOperation_CRCTransfer"] }); var isSuccess = await _qcChallengeRepository.SaveChangesAsync(); return ResponseOutput.Result(isSuccess); } #endregion /// /// 上传界面 更新受试者访视基准日期 是否入组确认,以及访视 是否PD进展 /// /// /// [HttpPut("{trialId:guid}")] [TrialGlobalLimit("AfterStopCannNotOpt")] public async Task UpdateSubjectAndSVInfo(UploadSubjectAndVisitCommand command) { var dbSubjectVisit = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == command.SubjectVisitId)).IfNullThrowException(); dbSubjectVisit.PDState = command.PDState; if (command.IsEnrollmentConfirm != null) { if (await _subjectVisitRepository.Where(t => t.Id == command.SubjectVisitId) .AnyAsync(t => t.SubmitState == SubmitStateEnum.Submitted && t.IsEnrollmentConfirm != command.IsEnrollmentConfirm)) { //---该访视已提交,不能修改入组确认状态 return ResponseOutput.NotOk(_localizer["QCOperation_CannotModifyConfirmation"]); } // if (await _subjectVisitRepository.Where(t => t.Id == command.SubjectVisitId) //.AnyAsync(t => t.IsEnrollmentConfirm != command.IsEnrollmentConfirm && t.RequestBackState == RequestBackStateEnum.PM_AgressBack)) // { // //---该访视为回退访视,不允许修改PD确认状态 // return ResponseOutput.NotOk(_localizer["QCOperation_CannotModifyPDStatus"]); // } dbSubjectVisit.IsEnrollmentConfirm = command.IsEnrollmentConfirm.Value; } if (command.SubjectFirstGiveMedicineTime != null) { await _subjectRepository.UpdatePartialFromQueryAsync(command.SubjectId, u => new Subject() { FirstGiveMedicineTime = command.SubjectFirstGiveMedicineTime, }, true); } await _subjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Ok(); } #region 转发影像 暂时不用 废弃 //[HttpPost("{trialId:guid}")] ////[Authorize(Policy = IRaCISPolicy.PM_APM)] //[TrialGlobalLimit( "AfterStopCannNotOpt" )] //public async Task ForwardSVDicomImage(Guid[] subjectVisitIdList) //{ // bool isSuccess = false; // foreach (var subjectVisitId in subjectVisitIdList) // { // var info = (await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); // var targetPath = "/IMPORT-IMAGES/" + info.TrialCode + "_" + info.SubjectCode + "_" + info.VisitName; // var path = FileStoreHelper.GetSubjectVisitDicomFolderPhysicalPath(_hostEnvironment, info.TrialId, info.TrialSiteId, info.SubjectId, info.SubjectVisitId); // try // { // // 主机及端口信息后面可以改到 配置文件 // SessionOptions sessionOptions = new SessionOptions // { // Protocol = Protocol.Sftp, // PortNumber = 8022, // HostName = "CS-690-sftp.mint-imaging.com", // UserName = "zdong", // Password = "Everest@2021", // SshHostKeyFingerprint = @"ecdsa-sha2-nistp384 384 59gkjJ5lMwv3jsB8Wz2B35tBAIor5pSd8PcJYtoamPo=" // }; // using (Session session = new Session()) // { // var studyFolders = (new DirectoryInfo(path)).GetDirectories(); // session.Open(sessionOptions); // if (!session.FileExists(targetPath)) // { // session.CreateDirectory(targetPath); // } // foreach (var studyFolder in studyFolders) // { // var targetFolder = Path.Combine(targetPath, studyFolder.Name); // if (!session.FileExists(targetFolder)) // { // session.CreateDirectory(targetFolder); // } // foreach (var file in studyFolder.GetFiles()) // { // if (file.Extension.Contains("dcm", StringComparison.OrdinalIgnoreCase)) // { // string remoteFilePath = // RemotePath.TranslateLocalPathToRemote(file.FullName, studyFolder.FullName, targetFolder); // var result = session.PutFiles(file.FullName, remoteFilePath, false); // if (!result.IsSuccess) // { // await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == subjectVisitId, // u => new SubjectVisit() { ForwardState = ForwardStateEnum.ForwardFailed }); // //---转发影像失败。 // return ResponseOutput.NotOk(_localizer["QCOperation_ForwardingFailed"] + result.Failures.ToString() + result.ToJson()); // } // } // } // } // } // await _subjectVisitRepository.UpdatePartialFromQueryAsync(t => t.Id == subjectVisitId, // u => new SubjectVisit() { ForwardState = ForwardStateEnum.Forwarded, ForwardUserId = _userInfo.Id, ForwardTime = DateTime.Now }); // isSuccess = true; // } // catch (Exception e) // { // await _subjectVisitRepository.UpdatePartialFromQueryAsync(t => t.Id == subjectVisitId, // u => new SubjectVisit() { ForwardState = ForwardStateEnum.ForwardFailed }); // // --转发影像失败 // return ResponseOutput.NotOk(_localizer["QCOperation_ForwardingFailed"] + e.Message); // } // } // await _subjectVisitRepository.SaveChangesAsync(); // //---转发影像失败。 // return isSuccess ? ResponseOutput.Ok() : ResponseOutput.NotOk(_localizer["QCOperation_ForwardingFailed"]); //} #endregion } }