2013 lines
		
	
	
		
			83 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			2013 lines
		
	
	
		
			83 KiB
		
	
	
	
		
			C#
		
	
	
| using Hangfire;
 | ||
| using IRaCIS.Core.Application.MediatR.CommandAndQueries;
 | ||
| using IRaCIS.Core.Application.BackGroundJob;
 | ||
| using IRaCIS.Core.Application.Contracts;
 | ||
| using IRaCIS.Core.Application.Contracts.DTO;
 | ||
| using IRaCIS.Core.Application.Filter;
 | ||
| using IRaCIS.Core.Domain.Share;
 | ||
| using MediatR;
 | ||
| using Microsoft.AspNetCore.Mvc;
 | ||
| using System.Net.Http.Headers;
 | ||
| using Microsoft.AspNetCore.Http;
 | ||
| using MiniExcelLibs;
 | ||
| using ExcelDataReader;
 | ||
| using System.Text;
 | ||
| using System.Data;
 | ||
| using Microsoft.AspNetCore.Authorization;
 | ||
| using WinSCP;
 | ||
| using Magicodes.ExporterAndImporter.Excel;
 | ||
| using Newtonsoft.Json;
 | ||
| using IRaCIS.Core.Infrastructure;
 | ||
| using IRaCIS.Core.Application.Service.Inspection.DTO;
 | ||
| using Nito.AsyncEx;
 | ||
| using System.ComponentModel.DataAnnotations;
 | ||
| using IRaCIS.Core.Application.Auth;
 | ||
| using IRaCIS.Core.Application.Helper;
 | ||
| 
 | ||
| namespace IRaCIS.Core.Application.Image.QA
 | ||
| {
 | ||
|     [ApiExplorerSettings(GroupName = "Image")]
 | ||
|     public class QCOperationService : BaseService, IQCOperationService
 | ||
|     {
 | ||
| 
 | ||
|         private readonly DicomFileStoreHelper _dicomFileStoreHelper;
 | ||
|         private readonly IRepository<SubjectVisit> _subjectVisitRepository;
 | ||
|         private readonly IRepository<QCChallenge> _qcChallengeRepository;
 | ||
|         private readonly IRepository<DicomStudy> _dicomStudyRepository;
 | ||
|         private readonly IRepository<DicomSeries> _dicomSeriesrepository;
 | ||
|         private readonly IRepository<Subject> _subjectRepository;
 | ||
|         private readonly IRepository<QCChallengeDialog> _qCChallengeDialogrepository;
 | ||
|         private readonly IRepository<CheckChallengeDialog> _checkChallengeDialogrepository;
 | ||
|         private readonly IRepository<Trial> _trialRepository;
 | ||
|         private readonly IRepository<ConsistencyCheckFile> _consistencyCheckFileRepository;
 | ||
|         private readonly IMediator _mediator;
 | ||
| 
 | ||
|         private readonly AsyncLock _mutex = new AsyncLock();
 | ||
| 
 | ||
|         public QCOperationService(DicomFileStoreHelper dicomFileStoreHelper, IRepository<SubjectVisit> subjectVisitRepository,
 | ||
|             IRepository<QCChallenge> qcChallengeRepository,
 | ||
|             IRepository<Trial> trialRepository,
 | ||
|              IRepository<ConsistencyCheckFile> consistencyCheckFileRepository,
 | ||
|             IMediator mediator,
 | ||
|             IRepository<DicomStudy> dicomStudyRepository,
 | ||
|             IRepository<DicomSeries> dicomSeriesrepository,
 | ||
|             IRepository<Subject> subjectRepository,
 | ||
|              IRepository<QCChallengeDialog> qCChallengeDialogrepository,
 | ||
|               IRepository<CheckChallengeDialog> checkChallengeDialogrepository
 | ||
|             )
 | ||
|         {
 | ||
|             _dicomFileStoreHelper = dicomFileStoreHelper;
 | ||
|             _subjectVisitRepository = subjectVisitRepository;
 | ||
|             _qcChallengeRepository = qcChallengeRepository;
 | ||
|             _dicomStudyRepository = dicomStudyRepository;
 | ||
|             this._dicomSeriesrepository = dicomSeriesrepository;
 | ||
|             this._subjectRepository = subjectRepository;
 | ||
|             this._qCChallengeDialogrepository = qCChallengeDialogrepository;
 | ||
|             this._checkChallengeDialogrepository = checkChallengeDialogrepository;
 | ||
|             _mediator = mediator;
 | ||
|             _trialRepository = trialRepository;
 | ||
|             this._consistencyCheckFileRepository = consistencyCheckFileRepository;
 | ||
|         }
 | ||
| 
 | ||
|         #region QC质疑 以及回复 关闭
 | ||
| 
 | ||
|         [HttpGet("{trialId:guid}/{subjectVisitId:guid}/{currentQCType:int}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.IQC)]
 | ||
|         public async Task<IResponseOutput> VerifyQCCanAddChallenge(Guid subjectVisitId, [FromRoute] CurrentQC currentQCType)
 | ||
|         {
 | ||
|             await VerifyIsCanQCAsync(null, subjectVisitId);
 | ||
| 
 | ||
|             if (!await _repository.AnyAsync<TrialQCQuestionAnswer>(t => t.SubjectVisitId == subjectVisitId && t.CurrentQCEnum == currentQCType))
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("请先核查图像,并保存审核问题,然后再发质疑。");
 | ||
|             }
 | ||
|             return ResponseOutput.Ok();
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         ///  添加和更新质疑
 | ||
|         /// </summary>
 | ||
|         /// <param name="qaQuestionCommand"></param>
 | ||
|         /// <param name="trialId"></param>
 | ||
|         /// <param name="trialQCProcess"></param>
 | ||
|         /// <param name="currentQCType"></param>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPost("{trialId:guid}/{trialQCProcess:int}/{currentQCType:int}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.IQC)]
 | ||
|         public async Task<IResponseOutput> 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))
 | ||
|                 {
 | ||
|                     throw new BusinessValidationFailedException("当前访视未关闭的质疑已设置了同意CRC重传影像。请在CRC完成影像重传后,先关闭原质疑,再添加新的质疑。");
 | ||
| 
 | ||
|                 }
 | ||
| 
 | ||
|                 var trialConfig = (await _trialRepository.Where(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification }).FirstOrDefaultAsync()).IfNullThrowException();
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|                 using (await _mutex.LockAsync())
 | ||
|                 {
 | ||
|                     //获取编号
 | ||
|                     var code = _qcChallengeRepository.Where(t => t.TrialId == trialId).Select(t => t.Code).DefaultIfEmpty().Max();
 | ||
| 
 | ||
|                     var qcChallenge = _mapper.Map<QCChallenge>(qaQuestionCommand);
 | ||
| 
 | ||
|                     qcChallenge.QCProcessEnum = trialConfig.QCProcessEnum;
 | ||
|                     qcChallenge.CurrentQCEnum = currentQCType;
 | ||
|                     qcChallenge.TrialId = trialId;
 | ||
|                     qcChallenge.Code = code + 1;
 | ||
|                     qcChallenge.UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt;
 | ||
| 
 | ||
|                     qcChallenge.ChallengeCode = "Q" + qcChallenge.Code.ToString("D5");
 | ||
| 
 | ||
|                     qcChallenge = await _qcChallengeRepository.AddAsync(qcChallenge, true);
 | ||
| 
 | ||
|                     return ResponseOutput.Ok(qcChallenge.Id);
 | ||
| 
 | ||
|                 }
 | ||
| 
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
| 
 | ||
|                 await _repository.UpdateFromDTOAsync<QCChallenge, QCChallengeCommand>(qaQuestionCommand, true);
 | ||
| 
 | ||
|                 return ResponseOutput.Ok();
 | ||
| 
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 关闭质疑,什么情况下允许?
 | ||
|         /// </summary>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPut]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [UnitOfWork]
 | ||
|         [Authorize(Policy = IRaCISPolicy.IQC)]
 | ||
|         public async Task<IResponseOutput> 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)
 | ||
|             {
 | ||
|                 throw new BusinessValidationFailedException("CRC已申请重传或者QC同意重传,不允许关闭该质疑。请在QC拒绝重传申请或者CRC设置重传影像后,再关闭质疑。");
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
|             #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 = "关闭原因: " + input.closeReason
 | ||
|             });
 | ||
| 
 | ||
|             var success = await _qcChallengeRepository.SaveChangesAsync();
 | ||
| 
 | ||
|             #endregion
 | ||
| 
 | ||
| 
 | ||
|             return ResponseOutput.Result(success);
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 删除QC质疑记录
 | ||
|         /// </summary>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpDelete("{trialId:guid}/{subjectVisitId:guid}/{qcChallengeId:guid}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.IQC)]
 | ||
|         public async Task<IResponseOutput> DeleteQCChallenge(Guid qcChallengeId)
 | ||
