2545 lines
113 KiB
C#
2545 lines
113 KiB
C#
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 Org.BouncyCastle.Asn1.Cmp;
|
||
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>
|
||
/// <param name="ApplyReason"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="BusinessValidationFailedException"></exception>
|
||
[HttpPut]
|
||
public async Task<IResponseOutput> RequestImageBack(Guid subjectVisitId, string ApplyReason)
|
||
{
|
||
|
||
if (_subjectVisitRepository.Any(t => t.Id == subjectVisitId && t.QCChallengeList.Any(t => t.IsClosed == false)))
|
||
{
|
||
//有未关闭的质疑,不允许在此申请影像回退
|
||
throw new BusinessValidationFailedException(_localizer["QCOperation_HaveNotClosedQCChanllenge"]);
|
||
}
|
||
|
||
if (!_subjectVisitRepository.Any(t => t.Id == subjectVisitId && !t.VisitTaskList.Any(u => u.TaskState == TaskState.Effect) && 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"]);
|
||
}
|
||
|
||
if (_qcChallengeRepository.Any(t => t.SubjectVisitId == subjectVisitId && t.IsClosed == false && t.ReuploadEnum == QCChanllengeReuploadEnum.CRCRequestReupload))
|
||
{
|
||
throw new BusinessValidationFailedException(_localizer["QCOperation_SomeOneHaveApplyedImageBack"]);
|
||
}
|
||
|
||
|
||
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, ApplyReason = ApplyReason, 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>
|
||
/// <param name="resultRemark"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="BusinessValidationFailedException"></exception>
|
||
[HttpPut]
|
||
public async Task<IResponseOutput> AuditImageBack(Guid iamgeBackRecordId, bool isAgree, string? resultRemark)
|
||
{
|
||
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 (_subjectVisitRepository.Any(t => t.Id == subjectVisitId && t.QCChallengeList.Any(t => t.IsClosed == false)))
|
||
{
|
||
//有未关闭的质疑,不允许同意影像回退
|
||
throw new BusinessValidationFailedException(_localizer["QCOperation_HaveNotClosedQCChanllenge"]);
|
||
}
|
||
|
||
|
||
if (sv.SubmitState == SubmitStateEnum.Submitted && _subjectVisitRepository.Any(t => t.Id == backRecord.SubjectVisitId && !t.VisitTaskList.Any(u => u.TaskState == TaskState.Effect)))
|
||
{
|
||
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;
|
||
backRecord.ResultRemark = resultRemark ?? string.Empty;
|
||
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_SomeOneHaveApplyedImageBack"]);
|
||
}
|
||
|
||
if (sv.RequestBackState == RequestBackStateEnum.CRC_RequestBack)
|
||
{
|
||
// 该访视已在其他途径申请影像回退中,不允许申请
|
||
throw new BusinessValidationFailedException(_localizer["QCOperation_SomeOneHaveApplyedImageBack"]);
|
||
}
|
||
|
||
|
||
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.CRC_RequestBack)
|
||
{
|
||
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.RequestBackState = RequestBackStateEnum.PM_AgressBack;
|
||
|
||
sv.AuditState = AuditStateEnum.None;
|
||
sv.SubmitState = SubmitStateEnum.ToSubmit;
|
||
sv.ReadingStatus = ReadingStatusEnum.ImageNotSubmit;
|
||
|
||
//回退后,回退状态恢复
|
||
|
||
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, true)).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), false, true).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 });
|
||
|
||
if (fisrtPath.IsNotNullOrEmpty())
|
||
{
|
||
var prefix = fisrtPath.Substring(1, fisrtPath.LastIndexOf('/') - 1);
|
||
|
||
await _oSSService.DeleteFromPrefix(prefix, false);
|
||
await _oSSService.DeleteFromPrefix(prefix, true);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
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.SubjectVisitImageBackRecordList.Any(t => t.ImageBackState == ImageBackStateEnum.None))
|
||
.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.SubjectVisitImageBackRecordList.Any(t => t.ImageBackState == ImageBackStateEnum.None))
|
||
.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 (_subjectVisitImageBackRecordReposiotry.Any(t => t.SubjectVisitId == subjectVisitId && t.ImageBackState == ImageBackStateEnum.None))
|
||
{
|
||
//已申请的影像回退,不能领取
|
||
throw new BusinessValidationFailedException(_localizer["QCOperation_HaveApplyedImageBackCanNotObtain"]);
|
||
}
|
||
|
||
//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 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}")]
|
||
[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 _dicomInstanceRepository.BatchDeleteNoTrackingAsync(t => (t.DicomSerie.IsDeleted || t.IsDeleted) && t.SubjectVisitId == subjectVisitId);
|
||
await _dicomSeriesRepository.BatchDeleteNoTrackingAsync(t => 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 _dicomStudyRepository.BatchDeleteNoTrackingAsync(t => t.IsDeleted && t.SubjectVisitId == subjectVisitId);
|
||
await _dicomInstanceRepository.BatchDeleteNoTrackingAsync(t => (t.DicomSerie.IsDeleted || t.IsDeleted) && t.SubjectVisitId == subjectVisitId);
|
||
await _dicomSeriesRepository.BatchDeleteNoTrackingAsync(t => 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
|
||
{
|
||
//$"项目配置影像质控为双审。当前审核状态为 {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_SomeOneHaveApplyedImageBack"]);
|
||
}
|
||
|
||
if (sv.RequestBackState == RequestBackStateEnum.CRC_RequestBack)
|
||
{
|
||
// 该访视已在其他途径申请影像回退中,不允许申请
|
||
throw new BusinessValidationFailedException(_localizer["QCOperation_SomeOneHaveApplyedImageBack"]);
|
||
}
|
||
|
||
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
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
}
|
||
}
|