Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing Details

Uat_IRC_Net8
he 2025-07-28 17:04:59 +08:00
commit 71a4b241fd
7 changed files with 121 additions and 98 deletions

View File

@ -9,7 +9,7 @@ namespace IRaCIS.Core.Application.Contracts
Task<PageOutput<UnionStudyMonitorModel>> GetDicomAndNoneDicomStudyMonitorList(StudyQuery studyQuery); Task<PageOutput<UnionStudyMonitorModel>> GetDicomAndNoneDicomStudyMonitorList(StudyQuery studyQuery);
(List<string> SeriesInstanceUid, List<string> SopInstanceUid) GetHasUploadSeriesAndInstance(Guid studyId); (List<string> SeriesInstanceUid, List<string> SopInstanceUid) GetHasUploadSeriesAndInstance(Guid studyId);
DicomTrialSiteSubjectInfo GetSaveToDicomInfo(Guid subjectVisitId); DicomTrialSiteSubjectInfo GetSaveToDicomInfo(Guid subjectVisitId);
IResponseOutput<DicomStudyDTO> Item(Guid studyId); IResponseOutput<DicomStudyDTO> Item(Guid studyId,bool? isPacs);
Task<FileContentResult> Preview(Guid studyId); Task<FileContentResult> Preview(Guid studyId);
//IResponseOutput<List<VerifyStudyUploadResult>> VerifyStudyAllowUpload(VerifyUploadOrReupload verifyInfo); //IResponseOutput<List<VerifyStudyUploadResult>> VerifyStudyAllowUpload(VerifyUploadOrReupload verifyInfo);
} }

View File