|         {
 | ||
| 
 | ||
|             if (await _repository.AnyAsync<QCChallengeDialog>(t => t.QCChallengeId == qcChallengeId))
 | ||
|             {
 | ||
|                 ResponseOutput.NotOk("当前QC质疑已经回复。");
 | ||
|             }
 | ||
| 
 | ||
|             var qaRecord = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == qcChallengeId)).IfNullThrowException();
 | ||
| 
 | ||
| 
 | ||
|             await _qcChallengeRepository.DeleteAsync(qaRecord);
 | ||
| 
 | ||
| 
 | ||
|             var success1 = await _repository.SaveChangesAsync();
 | ||
| 
 | ||
|             return ResponseOutput.Result(success1 /*|| success2 || success3*/);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 针对 某条QC质疑 添加回复  
 | ||
|         /// </summary>
 | ||
|         /// <param name="qaDialogCommand"></param>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPost("{trialId:guid}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.CRC_IQC)]
 | ||
|         public async Task<IResponseOutput> AddQCChallengeReply(QADialogCommand qaDialogCommand)
 | ||
|         {
 | ||
|             var qaReply = _mapper.Map<QCChallengeDialog>(qaDialogCommand);
 | ||
| 
 | ||
|             await _qCChallengeDialogrepository.AddAsync(qaReply);
 | ||
| 
 | ||
|             qaReply.UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt;
 | ||
| 
 | ||
|             var dbQCChallenge = (await _repository.FirstOrDefaultAsync<QCChallenge>(t => t.Id == qaDialogCommand.QCChallengeId)).IfNullThrowException();
 | ||
| 
 | ||
|             dbQCChallenge.LatestMsgTime = DateTime.Now;
 | ||
| 
 | ||
| 
 | ||
|             dbQCChallenge.LatestReplyUserId = _userInfo.Id;
 | ||
| 
 | ||
| 
 | ||
|             var success = await _repository.SaveChangesAsync();
 | ||
| 
 | ||
| 
 | ||
|             return ResponseOutput.Result(success, qaReply);
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
| 
 | ||
|         #region 一致性核查
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 一致性核查 质疑的添加/回复
 | ||
|         /// </summary>
 | ||
|         /// <param name="checkDialogCommand"></param>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPost("{trialId:guid}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.PM_APM_CRC)]
 | ||
|         public async Task<IResponseOutput> AddCheckChallengeReply(CheckChallengeDialogCommand checkDialogCommand)
 | ||
|         {
 | ||
| 
 | ||
| 
 | ||
|             //修改一致性核查 质疑状态
 | ||
|             var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == checkDialogCommand.SubjectVisitId)).IfNullThrowException();
 | ||
| 
 | ||
|             var qaReply = _mapper.Map<CheckChallengeDialog>(checkDialogCommand);
 | ||
| 
 | ||
|             qaReply.UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt;
 | ||
| 
 | ||
|             await _repository.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("您没有权限回复一致性核查对话。");
 | ||
|             }
 | ||
| 
 | ||
|             var success = await _repository.SaveChangesAsync();
 | ||
| 
 | ||
|             return ResponseOutput.Result(success, qaReply.Id);
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 关闭  一致性核查质疑
 | ||
|         /// </summary>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPut("{trialId:guid}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.PM_APM)]
 | ||
|         public async Task<IResponseOutput> CloseCheckChallenge(CloseCheckChallengeDto input)
 | ||
|         {
 | ||
| 
 | ||
|             var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == input.subjectVisitId)).IfNullThrowException();
 | ||
| 
 | ||
|             if (sv.RequestBackState == RequestBackStateEnum.CRC_RequestBack)
 | ||
|             {
 | ||
|                 ResponseOutput.NotOk("当前访视处于申请回退状态, 不允许关闭质疑。");
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
|             sv.CloseTheReason = input.CloseCheckChallenge;
 | ||
| 
 | ||
|             sv.CheckChallengeState = CheckChanllengeTypeEnum.Closed;
 | ||
|             await _checkChallengeDialogrepository.AddAsync(new CheckChallengeDialog()
 | ||
|             {
 | ||
|                 SubjectVisitId = input.subjectVisitId,
 | ||
|                 TalkContent = "原因:" + input.CloseCheckChallenge,
 | ||
|                 UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt,
 | ||
| 
 | ||
|             });
 | ||
|             await _repository.SaveChangesAsync();
 | ||
| 
 | ||
|             return ResponseOutput.Ok(sv);
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 手动设置一致性核查通过
 | ||
|         /// </summary>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPut("{trialId:guid}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.PM_APM)]
 | ||
|         public async Task<IResponseOutput> 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("当前访视处于回退状态,不允许设置一致性核查通过");
 | ||
|             }
 | ||
| 
 | ||
|             if (sv.CheckChallengeState != CheckChanllengeTypeEnum.Closed && sv.AuditState == AuditStateEnum.QCPassed)
 | ||
|             {
 | ||
|                 ResponseOutput.NotOk("请先关闭一致性核查质疑后,再设置一致性核查通过。");
 | ||
|             }
 | ||
| 
 | ||
|             sv.CheckState = CheckStateEnum.CVPassed;
 | ||
| 
 | ||
|             sv.ForwardState = ForwardStateEnum.ToForward;
 | ||
|             sv.ManualPassReason = data.ManualPassReason;
 | ||
|             sv.CheckPassedTime = DateTime.Now;
 | ||
| 
 | ||
|             await _subjectVisitRepository.SaveChangesAsync();
 | ||
| 
 | ||
| 
 | ||
|             return ResponseOutput.Result(true);
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// CRC 请求回退
 | ||
|         /// </summary>
 | ||
|         /// <param name="subjectVisitId"></param>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPut("{trialId:guid}/{subjectVisitId:guid}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.CRC)]
 | ||
|         public async Task<IResponseOutput> CRCRequstCheckBack(Guid subjectVisitId)
 | ||
|         {
 | ||
|             var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException();
 | ||
| 
 | ||
| 
 | ||
|             if (sv.CheckState == CheckStateEnum.CVPassed)
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("当前访视已通过一致性核查,不允许申请回退");
 | ||
|             }
 | ||
| 
 | ||
|             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,
 | ||
|                 TalkContent = "CRC申请回退"
 | ||
|             });
 | ||
| 
 | ||
| 
 | ||
|             if (sv.RequestBackState == RequestBackStateEnum.NotRequest || sv.RequestBackState == RequestBackStateEnum.PM_NotAgree)
 | ||
|             {
 | ||
|                 sv.RequestBackState = RequestBackStateEnum.CRC_RequestBack;
 | ||
| 
 | ||
|                 await _subjectVisitRepository.SaveChangesAsync();
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("其他CRC已申请处理,请刷新页面");
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|             return ResponseOutput.Ok();
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
|         [HttpPut("{trialId:guid}/{subjectVisitId:guid}")]
 | ||
|         [Authorize(Policy = IRaCISPolicy.PM_APM)]
 | ||
|         public async Task<IResponseOutput> 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;
 | ||
| 
 | ||
|                 await _repository.AddAsync(new CheckChallengeDialog() { SubjectVisitId = subjectVisitId, TalkContent = "PM/APM拒绝一致性核查回退", UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt });
 | ||
| 
 | ||
|                 await _subjectVisitRepository.SaveChangesAsync();
 | ||
| 
 | ||
|                 return ResponseOutput.Ok();
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("您不具备操作权限。");
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 一致性核查 回退  对话记录不清除  只允许PM回退  [签名  不需要对]
 | ||
|         /// </summary>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPut("{trialId:guid}/{subjectVisitId:guid}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.PM_APM)]
 | ||
|         [UnitOfWork]
 | ||
|         public async Task<IResponseOutput> 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("当前访视还未进行核查或者核查已通过,不允许设置回退。");
 | ||
|             }
 | ||
| 
 | ||
|             //sv.CheckChallengeState = (int)CheckChanllengeTypeEnum.None;
 | ||
|             //sv.CheckState = CheckStateEnum.None;
 | ||
|             //sv.ChallengeState = (int)ChallengeStateEnum.No;
 | ||
| 
 | ||
|             sv.AuditState = AuditStateEnum.None;
 | ||
|             sv.SubmitState = SubmitStateEnum.ToSubmit;
 | ||
| 
 | ||
|             //回退后,回退状态恢复
 | ||
|             sv.RequestBackState = RequestBackStateEnum.NotRequest;
 | ||
|             sv.IsCheckBack = true;
 | ||
|             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);
 | ||
|             await _repository.AddAsync(new CheckChallengeDialog() { SubjectVisitId = subjectVisitId, TalkContent = "PM/APM同意一致性核查回退。", UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt });
 | ||
| 
 | ||
|             await _repository.BatchDeleteAsync<TrialQCQuestionAnswer>(t => t.SubjectVisitId == subjectVisitId);
 | ||
| 
 | ||
|             await _repository.BatchDeleteAsync<DicomInstance>(t => t.DicomSerie.IsDeleted);
 | ||
