diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs index 21ec662fa..747214ce8 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs @@ -1118,7 +1118,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc HtmlPath = k.HtmlPath, Path = k.Path, InstanceNumber = k.InstanceNumber, - + FileSize=k.FileSize, }).ToList() }) }); diff --git a/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs index 0d341df57..f0a760fe5 100644 --- a/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs +++ b/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs @@ -270,6 +270,7 @@ namespace IRaCIS.Core.Application.Contracts.DTO public QARelationInfo RelationInfo { get; set; } = new QARelationInfo(); + public List SecondReviewTimeList { get; set; } } diff --git a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs index 7d610c408..50e40de83 100644 --- a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs +++ b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs @@ -2013,6 +2013,8 @@ namespace IRaCIS.Core.Application.Contracts // 1代表第一个人QC数据 2 代表第二个人QC数据 public CurrentQC CurrentQCEnum { get; set; } + + public DateTime? SecondReviewTime { get; set; } } public class ForwardQuery : PageInput @@ -2167,6 +2169,7 @@ namespace IRaCIS.Core.Application.Contracts public class QCVisitBasicListViewModel { + public SecondReviewState SecondReviewState { get; set; } public bool IsSubjectQuit { get; set; } public ChallengeStateEnum ChallengeState { get; set; } public bool? IsConfirmedClinicalData { get; set; } diff --git a/IRaCIS.Core.Application/Service/QC/DTO/TrialQCQuestionConfigureViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/TrialQCQuestionConfigureViewModel.cs index 7773d1107..d06e4253f 100644 --- a/IRaCIS.Core.Application/Service/QC/DTO/TrialQCQuestionConfigureViewModel.cs +++ b/IRaCIS.Core.Application/Service/QC/DTO/TrialQCQuestionConfigureViewModel.cs @@ -35,6 +35,8 @@ namespace IRaCIS.Core.Application.Contracts public DateTime UpdateTime { get; set; } public Guid UpdateUserId { get; set; } + public bool IsQuestionQCAuditPassed { get; set; } + } public class QCQuestionFilterSelect diff --git a/IRaCIS.Core.Application/Service/QC/QCListService.cs b/IRaCIS.Core.Application/Service/QC/QCListService.cs index c3720526a..06755a396 100644 --- a/IRaCIS.Core.Application/Service/QC/QCListService.cs +++ b/IRaCIS.Core.Application/Service/QC/QCListService.cs @@ -7,6 +7,7 @@ using IRaCIS.Core.Infrastructure; using IRaCIS.Core.Infrastructure.Extention; using MassTransit.Initializers; using Microsoft.AspNetCore.Mvc; +using System.Linq; namespace IRaCIS.Core.Application.Image.QA { @@ -411,10 +412,28 @@ namespace IRaCIS.Core.Application.Image.QA var qacheckList = await GetQCQuestionAnswerList(subjectVisitId, sv.TrialId, trialQCProcess, currentQCType); + List secondReviewTimeList = new List(); + + if (sv.SecondReviewState != SecondReviewState.None) + { + secondReviewTimeList = _trialQCQuestionAnswerRepository.Where(t => t.SubjectVisitId == subjectVisitId && t.CurrentQCEnum == CurrentQC.SecondReview).Where(t => t.SecondReviewTime != null) + .Select(t => t.SecondReviewTime).Distinct().ToList(); + + var trialConfirmTime = _trialRepository.Where(t => t.Id == sv.TrialId).Select(t => t.QCQuestionConfirmedTime).FirstOrDefault(); + + if (!secondReviewTimeList.Contains(trialConfirmTime)) + { + secondReviewTimeList.Add(trialConfirmTime); + } + + } + return new TrialVisitQADTO { QCQuestionAnswerList = qacheckList, + SecondReviewTimeList = secondReviewTimeList, + IsHaveStudyClinicalData = await _clinicalDataTrialSetRepository.AnyAsync(x => x.IsConfirm && x.TrialId == sv.TrialId && x.ClinicalDataLevel == ClinicalLevel.Study), StudyList = temp.StudyList, ExistsManual = (await _IReadingImageTaskService.GetManualList(new GetManualListInDto() { TrialId = sv.TrialId })).Count() > 0, @@ -434,8 +453,45 @@ namespace IRaCIS.Core.Application.Image.QA [HttpPost] public async Task> GetQCQuestionAnswer(GetQCQuestionAnswerInDto inDto) { - var questionAnswerlist = await (from data in _trialQCQuestionRepository.Where(x => x.TrialId == inDto.TrialId && x.IsEnable) - join answer in _trialQCQuestionAnswerRepository.Where(x => x.SubjectVisitId == inDto.SubjectVisitId && x.QCProcessEnum == inDto.QCProcessEnum && x.CurrentQCEnum == inDto.CurrentQCEnum).AsQueryable() on data.Id equals answer.TrialQCQuestionConfigureId into answertemp + var subjectVisitId = inDto.SubjectVisitId; + + List questionAnswerlist = new List(); + + //判断当前访视质控是否完成,再判断复审是完成查看,还是添加编辑 + + var sv = await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).FirstNotNullAsync(); + + if (sv.SecondReviewState == SecondReviewState.None && sv.AuditState == AuditStateEnum.QCPassed) + { + + //现在之前历史质控的展示要从答案为主表取数据,添加和编辑按照之前方式 + + questionAnswerlist = await _trialQCQuestionAnswerRepository.Where(x => x.SubjectVisitId == inDto.SubjectVisitId && x.QCProcessEnum == inDto.QCProcessEnum && x.CurrentQCEnum == inDto.CurrentQCEnum) + .Select(data => new QCQuestionAnswer() + { + Answer = data.Answer, + ShowOrder = data.TrialQCQuestionConfigure.ShowOrder, + QuestionName = data.TrialQCQuestionConfigure.QuestionName, + Id = data.TrialQCQuestionConfigure.Id, + IsRequired = data.TrialQCQuestionConfigure.IsRequired, + ParentId = data.TrialQCQuestionConfigure.ParentId, + ParentTriggerValue = data.TrialQCQuestionConfigure.ParentTriggerValue, + Type = data.TrialQCQuestionConfigure.Type, + TypeValue = data.TrialQCQuestionConfigure.TypeValue + }).OrderBy(t => t.ShowOrder).ToListAsync(); + + } + else + { + + var secondReviewTime = inDto.SecondReviewTime != null ? (DateTime)inDto.SecondReviewTime : DateTime.Now; + + #region 之前编辑和审核通过后展示都是通过这个接口 + + questionAnswerlist = await (from data in _trialQCQuestionRepository.Where(x => x.TrialId == inDto.TrialId && x.IsEnable) + join answer in _trialQCQuestionAnswerRepository.Where(x => x.SubjectVisitId == inDto.SubjectVisitId && x.QCProcessEnum == inDto.QCProcessEnum && x.CurrentQCEnum == inDto.CurrentQCEnum) + .Where(t => inDto.CurrentQCEnum == CurrentQC.SecondReview ? t.SecondReviewTime >= secondReviewTime && t.SecondReviewTime <= secondReviewTime.AddSeconds(1) : true) + on data.Id equals answer.TrialQCQuestionConfigureId into answertemp from leftanswer in answertemp.DefaultIfEmpty() select new QCQuestionAnswer() { @@ -449,6 +505,17 @@ namespace IRaCIS.Core.Application.Image.QA Type = data.Type, TypeValue = data.TypeValue }).OrderBy(t => t.ShowOrder).ToListAsync(); + #endregion + } + + + + + + + + + var result = questionAnswerlist.Where(x => x.ParentId == null).OrderBy(t => t.ShowOrder).ToList(); result.ForEach(x => diff --git a/IRaCIS.Core.Application/Service/QC/QCOperationService.cs b/IRaCIS.Core.Application/Service/QC/QCOperationService.cs index 326fa9958..704d3b11e 100644 --- a/IRaCIS.Core.Application/Service/QC/QCOperationService.cs +++ b/IRaCIS.Core.Application/Service/QC/QCOperationService.cs @@ -730,6 +730,8 @@ namespace IRaCIS.Core.Application.Image.QA //验证是否能操作 await VerifyIsCanQCAsync(null, subjectVisitId); + var trialConfirmTime = _trialRepository.Where(t => t.Id == trialId).Select(t => t.QCQuestionConfirmedTime).FirstOrDefault(); + //更新 if (qcQuestionAnswerCommands.Any(t => t.Id != null)) { @@ -774,7 +776,9 @@ namespace IRaCIS.Core.Application.Image.QA { var addlist = _mapper.Map>(qcQuestionAnswerCommands); - addlist.ForEach(t => { t.TrialId = trialId; t.SubjectVisitId = subjectVisitId; t.CurrentQCEnum = currentQCType; t.QCProcessEnum = trialQCProcess; }); + + + addlist.ForEach(t => { t.TrialId = trialId; t.SubjectVisitId = subjectVisitId; t.CurrentQCEnum = currentQCType; t.QCProcessEnum = trialQCProcess; t.SecondReviewTime = currentQCType == CurrentQC.SecondReview ? trialConfirmTime : null; }); await _trialQCQuestionAnswerRepository.AddRangeAsync(addlist); diff --git a/IRaCIS.Core.Application/Service/QC/_MapConfig.cs b/IRaCIS.Core.Application/Service/QC/_MapConfig.cs index 226bd8161..3212a2882 100644 --- a/IRaCIS.Core.Application/Service/QC/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/QC/_MapConfig.cs @@ -479,6 +479,7 @@ namespace IRaCIS.Core.Application.Service CreateMap(); CreateMap() + .ForMember(d => d.IsQuestionQCAuditPassed, u => u.MapFrom(s => s.TrialQCQuestionAnswerList.Any(c => c.TrialQCQuestionConfigureId == s.Id && c.SubjectVisit.AuditState == AuditStateEnum.QCPassed))) .ForMember(d => d.ParentShowOrder, u => u.MapFrom(s => s.ParentQCQuestion.ShowOrder)); CreateMap(); diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs index 6461ea27c..94bfbd0b3 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs @@ -41,6 +41,7 @@ namespace IRaCIS.Core.Application IRepository _systemBasicDataRepository, IRepository _subjectVisitRepository, IRepository _enrollRepository, + IRepository _qcQuestionAnswerRepository, IRepository _trialStateChangeRepository, IRepository _readingTableQuestionTrialRepository, IRepository _dicomAERepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IFusionCache _fusionCache) : BaseService, ITrialConfigService @@ -861,9 +862,18 @@ namespace IRaCIS.Core.Application { await _trialRepository.UpdatePartialFromQueryAsync(t => t.Id == signConfirmDTO.TrialId, u => new Trial() { IsTrialUrgentConfirmed = true }); } - else + else if (signConfirmDTO.SignCode == ((int)SignEnum.TrialQCQuestionConfirmUpdate).ToString()) + { + await _trialRepository.UpdatePartialFromQueryAsync(t => t.Id == signConfirmDTO.TrialId, u => new Trial() { QCQuestionConfirmedTime = null, QCQuestionConfirmedUserId = null, IsQCQuestionConfirmed = false }); + } + else if (signConfirmDTO.SignCode == ((int)SignEnum.SecondReviewConfirm).ToString()) + { + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == signConfirmDTO.TrialId && t.AuditState == AuditStateEnum.QCPassed, u => new SubjectVisit() { SecondReviewState = SecondReviewState.WaitAudit }); - if (signConfirmDTO.SignCode == ((int)SignEnum.TrialQCQuestionConfirm).ToString()) + //删除复审中间临时数据 + await _qcQuestionAnswerRepository.BatchDeleteNoTrackingAsync(t => t.SubjectVisit.TrialId == signConfirmDTO.TrialId && t.SubjectVisit.AuditState != AuditStateEnum.QCPassed && t.CurrentQCEnum == CurrentQC.SecondReview); + } + else if (signConfirmDTO.SignCode == ((int)SignEnum.TrialQCQuestionConfirm).ToString()) { var trialConfig = (await _trialRepository @@ -909,9 +919,9 @@ namespace IRaCIS.Core.Application IsConfirm = true }); await _trialRepository.UpdatePartialFromQueryAsync(t => t.Id == signConfirmDTO.TrialId, u => new Trial() { QCQuestionConfirmedTime = DateTime.Now, QCQuestionConfirmedUserId = _userInfo.UserRoleId, IsQCQuestionConfirmed = true }); - await _trialRepository.SaveChangesAsync(); } + await _trialRepository.SaveChangesAsync(); return ResponseOutput.Ok(); diff --git a/IRaCIS.Core.Domain.Share/Common/SignEnum.cs b/IRaCIS.Core.Domain.Share/Common/SignEnum.cs index 43cbcf9f7..98c3eed27 100644 --- a/IRaCIS.Core.Domain.Share/Common/SignEnum.cs +++ b/IRaCIS.Core.Domain.Share/Common/SignEnum.cs @@ -29,6 +29,13 @@ namespace IRaCIS.Core.Domain.Share TrialQCQuestionConfirm=107, + + //重置质控问题配置 + + TrialQCQuestionConfirmUpdate=112, + + SecondReviewConfirm = 219, + } } diff --git a/IRaCIS.Core.Domain.Share/QC/CheckChanllengeTypeEnum.cs b/IRaCIS.Core.Domain.Share/QC/CheckChanllengeTypeEnum.cs index d242c1f2b..5c2e94ab8 100644 --- a/IRaCIS.Core.Domain.Share/QC/CheckChanllengeTypeEnum.cs +++ b/IRaCIS.Core.Domain.Share/QC/CheckChanllengeTypeEnum.cs @@ -19,4 +19,14 @@ namespace IRaCIS.Core.Domain.Share } + public enum SecondReviewState + { + None = 0, + + WaitAudit=1, + + AuditPassed=2, + + AuditFailed=3, + } } diff --git a/IRaCIS.Core.Domain.Share/QC/TrialQCProcess.cs b/IRaCIS.Core.Domain.Share/QC/TrialQCProcess.cs index bffe7530f..dd5bd63a2 100644 --- a/IRaCIS.Core.Domain.Share/QC/TrialQCProcess.cs +++ b/IRaCIS.Core.Domain.Share/QC/TrialQCProcess.cs @@ -14,7 +14,11 @@ First = 1, - Second = 2 + Second = 2, + + + //二次复核,只会一个人 + SecondReview=3, } diff --git a/IRaCIS.Core.Domain/QC/TrialQCQuestionAnswer.cs b/IRaCIS.Core.Domain/QC/TrialQCQuestionAnswer.cs index 24680ec64..822638e74 100644 --- a/IRaCIS.Core.Domain/QC/TrialQCQuestionAnswer.cs +++ b/IRaCIS.Core.Domain/QC/TrialQCQuestionAnswer.cs @@ -8,6 +8,9 @@ public class TrialQCQuestionAnswer : BaseFullAuditEntity #region 导航属性 [JsonIgnore] public TrialQCQuestion TrialQCQuestionConfigure { get; set; } + + [JsonIgnore] + public SubjectVisit SubjectVisit { get; set; } #endregion public Guid TrialId { get; set; } @@ -20,4 +23,7 @@ public class TrialQCQuestionAnswer : BaseFullAuditEntity public Guid SubjectVisitId { get; set; } public Guid TrialQCQuestionConfigureId { get; set; } + + + public DateTime? SecondReviewTime { get; set; } } diff --git a/IRaCIS.Core.Domain/Visit/SubjectVisit.cs b/IRaCIS.Core.Domain/Visit/SubjectVisit.cs index f95d5c47a..862560d79 100644 --- a/IRaCIS.Core.Domain/Visit/SubjectVisit.cs +++ b/IRaCIS.Core.Domain/Visit/SubjectVisit.cs @@ -75,7 +75,7 @@ public class SubjectVisit : BaseFullDeleteAuditEntity public List SubjectCriteriaEvaluationVisitFilterList { get; set; } [JsonIgnore] - public List SubjectVisitImageBackRecordList { get; set; } + public List SubjectVisitImageBackRecordList { get; set; } #endregion public Guid TrialSiteId { get; set; } @@ -188,7 +188,7 @@ public class SubjectVisit : BaseFullDeleteAuditEntity public Guid? SubmitUserId { get; set; } public ReadingStatusEnum ReadingStatus { get; set; } - + public SecondReviewState SecondReviewState { get; set; } } [Comment("受试者访视影像回退记录表")] diff --git a/IRaCIS.Core.Infra.EFCore/Common/AuditingData.cs b/IRaCIS.Core.Infra.EFCore/Common/AuditingData.cs index 07727f137..60c69dfda 100644 --- a/IRaCIS.Core.Infra.EFCore/Common/AuditingData.cs +++ b/IRaCIS.Core.Infra.EFCore/Common/AuditingData.cs @@ -217,6 +217,13 @@ namespace IRaCIS.Core.Infra.EFCore.Common extraIdentification = oldentity.IsConfigureEmail ? "/EmailUpdate" : "/EmailSave"; break; + case "configTrialBasicInfo/TrialConfigSignatureConfirm": + + if (entity.IsQCQuestionConfirmed == false) + { + extraIdentification = $"/ConfirmReset"; + } + break; } //var trialDicomAE = await _dbContext.TrialDicomAE.Where(t => t.TrialId == entity.Id).FirstOrDefaultAsync();