irc-netcore-api/IRaCIS.Core.Application/Service/QC/QCOperationService.cs

2526 lines
111 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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