|             await _repository.BatchDeleteAsync<DicomSeries>(t => t.IsDeleted);
 | ||
| 
 | ||
|             var success = await _subjectVisitRepository.SaveChangesAsync();
 | ||
| 
 | ||
| 
 | ||
|             return ResponseOutput.Result(success);
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 一致性核查 excel上传 支持三种格式
 | ||
|         /// </summary>
 | ||
|         /// <param name="file"></param>
 | ||
|         /// <param name="trialId"></param>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPost("{trialId:guid}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.PM_APM)]
 | ||
|         public async Task<IResponseOutput> UploadVisitCheckExcel(IFormFile file, Guid trialId)
 | ||
|         {
 | ||
|             //if (_userInfo.UserTypeEnumInt != (int)UserTypeEnum.ProjectManager && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.APM)
 | ||
|             //{
 | ||
|             //    return ResponseOutput.NotOk("只有PM/APM具有操作权限!");
 | ||
|             //}
 | ||
| 
 | ||
|             var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
 | ||
| 
 | ||
|             var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.ToString().Trim('"').ToLower();
 | ||
|             if (!fileName.EndsWith(".xlsx") && !fileName.EndsWith(".csv") && !fileName.EndsWith(".xls"))
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("支持.xlsx、.xls、.csv格式的文件上传。");
 | ||
|             }
 | ||
| 
 | ||
|             //上传根路径
 | ||
|             string uploadFolderPath = Path.Combine(rootPath,StaticData.TrialDataFolder, trialId.ToString(), "CheckExcel");
 | ||
| 
 | ||
|             if (!Directory.Exists(uploadFolderPath))
 | ||
|             {
 | ||
|                 Directory.CreateDirectory(uploadFolderPath);
 | ||
|             }
 | ||
| 
 | ||
|             //存放核对表
 | ||
|             var (trustedFileNameForFileStorage, realFileName) = FileStoreHelper.GetStoreFileName(fileName);
 | ||
| 
 | ||
|             var filePath = Path.Combine(uploadFolderPath,  trustedFileNameForFileStorage);
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|             using (FileStream fs = System.IO.File.Create(filePath))
 | ||
|             {
 | ||
|                 await file.CopyToAsync(fs);
 | ||
|                 await fs.FlushAsync();
 | ||
|             }
 | ||
| 
 | ||
|             //获取Excel表内容
 | ||
|             var config = new MiniExcelLibs.Csv.CsvConfiguration()
 | ||
|             {
 | ||
|                 StreamReaderFunc = (stream) => new StreamReader(stream, Encoding.GetEncoding("gb2312"))
 | ||
|             };
 | ||
| 
 | ||
|             var etcCheckList = new List<CheckViewModel>();
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|             #region MiniExcel  需要自己验证数据格式规范
 | ||
| 
 | ||
|             //if (fileName.EndsWith(".csv"))
 | ||
|             //{
 | ||
|             //    //因为csv 需要加配置文件 不然都是null
 | ||
|             //    etcCheckList = MiniExcel.Query<CheckViewModel>(filePath, null, configuration: config).ToList();
 | ||
|             //}
 | ||
|             //else if (fileName.EndsWith(".xlsx"))
 | ||
|             //{
 | ||
|             //    
 | ||
| 
 | ||
| 
 | ||
|             //    etcCheckList = MiniExcel.Query<CheckViewModel>(filePath).ToList();
 | ||
|             //}
 | ||
| 
 | ||
|             #endregion
 | ||
| 
 | ||
|             //Magicodes 支持自定义特性验证
 | ||
|             if (fileName.EndsWith(".xlsx"))
 | ||
|             {
 | ||
|                 var Importer = new ExcelImporter();
 | ||
| 
 | ||
|                 var import = await Importer.Import<CheckViewModel>(File.OpenRead(filePath));
 | ||
| 
 | ||
|                 if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
 | ||
| 
 | ||
|                 if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
 | ||
| 
 | ||
|                 if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
 | ||
| 
 | ||
|                 etcCheckList = import.Data.ToList();
 | ||
|             }
 | ||
|             else if (fileName.EndsWith(".csv"))
 | ||