@ -11,6 +11,8 @@ namespace IRaCIS.Core.Application.Services
[AllowAnonymous] [AllowAnonymous]
public class SeriesService(IRepository<DicomSeries> _seriesRepository, public class SeriesService(IRepository<DicomSeries> _seriesRepository,
IRepository<DicomInstance> _instanceRepository, IRepository<DicomInstance> _instanceRepository,
IRepository<SCPSeries> _scpSeriesRepository,
IRepository<SCPInstance> _scpInstanceRepository,
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IWebHostEnvironment _hostEnvironment) : BaseService IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IWebHostEnvironment _hostEnvironment) : BaseService
{ {
@ -22,104 +24,96 @@ namespace IRaCIS.Core.Application.Services
/// <param name="studyId"> Dicom检查的Id </param> /// <param name="studyId"> Dicom检查的Id </param>
/// <param name="isReading"></param> /// <param name="isReading"></param>
[HttpGet, Route("{studyId:guid}")] [HttpGet, Route("{studyId:guid}")]
public async Task<IResponseOutput<List<DicomSeriesDTO>>> List(Guid studyId, bool? isReading) public async Task<IResponseOutput<List<DicomSeriesDTO>>> List(Guid studyId, bool? isReading, bool? isPacs)
{ {
//质控的时候要标记序列和instance 删除 ,所以要返回全部,但是 质控通过后pm 进去看的时候要看过滤后的 if (isPacs == true)
var qcAuditState = await _seriesRepository.Where(s => s.StudyId == studyId).Select(t => t.DicomStudy.SubjectVisit.AuditState).FirstOrDefaultAsync();
//质控通过以后,预览过滤删除的 质控之前的不过滤
var isQCFinished = qcAuditState == AuditStateEnum.QCPassed;
//断点
var seriesList = await _seriesRepository.Where(s => s.StudyId == studyId).IgnoreQueryFilters()
.WhereIf(isQCFinished, t => t.IsDeleted == false)
.WhereIf(isReading == true, t => t.IsReading == true)
.OrderBy(s => s.SeriesNumber).ThenBy(s => s.SeriesTime).ThenBy(s => s.CreateTime)
.ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).ToListAsync();
var instanceList = await _instanceRepository.Where(s => s.StudyId == studyId).IgnoreQueryFilters()
.WhereIf(isQCFinished, t => t.IsDeleted == false)
.WhereIf(isReading == true, t => t.IsReading == true)
.OrderBy(t => t.SeriesId).ThenBy(t => t.InstanceNumber)
.ThenBy(s => s.InstanceTime).ThenBy(s => s.CreateTime)
.Select(t => new { t.SeriesId, t.Id, t.Path, t.NumberOfFrames, t.InstanceNumber, t.HtmlPath, t.IsReading, t.IsDeleted, t.FileSize }).ToListAsync();//.GroupBy(u => u.SeriesId);
foreach (var series in seriesList)
{ {
var seriesList = await _scpSeriesRepository.Where(s => s.StudyId == studyId).IgnoreQueryFilters()
.OrderBy(s => s.SeriesNumber).ThenBy(s => s.SeriesTime).ThenBy(s => s.CreateTime)
.ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).ToListAsync();
series.InstanceInfoList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k => var instanceList = await _scpInstanceRepository.Where(s => s.StudyId == studyId).IgnoreQueryFilters()
new InstanceBasicInfo() .OrderBy(t => t.SeriesId).ThenBy(t => t.InstanceNumber)
{ .ThenBy(s => s.InstanceTime).ThenBy(s => s.CreateTime)
Id = k.Id, .Select(t => new { t.SeriesId, t.Id, t.Path, t.NumberOfFrames, t.InstanceNumber, t.FileSize }).ToListAsync();//.GroupBy(u => u.SeriesId);
NumberOfFrames = k.NumberOfFrames,
HtmlPath = k.HtmlPath,
Path = k.Path,
InstanceNumber = k.InstanceNumber,
IsReading = k.IsReading,
IsDeleted = k.IsDeleted,
FileSize = k.FileSize
}).ToList();
series.InstanceCount = series.InstanceInfoList.Count; foreach (var series in seriesList)
{
series.InstanceInfoList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k =>
new InstanceBasicInfo()
{
Id = k.Id,
NumberOfFrames = k.NumberOfFrames,
HtmlPath = string.Empty,
Path = k.Path,
InstanceNumber = k.InstanceNumber,
IsReading = true,
IsDeleted = false,
FileSize = k.FileSize
}).ToList();
series.InstanceCount = series.InstanceInfoList.Count;
}
return ResponseOutput.Ok(seriesList);
}
else
{
//质控的时候要标记序列和instance 删除 ,所以要返回全部,但是 质控通过后pm 进去看的时候要看过滤后的
var qcAuditState = await _seriesRepository.Where(s => s.StudyId == studyId).Select(t => t.DicomStudy.SubjectVisit.AuditState).FirstOrDefaultAsync();
//质控通过以后,预览过滤删除的 质控之前的不过滤
var isQCFinished = qcAuditState == AuditStateEnum.QCPassed;
//断点
var seriesList = await _seriesRepository.Where(s => s.StudyId == studyId).IgnoreQueryFilters()
.WhereIf(isQCFinished, t => t.IsDeleted == false)
.WhereIf(isReading == true, t => t.IsReading == true)
.OrderBy(s => s.SeriesNumber).ThenBy(s => s.SeriesTime).ThenBy(s => s.CreateTime)
.ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).ToListAsync();
var instanceList = await _instanceRepository.Where(s => s.StudyId == studyId).IgnoreQueryFilters()
.WhereIf(isQCFinished, t => t.IsDeleted == false)
.WhereIf(isReading == true, t => t.IsReading == true)
.OrderBy(t => t.SeriesId).ThenBy(t => t.InstanceNumber)
.ThenBy(s => s.InstanceTime).ThenBy(s => s.CreateTime)
.Select(t => new { t.SeriesId, t.Id, t.Path, t.NumberOfFrames, t.InstanceNumber, t.HtmlPath, t.IsReading, t.IsDeleted, t.FileSize }).ToListAsync();//.GroupBy(u => u.SeriesId);
foreach (var series in seriesList)
{
series.InstanceInfoList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k =>
new InstanceBasicInfo()
{
Id = k.Id,
NumberOfFrames = k.NumberOfFrames,
HtmlPath = k.HtmlPath,
Path = k.Path,
InstanceNumber = k.InstanceNumber,
IsReading = k.IsReading,
IsDeleted = k.IsDeleted,
FileSize = k.FileSize
}).ToList();
series.InstanceCount = series.InstanceInfoList.Count;
}
return ResponseOutput.Ok(seriesList);
} }
#region 暂时废弃
//bool hasKeyInstance = false;
//var SeriesIdList = _imageLabelRepository.Where(u => u.TpCode == tpCode).Select(s => s.SeriesId).Distinct().ToList();
//var instanceIdList = _imageLabelRepository.Where(u => u.TpCode == tpCode).Select(s => s.InstanceId).Distinct().ToList();
//foreach (var item in seriesList)
//{
// if (SeriesIdList.Contains(item.Id))
// {
// item.HasLabel = true;
// hasKeyInstance = true;
// }
// else item.HasLabel = false;
//}
//if (hasKeyInstance)
//{
// seriesList.Add(new DicomSeriesWithLabelDTO
// {
// KeySeries = true,
// Id = SeriesIdList[0],
// InstanceCount = instanceIdList.Count,
// HasLabel = true,
// Modality = seriesList[0].Modality,
// Description = "Key Series"
// });
//}
//var idList = await _instanceRepository.Where(s => s.StudyId == studyId).OrderBy(t => t.SeriesId).ThenBy(t => t.InstanceNumber)
// .ThenBy(s => s.InstanceTime).ThenBy(s => s.CreateTime)
// .Select(t => new { t.SeriesId, t.Id, t.Path }).ToListAsync();//.GroupBy(u => u.SeriesId);
//foreach (var item in seriesList)
//{
// if (item.KeySeries)
// {
// item.InstanceList = instanceIdList;
// }
// else
// {
// //item.InstanceList = idList.Where(s => s.SeriesId == item.Id).OrderBy(t => t.InstanceNumber)
// // .ThenBy(s => s.InstanceTime).ThenBy(s => s.CreateTime).Select(u => u.Id).ToList();
// item.InstanceList = idList.Where(s => s.SeriesId == item.Id).Select(u => u.Id).ToList();
// item.InstancePathList = idList.Where(s => s.SeriesId == item.Id).Select(u => u.Path).ToList();
// }
//}
#endregion
return ResponseOutput.Ok(seriesList);
} }

