From 9a5258bf83059b97d0494503b7bbb15ba015a8db Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Tue, 15 Jul 2025 14:53:00 +0800 Subject: [PATCH] =?UTF-8?q?QC=E5=A4=8D=E6=A0=B8=20-=E4=BA=8C=E6=AC=A1?= =?UTF-8?q?=E6=8F=90=E4=BA=A4-uat-2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/InspectionController.cs | 72 +++++++++++-------- .../Service/Inspection/DTO/InspectionModel.cs | 2 + .../Service/QC/DTO/QARecordViewModel.cs | 10 ++- .../QC/Interface/IQCOperationService.cs | 2 +- .../Service/QC/QCListService.cs | 38 +++++++--- .../Service/QC/QCOperationService.cs | 41 +++++++++++ IRaCIS.Core.Domain/BaseModel/Entity.cs | 5 ++ 7 files changed, 127 insertions(+), 43 deletions(-) diff --git a/IRaCIS.Core.API/Controllers/InspectionController.cs b/IRaCIS.Core.API/Controllers/InspectionController.cs index 56187a561..5d0206baa 100644 --- a/IRaCIS.Core.API/Controllers/InspectionController.cs +++ b/IRaCIS.Core.API/Controllers/InspectionController.cs @@ -52,7 +52,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/ReadingImageTask/SubmitOncologyReadingInfo")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task SetOncologyReadingInfo(DataInspectionDto opt) @@ -69,7 +69,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/ReadingImageTask/SubmitDicomVisitTask")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task SubmitDicomVisitTask(DataInspectionDto opt) @@ -88,7 +88,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/ReadingImageTask/SubmitGlobalReadingInfo")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task SubmitGlobalReadingInfo(DataInspectionDto opt) @@ -107,7 +107,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/configTrialBasicInfo/TrialReadingInfoSign")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task TrialReadingInfoSign(DataInspectionDto opt) @@ -126,7 +126,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/ReadingMedicalReview/FinishMedicalReview")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task FinishMedicalReview(DataInspectionDto opt) @@ -143,7 +143,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/ReadingMedicineQuestion/ConfirmReadingMedicineQuestion")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task ConfirmReadingMedicineQuestion(DataInspectionDto opt) @@ -161,7 +161,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/ReadingImageTask/SubmitVisitTaskQuestions")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task SubmitVisitTaskQuestions(DataInspectionDto opt) @@ -179,7 +179,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/ClinicalAnswer/CRCSignClinicalData")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task CRCSignClinicalData(DataInspectionDto opt) @@ -197,7 +197,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/ClinicalAnswer/CRCConfirmClinical")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task CRCConfirmClinical(DataInspectionDto opt) @@ -214,7 +214,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/ClinicalAnswer/CRCCancelConfirmClinical")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task CRCCancelConfirmClinical(DataInspectionDto opt) @@ -232,7 +232,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/ClinicalAnswer/PMConfirmClinical")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task PMConfirmClinical(DataInspectionDto opt) @@ -250,7 +250,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/ReadingClinicalData/SignConsistencyAnalysisReadingClinicalData")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task SignConsistencyAnalysisReadingClinicalData(DataInspectionDto opt) @@ -267,7 +267,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/ClinicalAnswer/SubmitClinicalFormAndSign")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task SubmitClinicalFormAndSign(DataInspectionDto opt) @@ -284,7 +284,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/ReadingImageTask/SubmitJudgeVisitTaskResult")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task SubmitJudgeVisitTaskResult(DataInspectionDto opt) @@ -303,7 +303,7 @@ namespace IRaCIS.Core.API.Controllers /// [HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialBasicInfoConfirm")] [UnitOfWork] - [TrialGlobalLimit( "BeforeOngoingCantOpt" )] + [TrialGlobalLimit("BeforeOngoingCantOpt")] public async Task ConfigTrialBasicInfoConfirm(DataInspectionDto opt) { @@ -345,7 +345,7 @@ namespace IRaCIS.Core.API.Controllers /// [HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialUrgentInfoConfirm")] [UnitOfWork] - [TrialGlobalLimit( "BeforeOngoingCantOpt" )] + [TrialGlobalLimit("BeforeOngoingCantOpt")] public async Task ConfigTrialUrgentInfoConfirm(DataInspectionDto opt) { opt.Data.IsTrialUrgentConfirmed = true; @@ -358,7 +358,7 @@ namespace IRaCIS.Core.API.Controllers [HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialPACSInfoConfirm")] [UnitOfWork] - [TrialGlobalLimit( "BeforeOngoingCantOpt" )] + [TrialGlobalLimit("BeforeOngoingCantOpt")] public async Task ConfigTrialPACSInfoConfirm(DataInspectionDto opt) { opt.Data.IsTrialPACSConfirmed = true; @@ -374,7 +374,7 @@ namespace IRaCIS.Core.API.Controllers /// [HttpPost, Route("Inspection/configTrialBasicInfo/TrialConfigSignatureConfirm")] [UnitOfWork] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] public async Task TrialConfigSignatureConfirm(DataInspectionDto opt) { @@ -391,7 +391,7 @@ namespace IRaCIS.Core.API.Controllers /// [HttpPost, Route("Inspection/ReadingCriterion/ResetAndAsyncCriterion")] [UnitOfWork] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] public async Task ResetAndAsyncCriterion(DataInspectionDto opt) { @@ -409,7 +409,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/QCOperation/CRCRequestToQC")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task CRCRequestToQC(DataInspectionDto opt) { @@ -424,21 +424,31 @@ namespace IRaCIS.Core.API.Controllers /// 设置QC 通过或者不通过 7:QC failed 8:QC passed /// [HttpPost, Route("Inspection/QCOperation/QCPassedOrFailed")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task QCPassedOrFailed(DataInspectionDto opt) { var singid = await _inspectionService.RecordSing(opt.SignInfo); - var result = await _qCOperationService.QCPassedOrFailed(opt.Data.trialId, opt.Data.subjectVisitId, opt.Data.auditState); - await _inspectionService.CompletedSign(singid, result); - return result; + if (opt.Data.IsSecondPass != null) + { + var result = await _qCOperationService.QCSecondReviewPassedOrFailed(opt.Data.trialId, opt.Data.subjectVisitId, (bool)opt.Data.IsSecondPass); + await _inspectionService.CompletedSign(singid, result); + return result; + } + else + { + var result = await _qCOperationService.QCPassedOrFailed(opt.Data.trialId, opt.Data.subjectVisitId, opt.Data.auditState); + await _inspectionService.CompletedSign(singid, result); + return result; + } + } /// /// 一致性核查 回退 对话记录不清除 只允许PM回退 /// [HttpPost, Route("Inspection/QCOperation/CheckBack")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task CheckBack(DataInspectionDto opt) { @@ -455,7 +465,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/ReadClinicalData/ReadClinicalDataSign")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task ReadClinicalDataSign(DataInspectionDto opt) { @@ -470,7 +480,7 @@ namespace IRaCIS.Core.API.Controllers /// CRC 设置已经重传完成 /// [HttpPost, Route("Inspection/QCOperation/SetReuploadFinished")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task SetReuploadFinished(DataInspectionDto opt) { @@ -486,7 +496,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/TrialConfig/updateTrialState")] - [TrialGlobalLimit( "BeforeOngoingCantOpt")] + [TrialGlobalLimit("BeforeOngoingCantOpt")] [UnitOfWork] public async Task UpdateTrialState(DataInspectionDto opt) { @@ -502,7 +512,7 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/TrialDocument/userConfirm")] - [TrialGlobalLimit( "BeforeOngoingCantOpt", "SignSystemDocNoTrialId", "AfterStopCannNotOpt" )] + [TrialGlobalLimit("BeforeOngoingCantOpt", "SignSystemDocNoTrialId", "AfterStopCannNotOpt")] [UnitOfWork] public async Task UserConfirm(DataInspectionDto opt) { @@ -519,10 +529,10 @@ namespace IRaCIS.Core.API.Controllers /// /// [HttpPost, Route("Inspection/VisitTask/ConfirmReReading")] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] + [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] - public async Task ConfirmReReading(DataInspectionDto opt, [FromServices] IVisitTaskService _visitTaskService) + public async Task ConfirmReReading(DataInspectionDto opt, [FromServices] IVisitTaskService _visitTaskService) { var singId = await _inspectionService.RecordSing(opt.SignInfo); var result = await _visitTaskService.ConfirmReReading(opt.Data); diff --git a/IRaCIS.Core.Application/Service/Inspection/DTO/InspectionModel.cs b/IRaCIS.Core.Application/Service/Inspection/DTO/InspectionModel.cs index 187c35e81..7d932a84d 100644 --- a/IRaCIS.Core.Application/Service/Inspection/DTO/InspectionModel.cs +++ b/IRaCIS.Core.Application/Service/Inspection/DTO/InspectionModel.cs @@ -199,6 +199,8 @@ namespace IRaCIS.Core.Application.Service.Inspection.DTO public Guid subjectVisitId { get; set; } public AuditStateEnum auditState { get; set; } + + public bool? IsSecondPass { get; set; } } public class SetSeriesStateDto diff --git a/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs index f0a760fe5..3f34cfa0a 100644 --- a/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs +++ b/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs @@ -251,7 +251,13 @@ namespace IRaCIS.Core.Application.Contracts.DTO } - + public class SecondReviewDto + { + public DateTime? SecondReviewTime { get; set; } + public string FullName { get; set; } + public string UserName { get; set; } + public DateTime? SignTime { get; set; } + } public class TrialVisitQADTO @@ -270,7 +276,7 @@ namespace IRaCIS.Core.Application.Contracts.DTO public QARelationInfo RelationInfo { get; set; } = new QARelationInfo(); - public List SecondReviewTimeList { get; set; } + public List SecondReviewList { get; set; } } diff --git a/IRaCIS.Core.Application/Service/QC/Interface/IQCOperationService.cs b/IRaCIS.Core.Application/Service/QC/Interface/IQCOperationService.cs index 4b9093508..59d99ad3f 100644 --- a/IRaCIS.Core.Application/Service/QC/Interface/IQCOperationService.cs +++ b/IRaCIS.Core.Application/Service/QC/Interface/IQCOperationService.cs @@ -13,7 +13,7 @@ namespace IRaCIS.Core.Application.Image.QA Task QCPassedOrFailed(Guid trialId, Guid subjectVisitId, [FromRoute] AuditStateEnum auditState); Task SetCheckPass(SetCheckPassDt data); - + Task QCSecondReviewPassedOrFailed(Guid trialId, Guid subjectVisitId, bool isSecondPass); Task AddCheckChallengeReply(CheckChallengeDialogCommand checkDialogCommand); diff --git a/IRaCIS.Core.Application/Service/QC/QCListService.cs b/IRaCIS.Core.Application/Service/QC/QCListService.cs index 06755a396..c701f578f 100644 --- a/IRaCIS.Core.Application/Service/QC/QCListService.cs +++ b/IRaCIS.Core.Application/Service/QC/QCListService.cs @@ -410,29 +410,49 @@ namespace IRaCIS.Core.Application.Image.QA var temp = await GetVisitQCStudyAndSeriesList(subjectVisitId); - var qacheckList = await GetQCQuestionAnswerList(subjectVisitId, sv.TrialId, trialQCProcess, currentQCType); + //var qacheckList = await GetQCQuestionAnswerList(subjectVisitId, sv.TrialId, trialQCProcess, currentQCType); + + List secondReviewList = new List(); - 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(); + secondReviewList = _trialQCQuestionAnswerRepository.Where(t => t.SubjectVisitId == subjectVisitId && t.CurrentQCEnum == CurrentQC.SecondReview).Where(t => t.SecondReviewTime != null) + .Select(t => new SecondReviewDto { SecondReviewTime = t.SecondReviewTime, SignTime = t.UpdateTime, FullName = t.UpdateUserRole.FullName, UserName = t.UpdateUserRole.UserName }).Distinct().ToList(); + + var secondReviewTimeList = secondReviewList.Select(t => t.SecondReviewTime).Distinct().ToList(); + + //首次加入 if (!secondReviewTimeList.Contains(trialConfirmTime)) { - secondReviewTimeList.Add(trialConfirmTime); + secondReviewList.Add(new SecondReviewDto() { SecondReviewTime = trialConfirmTime }); } - + else + { + if (sv.SecondReviewState == SecondReviewState.WaitAudit) + { + foreach (var item in secondReviewList) + { + if (item.SecondReviewTime == trialConfirmTime) + { + item.SignTime = null; + item.FullName = ""; + item.UserName = ""; + } + } + } + } + secondReviewList = secondReviewList.OrderByDescending(t => t.SecondReviewTime).ToList(); } + return new TrialVisitQADTO { - QCQuestionAnswerList = qacheckList, + //QCQuestionAnswerList = qacheckList, - SecondReviewTimeList = secondReviewTimeList, + SecondReviewList = secondReviewList, IsHaveStudyClinicalData = await _clinicalDataTrialSetRepository.AnyAsync(x => x.IsConfirm && x.TrialId == sv.TrialId && x.ClinicalDataLevel == ClinicalLevel.Study), StudyList = temp.StudyList, diff --git a/IRaCIS.Core.Application/Service/QC/QCOperationService.cs b/IRaCIS.Core.Application/Service/QC/QCOperationService.cs index 704d3b11e..92fc776e0 100644 --- a/IRaCIS.Core.Application/Service/QC/QCOperationService.cs +++ b/IRaCIS.Core.Application/Service/QC/QCOperationService.cs @@ -727,9 +727,15 @@ namespace IRaCIS.Core.Application.Image.QA //[Authorize(Policy = IRaCISPolicy.IQC)] public async Task AddOrUpdateQCQuestionAnswerList(QCQuestionAnswerCommand[] qcQuestionAnswerCommands, Guid trialId, Guid subjectVisitId, [FromRoute] TrialQCProcess trialQCProcess, [FromRoute] CurrentQC currentQCType) { + if (currentQCType == CurrentQC.SecondReview) + { + //二次复核自动领取,如果有人先领取了,那么后续不能操作 + await _subjectVisitRepository.UpdatePartialFromQueryAsync(t => t.Id == subjectVisitId && t.CurrentActionUserId == null, u => new SubjectVisit() { CurrentActionUserId = _userInfo.UserRoleId }); + } //验证是否能操作 await VerifyIsCanQCAsync(null, subjectVisitId); + var trialConfirmTime = _trialRepository.Where(t => t.Id == trialId).Select(t => t.QCQuestionConfirmedTime).FirstOrDefault(); //更新 @@ -2081,8 +2087,43 @@ namespace IRaCIS.Core.Application.Image.QA } + public async Task QCSecondReviewPassedOrFailed(Guid trialId, Guid subjectVisitId, bool isSecondPass) + { + if (!await _trialUserRoleRepository.AnyAsync(t => t.TrialId == trialId && t.UserId == _userInfo.UserRoleId)) + { + //---您已经被移出项目,没有操作权限。 + return ResponseOutput.NotOk(_localizer["QCOperation_RemoveItem"]); + } + var sv = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId); + + await VerifyIsCanQCAsync(sv); + + var trialConfirmTime = _trialRepository.Where(t => t.Id == sv.TrialId).Select(t => t.QCQuestionConfirmedTime).FirstOrDefault(); + + if (sv.SecondReviewState == SecondReviewState.WaitAudit) + { + if (isSecondPass) + { + sv.SecondReviewState = SecondReviewState.AuditPassed; + + } + else + { + sv.SecondReviewState = SecondReviewState.AuditFailed; + } + + await _trialQCQuestionAnswerRepository.BatchUpdateNoTrackingAsync(t => t.SubjectVisitId == sv.Id && t.CurrentQCEnum == CurrentQC.SecondReview && t.SecondReviewTime == trialConfirmTime, u => new TrialQCQuestionAnswer() + { + UpdateUserId = _userInfo.UserRoleId, + UpdateTime = DateTime.Now + }); + } + await _subjectVisitRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } /// /// 设置、取消 访视紧急 diff --git a/IRaCIS.Core.Domain/BaseModel/Entity.cs b/IRaCIS.Core.Domain/BaseModel/Entity.cs index 55847a041..8fc614e3e 100644 --- a/IRaCIS.Core.Domain/BaseModel/Entity.cs +++ b/IRaCIS.Core.Domain/BaseModel/Entity.cs @@ -108,6 +108,11 @@ public abstract class BaseFullAuditEntity : Entity, IAuditUpdate, IAuditAdd [ForeignKey("CreateUserId")] [JsonIgnore] public UserRole CreateUserRole { get; set; } + + + [ForeignKey("CreateUserId")] + [JsonIgnore] + public UserRole UpdateUserRole { get; set; } } public abstract class BaseFullDeleteAuditEntity : Entity, IAuditUpdate, IAuditAdd, ISoftDelete {