|             {
 | ||
|                 #region   临时方案   MiniExcel读取  然后保存为xlsx  再用 Magicodes验证数据
 | ||
| 
 | ||
|                 //因为csv 需要加配置文件 不然都是null
 | ||
|                 etcCheckList = MiniExcel.Query<CheckViewModel>(filePath, null, configuration: config).ToList();
 | ||
| 
 | ||
|                 var csVToXlsxPath = Path.Combine(uploadFolderPath, DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss") + Path.GetFileNameWithoutExtension(fileName) + ".xlsx");
 | ||
| 
 | ||
|                 await MiniExcel.SaveAsAsync(csVToXlsxPath, etcCheckList, excelType: ExcelType.XLSX);
 | ||
| 
 | ||
| 
 | ||
|                 var Importer = new ExcelImporter();
 | ||
| 
 | ||
|                 var import = await Importer.Import<CheckViewModel>(File.OpenRead(csVToXlsxPath));
 | ||
| 
 | ||
|                 if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
 | ||
| 
 | ||
|                 if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
 | ||
| 
 | ||
|                 if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
 | ||
| 
 | ||
|                 etcCheckList = import.Data.ToList();
 | ||
| 
 | ||
|                 #endregion
 | ||
| 
 | ||
| 
 | ||
|                 #region 导入组件有问题  excel编码格式
 | ||
|                 //var Importer = new CsvImporter();
 | ||
| 
 | ||
|                 //var import = await Importer.Import<CheckViewModel>(File.OpenRead(filePath));
 | ||
| 
 | ||
|                 //if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
 | ||
| 
 | ||
|                 //if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
 | ||
| 
 | ||
|                 //if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
 | ||
| 
 | ||
|                 //etcCheckList = import.Data.ToList();
 | ||
|                 #endregion
 | ||
| 
 | ||
|             }
 | ||
|             //ExcelReaderFactory  需要自己验证数据 并且从固定列取数据
 | ||
|             else
 | ||
|             {
 | ||
|                 //为了支持 xls  引入新的组件库
 | ||
|                 using (var stream = System.IO.File.Open(filePath, FileMode.Open, FileAccess.Read))
 | ||
|                 {
 | ||
|                     // Auto-detect format, supports:
 | ||
|                     //  - Binary Excel files (2.0-2003 format; *.xls)
 | ||
|                     //  - OpenXml Excel files (2007 format; *.xlsx, *.xlsb)
 | ||
|                     using (var reader = ExcelReaderFactory.CreateReader(stream))
 | ||
|                     {
 | ||
| 
 | ||
|                         // 2. Use the AsDataSet extension method
 | ||
|                         var dateset = reader.AsDataSet();
 | ||
| 
 | ||
|                         foreach (DataRow col in dateset.Tables[0].Rows)
 | ||
|                         {
 | ||
| 
 | ||
|                             etcCheckList.Add(new CheckViewModel()
 | ||
|                             {
 | ||
|                                 SiteCode = col[0].ToString(),
 | ||
|                                 SubjectCode = col[1].ToString(),
 | ||
|                                 VisitName = col[2].ToString(),
 | ||
|                                 StudyDate = col[3].ToString(),
 | ||
|                                 Modality = col[4].ToString(),
 | ||
|                             });
 | ||
|                         }
 | ||
| 
 | ||
|                         etcCheckList.Remove(etcCheckList[0]);
 | ||
| 
 | ||
|                         // The result of each spreadsheet is in result.Tables
 | ||
|                     }
 | ||
|                 }
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
|             //etcCheckList = etcCheckList.Where(t=>)
 | ||
| 
 | ||
|             if (etcCheckList == null || etcCheckList.Count == 0)
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("请保证上传数据符合模板文件中的样式,且存在有效数据。");
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 //处理Excel  有时只是清除某些行的数据  读取也会读到数据,只是数据是null   后面处理的时候转为字符串为报错
 | ||
|                 etcCheckList.ForEach(t =>
 | ||
|                 {
 | ||
|                     t.Modality = t.Modality ?? string.Empty;
 | ||
|                     t.SiteCode = t.SiteCode ?? string.Empty;
 | ||
|                     t.SubjectCode = t.SubjectCode ?? string.Empty;
 | ||
|                     t.VisitName = t.VisitName ?? string.Empty;
 | ||
|                     t.StudyDate = t.StudyDate ?? string.Empty;
 | ||
|                 });
 | ||
| 
 | ||
|                 var dt = DateTime.Now;
 | ||
| 
 | ||
|                 etcCheckList = etcCheckList.Where(t => !(t.Modality == string.Empty && t.SiteCode == string.Empty && t.SubjectCode == string.Empty && t.VisitName == string.Empty && t.StudyDate == string.Empty && DateTime.TryParse(t.StudyDate, out dt))).ToList();
 | ||
| 
 | ||
|                 if (etcCheckList.Count == 0)
 | ||
|                 {
 | ||
|                     return ResponseOutput.NotOk("请保证上传数据符合模板文件中的样式,且存在有效数据。");
 | ||
|                 }
 | ||
| 
 | ||
|             }
 | ||
| 
 | ||
|             await _consistencyCheckFileRepository.AddAsync(new ConsistencyCheckFile()
 | ||
|             {
 | ||
|                 TrialId = trialId,
 | ||
|                 CreateTime = DateTime.Now,
 | ||
|                 FileName = fileName,
 | ||
|                 FilePath = filePath,
 | ||
|                 RelativePaths = filePath.Replace(rootPath, ""),
 | ||
|                 CreateUserId = _userInfo.Id
 | ||
|             });
 | ||
| 
 | ||
|             //new()
 | ||
|             //{
 | ||
|             //    TrialId = trialId,
 | ||
|             //    CreateTime = DateTime.Now,
 | ||
|             //    FileName = fileName,
 | ||
|             //    FilePath = filePath,
 | ||
|             //    CreateUser =
 | ||
|             //    RelativePaths = filePath.Replace(rootPath, ""); 
 | ||
|             //};
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|             await _mediator.Send(new ConsistencyVerificationRequest() { ETCList = etcCheckList, TrialId = trialId });
 | ||
| 
 | ||
|             return ResponseOutput.Ok();
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
| 
 | ||
|         #region QC 核对问题  操作检查各种操作
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 添加或者更新 QC核对问题列表   两个人不能同时操作,就算意外进去了,提交数据,也不会覆盖前一个人数据, 后台已经做好判断
 | ||
|         /// </summary>
 | ||
|         [HttpPost("{trialId:guid}/{subjectVisitId:guid}/{trialQCProcess:int}/{currentQCType:int}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.IQC)]
 | ||
|         public async Task<IResponseOutput> 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<TrialQCQuestionAnswer>(t => t.SubjectVisitId == subjectVisitId && t.QCProcessEnum == trialQCProcess && t.CurrentQCEnum == currentQCType);
 | ||
| 
 | ||
|                 //var addlist = _mapper.Map<List<TrialQCQuestionAnswer>>(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 _repository.Where<TrialQCQuestionAnswer>(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 _repository.SaveChangesAsync());
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 var addlist = _mapper.Map<List<TrialQCQuestionAnswer>>(qcQuestionAnswerCommands);
 | ||
| 
 | ||
|                 addlist.ForEach(t => { t.TrialId = trialId; t.SubjectVisitId = subjectVisitId; t.CurrentQCEnum = currentQCType; t.QCProcessEnum = trialQCProcess; });
 | ||
| 
 | ||
|                 await _repository.AddRangeAsync(addlist);
 | ||
| 
 | ||
|                 return ResponseOutput.Result(await _repository.SaveChangesAsync());
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         ///  1、设置为不读片,2 设置为读片(取消 先前设置为不读片)  4 设置为删除(数据库记录软删除) 5 恢复为未删除
 | ||
|         /// </summary>
 | ||
|         /// <param name="subjectVisitId"></param>
 | ||
|         /// <param name="studyId"></param>
 | ||
|         /// <param name="seriesId"></param>
 | ||
|         /// <param name="state"></param>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{studyId:guid}/{seriesId:guid}/{state:int}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.IQC)]
 | ||
|         public async Task<IResponseOutput> SetSeriesState(Guid subjectVisitId, Guid studyId, Guid seriesId, int state)
 | ||
|         {
 | ||
| 
 | ||
|             await VerifyIsCanQCAsync(null, subjectVisitId);
 | ||
| 
 | ||
|             var series = (await _repository.Where<DicomSeries>(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 _repository.Where<DicomStudy>(t => t.Id == studyId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException();
 | ||
| 
 | ||
|                 var instanceIdList = await _repository.Where<DicomInstance>(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 _repository.Where<DicomStudy>(t => t.Id == studyId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException();
 | ||
| 
 | ||
| 
 | ||
|                 var instanceIdList = await _repository.Where<DicomInstance>(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 _repository.SaveChangesAsync());
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         ///type 1 :study 2: series 3:非dicom  QC修改检查部位和 拍片类型
 | ||
|         /// </summary>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPost("{trialId:guid}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.IQC)]
 | ||
|         public async Task<IResponseOutput> UpdateModality(UpdateModalityCommand updateModalityCommand)
 | ||
|         {
 | ||
| 
 | ||
|             await VerifyIsCanQCAsync(null, updateModalityCommand.SubjectVisitId);
 | ||
| 
 | ||
| 
 | ||
|             DateTime time = DateTime.Now.AddMilliseconds(500);
 | ||
|             if (updateModalityCommand.Type == 1)
 | ||
|             {
 | ||
| 
 | ||
|                 var studyId = updateModalityCommand.Id;
 | ||
|                 var study = (await _repository.FirstOrDefaultAsync<DicomStudy>(t => t.Id == studyId)).IfNullThrowException();
 | ||
| 
 | ||
| 
 | ||
|                 study.BodyPartForEdit = updateModalityCommand.BodyPart;
 | ||
|                 study.Modalities = updateModalityCommand.Modality;
 | ||
|                 await _repository.BatchUpdateAsync<DicomSeries>(t => t.StudyId == studyId, r => new DicomSeries() { BodyPartForEdit = updateModalityCommand.BodyPart, Modality = updateModalityCommand.Modality });
 | ||
|             }
 | ||
|             else if (updateModalityCommand.Type == 2)
 | ||
|             {
 | ||
| 
 | ||
|                 var seriesId = updateModalityCommand.Id;
 | ||
|                 var series = (await _repository.FirstOrDefaultAsync<DicomSeries>(t => t.Id == seriesId)).IfNullThrowException();
 | ||
|                 series.BodyPartForEdit = updateModalityCommand.BodyPart;
 | ||
| 
 | ||
|             }
 | ||
|             else if (updateModalityCommand.Type == 3)
 | ||
|             {
 | ||
| 
 | ||
|             }
 | ||
| 
 | ||
|             await _repository.SaveChangesAsync();
 | ||
|             return ResponseOutput.Ok();
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 验证是否质疑都关闭了 可以审核通过和不通过
 | ||
|         /// </summary>
 | ||
|         /// <param name="subjectVisitId"></param>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpGet("{trialId:guid}/{subjectVisitId:guid}")]
 | ||
|         public async Task<IResponseOutput> VerifyCanQCPassedOrFailed(Guid subjectVisitId)
 | ||
|         {
 | ||
|             await VerifyIsCanQCAsync(null, subjectVisitId);
 | ||
| 
 | ||
|             if (await _repository.AnyAsync<QCChallenge>(t => t.SubjectVisitId == subjectVisitId && t.IsClosed == false))
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("当前访视有质疑未关闭,不允许该操作");
 | ||
|             }
 | ||
|             return ResponseOutput.Ok();
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 删除检查列表  不能用delete方法  传递数组
 | ||
|         /// </summary>
 | ||
|         /// <param name="ids"></param>
 | ||
|         /// <param name="subjectVisitId"></param>
 | ||
|         /// <param name="trialId"></param>
 | ||
|         /// <returns></returns>SeriesCount
 | ||
|         [HttpPost, Route("{trialId:guid}/{subjectVisitId:guid}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [UnitOfWork]
 | ||
|         [Authorize(Policy = IRaCISPolicy.IQC)]
 | ||
|         public async Task<IResponseOutput> 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))))
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("CRC已经提交,不允许删除。");
 | ||
|             }
 | ||
|             var waitDeleteStudyList = await _dicomStudyRepository.Where(x => ids.Contains(x.Id)).ToListAsync();
 | ||
| 
 | ||
|             foreach (var study in waitDeleteStudyList)
 | ||
|             {
 | ||
| 
 | ||
|                 var id = study.Id;
 | ||
| 
 | ||
|                 await _dicomStudyRepository.DeleteAsync(study, true);
 | ||
|                 var succeess2 = await _repository.BatchDeleteAsync<DicomInstance>(t => t.StudyId == id);
 | ||
| 
 | ||
|                 var DicomSeriess = await _repository.GetQueryable<DicomSeries>().Where(t => t.StudyId == id).Select(x => new
 | ||
|                 {
 | ||
|                     x.StudyId,
 | ||
|                     x.SubjectId,
 | ||
|                     x.SiteId,
 | ||
|                     x.TrialId,
 | ||
|                     x.Id,
 | ||
|                     x.SubjectVisitId,
 | ||
|                     x.SeriesTime,
 | ||
|                     x.IsReading,
 | ||
|                     x.InstanceCount,
 | ||
|                     x.SeriesNumber,
 | ||
|                     StudyCode = x.DicomStudy.StudyCode,
 | ||
|                     Modalities = x.DicomStudy.Modalities,
 | ||
| 
 | ||
|                 }).ToListAsync();
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|                 var success3 = await _dicomSeriesrepository.DeleteFromQueryAsync(t => t.StudyId == id, true);
 | ||
|                 var success4 = await _repository.BatchDeleteAsync<StudyMonitor>(t => t.StudyId == id);
 | ||
| 
 | ||
|                 //删除 物理文件
 | ||
| 
 | ||
|                 var instanceIdList = await _repository.Where<DicomInstance>(t => t.StudyId == id)
 | ||
|                     .Select(t => new { InstanceId = t.Id, t.SeriesId, t.StudyId, t.SubjectId, t.SiteId }).ToListAsync();
 | ||
| 
 | ||
|                 instanceIdList.ForEach(t =>
 | ||
|                 {
 | ||
|                     var path = _dicomFileStoreHelper.GetInstanceFilePath(new DicomStudy() { Id = t.StudyId, SubjectId = t.SubjectId, TrialId = trialId, SiteId = t.SiteId, SubjectVisitId = subjectVisitId }, t.SeriesId, t.InstanceId.ToString());
 | ||
| 
 | ||
|                     if (System.IO.File.Exists(path))
 | ||
|                     {
 | ||
|                         File.Delete(path);
 | ||
|                     }
 | ||
| 
 | ||
|                 });
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|             var subvisit = await _repository.GetQueryable<SubjectVisit>().FirstOrDefaultAsync(x => x.Id == subjectVisitId);
 | ||
| 
 | ||
|             return ResponseOutput.Ok();
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
|         [HttpGet]
 | ||
|         public async Task<IResponseOutput> 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.Id)
 | ||
|             {
 | ||
|                 throw new BusinessValidationFailedException("您不是该质控任务当前领取人,没有操作权限!");
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         #region  临床数据签名  领取、 设置紧急、RequestToQC QC通过、不通过
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>替换当前领取人 </summary>
 | ||
|         [HttpPut("{trialId:guid}/{subjectVisitId:guid}")]
 | ||
|         [Authorize(Policy = IRaCISPolicy.IQC)]
 | ||
|         public async Task<IResponseOutput> 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("当前访视的影像质控任务已被原领取人释放。您可以通过“领取”获得", ApiResponseCodeEnum.NeedTips);
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
|             dbSubjectVisit.IsTake = true;
 | ||
| 
 | ||
|             dbSubjectVisit.CurrentActionUserId = _userInfo.Id;
 | ||
| 
 | ||
|             dbSubjectVisit.CurrentActionUserExpireTime = DateTime.Now.AddHours(1);
 | ||
| 
 | ||
|             var success = await _subjectVisitRepository.SaveChangesAsync();
 | ||
| 
 | ||
|             return ResponseOutput.Result(success);
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 手动领取 或者取消 QC任务
 | ||
|         /// </summary>
 | ||
|         /// <param name="trialId"></param>
 | ||
|         /// <param name="subjectVisitId"></param>
 | ||
|         /// <param name="obtaionOrCancel">true 获取  false是取消领取</param>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{obtaionOrCancel:bool}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.IQC)]
 | ||
|         public async Task<IResponseOutput> 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("当前访视已被领取,不允许领取");
 | ||
| 
 | ||
|                     return ResponseOutput.NotOk("当前访视的影像质控任务已被其他QC领取,不允许领取", ApiResponseCodeEnum.NeedTips);
 | ||
| 
 | ||
|                 }
 | ||
| 
 | ||
|                 if (await _subjectVisitRepository.AnyAsync(t => t.Trial.QCQuestionConfirmedUserId == null && t.Id == subjectVisitId))
 | ||
|                 {
 | ||
|                     return ResponseOutput.NotOk("请先配置影像质控审核问题,再领取影像质控任务", 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)
 | ||
|                 {
 | ||
|                     return ResponseOutput.NotOk("项目配置为不审,没有领取QC Task");
 | ||
|                 }
 | ||
|                 else if (trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit)
 | ||
|                 {
 | ||
|                     if (dbSubjectVisit.PreliminaryAuditUserId == _userInfo.Id)
 | ||
|                     {
 | ||
|                         return ResponseOutput.NotOk("初审已通过,不能继续领取");
 | ||
|                     }
 | ||
| 
 | ||
|                     if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.ToAudit)
 | ||
|                     {
 | ||
|                         dbSubjectVisit.AuditState = AuditStateEnum.InPrimaryQC;
 | ||
|                     }
 | ||
|                     if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC)
 | ||
|                     {
 | ||
|                         //单审QC 释放后 也是可以领取的
 | ||
|                     }
 | ||
|                     else
 | ||
|                     {
 | ||
|                         return ResponseOutput.NotOk("项目配置为单审,不满足SubmmitState:已提交 或者 AuditState:待审核/审核中, 不允许领取,请刷新界面");
 | ||
|                     }
 | ||
|                 }
 | ||
|                 else if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit)
 | ||
|                 {
 | ||
|                     if (dbSubjectVisit.PreliminaryAuditUserId == _userInfo.Id)
 | ||
|                     {
 | ||
|                         return ResponseOutput.NotOk("复审不能和初审是同一个人");
 | ||
|                     }
 | ||
| 
 | ||
|                     //提交 并且初审通过  那么领取后进入 复审中
 | ||
|                     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
 | ||
|                     {
 | ||
|                         return ResponseOutput.NotOk("项目配置为复审,不满足提交状态:已提交 或者 审核状态:待审核/QC中, 不允许领取,请刷新界面");
 | ||
|                     }
 | ||
|                 }
 | ||
| 
 | ||
|                 #endregion
 | ||
| 
 | ||
|                 dbSubjectVisit.IsTake = true;
 | ||
| 
 | ||
|                 dbSubjectVisit.CurrentActionUserId = _userInfo.Id;
 | ||
| 
 | ||
|                 dbSubjectVisit.CurrentActionUserExpireTime = DateTime.Now.AddHours(1);
 | ||
| 
 | ||
| 
 | ||
|                 //启动定时任务 1h后处理
 | ||
|                 //BackgroundJob.Schedule<IObtainTaskAutoCancelJob>(t => t.CancelQCObtaion(subjectVisitId, DateTime.Now), TimeSpan.FromHours(1));
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 if (dbSubjectVisit!.CurrentActionUserId != _userInfo.Id)
 | ||
|                 {
 | ||
|                     return ResponseOutput.NotOk("您不是该质控任务当前领取人,没有操作权限!", ApiResponseCodeEnum.NeedTips);
 | ||
|                 }
 | ||
| 
 | ||
| 
 | ||
|                 if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit)
 | ||
|                 {
 | ||
|                     return ResponseOutput.NotOk("项目配置影像质控为不审,不需要取消任务功能");
 | ||
|                 }
 | ||
|                 else if (trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit)
 | ||
|                 {
 | ||
| 
 | ||
|                     if (dbSubjectVisit.SubmitState == SubmitStateEnum.Submitted && dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC)
 | ||
|                     {
 | ||
|                         dbSubjectVisit.AuditState = AuditStateEnum.ToAudit;
 | ||
|                     }
 | ||
| 
 | ||
|                     else
 | ||
|                     {
 | ||
|                         return ResponseOutput.NotOk("当前访视影像质控任务没有当前领取人,不能释放。");
 | ||
|                     }
 | ||
|                 }
 | ||
|                 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("当前访视影像质控任务没有当前领取人, 不能释放");
 | ||
|                     }
 | ||
|                 }
 | ||
| 
 | ||
|                 dbSubjectVisit.IsTake = false;
 | ||
| 
 | ||
|                 dbSubjectVisit.CurrentActionUserId = null;
 | ||
| 
 | ||
|                 dbSubjectVisit.CurrentActionUserExpireTime = null;
 | ||
| 
 | ||
|             }
 | ||
|             var success = await _subjectVisitRepository.SaveChangesAsync();
 | ||
|             return ResponseOutput.Result(success);
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|         [HttpPost]
 | ||
|         public async Task<IResponseOutput> VerifyCRCRequestToQC(CRCRequestToQCCommand cRCRequestToQCCommand)
 | ||
|         {
 | ||
|             var dbSubjectVisitList = await _subjectVisitRepository.Where(t => cRCRequestToQCCommand.SubjectVisitIds.Contains(t.Id), true).ToListAsync();
 | ||
| 
 | ||
|             //普通提交
 | ||
|             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<string>();
 | ||
| 
 | ||
|                 if (sv.PDState == PDStateEnum.PDProgress)
 | ||
|                 {
 | ||
| 
 | ||
|                     if (nameList.Count() > 0)
 | ||
|                     {
 | ||
|                         return ResponseOutput.NotOk($"当前访视要求进行疾病进展确认。请在提交当前访视前,先处理未提交的前序访视:{string.Join('、', nameList)}。", 1, ApiResponseCodeEnum.NeedTips);
 | ||
|                     }
 | ||
|                 }
 | ||
|                 else
 | ||
|                 {
 | ||
|                     if (nameList.Count() > 0)
 | ||
|                     {
 | ||
| 
 | ||
|                         return ResponseOutput.NotOk($"在提交当前访视后,请尽快处理尚未提交的前序访视:{string.Join('、', nameList)}。", 0, ApiResponseCodeEnum.NeedTips);
 | ||
|                     }
 | ||
|                 }
 | ||
|             }
 | ||
| 
 | ||
|             return ResponseOutput.Ok();
 | ||
| 
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// CRC RequestToQC  批量提交    [需要签名 不需要对]
 | ||
|         /// </summary>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPost]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.CRC)]
 | ||
|         public async Task<IResponseOutput> CRCRequestToQC(CRCRequestToQCCommand cRCRequestToQCCommand)
 | ||
|         {
 | ||
|             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 dbSubjectVisitList = await _subjectVisitRepository.Where(t => cRCRequestToQCCommand.SubjectVisitIds.Contains(t.Id), true).Include(t => t.Subject).ToListAsync();
 | ||
| 
 | ||
| 
 | ||
|             if (dbSubjectVisitList.Any(t => t.SubmitState == SubmitStateEnum.None))
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("有访视未上传任何Dicom/非Dicom影像数据,不允许提交");
 | ||
|             }
 | ||
| 
 | ||
|             //单个提交提示信息
 | ||
|             if (dbSubjectVisitList.Count() == 1 && dbSubjectVisitList.First().SubmitState == SubmitStateEnum.Submitted)
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("当前访视的影像数据,已经由其他CRC提交。", 3, ApiResponseCodeEnum.NeedTips);
 | ||
|             }
 | ||