View File

@ -15,11 +15,11 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
public class StudyService(IRepository<SubjectVisit> _subjectVisitRepository, public class StudyService(IRepository<SubjectVisit> _subjectVisitRepository,
IRepository<DicomInstance> _dicomInstanceRepository, IRepository<DicomInstance> _dicomInstanceRepository,
IRepository<DicomSeries> _dicomSeriesRepository, IRepository<DicomSeries> _dicomSeriesRepository,
IRepository<DicomStudy> _dicomstudyRepository, IRepository<DicomStudy> _dicomStudyRepository,
IRepository<Dictionary> _dictionaryRepository, IRepository<Dictionary> _dictionaryRepository,
IRepository<Trial> _trialRepository, IRepository<Trial> _trialRepository,
IRepository<VisitTask> _visitTaskRepository, IRepository<VisitTask> _visitTaskRepository,
IRepository<DicomStudy> _dicomStudyRepository, IRepository<SCPStudy> _scpStudyRepository,
IRepository<Subject> _subjectRepository, IRepository<Subject> _subjectRepository,
IRepository<StudyMonitor> _studyMonitorRepository, IRepository<StudyMonitor> _studyMonitorRepository,
IRepository<SystemAnonymization> _systemAnonymizationRepository, IRepository<SystemAnonymization> _systemAnonymizationRepository,
@ -159,7 +159,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
studyMonitor.FileSize = incommand.Study.SeriesList.SelectMany(t => t.InstanceList).Sum(t => t.FileSize); studyMonitor.FileSize = incommand.Study.SeriesList.SelectMany(t => t.InstanceList).Sum(t => t.FileSize);
var studyId = IdentifierHelper.CreateGuid(incommand.Study.StudyInstanceUid, incommand.TrialId.ToString()); var studyId = IdentifierHelper.CreateGuid(incommand.Study.StudyInstanceUid, incommand.TrialId.ToString());
var findStudy = await _dicomstudyRepository.FirstOrDefaultAsync(t => t.Id == studyId); var findStudy = await _dicomStudyRepository.FirstOrDefaultAsync(t => t.Id == studyId);
studyMonitor.StudyId = studyId; studyMonitor.StudyId = studyId;
studyMonitor.StudyCode = findStudy?.StudyCode ?? ""; studyMonitor.StudyCode = findStudy?.StudyCode ?? "";
@ -183,7 +183,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
using (await @lock.AcquireAsync()) using (await @lock.AcquireAsync())
{ {
//查询数据库获取最大的Code 没有记录则为0 //查询数据库获取最大的Code 没有记录则为0
var dbStudyCodeIntMax = _dicomstudyRepository.Where(s => s.TrialId == trialId).Select(t => t.Code).DefaultIfEmpty().Max(); var dbStudyCodeIntMax = _dicomStudyRepository.Where(s => s.TrialId == trialId).Select(t => t.Code).DefaultIfEmpty().Max();
//获取缓存中的值 并发的时候,需要记录,已被占用的值 这样其他线程在此占用的最大的值上递增 //获取缓存中的值 并发的时候,需要记录,已被占用的值 这样其他线程在此占用的最大的值上递增
var cacheMaxCodeInt = await _fusionCache.GetOrDefaultAsync<int>(CacheKeys.TrialStudyMaxCode(trialId)); var cacheMaxCodeInt = await _fusionCache.GetOrDefaultAsync<int>(CacheKeys.TrialStudyMaxCode(trialId));
@ -213,7 +213,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
SpecialArchiveStudyDeal(addStudy); SpecialArchiveStudyDeal(addStudy);
modalitys = addStudy.Modalities; modalitys = addStudy.Modalities;
await _dicomstudyRepository.AddAsync(addStudy); await _dicomStudyRepository.AddAsync(addStudy);
@ -628,9 +628,17 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
/// <param name="studyId"> Dicom检查的Id </param> /// <param name="studyId"> Dicom检查的Id </param>
[HttpGet, Route("{studyId:guid}")] [HttpGet, Route("{studyId:guid}")]
[AllowAnonymous] [AllowAnonymous]
public IResponseOutput<DicomStudyDTO> Item(Guid studyId) public IResponseOutput<DicomStudyDTO> Item(Guid studyId,bool? isPacs)
{ {
return ResponseOutput.Ok(_mapper.Map<DicomStudyDTO>(_dicomStudyRepository.Where().FirstOrDefault(s => s.Id == studyId))); if (isPacs == true)
{
return ResponseOutput.Ok(_mapper.Map<DicomStudyDTO>(_scpStudyRepository.Where().FirstOrDefault(s => s.Id == studyId)));
}
else
{
return ResponseOutput.Ok(_mapper.Map<DicomStudyDTO>(_dicomStudyRepository.Where().FirstOrDefault(s => s.Id == studyId)));
}
} }

View File

@ -154,6 +154,11 @@ namespace IRaCIS.Core.Application.Service
.ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.Subject.TrialSite.TrialSiteCode)) .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.Subject.TrialSite.TrialSiteCode))
; ;
CreateMap<SCPStudy, DicomStudyDTO>();
CreateMap<SCPSeries, DicomSeriesDTO>();
} }
} }