|             else if(dbSubjectVisitList.Any(t => t.SubmitState == SubmitStateEnum.Submitted))
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("当前批量提交访视的影像数据,其中部分已由其他CRC提交。", 3, ApiResponseCodeEnum.NeedTips);
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|             // 别人未提交的
 | ||
|             foreach (var dbSubjectVisit in dbSubjectVisitList)
 | ||
|             {
 | ||
| 
 | ||
|                 //基线不验证
 | ||
|                 if (trialConfig.IsHaveFirstGiveMedicineDate && !dbSubjectVisit.IsBaseLine && dbSubjectVisit.Subject.FirstGiveMedicineTime == null)
 | ||
|                 {
 | ||
|                     return ResponseOutput.NotOk("项目配置了需要填写访视基准日期。但是受试者没有填写访视基准日期,不允许提交");
 | ||
|                 }
 | ||
| 
 | ||
|                 //基线  且配置了临床数据  
 | ||
|                 if (trialConfig.ClinicalInformationTransmissionEnum != 0 && dbSubjectVisit.IsBaseLine/*&&dbSubjectVisit.ClinicalDataSignUserId==null*/)
 | ||
|                 {
 | ||
|                     //已确认临床数据完整性
 | ||
|                     dbSubjectVisit.IsConfirmedClinicalData = true;
 | ||
| 
 | ||
|                     //var signSuccess = await _repository.UpdateFromQueryAsync<TrialSign>(t => t.Id == cRCRequestToQCCommand.SignId, u => new TrialSign() { IsCompleted = true });
 | ||
| 
 | ||
| 
 | ||
|                     ////现在修改为  提交时 设置签名信息
 | ||
|                     //dbSubjectVisit.ClinicalDataSignUserId = _userInfo.Id;
 | ||
|                     //dbSubjectVisit.ClinicalDataSignTime = DateTime.Now;
 | ||
| 
 | ||
|                     //那么没有录入 不允许提交
 | ||
|                     //if (!await _repository.AnyAsync<SubjectVisit>(t => t.PreviousHistoryList.Any() || t.PreviousOtherList.Any() || t.PreviousSurgeryList.Any()))
 | ||
|                     //{
 | ||
|                     //    return ResponseOutput.NotOk("没有临床数据,不允许提交");
 | ||
|                     //}
 | ||
| 
 | ||
|                     //return ResponseOutput.NotOk("没有签名临床数据,不允许提交");
 | ||
|                 }
 | ||
| 
 | ||
| 
 | ||
|                 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))
 | ||
|                 {
 | ||
|                     dbSubjectVisit.IsUrgent = true;
 | ||
| 
 | ||
| 
 | ||
|                     //PD确认的紧急会把前面所有未QC完成的访视均标记为紧急
 | ||
| 
 | ||
|                     var previosSVlist = await _subjectVisitRepository.Where(t => t.SubjectId == dbSubjectVisit.SubjectId && t.VisitNum < dbSubjectVisit.VisitNum && t.IsUrgent == false && t.SubmitState == SubmitStateEnum.Submitted, true).ToListAsync();
 | ||
| 
 | ||
|                     previosSVlist.ForEach(t =>
 | ||
|                     {
 | ||
|                         t.IsUrgent = true;
 | ||
|                     });
 | ||
| 
 | ||
|                 }
 | ||
| 
 | ||
| 
 | ||
|                 if (dbSubjectVisit.SubmitState == SubmitStateEnum.ToSubmit)
 | ||
|                 {
 | ||
|                     dbSubjectVisit.SubmitState = SubmitStateEnum.Submitted;
 | ||
|                     dbSubjectVisit.SubmitTime = DateTime.Now;
 | ||
| 
 | ||
|                 }
 | ||
|                 //不审 直接QC通过  可能一致性核查  也可能不一致性核查
 | ||
|                 if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit)
 | ||
|                 {
 | ||
|                     dbSubjectVisit.AuditState = AuditStateEnum.QCPassed;
 | ||
| 
 | ||
|                     // 不一致性核查 就CVPassed ToForward  否则就是待核查
 | ||
|                     dbSubjectVisit.CheckState = trialConfig.IsImageConsistencyVerification ? CheckStateEnum.ToCheck : CheckStateEnum.CVPassed;
 | ||
| 
 | ||
|                     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;
 | ||
|                 }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|             var success = await _repository.SaveChangesAsync();
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|             return ResponseOutput.Ok(success);
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 设置QC 通过或者不通过   7:QC failed  8:QC passed   [签名  不需要对]
 | ||
|         /// </summary>
 | ||
|         /// <param name="trialId"></param>
 | ||
|         /// <param name="subjectVisitId"></param>
 | ||
|         /// <param name="auditState"></param>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPost("{trialId:guid}/{subjectVisitId:guid}/{auditState:int}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.IQC)]
 | ||
|         public async Task<IResponseOutput> QCPassedOrFailed(Guid trialId, Guid subjectVisitId, [FromRoute] AuditStateEnum auditState)
 | ||
|         {
 | ||
| 
 | ||
|             if (!await _repository.AnyAsync<TrialUser>(t => t.TrialId == trialId && t.UserId == _userInfo.Id))
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("您已经被移出项目,没有操作权限。");
 | ||
|             }
 | ||
| 
 | ||
|             //判断质疑是否都关闭了
 | ||
|             if (await _repository.AnyAsync<QCChallenge>(t => t.SubjectVisitId == subjectVisitId && t.IsClosed == false))
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("当前访视有影像质控质疑未关闭,不能进行此操作。");
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|             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);
 | ||
| 
 | ||
|             //有人QC Passed
 | ||
|             if (auditState == AuditStateEnum.QCPassed)
 | ||
|             {
 | ||
|                 //判断 QC流程  不审  单审  双审
 | ||
| 
 | ||
|                 if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit)
 | ||
|                 {
 | ||
|                     return ResponseOutput.NotOk("项目配置影像质控为不审,不需要设置为影像质控通过。");
 | ||
| 
 | ||
|                 }
 | ||
|                 else if (trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit)
 | ||
|                 {
 | ||
|                     if (dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC)
 | ||
|                     {
 | ||
|                         if (!await _repository.AnyAsync<TrialQCQuestionAnswer>(t => t.SubjectVisitId == subjectVisitId && t.CurrentQCEnum == CurrentQC.First))
 | ||
|                         {
 | ||
|                             return ResponseOutput.NotOk("影像质控审核问题没有保存,不能进行此操作。");
 | ||
|                         }
 | ||
| 
 | ||
|                         // 单审 
 | ||
|                         dbSubjectVisit.AuditState = AuditStateEnum.QCPassed;
 | ||
|                         dbSubjectVisit.CheckState = trialConfig.IsImageConsistencyVerification ? CheckStateEnum.ToCheck : CheckStateEnum.CVPassed;
 | ||
|                         dbSubjectVisit.ForwardState = trialConfig.IsImageConsistencyVerification ? ForwardStateEnum.None : ForwardStateEnum.ToForward;
 | ||
|                         dbSubjectVisit.PreliminaryAuditUserId = _userInfo.Id;
 | ||
|                         dbSubjectVisit.PreliminaryAuditTime = DateTime.Now;
 | ||
| 
 | ||
|                     }
 | ||
|                     else
 | ||
|                     {
 | ||
|                         return ResponseOutput.NotOk("项目配置影像质控为单审,当前访视影像质控任务不能从当前审核状态变更到 审核通过。");
 | ||
|                     }
 | ||
| 
 | ||
|                 }
 | ||