View File

@ -717,7 +717,7 @@ namespace IRaCIS.Core.Application.Image.QA
var qaChallengeQuery = _qcChallengeRepository.Where(t => t.SubjectVisitId == subjectVisitId && t.QCProcessEnum == trialQCProcess) var qaChallengeQuery = _qcChallengeRepository.Where(t => t.SubjectVisitId == subjectVisitId && t.QCProcessEnum == trialQCProcess)
.ProjectTo<ChallengeAndDialog>(_mapper.ConfigurationProvider, new { currentUserId = _userInfo.UserRoleId }); .ProjectTo<ChallengeAndDialog>(_mapper.ConfigurationProvider, new { currentUserId = _userInfo.UserRoleId });
var list = await qaChallengeQuery.ToListAsync(); var list = await qaChallengeQuery.OrderByDescending(t=>t.CreateTime).ToListAsync();
//list.ForEach(t => t.DialogList.ToList().ForEach(u => u.IsCurrentUser = _userInfo.Id == u.CreateUserId)); //list.ForEach(t => t.DialogList.ToList().ForEach(u => u.IsCurrentUser = _userInfo.Id == u.CreateUserId));

View File

@ -1269,6 +1269,7 @@ namespace IRaCIS.Core.Application.Image.QA
var isSecondReview = _subjectVisitRepository.Any(t => t.Id == inDto.SubjectVisitId && t.SecondReviewState == SecondReviewState.AuditPassed); var isSecondReview = _subjectVisitRepository.Any(t => t.Id == inDto.SubjectVisitId && t.SecondReviewState == SecondReviewState.AuditPassed);
var currentActionList = await _subjectVisitRepository.Where(x => x.TrialId == inDto.TrialId && x.CurrentActionUserId == _userInfo.UserRoleId) var currentActionList = await _subjectVisitRepository.Where(x => x.TrialId == inDto.TrialId && x.CurrentActionUserId == _userInfo.UserRoleId)
.Where(t => t.Subject.Status != SubjectStatus.EndOfVisit)
.WhereIf(isDistinguishType, t => isSecondReview == false ? t.SecondReviewState == SecondReviewState.None : t.SecondReviewState == SecondReviewState.WaitAudit) .WhereIf(isDistinguishType, t => isSecondReview == false ? t.SecondReviewState == SecondReviewState.None : t.SecondReviewState == SecondReviewState.WaitAudit)
.OrderByDescending(x => x.IsUrgent) .OrderByDescending(x => x.IsUrgent)
.ThenBy(x => x.Subject.Code).ThenBy(x => x.VisitNum).ToListAsync(); .ThenBy(x => x.Subject.Code).ThenBy(x => x.VisitNum).ToListAsync();
@ -1293,6 +1294,7 @@ namespace IRaCIS.Core.Application.Image.QA
break; break;
case TrialQCProcess.SingleAudit: case TrialQCProcess.SingleAudit:
visitList = await _subjectVisitRepository visitList = await _subjectVisitRepository
.Where(t => t.Subject.Status != SubjectStatus.EndOfVisit)
.WhereIf(isDistinguishType, t => isSecondReview == false ? t.SecondReviewState == SecondReviewState.None : t.SecondReviewState == SecondReviewState.WaitAudit) .WhereIf(isDistinguishType, t => isSecondReview == false ? t.SecondReviewState == SecondReviewState.None : t.SecondReviewState == SecondReviewState.WaitAudit)
.Where(x => !x.SubjectVisitImageBackRecordList.Any(t => t.ImageBackState == ImageBackStateEnum.None)) .Where(x => !x.SubjectVisitImageBackRecordList.Any(t => t.ImageBackState == ImageBackStateEnum.None))
.Where(x => x.SubmitState == SubmitStateEnum.Submitted && x.SecondReviewState == SecondReviewState.None .Where(x => x.SubmitState == SubmitStateEnum.Submitted && x.SecondReviewState == SecondReviewState.None
@ -1330,6 +1332,7 @@ namespace IRaCIS.Core.Application.Image.QA
case TrialQCProcess.DoubleAudit: case TrialQCProcess.DoubleAudit:
visitList = await _subjectVisitRepository visitList = await _subjectVisitRepository
.Where(t => t.Subject.Status != SubjectStatus.EndOfVisit)
.WhereIf(isDistinguishType, t => isSecondReview == false ? t.SecondReviewState == SecondReviewState.None : t.SecondReviewState == SecondReviewState.WaitAudit) .WhereIf(isDistinguishType, t => isSecondReview == false ? t.SecondReviewState == SecondReviewState.None : t.SecondReviewState == SecondReviewState.WaitAudit)
.Where(x => !x.SubjectVisitImageBackRecordList.Any(t => t.ImageBackState == ImageBackStateEnum.None)) .Where(x => !x.SubjectVisitImageBackRecordList.Any(t => t.ImageBackState == ImageBackStateEnum.None))
.Where(x => x.SubmitState == SubmitStateEnum.Submitted && x.TrialId == inDto.TrialId && .Where(x => x.SubmitState == SubmitStateEnum.Submitted && x.TrialId == inDto.TrialId &&
@ -2305,6 +2308,19 @@ namespace IRaCIS.Core.Application.Image.QA
{ {
//复核时 删除qc 所做的答案 //复核时 删除qc 所做的答案
sv.AuditState = AuditStateEnum.ToAudit;
sv.CurrentActionUserExpireTime =null;
sv.CurrentActionUserId = null;
sv.IsTake = false;
sv.PreliminaryAuditUserId = null;
sv.PreliminaryAuditTime = null;
sv.ReviewAuditUserId = null;
sv.ReviewAuditTime = null;
sv.SecondReviewState = SecondReviewState.None;
await _trialQCQuestionAnswerRepository.BatchDeleteNoTrackingAsync(t => t.SubjectVisitId == qcChallenge.SubjectVisitId); await _trialQCQuestionAnswerRepository.BatchDeleteNoTrackingAsync(t => t.SubjectVisitId == qcChallenge.SubjectVisitId);
} }

View File

@ -7,7 +7,7 @@
//访视结束 //访视结束
OutOfVisit = 2, OutOfVisit = 2,
//访视中止 //受试者中止
EndOfVisit = 3, EndOfVisit = 3,
} }
} }