|                 else if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit)
 | ||
|                 {
 | ||
| 
 | ||
|                     // 双审  如果当前db 状态是 InPrimaryQC  当前操作为 QCPassed  那么设置为 PrimaryQCPassed
 | ||
|                     if (dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC)
 | ||
|                     {
 | ||
| 
 | ||
|                         if (!await _repository.AnyAsync<TrialQCQuestionAnswer>(t => t.SubjectVisitId == subjectVisitId && t.CurrentQCEnum == CurrentQC.First))
 | ||
|                         {
 | ||
|                             return ResponseOutput.NotOk("影像质控审核问题没有保存,不能进行此操作。");
 | ||
|                         }
 | ||
| 
 | ||
|                         dbSubjectVisit.AuditState = AuditStateEnum.PrimaryQCPassed;
 | ||
|                         dbSubjectVisit.PreliminaryAuditUserId = _userInfo.Id;
 | ||
|                         dbSubjectVisit.PreliminaryAuditTime = DateTime.Now;
 | ||
| 
 | ||
| 
 | ||
|                     }
 | ||
|                     else if (dbSubjectVisit.AuditState == AuditStateEnum.InSecondaryQC)
 | ||
|                     {
 | ||
| 
 | ||
|                         if (!await _repository.AnyAsync<TrialQCQuestionAnswer>(t => t.SubjectVisitId == subjectVisitId && t.CurrentQCEnum == CurrentQC.Second))
 | ||
|                         {
 | ||
|                             return ResponseOutput.NotOk("影像质控审核问题没有保存,不能进行此操作。");
 | ||
|                         }
 | ||
| 
 | ||
|                         dbSubjectVisit.AuditState = AuditStateEnum.QCPassed;
 | ||
| 
 | ||
|                         dbSubjectVisit.CheckState = trialConfig.IsImageConsistencyVerification ? CheckStateEnum.ToCheck : CheckStateEnum.CVPassed;
 | ||
| 
 | ||
|                         dbSubjectVisit.ForwardState = trialConfig.IsImageConsistencyVerification ? ForwardStateEnum.None : ForwardStateEnum.ToForward;
 | ||
| 
 | ||
|                         dbSubjectVisit.ReviewAuditUserId = _userInfo.Id;
 | ||
| 
 | ||
|                         dbSubjectVisit.ReviewAuditTime = DateTime.Now;
 | ||
| 
 | ||
| 
 | ||
|                     }
 | ||
|                     else
 | ||
|                     {
 | ||
|                         return ResponseOutput.NotOk($"项目配置影像质控为双审。当前审核状态为 {dbSubjectVisit.AuditState},不能变更到 审核通过。");
 | ||
|                     }
 | ||
|                 }
 | ||
|             }
 | ||
| 
 | ||
|             else if (auditState == AuditStateEnum.QCFailed)
 | ||
|             {
 | ||
|                 //判断 QC流程  不审  单审  双审
 | ||
| 
 | ||
|                 if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit)
 | ||
|                 {
 | ||
|                     return ResponseOutput.NotOk("项目配置影像质控为不审,不允许设置影像质控终止。");
 | ||
|                 }
 | ||
|                 else if (trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit)
 | ||
|                 {
 | ||
|                     // 单审 
 | ||
|                     if (dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC)
 | ||
|                     {
 | ||
|                         dbSubjectVisit.AuditState = AuditStateEnum.QCFailed;
 | ||
| 
 | ||
|                     }
 | ||
|                     else
 | ||
|                     {
 | ||
|                         return ResponseOutput.NotOk("项目配置影像质控为单审,当前审核状态不为 InPrimaryQC,不能变更到 审核终止");
 | ||
|                     }
 | ||
|                 }
 | ||
|                 else if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit)
 | ||
|                 {
 | ||
|                     // 双审 
 | ||
|                     if (dbSubjectVisit.AuditState == AuditStateEnum.InPrimaryQC || dbSubjectVisit.AuditState == AuditStateEnum.InSecondaryQC)
 | ||
|                     {
 | ||
|                         dbSubjectVisit.AuditState = AuditStateEnum.QCFailed;
 | ||
|                     }
 | ||
|                     else
 | ||
|                     {
 | ||
|                         return ResponseOutput.NotOk($"项目配置影像质控为双审,当前审核状态为 {dbSubjectVisit.AuditState},不能变更到 审核终止");
 | ||
|                     }
 | ||
| 
 | ||
| 
 | ||
|                 }
 | ||
|             }
 | ||
|             dbSubjectVisit.Auditor = _userInfo.Id;
 | ||
|             dbSubjectVisit.IsTake = false;
 | ||
|             dbSubjectVisit.CurrentActionUserId = null;
 | ||
|             dbSubjectVisit.CurrentActionUserExpireTime = null;
 | ||
| 
 | ||
|             //删除 软删除的物理文件
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|             var instanceIdList = await _repository.Where<DicomInstance>(t => t.DicomSerie.IsDeleted && t.SubjectVisitId == subjectVisitId)
 | ||
|                 .Select(t => new { InstanceId = t.Id, t.SeriesId, t.StudyId, t.SubjectId, t.SiteId }).ToListAsync();
 | ||
| 
 | ||
| 
 | ||
|             instanceIdList.ForEach(t =>
 | ||
|             {
 | ||
|                 var path = _dicomFileStoreHelper.GetInstanceFilePath(new DicomStudy() { Id = t.StudyId, SubjectId = t.SubjectId, TrialId = trialId, SiteId = t.SiteId, SubjectVisitId = subjectVisitId }, t.SeriesId, t.InstanceId.ToString());
 | ||
| 
 | ||
|                 if (System.IO.File.Exists(path))
 | ||
|                 {
 | ||
|                     File.Delete(path);
 | ||
|                 }
 | ||
| 
 | ||
|             });
 | ||
| 
 | ||
|             await _repository.SaveChangesAsync();
 | ||
| 
 | ||
|             //var success = await _repository.BatchUpdateAsync<TrialSign>(t => t.Id == signId, u => new TrialSign() { IsCompleted = true });
 | ||
| 
 | ||
|             return ResponseOutput.Result(true);
 | ||
| 
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 设置、取消 访视紧急
 | ||
|         /// </summary>
 | ||
|         /// <param name="trialId"></param>
 | ||
|         /// <param name="subjectVisitId"></param>
 | ||
|         /// <param name="setOrCancel"></param>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{setOrCancel:bool}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.IQC)]
 | ||
|         public async Task<IResponseOutput> SetVisitUrgent(Guid trialId, Guid subjectVisitId, bool setOrCancel)
 | ||
|         {
 | ||
|             var sv = (await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId)).IfNullThrowException();
 | ||
| 
 | ||
|             sv.IsUrgent = setOrCancel;
 | ||
|             var success = await _repository.SaveChangesAsync();
 | ||
|             return ResponseOutput.Ok();
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
| 
 | ||
|         #region 重传、重传完设置
 | ||
| 
 | ||
|         /// <summary>验证质疑对话页面  是否可以 跳转到重传页面 进行重传  </summary>
 | ||
|         [HttpGet("{trialId:guid}/{qcChallengeId:guid}")]
 | ||
|         public async Task<IResponseOutput> VerifyReuploadIsCanJump(Guid trialId, Guid qcChallengeId)
 | ||
|         {
 | ||
|             var qcChallenge = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == qcChallengeId)).IfNullThrowException();
 | ||
| 
 | ||
|             if (qcChallenge.ReuploadEnum != QCChanllengeReuploadEnum.QCAgreeUpload)
 | ||
|             {
 | ||
|                 throw new BusinessValidationFailedException("当前重传状态不为QC同意重传,不允许重传");
 | ||
|             }
 | ||
|             return ResponseOutput.Ok();
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// QA设置  同意重传
 | ||
|         /// </summary>
 | ||
|         /// <param name="trialId"></param> 
 | ||
|         /// <param name="qcChallengeId"></param>
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPut("{trialId:guid}/{subjectVisitId:guid}/{qcChallengeId:guid}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.IQC)]
 | ||
|         public async Task<IResponseOutput> SetNeedReupload(Guid trialId, Guid qcChallengeId)
 | ||
|         {
 | ||
| 
 | ||
|             //获取项目配置
 | ||
|             var trialConfig = await _repository.Where<Trial>(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification })
 | ||
|                 .FirstOrDefaultAsync().IfNullThrowException();
 | ||
| 
 | ||
|             if (trialConfig.QCProcessEnum == TrialQCProcess.NotAudit)
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("不审操作,不会有需要重传的操作!");
 | ||
|             }
 | ||
| 
 | ||
|             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)
 | ||
|             {
 | ||
|                 throw new BusinessValidationFailedException("当前重传状态不为CRC申请重传,不允许设置同意重传");
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
|             if (await _qcChallengeRepository.CountAsync(t => t.ReuploadEnum == QCChanllengeReuploadEnum.QCAgreeUpload && t.SubjectVisitId == qcChallenge.SubjectVisitId && t.IsClosed == false) >= 1)
 | ||
|             {
 | ||
|                 return ResponseOutput.NotOk("当前访视,有一个未关闭的质疑 QC设置了同意重传,CRC还未完成上传,当前不允许再次设置");
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|             qcChallenge.ReuploadEnum = QCChanllengeReuploadEnum.QCAgreeUpload;
 | ||
|             qcChallenge.LatestMsgTime = DateTime.Now;
 | ||
|             qcChallenge.LatestReplyUserId = _userInfo.Id;
 | ||
| 
 | ||
|             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,
 | ||
|                 TalkContent = "QC同意重传"
 | ||
|             });
 | ||
| 
 | ||
|             //双审 并且是2QC 那么需要回退到1QC  讲1QC数据清除
 | ||
|             if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit && await _repository.AnyAsync<QCChallenge>(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.Id;
 | ||
| 
 | ||
|                 //BackgroundJob.Schedule<IObtainTaskAutoCancelJob>(t => t.CancelQCObtaion(qcChallenge.SubjectVisitId, DateTime.Now), TimeSpan.FromHours(1));
 | ||
|                 sv.IsTake = true;
 | ||
| 
 | ||
|                 sv.PreliminaryAuditUserId = null;
 | ||
|                 sv.ReviewAuditUserId = null;
 | ||
| 
 | ||
| 
 | ||
|                 //删除1QC    填写的问题答案
 | ||
| 
 | ||
|                 await _repository.BatchDeleteAsync<TrialQCQuestionAnswer>(t => t.SubjectVisitId == qcChallenge.SubjectVisitId && t.CurrentQCEnum == CurrentQC.First);
 | ||
| 
 | ||
|                 //2QC 数据变为1QC
 | ||
|                 await _repository.BatchUpdateAsync<QCChallenge>(t => t.SubjectVisitId == qcChallenge.SubjectVisitId && t.CurrentQCEnum == CurrentQC.Second, k => new QCChallenge() { CurrentQCEnum = CurrentQC.First });
 | ||
|                 await _repository.BatchUpdateAsync<TrialQCQuestionAnswer>(t => t.SubjectVisitId == qcChallenge.SubjectVisitId && t.CurrentQCEnum == CurrentQC.Second, k => new TrialQCQuestionAnswer() { CurrentQCEnum = CurrentQC.First });
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
|             var success = await _repository.SaveChangesAsync();
 | ||
| 
 | ||
| 
 | ||
|             return ResponseOutput.Result(success);
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// CRC  设置已经重传完成   [需要签名 不需要对]
 | ||
|         /// </summary>
 | ||
| 
 | ||
|         /// <returns></returns>
 | ||
|         [HttpPost]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.CRC)]
 | ||
|         public async Task<IResponseOutput> SetReuploadFinished(CRCReuploadFinishedCommand cRCReuploadFinishedCommand)
 | ||
|         {
 | ||
| 
 | ||
|             var qcChallenge = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == cRCReuploadFinishedCommand.QCChallengeId)).IfNullThrowException();
 | ||
| 
 | ||
|             if (qcChallenge.ReuploadEnum != QCChanllengeReuploadEnum.QCAgreeUpload)
 | ||
|             {
 | ||
|                 throw new BusinessValidationFailedException("当前重传状态不为QC同意重传,不允许设置重传完成");
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
|             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("当前没有影像,不允许设置重传完成");
 | ||
|             }
 | ||
| 
 | ||
|             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.Id;
 | ||
| 
 | ||
|             qcChallenge.ReUploader = _userInfo.RealName;
 | ||
| 
 | ||
|             qcChallenge.LatestMsgTime = DateTime.Now;
 | ||
| 
 | ||
|             qcChallenge.LatestReplyUserId = _userInfo.Id;
 | ||
| 
 | ||
|             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,
 | ||
| 
 | ||
|                 TalkContent = "CRC已重传完成"
 | ||
|             });
 | ||
| 
 | ||
| 
 | ||
|             //基线  且配置了临床数据  
 | ||
|             if (trialConfig.ClinicalInformationTransmissionEnum != 0 && dbSubjectVisit.IsBaseLine)
 | ||
|             {
 | ||
|                 //已确认临床数据完整性
 | ||
|                 dbSubjectVisit.IsConfirmedClinicalData = true;
 | ||
| 
 | ||
|                 //var signSuccess = await _repository.BatchUpdateAsync<TrialSign>(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}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         [Authorize(Policy = IRaCISPolicy.CRC)]
 | ||
|         public async Task<IResponseOutput> CRCRequestReUpload(Guid qcChallengeId)
 | ||
|         {
 | ||
|             var qcChallenge = (await _qcChallengeRepository.FirstOrDefaultAsync(t => t.Id == qcChallengeId)).IfNullThrowException();
 | ||
| 
 | ||
| 
 | ||
|             if (qcChallenge.ReuploadEnum != QCChanllengeReuploadEnum.None && qcChallenge.ReuploadEnum != QCChanllengeReuploadEnum.CRCReuploaded)
 | ||
|             {
 | ||
|                 throw new BusinessValidationFailedException("当前质疑重传状态不为初始状态|CRC重传完成状态,不允许申请重传");
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
|             if (qcChallenge.ReuploadEnum == QCChanllengeReuploadEnum.CRCReuploaded)
 | ||
|             {
 | ||
|                 qcChallenge.ReUploadedTime = null;
 | ||
|             }
 | ||
| 
 | ||
|             qcChallenge.LatestMsgTime = DateTime.Now;
 | ||
|             qcChallenge.LatestReplyUserId = _userInfo.Id;
 | ||
|             qcChallenge.ReuploadEnum = QCChanllengeReuploadEnum.CRCRequestReupload;
 | ||
| 
 | ||
|             qcChallenge.DialogList.Add(new QCChallengeDialog()
 | ||
|             {
 | ||
|                 SubjectVisitId = qcChallenge.SubjectVisitId,
 | ||
|                 UserTypeEnum = (UserTypeEnum)_userInfo.UserTypeEnumInt,
 | ||
|                 QCChallengeId = qcChallenge.Id,
 | ||
|                 TalkContent = "CRC申请重传/上传影像"
 | ||
|             });
 | ||
| 
 | ||
|             var isSuccess = await _qcChallengeRepository.SaveChangesAsync();
 | ||
| 
 | ||
|             return ResponseOutput.Result(isSuccess);
 | ||
|         }
 | ||
| 
 | ||
|         #endregion
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>
 | ||
|         /// 上传界面 更新受试者访视基准日期  是否入组确认,以及访视 是否PD进展
 | ||
|         /// </summary>
 | ||
|         /// <param name="command"></param>
 | ||
|         /// <returns></returns>
 | ||
| 
 | ||
|         [HttpPut("{trialId:guid}")]
 | ||
|         [TypeFilter(typeof(TrialResourceFilter))]
 | ||
|         public async Task<IResponseOutput> 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("该访视已提交,不能修改入组确认状态");
 | ||
|                 }
 | ||
| 
 | ||
|                 if (await _subjectVisitRepository.Where(t => t.Id == command.SubjectVisitId)
 | ||
|                .AnyAsync(t => t.IsEnrollmentConfirm != command.IsEnrollmentConfirm && t.RequestBackState == RequestBackStateEnum.PM_AgressBack))
 | ||
|                 {
 | ||
|                     return ResponseOutput.NotOk("该访视为回退访视,不允许修改PD确认状态");
 | ||
|                 }
 | ||
| 
 | ||
|                 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();
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|         [HttpPost("{trialId:guid}")]
 | ||
|         [Authorize(Policy = IRaCISPolicy.PM_APM)]
 | ||
|         public async Task<IResponseOutput> ForwardSVDicomImage(Guid[] subjectVisitIdList)
 | ||
|         {
 | ||
| 
 | ||
|             bool isSuccess = false;
 | ||
| 
 | ||
| 
 | ||
|             foreach (var subjectVisitId in subjectVisitIdList)
 | ||
|             {
 | ||
| 
 | ||
| 
 | ||
|                 var info = await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).ProjectTo<DicomTrialSiteSubjectInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
 | ||
| 
 | ||
| 
 | ||
|                 var targetPath = "/IMPORT-IMAGES/" + info.TrialCode + "_" + info.SubjectCode + "_" + info.VisitName;
 | ||
| 
 | ||
|                 var path = _dicomFileStoreHelper.GetSubjectVisitPath(info.TrialId, info.SiteId, 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("转发影像失败。" + 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("转发影像失败: " + e.Message);
 | ||
|                 }
 | ||
| 
 | ||
| 
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|             await _subjectVisitRepository.SaveChangesAsync();
 | ||
| 
 | ||
|             return isSuccess ? ResponseOutput.Ok() : ResponseOutput.NotOk("转发影像失败。");
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|     }
 | ||
| }
 |