using IRaCIS.Application.Interfaces; using IRaCIS.Application.Contracts; using IRaCIS.Core.Application.Filter; using IRaCIS.Core.Domain.Share; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; using IRaCIS.Core.Application.Auth; using MassTransit; using Panda.DynamicWebApi.Attributes; using DocumentFormat.OpenXml.Spreadsheet; using AutoMapper.EntityFrameworkCore; using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Application.Service.Reading.Dto; using Microsoft.Extensions.Options; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Json; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SharpCompress.Common; using System.Reactive.Subjects; using Subject = IRaCIS.Core.Domain.Models.Subject; using IRaCIS.Core.Application.ViewModel; using Medallion.Threading; using IRaCIS.Core.Infrastructure; using IRaCIS.Core.Application.Contracts; using MailKit.Search; using DocumentFormat.OpenXml.Office2010.Excel; using IRaCIS.Core.Application.Contracts.Dicom.DTO; using IRaCIS.Core.Application.Helper; using NPOI.SS.Formula.Functions; using System.Linq; using System.Linq.Dynamic.Core; using System.Text; using DocumentFormat.OpenXml.EMMA; using Azure; using System.IO.Compression; using static IRaCIS.Core.Domain.Share.StaticData; using FellowOakDicom; using DocumentFormat.OpenXml.Office2010.Drawing; using IDistributedLockProvider = Medallion.Threading.IDistributedLockProvider; using DocumentFormat.OpenXml.InkML; using Microsoft.AspNetCore.Hosting; using IRaCIS.Core.Infrastructure.Encryption; using FellowOakDicom.Network.Client; using FellowOakDicom.Network; using Microsoft.Extensions.Logging; using DocumentFormat.OpenXml.Wordprocessing; using System; using static Microsoft.EntityFrameworkCore.DbLoggerCategory; using DocumentFormat.OpenXml.Vml.Office; using IRaCIS.Core.Infra.EFCore.Migrations; using System.Dynamic; using System.Threading.Channels; using NPOI.HSSF.Record.Chart; using IRaCIS.Core.Application.Helper.OtherTool; namespace IRaCIS.Application.Services { [ApiExplorerSettings(GroupName = "Trial")] public class PatientService(IRepository _studySubjectVisitRepository, IRepository _subjectPatientRepository, IRepository _trialRepository, IRepository _patientRepository, IRepository _studyRepository, IRepository _subjectRepository, IRepository _subjectVisitRepository, IRepository _subejctVisitDownloadRepository, IRepository _SCPImageUploadRepository, IRepository _userRepository, IRepository _hIRHospitalRepository, IRepository _dicomAEReposiotry, IRepository _scpInstanceRepository, IOptionsMonitor _basicSystemConfigConfig, ILogger _logger, IDistributedLockProvider _distributedLockProvider, IMapper _mapper, IUserInfo _userInfo, IWebHostEnvironment _hostEnvironment, IStringLocalizer _localizer, IFusionCache _fusionCache ) : BaseService { #region 医院信息管理 /// /// 获取医院的配置信息 /// /// [AllowAnonymous] public async Task GetHospitalInfo() { var query = _hIRHospitalRepository.Where(t => t.IsDefault == true).ProjectTo(_mapper.ConfigurationProvider); return await query.FirstNotNullAsync(); } [AllowAnonymous] [HttpPut] public async Task UpdateDefaultHospital(Guid hirHospitalId) { await _hIRHospitalRepository.BatchUpdateNoTrackingAsync(t => t.Id != hirHospitalId, u => new HIRHospital() { IsDefault = false }); await _hIRHospitalRepository.BatchUpdateNoTrackingAsync(t => t.Id == hirHospitalId, u => new HIRHospital() { IsDefault = true }); var hospitalInfo = _hIRHospitalRepository.Where(t => t.IsDefault == true).FirstNotNullAsync(); //切换了医院,验证获取医院这里也需要实时变更 await _fusionCache.SetAsync(CacheKeys.Hospital, hospitalInfo, TimeSpan.FromDays(7)); return ResponseOutput.Ok(); } [AllowAnonymous] [HttpPost] public async Task> GetHIRHospitalList(HIRHospitalQuery inQuery) { var hIRHospitalQueryable = _hIRHospitalRepository .WhereIf(!string.IsNullOrWhiteSpace(inQuery.HospitalName), t => t.HospitalName.Contains(inQuery.HospitalName) || t.HospitalAliasName.Contains(inQuery.HospitalName)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Country), t => t.Country.Contains(inQuery.Country)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.City), t => t.City.Contains(inQuery.Country)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Phone), t => t.Phone.Contains(inQuery.Phone)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Province), t => t.Province.Contains(inQuery.Province)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Address), t => t.Address.Contains(inQuery.Address)) .ProjectTo(_mapper.ConfigurationProvider); var list = await hIRHospitalQueryable.ToPagedListAsync(inQuery); return list; } public async Task AddOrUpdateHIRHospital(HIRHospitalAddOrEdit addOrEditHIRHospital) { var exp = new EntityVerifyExp() { VerifyExp = h => h.IsDefault.Equals(addOrEditHIRHospital.IsDefault), //"默认医院只允许一个" VerifyMsg = _localizer["Patient_OnlyOneDefaultHospital"] }; var exp2 = new EntityVerifyExp() { VerifyExp = h => h.HospitalCode.Equals(addOrEditHIRHospital.HospitalCode) || h.HospitalName.Equals(addOrEditHIRHospital.HospitalName) || h.HospitalAliasName.Equals(addOrEditHIRHospital.HospitalAliasName), //医院编码、名称不能重复 VerifyMsg = _localizer["Patient_RepeatHospitalCodeOrName"] }; var entity = await _hIRHospitalRepository.InsertOrUpdateAsync(addOrEditHIRHospital, true, exp, exp2); return ResponseOutput.Ok(entity.Id.ToString()); } [HttpDelete("{hIRHospitalId:guid}")] public async Task DeleteHIRHospital(Guid hIRHospitalId) { var success = await _hIRHospitalRepository.DeleteFromQueryAsync(t => t.Id == hIRHospitalId, true); return ResponseOutput.Ok(); } #endregion #region 项目管理 /// /// 获取系统已确认的标准 /// /// /// public async Task GetSystemConfirmedCreiterionList([FromServices] IRepository _readingQuestionCriterionSystemRepository) { var list = await _readingQuestionCriterionSystemRepository.Where(x => x.IsEnable && x.IsCompleteConfig).OrderBy(t => t.ShowOrder).Select(t => new { t.CriterionType, t.CriterionName, t.Id, t.Description }).ToListAsync(); return ResponseOutput.Ok(list); } /// /// 获取项目列表 (PM CRC 共用) /// /// /// [HttpPost] public async Task>> GetTrialList(NewTrialQuery inQuery) { var trialQuery = _trialRepository .WhereIf(!string.IsNullOrWhiteSpace(inQuery.ResearchProgramNo), t => t.ResearchProgramNo.Contains(inQuery.ResearchProgramNo)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.ExperimentName), t => t.ExperimentName.Contains(inQuery.ExperimentName)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SponsorName), t => t.Sponsor.Contains(inQuery.SponsorName)) .WhereIf(inQuery.TrialType != null, t => t.TrialType == inQuery.TrialType) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.TrialCode), t => t.TrialCode.Contains(inQuery.TrialCode)) .WhereIf(_userInfo.UserTypeEnumInt != (int)UserTypeEnum.SuperAdmin && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.Admin && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.OA , t => t.TrialUserList.Any(t => t.UserId == _userInfo.Id && t.IsDeleted == false) && t.IsDeleted == false) .ProjectTo(_mapper.ConfigurationProvider); var pageList = await trialQuery.ToPagedListAsync(inQuery, nameof(NewTrialQuery.ResearchProgramNo)); return ResponseOutput.Ok(pageList); } [HttpGet("{trialId:guid}")] public async Task GetTrialInfo(Guid trialId, [FromServices] IOptionsMonitor _basicSystemConfigConfig) { var trial = (await _trialRepository.Where(o => o.Id == trialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider, new { isEn_Us = _userInfo.IsEn_Us }).FirstOrDefaultAsync()).IfNullThrowException(); if (!string.IsNullOrEmpty(trial.AuthorizationEncrypt)) { try { //解析加密信息 var decodedText = AesEncryption.Decrypt(trial.AuthorizationEncrypt, _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt"); var authInfo = JsonConvert.DeserializeObject(decodedText); return ResponseOutput.Ok(trial, authInfo); } catch (Exception) { //项目未授权,请确认。 return ResponseOutput.Ok(trial, msg: _localizer["Patient_TrialNotAuthorization"], code: ApiResponseCodeEnum.NeedTips); } } return ResponseOutput.Ok(trial); } /// /// 添加更新项目 /// /// /// /// /// /// [HttpPost] [UnitOfWork] public async Task AddOrUpdateTrial(AddOrUpdateTrialCommand inCommand, [FromServices] IOptionsMonitor _systemEmailSendConfig, [FromServices] IOptionsMonitor _basicSystemConfigConfig, [FromServices] IOptionsMonitor _systemHospitalOption, [FromServices] IRepository _trialDictionaryRepository, [FromServices] IRepository _trialUserRepository, [FromServices] IFusionCache _provider) { var code = _systemHospitalOption.CurrentValue.HospitalCode; //if (await _trialRepository.CountAsync(u => u.TrialStatusStr == StaticData.TrialState.TrialOngoing) > _systemHospitalOption.CurrentValue.TrialKeepCount) //{ // throw new BusinessValidationFailedException($"已超过当前系统配置的未启动项目数量的限制,您的操作被限制,请获取授权再进行操作!"); //} inCommand.ResearchProgramNo = inCommand.ResearchProgramNo.Trim(); if (inCommand.Id == Guid.Empty || inCommand.Id == null) { if (await _trialRepository.AnyAsync(u => u.TrialCode == inCommand.TrialCode)) { //---已经存在相同的项目编号。 throw new BusinessValidationFailedException(_localizer["Trial_DuplicateProjectNumber"]); } foreach (var criterionType in inCommand.CriterionTypeList) { if (await _trialRepository.AnyAsync(u => u.ResearchProgramNo == inCommand.ResearchProgramNo && u.CriterionTypes.Contains($"|{(int)criterionType}|"))) { //"已存在研究方案号为{inCommand.ResearchProgramNo},该标准的项目" throw new BusinessValidationFailedException(_localizer["Patient_SameResearchProgramNoTrial", inCommand.ResearchProgramNo]); } } var dbMaxCode = await _trialRepository.Where(t => t.CreateTime.Year == DateTime.Now.Year && t.TrialType == inCommand.TrialType).Select(t => t.Code).DefaultIfEmpty().MaxAsync(); var currentYearMaxCodeNext = dbMaxCode + 1; var trial = _mapper.Map(inCommand); //trial.Id = NewId.NextGuid(); var yearStr = DateTime.Now.Year.ToString(); trial.Code = currentYearMaxCodeNext; trial.TrialCode = (trial.TrialType == TrialType.ScientificResearch ? $"Z{code}RT" : $"Z{code}CT") + yearStr.Substring(yearStr.Length - 2) + currentYearMaxCodeNext.ToString("D3"); trial.CriterionTypes = $"|{string.Join('|', inCommand.CriterionTypeList.Select(x => ((int)x).ToString()).ToList())}|"; //多选信息 inCommand.ModalityIds.ForEach(modalityId => trial.TrialDicList.Add(new TrialDictionary() { DictionaryId = modalityId, KeyName = StaticData.Modality, TrialId = trial.Id })); //添加项目后 项目状态变更为申请下载简历 trial.TrialEnrollStatus = (int)TrialEnrollStatus.ChooseDoctor; trial = await _trialRepository.AddAsync(trial); //如果是PM, 则需要将该人员添加到 运维人员表 //添加运维人员PM await _trialUserRepository.AddAsync(new TrialUser() { TrialId = trial.Id, UserId = _userInfo.Id, JoinTime = DateTime.Now }); //默认采用系统邮件 trial.EmailAuthorizationCode = _systemEmailSendConfig.CurrentValue.AuthorizationCode; trial.EmailFromEmail = _systemEmailSendConfig.CurrentValue.FromEmail; trial.EmailFromName = _systemEmailSendConfig.CurrentValue.FromName; trial.EmailSMTPServerAddress = _systemEmailSendConfig.CurrentValue.Host; trial.EmailSMTPServerPort = _systemEmailSendConfig.CurrentValue.Port; trial.IsConfigureEmail = true; trial.CreateTime = DateTime.Now; var success = await _trialRepository.SaveChangesAsync(); var caheInfo = new TrialCacheInfo() { TrialId = trial.Id, TrialStatusStr = trial.TrialStatusStr, CriterionTypes = trial.CriterionTypes, AuthorizationEncrypt = trial.AuthorizationEncrypt, AuthorizationDate = trial.AuthorizationDate, CreateUserId = trial.CreateUserId, TrialCode = trial.TrialCode }; await _provider.SetAsync(trial.Id.ToString(), caheInfo, TimeSpan.FromDays(7)); return ResponseOutput.Ok(trial); } else { var updateModel = inCommand; foreach (var criterionType in inCommand.CriterionTypeList) { if (await _trialRepository.AnyAsync(u => u.ResearchProgramNo == inCommand.ResearchProgramNo && u.CriterionTypes.Contains($"|{(int)criterionType}|") && u.Id != updateModel.Id)) { throw new BusinessValidationFailedException(_localizer["Patient_SameResearchProgramNoTrial", inCommand.ResearchProgramNo]); } } if (!await _trialRepository.AnyAsync(u => u.Id == inCommand.Id && (u.TrialStatusStr == StaticData.TrialState.TrialInitializing || u.TrialStatusStr == StaticData.TrialState.TrialOngoing))) { //---项目状态只有处于:初始化或者进行中时,才允许操作。 throw new BusinessValidationFailedException(_localizer["Trial_InvalidProjectStatus"]); } // 判断项目Id 是否已经存在 if (await _trialRepository.AnyAsync(u => u.TrialCode == updateModel.TrialCode && u.Id != updateModel.Id)) { //---已经存在相同的项目编号。 throw new BusinessValidationFailedException(_localizer["Trial_DuplicateProjectNumber"]); } var trial = await _trialRepository.FirstOrDefaultAsync(t => t.Id == updateModel.Id); //删除中间表 Title对应的记录 await _trialDictionaryRepository.BatchDeleteNoTrackingAsync(t => t.TrialId == updateModel.Id); //重新插入新的 Title记录 updateModel.ModalityIds.ForEach(modalityId => trial.TrialDicList.Add(new TrialDictionary() { DictionaryId = modalityId, KeyName = StaticData.Modality, TrialId = trial.Id })); _mapper.Map(updateModel, trial); trial.CriterionTypes = $"|{string.Join('|', inCommand.CriterionTypeList.Select(x => ((int)x).ToString()).ToList())}|"; var caheInfo = new TrialCacheInfo() { TrialId = trial.Id, TrialStatusStr = trial.TrialStatusStr, CriterionTypes = trial.CriterionTypes, AuthorizationEncrypt = trial.AuthorizationEncrypt, AuthorizationDate = trial.AuthorizationDate, CreateUserId = trial.CreateUserId, TrialCode = trial.TrialCode }; await _provider.SetAsync(trial.Id.ToString(), caheInfo, TimeSpan.FromDays(7)); var success = await _trialRepository.SaveChangesAsync(); return ResponseOutput.Ok(trial); } } #endregion #region 授权和激活 public async Task> GetTrialAuthorizationInfo(Guid trialId) { var query = _hIRHospitalRepository.Where(t => t.IsDefault == true).ProjectTo(_mapper.ConfigurationProvider); var hospitalInfo = await query.FirstNotNullAsync(); var trialInfo = _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).FirstOrDefault(); var authInfo = new TrialAuthorizationInfo() { TrialId = trialInfo.Id, TrialCode = trialInfo.TrialCode, CreateUserId = trialInfo.CreateUserId, PurchaseDuration = 1, CriterionTypeList = trialInfo.CriterionTypeList, //CriterionTypes = trialInfo.CriterionTypes, HospitalName = hospitalInfo.HospitalName, HospitalCode = hospitalInfo.HospitalCode, }; return ResponseOutput.Ok(authInfo); } /// /// 获取项目授权码 /// /// /// [HttpPost] [AllowAnonymous] public async Task GetTrialAuthorizationCode(TrialAuthorizationInfo authInfo) { // 将明文信息转换成 Base64 编码 string base64EncodedText = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(authInfo))); return ResponseOutput.Ok(base64EncodedText); } /// /// 获取授权码明文信息 /// /// /// [AllowAnonymous] public async Task GetAuthorizationCodeInfo(string authorizationCode) { // 解密 Base64 编码后的数据 byte[] base64DecodedBytes = Convert.FromBase64String(authorizationCode); string decodedText = System.Text.Encoding.UTF8.GetString(base64DecodedBytes); var authInfo = JsonConvert.DeserializeObject(decodedText); if (authInfo == null) { return ResponseOutput.NotOk("不能解析该项目授权码"); } return ResponseOutput.Ok(authInfo); } /// /// 获取项目激活码 /// /// /// /// [AllowAnonymous] [HttpPost] public async Task GetTrialActivationCode(TrialAuthorizationInfo authorizationInfo, [FromServices] IOptionsMonitor _basicSystemConfigConfig) { authorizationInfo.ActiveDeadLineDate = DateTime.Now.Date.AddDays(8).AddSeconds(-1); var info = AesEncryption.Encrypt($"{JsonConvert.SerializeObject(authorizationInfo)}", _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt"); return ResponseOutput.Ok(info); } /// /// 激活码获取明文信息 /// /// /// /// /// public async Task GetActivationCodeInfo(string activationCode, [FromServices] IOptionsMonitor _basicSystemConfigConfig) { var decodedText = string.Empty; try { decodedText = AesEncryption.Decrypt(activationCode, _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt"); } catch (Exception) { //"激活码有误,请核对,无法激活" return ResponseOutput.NotOk(_localizer["Trial_ActivationCodeError"]); } var authInfo = JsonConvert.DeserializeObject(decodedText); if (authInfo != null) { return ResponseOutput.Ok(authInfo); } else { //"激活码信息有误!" return ResponseOutput.NotOk(_localizer["Trial_ActivationCodeError"] ); } } /// /// 设置项目授权信息 /// /// /// /// [HttpPut] public async Task ActivateTrial(Guid trialId, string activationCode, [FromServices] IOptionsMonitor _basicSystemConfigConfig, [FromServices] IFusionCache _provider) { var query = _hIRHospitalRepository.Where(t => t.IsDefault == true).ProjectTo(_mapper.ConfigurationProvider); var hospitalInfo = await query.FirstNotNullAsync(); var hospitalCode = hospitalInfo.HospitalCode; var trialInfo = _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).FirstOrDefault(); var decodedText = string.Empty; try { decodedText = AesEncryption.Decrypt(activationCode, _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt"); } catch (Exception) { //激活码有误,请核对,无法激活 return ResponseOutput.NotOk(_localizer["Trial_ActivationCodeError"]); } var authInfo = JsonConvert.DeserializeObject(decodedText); if (authInfo != null) { if (authInfo.TrialCode != trialInfo.TrialCode || authInfo.CreateUserId != trialInfo.CreateUserId || authInfo.HospitalCode != hospitalCode || authInfo.TrialId != trialInfo.Id || trialInfo.CriterionTypeList.Except(authInfo.CriterionTypeList).Count() != 0) { //"该授权码与该项目不匹配" return ResponseOutput.NotOk(_localizer["Trial_ActivationCodeIsNotCurrentTrialError"]); } if (DateTime.Now > authInfo.ActiveDeadLineDate) { //$"当前时间{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}超过激活时间{authInfo.ActiveDeadLineDate.Value.ToString("yyyy-MM-dd HH:mm:ss")},请联系授权方重新获取激活码" return ResponseOutput.NotOk(_localizer["Trial_ActivationCodeActiveTimeError", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), authInfo.ActiveDeadLineDate.Value.ToString("yyyy-MM-dd HH:mm:ss")]); } //截止日期 var deadLineDate = DateTime.Now.Date.AddMonths(authInfo.PurchaseDuration).AddDays(1).AddSeconds(-1); authInfo.AuthorizationDeadLineDate = deadLineDate; authInfo.ActiveTime = DateTime.Now; var newActivationCode = AesEncryption.Encrypt($"{JsonConvert.SerializeObject(authInfo)}", _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt"); await _trialRepository.BatchUpdateNoTrackingAsync(t => t.Id == trialId, u => new Trial() { AuthorizationEncrypt = newActivationCode, AuthorizationDate = deadLineDate }); var caheInfo = new TrialCacheInfo() { TrialId = trialInfo.Id, TrialStatusStr = trialInfo.TrialStatusStr, AuthorizationEncrypt = newActivationCode, CriterionTypes = trialInfo.CriterionTypes, AuthorizationDate = trialInfo.AuthorizationDate, CreateUserId = trialInfo.CreateUserId, TrialCode = trialInfo.TrialCode }; await _fusionCache.SetAsync(CacheKeys.Trial(trialInfo.Id.ToString()), caheInfo, TimeSpan.FromDays(7)); } else { // 激活信息反序列化错误 return ResponseOutput.NotOk(_localizer["Trial_ActivationCodeDeserializeError"] ); } return ResponseOutput.Ok(); } #endregion #region 患者检查管理 /// ///检查管理-> 检查Tab 患者列表 (带加入的项目信息 以及检查统计) 原型标注错误,不是检查列表 /// /// /// [HttpPost] public async Task>> GetPatientList(PatientTrialQuery inQuery) { var isAdminOrOA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.Admin || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.OA || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin; #region new ok var query = _patientRepository .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.PatientIdStr.Contains(inQuery.PatientIdStr)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.PatientName.Contains(inQuery.PatientName)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.ExperimentName), t => t.SubjectPatientList.Any(t => t.Subject.Trial.ExperimentName.Contains(inQuery.ExperimentName))) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAE), t => t.SCPStudyList.Any(t => t.CallingAE == inQuery.CallingAE)) .WhereIf(inQuery.BeginPushTime != null, t => t.LatestPushTime >= inQuery.BeginPushTime) .WhereIf(inQuery.EndPushTime != null, t => t.LatestPushTime <= inQuery.EndPushTime); foreach (var calledAE in inQuery.CalledAEList) { query = query.Where(t => t.SCPStudyList.Select(c => c.CalledAE).Contains(calledAE)); } var resultQuery = from patient in query select new PatientTrialView() { PatientId = patient.Id, PatientBirthDate = patient.PatientBirthDate, CreateTime = patient.CreateTime, CalledAEList = patient.SCPStudyList.Select(t => t.CalledAE).Distinct().ToList(), CallingAEList = patient.SCPStudyList.Select(t => t.CallingAE).Distinct().ToList(), CreateUserId = patient.CreateUserId, UpdateTime = patient.UpdateTime, UpdateUserId = patient.UpdateUserId, EarliestStudyTime = patient.EarliestStudyTime, LatestStudyTime = patient.LatestStudyTime, LatestPushTime = patient.LatestPushTime, PatientAge = patient.PatientAge, PatientName = patient.PatientName, PatientIdStr = patient.PatientIdStr, PatientSex = patient.PatientSex, StudyCount = patient.SCPStudyList.Count(), TrialList = patient.SubjectPatientList.Where(t => isAdminOrOA ? true : t.Subject.Trial.TrialUserList.Any(t => t.UserId == _userInfo.Id)).Select(c => new PatientTrialStatInfo() { ExperimentName = c.Subject.Trial.ExperimentName, VisitCount = c.Subject.SubjectVisitList.Count() }).ToList(), }; var pageList = await resultQuery.ToPagedListAsync(inQuery, nameof(PatientQueryView.PatientIdStr)); #endregion return ResponseOutput.Ok(pageList); } /// /// 检查管理-> 患者加入项目 初始化勾选列表 /// /// /// [HttpPost] public async Task>> GetPatientJoinTrialInitList(PatientJoinTrialInitQuery inQuery) { //排除已参与的项目列表 var exceptQuery = _subjectPatientRepository.Where(t => t.PatientId == inQuery.PatientId).Select(t => t.Subject.TrialId); var trialQuery = _trialRepository.Where(t => t.TrialStatusStr == StaticData.TrialState.TrialOngoing) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Filter), t => t.ResearchProgramNo.Contains(inQuery.Filter) || t.ExperimentName.Contains(inQuery.Filter)) .Where(t => !exceptQuery.Any(c => c == t.Id)).ProjectTo(_mapper.ConfigurationProvider); var pageList = await trialQuery.ToPagedListAsync(inQuery, nameof(PatientJoinTrialInitView.ResearchProgramNo)); return ResponseOutput.Ok(pageList); } /// /// 检查管理-> 患者加入项目 下拉框勾选列表 /// /// /// [HttpPost] public async Task>> GetPatientJoinTrialInitSelectList(PatientJoinTrialInitQuery inQuery) { //排除已参与的项目列表 var exceptQuery = _subjectPatientRepository.Where(t => t.PatientId == inQuery.PatientId).Select(t => t.Subject.TrialId); var trialQuery = _trialRepository.Where(t => t.TrialStatusStr == StaticData.TrialState.TrialOngoing) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Filter), t => t.ResearchProgramNo.Contains(inQuery.Filter) || t.ExperimentName.Contains(inQuery.Filter)) .Where(t => t.TrialUserList.Any(c => c.UserId == _userInfo.Id)) .Where(t => !exceptQuery.Any(c => c == t.Id)).ProjectTo(_mapper.ConfigurationProvider); var list = trialQuery.ToList(); return ResponseOutput.Ok(list); } /// /// 检查管理-> 患者已加入的列表(原型有误,应该展示 项目 下的subject 绑定关系) /// /// /// [HttpPost] public async Task>> GetPatientJoinedTrialList(PatientJoinedTrialQuery inQuery) { var isAdminOrOA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.Admin || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.OA || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin; var trialQuery = _subjectPatientRepository.Where(t => t.PatientId == inQuery.PatientId) .Where(t => isAdminOrOA ? true : t.Subject.Trial.TrialUserList.Any(t => t.UserId == _userInfo.Id)) .ProjectTo(_mapper.ConfigurationProvider); var pageList = await trialQuery.ToPagedListAsync(inQuery); return ResponseOutput.Ok(pageList); } /// /// 检查管理-> 获取患者检查列表(同步影像数据之前的) /// /// /// [HttpPost] public async Task> GetPatientStudyList(PatientStudyInfoQuery inQuery) { var query = from scpStudy in _studyRepository.Where(t => t.PatientId == inQuery.PatientId) .WhereIf(inQuery.EarliestStudyTime != null, t => t.StudyTime >= inQuery.EarliestStudyTime) .WhereIf(inQuery.LatestStudyTime != null, t => t.StudyTime <= inQuery.LatestStudyTime) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Modalities), t => t.Modalities.Contains(inQuery.Modalities)) select new PatientStudySimpleView() { Description = scpStudy.Description, CalledAE = scpStudy.CalledAE, CallingAE = scpStudy.CallingAE, InstanceCount = scpStudy.InstanceCount, Modalities = scpStudy.Modalities, PatientId = scpStudy.PatientId, AccessionNumber = scpStudy.AccessionNumber, SCPStudyId = scpStudy.Id, SeriesCount = scpStudy.SeriesCount, StudyTime = scpStudy.StudyTime, }; var pageList = await query.ToPagedListAsync(inQuery, nameof(PatientStudySimpleView.StudyTime)); return pageList; } public async Task>> GetPatientSeriesList(Guid scpStudyId, [FromServices] IRepository _seriesRepository, [FromServices] IRepository _instanceRepository ) { var seriesList = await _seriesRepository.Where(s => s.StudyId == scpStudyId).OrderBy(s => s.SeriesNumber). ThenBy(s => s.SeriesTime).ThenBy(s => s.CreateTime) .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); var instanceList = await _instanceRepository.Where(s => s.StudyId == scpStudyId).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 }).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 = string.Empty, Path = k.Path, InstanceNumber = k.InstanceNumber, }).ToList(); } var study = await _studyRepository.FirstOrDefaultAsync(t => t.Id == scpStudyId); return ResponseOutput.Ok(seriesList, study); } [UnitOfWork] [HttpDelete] public async Task DeletePatientStudy(Guid patiendId, Guid scpStudyId, [FromServices] IRepository _SeriesRepository, [FromServices] IRepository _instanceRepository) { if (_studySubjectVisitRepository.Any(t => t.SCPStudyId == scpStudyId && t.StudyId != null)) { return ResponseOutput.NotOk("该检查已绑定某项目下受试者访视(不一定是你所创建的项目)并已提交生成任务,不允许删除该检查"); } else { await _subjectPatientRepository.DeleteFromQueryAsync(t => t.PatientId == patiendId); await _studySubjectVisitRepository.DeleteFromQueryAsync(t => t.SCPStudyId == scpStudyId); } await _studyRepository.BatchDeleteNoTrackingAsync(t => t.Id == scpStudyId); await _SeriesRepository.BatchDeleteNoTrackingAsync(t => t.StudyId == scpStudyId); await _instanceRepository.BatchDeleteNoTrackingAsync(t => t.StudyId == scpStudyId); return ResponseOutput.Ok(); } /// /// 清除该患者绑定的受试者的所有的数据、(subject subjectVisit visitTask dicom) /// /// /// [UnitOfWork] public async Task DeletePatientStudyAllData(Guid patientId, [FromServices] IRepository _visitTaskRepository, [FromServices] IRepository _SeriesRepository, [FromServices] IRepository _instanceRepository, [FromServices] IRepository _dicomStudyRepository, [FromServices] IRepository _dicomSeriesRepository, [FromServices] IRepository _dicomInstanceRepository, [FromServices] IOSSService oSSService) { //清理自己管理的项目的数据 var subjectPatientList = await _subjectPatientRepository.Where(t => t.PatientId == patientId && t.Subject.Trial.TrialUserList.Any(t => t.UserId == _userInfo.Id)) .Select(t => new { t.SubjectId, StudyInstanceUidList = t.Patient.SCPStudyList.Select(t => t.StudyInstanceUid).ToList() }).ToListAsync(); if (_studyRepository.Any(t => t.IsUploadFinished == false && t.PatientId == patientId)) { return ResponseOutput.NotOk("当前患者有检查正在上传,不允许清理数据"); } foreach (var item in subjectPatientList) { var subjectId = item.SubjectId; await _subjectRepository.BatchDeleteNoTrackingAsync(t => t.Id == subjectId); await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); await _subjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); await _visitTaskRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); await _dicomStudyRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); await _dicomSeriesRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); await _dicomInstanceRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); } var instanceUidList = subjectPatientList.SelectMany(t => t.StudyInstanceUidList).Distinct().ToList(); foreach (var studyInstanceUid in instanceUidList) { { var ossFolderPath = $"Dicom/{studyInstanceUid}"; await oSSService.DeleteFromPrefix(ossFolderPath); } } var sCPStudyIdList = _studyRepository.Where(t => t.PatientId == patientId).Select(t => t.Id).ToList(); await _patientRepository.BatchDeleteNoTrackingAsync(t => t.Id == patientId); foreach (var item in sCPStudyIdList) { await _studyRepository.BatchDeleteNoTrackingAsync(t => t.Id == item); await _SeriesRepository.BatchDeleteNoTrackingAsync(t => t.StudyId == item); await _instanceRepository.BatchDeleteNoTrackingAsync(t => t.StudyId == item); } return ResponseOutput.Ok(); } #endregion #region 受试者管理 [TrialGlobalLimit("AfterStopCannNotOpt")] [HttpPost] public async Task> AddOrUpdateSubject([FromBody] AddOrUpdateSubjectCommand subjectCommand) { var svlist = new List(); var verifyExp1 = new EntityVerifyExp() { VerifyExp = u => u.Code == subjectCommand.Code && u.TrialId == subjectCommand.TrialId, //---已存在具有相关受试者编号的受试者。 VerifyMsg = _localizer["Subject_DuplicateSubjectNum"] }; Subject? mapedSubject = null; if (subjectCommand.Id == null) //insert { mapedSubject = await _subjectRepository.InsertFromDTOAsync(subjectCommand, false, verifyExp1); } else //update { mapedSubject = await _subjectRepository.UpdateFromDTOAsync(subjectCommand, false, false, verifyExp1/*, verifyExp2*/); } await _subjectRepository.SaveChangesAsync(); return ResponseOutput.Ok(mapedSubject.Id.ToString()); } /// /// 受试者管理-> 受试者列表 (带患者信息,患者信息是数组) /// /// /// [HttpPost] public async Task>> GetPatientSubejctList(PatientSubjectQuery inQuery) { var subjectQuery = _subjectRepository.Where(u => u.TrialId == inQuery.TrialId) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Code), t => t.Code.Contains(inQuery.Code)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.ShortName), t => t.ShortName.Contains(inQuery.ShortName)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Sex), t => t.Sex.Contains(inQuery.Sex)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.SubjectPatientList.Any(t => t.Patient.PatientIdStr.Contains(inQuery.PatientIdStr))) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.SubjectPatientList.Any(t => t.Patient.PatientName.Contains(inQuery.PatientName))) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.SubjectPatientList.Any(t => t.Patient.PatientSex.Contains(inQuery.PatientSex))) .WhereIf(inQuery.Status != null, t => t.Status == inQuery.Status) .ProjectTo(_mapper.ConfigurationProvider); var pageList = await subjectQuery.ToPagedListAsync(inQuery, nameof(PatienSubejctView.Code)); return ResponseOutput.Ok(pageList); } /// /// 受试者管理-> 患者列表 (subject 列表进入,进行关系绑定初始化列表,排除已绑定的患者和已绑定给其他subject的患者) /// /// /// [HttpPost] public async Task>> GetPatientInitList(PatientQuery inQuery) { var query = _patientRepository .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.PatientIdStr.Contains(inQuery.PatientIdStr)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.PatientName.Contains(inQuery.PatientName)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAE), t => t.SCPStudyList.Any(t => t.CallingAE == inQuery.CallingAE)) .WhereIf(inQuery.EarliestStudyTime != null, t => t.EarliestStudyTime >= inQuery.EarliestStudyTime) .WhereIf(inQuery.LatestStudyTime != null, t => t.LatestStudyTime <= inQuery.LatestStudyTime) //排除该受试者已绑定的患者 .WhereIf(inQuery.SubjectId != null, t => !t.SubjectPatientList.Any(u => u.SubjectId == inQuery.SubjectId)) //排除该项目已绑定的其他患者 .Where(t => !t.SubjectPatientList.Any(c => c.Subject.TrialId == inQuery.TrialId)); foreach (var calledAE in inQuery.CalledAEList) { query = query.Where(t => t.SCPStudyList.Select(c => c.CalledAE).Contains(calledAE)); } var patientQuery = query.ProjectTo(_mapper.ConfigurationProvider); var pageList = await patientQuery.ToPagedListAsync(inQuery, nameof(PatientQueryView.PatientIdStr)); return ResponseOutput.Ok(pageList); } /// /// 受试者管理->患者列表 Dicom AE 下拉框数据获取 /// /// public async Task> GetDicomCalledAEList() { var list = await _studyRepository.Select(t => t.CalledAE).Distinct().ToListAsync(); return list; } public async Task> GetDicomCallingAEList() { var list = await _studyRepository.Select(t => t.CallingAE).Distinct().ToListAsync(); return list; } /// ///受试者管理-> 患者列表 模糊搜索下拉 选择subject 排除已绑定并提交的 /// /// /// [HttpPost] public async Task> GetTrialSubejctSelectList(SubjectSelectQuery inQuery) { var list = await _subjectRepository.Where(t => t.TrialId == inQuery.TrialId && t.Status == SubjectStatus.OnVisit) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Code.Contains(inQuery.SubjectCode)) .WhereIf(inQuery.SubjectId != null, t => t.Id == inQuery.SubjectId) //.Where(t => !t.SubjectVisitList.SelectMany(t => t.SCPStudySubjectVisitList).Any(c => c.StudyId != null)) .Select(t => new SubjectSelectDto() { SubejctId = t.Id, SubjectCode = t.Code, Status = t.Status, Sex = t.Sex, ShortName = t.ShortName, Age = t.Age, BirthDate = t.BirthDate, PatientList = t.SubjectPatientList.Select(c => new PatienBasicInfo() { PatientId = c.PatientId, PatientIdStr = c.Patient.PatientIdStr }).ToList() }) .ToListAsync(); return list; } #endregion #region 患者和受试者绑定,生成访视,预先绑定检查和访视 [HttpPost] public async Task GetVisitStudyVerifyTime(VisitStudyVerifyTimeQuery inQuery) { var scpStudyList = await _studySubjectVisitRepository.Where(t => t.TrialId == inQuery.TrialId && t.SubjectId == inQuery.SubjectId && t.SCPStudyId != inQuery.SCPStudyId) .Select(t => new { SubjectVisitId = t.SubjectVisitId, StudyTime = t.SCPStudy.StudyTime, t.SubjectVisit.SubmitState, t.SubjectVisit.VisitName, VisitNum = t.SubjectVisit.VisitNum, SCPStudyId = t.SCPStudyId }) .ToListAsync(); var result = scpStudyList.GroupBy(t => new { t.VisitName, t.VisitNum, t.SubjectVisitId, t.SubmitState }) .Select(g => new { g.Key.SubmitState, g.Key.VisitNum, g.Key.VisitName, g.Key.SubjectVisitId, VisitMaxStudyTime = g.Max(c => c.StudyTime), VisitMinStudyTime = g.Min(c => c.StudyTime) }); return ResponseOutput.Ok(result); } public class AuToBindingStudyInfo { public Guid SCPStudyId { get; set; } public DateTime? StudyTime { get; set; } } private async Task DealAutoBindingStudyAsync(Guid trialId, Guid subjectId, List studyList, decimal? startBindVisitNum = null) { studyList = studyList.OrderBy(t => t.StudyTime).ToList(); //自动创建访视 和检查绑定 //1. 查询已存在的访视 var subjectAllVisitList = await _subjectVisitRepository.Where(t => t.SubjectId == subjectId) .Select(t => new { t.SubjectId, SubjectVisitId = t.Id, t.SubmitState, VisitNum = t.VisitNum, MaxStudyTime = t.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime), MinStudyTime = t.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime) }) .ToListAsync(); //2、获取项目配置 var trialconfig = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.BlindBaseLineName, t.BlindFollowUpPrefix }).FirstOrDefault(); //3、 未提交的最小的访视号 从这个访视开始绑定 因为重新开始绑定,所以将访视未提交的状态重置 var subjectMaxVisitNum = startBindVisitNum == null ? subjectAllVisitList.Where(t => t.SubmitState != SubmitStateEnum.Submitted).MinOrDefault(t => t.VisitNum) : startBindVisitNum.Value; await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.SubjectId == subjectId && t.SubmitState != SubmitStateEnum.Submitted && t.VisitNum > subjectMaxVisitNum, c => new SubjectVisit() { SubmitState = SubmitStateEnum.None }); List<(int VisitCount, Guid SCPStudyId)> visits = new List<(int, Guid)>(); int visitCount = 0; DateTime? lastVisitTime = null; foreach (var study in studyList) { if (lastVisitTime == null || (study.StudyTime - lastVisitTime.Value).Value.TotalDays >= 15) { // 当前时间点与上一个访视时间点间隔大于等于 15 天,需要建立一个新的访视 visitCount++; visits.Add((visitCount, study.SCPStudyId)); } else { visits.Add((visitCount, study.SCPStudyId)); } lastVisitTime = study.StudyTime; } //4、生成访视 并且绑定 for (int i = 0; i < visitCount; i++) { var bindSubjectVisitId = Guid.Empty; var bindVisitNum = i + subjectMaxVisitNum; var existSubjectVisit = subjectAllVisitList.FirstOrDefault(t => t.SubjectId == subjectId && t.VisitNum == bindVisitNum); if (existSubjectVisit == null) { bindSubjectVisitId = NewId.NextGuid(); //基线 if (bindVisitNum == 0) { await _subjectVisitRepository.AddAsync(new SubjectVisit() { TrialId = trialId, SubjectId = subjectId, VisitName = trialconfig.BlindBaseLineName, VisitNum = bindVisitNum, Id = bindSubjectVisitId, SubmitState = SubmitStateEnum.ToSubmit, IsBaseLine = true }); } else { await _subjectVisitRepository.AddAsync(new SubjectVisit() { TrialId = trialId, SubjectId = subjectId, VisitName = trialconfig.BlindFollowUpPrefix + $" {(int)bindVisitNum}", VisitNum = bindVisitNum, Id = bindSubjectVisitId, SubmitState = SubmitStateEnum.ToSubmit }); } } else { bindSubjectVisitId = existSubjectVisit.SubjectVisitId; } var currentVisitStudyList = visits.Where(t => t.VisitCount == (i + 1)).ToList(); foreach (var item in currentVisitStudyList) { //访视状态为未提交才绑定 if (!subjectAllVisitList.Any(t => t.SubjectId == subjectId && t.SubjectVisitId == bindSubjectVisitId && t.SubmitState == SubmitStateEnum.Submitted)) { var find = await _subjectVisitRepository.FindAsync(bindSubjectVisitId); find.SubmitState = SubmitStateEnum.ToSubmit; await _studySubjectVisitRepository.AddAsync(new SCPStudySubjectVisit() { TrialId = trialId, SubjectVisitId = bindSubjectVisitId, SCPStudyId = item.SCPStudyId, SubjectId = subjectId }); } } } await _subjectPatientRepository.SaveChangesAsync(); } /// /// 之前患者和subject已绑定后,新来了的检查 可能需要新建访视,或者和已有访视在同一区间,需要自动绑定 /// /// /// [HttpPost] [TrialGlobalLimit("AfterStopCannNotOpt")] public async Task AutoBindingPatientStudyVisit(AutoBindingPatientStudyVisitCommand inCommand) { //找到已绑定患者,但是没绑定检查的 新来的检查->现在换成未提交的 //var query = from scpStudy in _studyRepository.Where(t => !t.SCPStudySubjectVisitList.Any(t => t.TrialId == inCommand.TrialId)) var query = from scpStudy in _studyRepository.Where(t => !t.SCPStudySubjectVisitList.Any(t => t.TrialId == inCommand.TrialId && t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted)) join subjectPatient in _subjectPatientRepository.Where(t => t.Subject.TrialId == inCommand.TrialId) on scpStudy.PatientId equals subjectPatient.PatientId select new { subjectPatient.SubjectId, subjectPatient.PatientId, SCPStudyId = scpStudy.Id, StudyTime = scpStudy.StudyTime }; var list = query.ToList(); if (list.Count > 0) { //2、获取项目配置 var trialconfig = _trialRepository.Where(t => t.Id == inCommand.TrialId).Select(t => new { t.BlindBaseLineName, t.BlindFollowUpPrefix }).FirstOrDefault(); var subjectIdList = list.Select(t => t.SubjectId).ToList(); var allSubjectVisitList = await _subjectVisitRepository.Where(t => subjectIdList.Contains(t.SubjectId)).Select(t => new { t.SubjectId, SubjectVisitId = t.Id, t.SubmitState, VisitNum = t.VisitNum, MaxStudyTime = t.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime), MinStudyTime = t.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime) }).ToListAsync(); foreach (var g in list.GroupBy(t => t.SubjectId)) { var subjectId = g.Key; var subjectStudyList = g.ToList(); var subjectVisitList = allSubjectVisitList.Where(t => t.SubjectId == subjectId).ToList(); // 预先处理1: 删除未提交的所有绑定的检查记录,所有检查一起考虑绑定 await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted); //预处理2 找到该subjecct 已提交的访视的最大检查时间,绑定的检查时间要比这个时间要大 var maxStudyTime = subjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted).MaxOrDefault(t => t.MaxStudyTime); var needDealStudyList = subjectStudyList.WhereIf(maxStudyTime != null, t => t.StudyTime > maxStudyTime).Select(t => new AuToBindingStudyInfo() { SCPStudyId = t.SCPStudyId, StudyTime = t.StudyTime }).ToList(); await DealAutoBindingStudyAsync(inCommand.TrialId, subjectId, needDealStudyList); } await _subjectVisitRepository.SaveChangesAsync(); } return ResponseOutput.Ok(); } public async Task VerifyTrialSubject(VerifyTrialSubjectCommand inCommand, [FromServices] IRepository _subjectVisitReposiotry) { var find = await _subjectRepository.FirstOrDefaultAsync(t => t.TrialId == inCommand.TrialId && t.Code == inCommand.SubjectCode); if (find != null) { //$"项目中已存在编号为“{find.Code}”的受试者,状态为:{(find.Status == SubjectStatus.OnVisit ? "访视中" : "访视结束")},不允许添加。" var identification = find.Status == SubjectStatus.OnVisit ? "Patient_TrialExistSubjectCodeError1" : "Patient_TrialExistSubjectCodeError2"; return ResponseOutput.NotOk(_localizer[identification, find.Code], ApiResponseCodeEnum.NeedTips); } return ResponseOutput.Ok(); } /// /// 建立subject与患者绑定 如果是下拉,则传递SubjectId,如果不存在,创建,那么就传递 SubjectCode /// /// 绑定以后,后台自动创建访视 和检查预先绑定 /// /// /// [HttpPost] [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task AddSubjectPatientBinding(AddSubjectPatientCommand inCommand, [FromServices] IRepository _subjectVisitReposiotry) { var patientInfo = _patientRepository.Where(t => inCommand.PatientIdList.Contains(t.Id)).Select(t => new { t.PatientBirthDate, t.PatientName, t.PatientSex }).FirstOrDefault(); var subjectId = Guid.Empty; if (inCommand.SubjectId == null) { var find = await _subjectRepository.FirstOrDefaultAsync(t => t.TrialId == inCommand.TrialId && t.Code == inCommand.SubjectCode); if (find == null) { subjectId = NewId.NextSequentialGuid(); var subjectName = patientInfo?.PatientName ?? string.Empty; DateTime date; DateTime? birthDate = DateTime.TryParse(patientInfo.PatientBirthDate, out date) ? date : null; var sex = patientInfo.PatientSex?.ToUpper(); // 计算年龄 int? age = null; if (birthDate != null) { var patientAge = DateTime.Now.Year - birthDate.Value.Year; // 如果生日还未到,年龄减去一岁 if (DateTime.Now < birthDate.Value.AddYears(patientAge)) { patientAge--; } age = patientAge; } await _subjectRepository.AddAsync(new Subject() { Id = subjectId, Code = inCommand.SubjectCode, TrialId = inCommand.TrialId, ShortName = subjectName, BirthDate = birthDate, Sex = sex, Age = age }); } else { var identification = find.Status == SubjectStatus.OnVisit ? "Patient_TrialExistSubjectCodeError1" : "Patient_TrialExistSubjectCodeError2"; return ResponseOutput.NotOk(_localizer[identification, find.Code]); //subjectId = find.Id; } } else { subjectId = (Guid)inCommand.SubjectId; } foreach (var item in inCommand.PatientIdList) { if (!_subjectPatientRepository.Any(t => t.PatientId == item && t.SubjectId == subjectId)) { await _subjectPatientRepository.AddAsync(new SubjectPatient() { PatientId = item, SubjectId = subjectId }); } } // 预先处理1: 数据库可能有已存在的subject 患者绑定,在这里要一起考虑绑定 var dbPatientIdList = _subjectPatientRepository.Where(t => t.SubjectId == subjectId).Select(t => t.PatientId).ToList(); inCommand.PatientIdList = dbPatientIdList.Union(inCommand.PatientIdList).Distinct().ToList(); // 预先处理2: 删除未提交的所有绑定的检查记录,所有检查一起考虑绑定 await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted); //预处理3 找到该subjecct 已提交的访视的最大检查时间,绑定的检查时间要比这个时间要大 var maxStudyTime = _studySubjectVisitRepository.Where(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted) .MaxOrDefault(t => t.SCPStudy.StudyTime); // 预处理4: 处理需要绑定的检查 //获取 该受试者绑定患者已存在的检查,考虑要生成多少个访视,去除已提交的检查 var studyList = await _studyRepository.Where(t => inCommand.PatientIdList.Contains(t.PatientId) && !t.SCPStudySubjectVisitList.Any(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted)) .WhereIf(maxStudyTime != null, t => t.StudyTime > maxStudyTime) .Select(t => new AuToBindingStudyInfo { SCPStudyId = t.Id, StudyTime = t.StudyTime }).OrderBy(t => t.StudyTime).Distinct().ToListAsync(); await DealAutoBindingStudyAsync(inCommand.TrialId, subjectId, studyList); return ResponseOutput.Ok(subjectId); } /// /// 删除 受试者 和患者之间的绑定 /// /// /// [HttpDelete] [UnitOfWork] [TrialGlobalLimit("AfterStopCannNotOpt")] public async Task DeleteSubjectPatientBinding(DeleteSubejctPatientCommand inCommand, [FromServices] IRepository _visitTaskRepository, [FromServices] IRepository _dicomStudyRepository, [FromServices] IRepository _dicomSeriesRepository, [FromServices] IRepository _dicomInstanceRepository) { var subjectId = inCommand.SubjectId; var patientId = inCommand.PatientId; var find = await _subjectPatientRepository.FirstOrDefaultAsync(t => t.PatientId == patientId && t.SubjectId == subjectId); if (find != null) { //该患者检查 没有绑定提交的 if (!_studySubjectVisitRepository.Any(t => t.SubjectId == find.SubjectId && t.SCPStudy.PatientId == patientId && t.StudyId != null)) { await _subjectPatientRepository.DeleteAsync(find); await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == find.SubjectId && t.SCPStudy.PatientId == patientId); await _subjectPatientRepository.SaveChangesAsync(); //触发其余访视检查自动绑定 // 预先处理1: 数据库可能有已存在的subject 患者绑定,在这里要一起考虑绑定 var dbPatientIdList = _subjectPatientRepository.Where(t => t.SubjectId == subjectId).Select(t => t.PatientId).ToList(); // 预先处理2: 删除未提交的所有绑定的检查记录,所有检查一起考虑绑定 await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted); //预处理3 找到该subjecct 已提交的访视的最大检查时间,绑定的检查时间要比这个时间要大 var maxStudyTime = _studySubjectVisitRepository.Where(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted) .MaxOrDefault(t => t.SCPStudy.StudyTime); // 预处理4: 处理需要绑定的检查 //获取 该受试者绑定患者已存在的检查,考虑要生成多少个访视,去除已提交的检查 var studyList = await _studyRepository.Where(t => dbPatientIdList.Contains(t.PatientId) && !t.SCPStudySubjectVisitList.Any(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted)) .WhereIf(maxStudyTime != null, t => t.StudyTime > maxStudyTime) .Select(t => new AuToBindingStudyInfo { SCPStudyId = t.Id, StudyTime = t.StudyTime }).OrderBy(t => t.StudyTime).Distinct().ToListAsync(); await DealAutoBindingStudyAsync(inCommand.TrialId, subjectId, studyList); } else if (_studySubjectVisitRepository.Any(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted)) { //该受试者有访视已提交,不允许解除受试者和该患者的绑定关系 return ResponseOutput.NotOk(_localizer["Patient_SubjectVisitSubmitedCannotUnBinding"]); } //当前是最后移除的患者 if (!_subjectPatientRepository.Any(t => t.SubjectId == subjectId && t.PatientId != patientId)) { #region 设置了末次访视,然后一起删除会报 级联错误 ////q1:单独查询删除 //await _subjectRepository.DeleteFromQueryAsync(t => t.Id == subjectId); //await _subjectVisitRepository.DeleteFromQueryAsync(t => t.SubjectId == subjectId); //q2 一起查询删除 没区别,一样报错 //var subject = await _subjectRepository.Where(t => t.Id == subjectId, true).Include(t => t.SubjectVisitList).FirstOrDefaultAsync(); //await _subjectRepository.DeleteAsync(subject); #endregion //q3 设置导航属性为null 依旧不行 //var subject = await _subjectRepository.Where(t => t.Id == subjectId, true).Include(t => t.SubjectVisitList).FirstOrDefaultAsync(); //subject.FinalSubjectVisit = null; //subject.FinalSubjectVisitId = null; //foreach (var item in subject.SubjectVisitList) //{ // item.SubjectId = Guid.Empty; // item.Subject = null; //} //await _subjectRepository.DeleteAsync(subject); await _subjectVisitRepository.DeleteFromQueryAsync(t => t.SubjectId == subjectId, true); await _subjectRepository.DeleteFromQueryAsync(t => t.Id == subjectId); } await _subjectPatientRepository.SaveChangesAsync(); ////暂时删除访视 上线删除 //await _subjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); //await _visitTaskRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); //await _dicomStudyRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); //await _dicomSeriesRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); //await _dicomInstanceRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); } return ResponseOutput.Ok(); } /// /// 患者检查 与SubjectVisit 的绑定 /// /// /// [HttpPost] [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] [Obsolete] public async Task AddSubjectPatientStudyBinding(List inCommandList) { var subjectId = inCommandList.First().SubjectId; //查询数据库所有的绑定,然后将修改的绑定更改过去,验证是否符合要求 var allSubjectVisitList = _subjectVisitRepository.Where(t => t.SubjectId == subjectId).Select(t => new { SubjectVisitId = t.Id, t.VisitNum }).ToList(); var allStudyList = await _studySubjectVisitRepository.Where(t => t.SubjectId == subjectId).Select(t => new VerifyVisitStudyDTO() { SubjectVisitId = t.SubjectVisitId, StudyTime = t.SCPStudy.StudyTime, VisitNum = t.SubjectVisit.VisitNum, SCPStudyId = t.SCPStudyId }).ToListAsync(); foreach (var item in inCommandList) { var find = allStudyList.Where(t => t.SCPStudyId == item.SCPStudyId).FirstOrDefault(); if (find != null) { find.SubjectVisitId = item.SubjectVisitId; find.VisitNum = allSubjectVisitList.Where(t => t.SubjectVisitId == item.SubjectVisitId).FirstOrDefault().VisitNum; } } foreach (var item in inCommandList) { var currentVisitStudyList = allStudyList.Where(t => t.SubjectVisitId == item.SubjectVisitId).OrderBy(t => t.StudyTime).ToList(); var currentVisitMaxTimeStudy = currentVisitStudyList.LastOrDefault(); if (currentVisitMaxTimeStudy != null) { var currentVisitMaxStudyTime = currentVisitMaxTimeStudy.StudyTime; if (allStudyList.Where(t => t.VisitNum < currentVisitMaxTimeStudy.VisitNum).Any(t => t.StudyTime > currentVisitMaxStudyTime)) { return ResponseOutput.NotOk(_localizer["当前提交的访视中,前序访视的检查时间比当前绑定访视的检查时间要大,请核查!"]); } if (allStudyList.Where(t => t.VisitNum > currentVisitMaxTimeStudy.VisitNum).Any(t => t.StudyTime < currentVisitMaxStudyTime)) { return ResponseOutput.NotOk(_localizer["当前提交的访视中,后序访视的检查时间比当前绑定访视的检查时间要小,请核查!"]); } } } var studyIdList = inCommandList.Select(t => t.SCPStudyId).ToList(); await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => studyIdList.Contains(t.SCPStudyId)); foreach (var item in inCommandList) { var sv = await _subjectVisitRepository.FindAsync(item.SubjectVisitId); sv.SubmitState = SubmitStateEnum.ToSubmit; await _studySubjectVisitRepository.AddAsync(new SCPStudySubjectVisit() { TrialId = item.TrialId, SCPStudyId = item.SCPStudyId, SubjectVisitId = item.SubjectVisitId, SubjectId = item.SubjectId }); } await _studySubjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Ok(); } /// /// 修改 访视 和检查的绑定关系 IsAdd 区分添加 还是移除 /// /// /// [TrialGlobalLimit("AfterStopCannNotOpt")] [UnitOfWork] public async Task UpdateSubjectVisitStudyBinding(UpdateSubjectVisitStudyBindingCommand inCommand) { var subjectId = inCommand.SubjectId; var subjectVisitId = inCommand.SubjectVisitId; if (_subjectRepository.Any(t => t.Id == subjectId && t.Status == SubjectStatus.EndOfVisit)) { //受试者状态为访视结束,不允许绑定访视和检查 return ResponseOutput.NotOk(_localizer["Patient_SubjectEndOfVisit"]); } var subjectVisit = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId); if (inCommand.IsAdd) { //找到所有访视的检查 var allStudyList = await _studySubjectVisitRepository.Where(t => t.SubjectId == subjectId).Select(t => new VerifyVisitStudyDTO() { SubjectVisitId = t.SubjectVisitId, StudyTime = t.SCPStudy.StudyTime, VisitNum = t.SubjectVisit.VisitNum, SCPStudyId = t.SCPStudyId }).ToListAsync(); var bindVisit = _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new { SubjectVisitId = t.Id, t.VisitNum, SubjectCode = t.Subject.Code }).FirstOrDefault(); var bindStudy = _studyRepository.Where(t => t.Id == inCommand.SCPStudyId).Select(t => new { SCPStudyId = t.Id, t.StudyTime }).FirstOrDefault(); //是否之前和其他访视绑定 var findStudy = allStudyList.FirstOrDefault(t => t.SCPStudyId == inCommand.SCPStudyId); if (findStudy != null) { findStudy.SubjectVisitId = bindVisit.SubjectVisitId; findStudy.VisitNum = bindVisit.VisitNum; } else { //新的检查 allStudyList.Add(new VerifyVisitStudyDTO() { SCPStudyId = inCommand.SCPStudyId, SubjectVisitId = bindVisit.SubjectVisitId, VisitNum = bindVisit.VisitNum, StudyTime = bindStudy.StudyTime }); } var visitOrderStudyList = allStudyList.OrderBy(t => t.VisitNum).ThenBy(t => t.StudyTime).ToList(); var studyTimeOrderList = visitOrderStudyList.OrderBy(t => t.StudyTime).ToList(); bool arraysEqual = visitOrderStudyList.SequenceEqual(studyTimeOrderList); if (!arraysEqual) { //_localizer[$"{bindVisit.SubjectCode}所提交的访视中的检查时间,不符合后续访视的检查时间比前序检查的时间大的要求"] return ResponseOutput.NotOk(_localizer["Patient_SubjectVisitSubmmitTimeVerifyError", bindVisit.SubjectCode]); } //如果该检查之前和其他访视绑定,那么更新绑定的访视就可以 var otherFind = await _studySubjectVisitRepository.FirstOrDefaultAsync(t => t.SCPStudyId == inCommand.SCPStudyId && t.SubjectId == inCommand.SubjectId); if (otherFind == null) { await _studySubjectVisitRepository.AddAsync(new SCPStudySubjectVisit() { TrialId = inCommand.TrialId, SCPStudyId = inCommand.SCPStudyId, SubjectVisitId = subjectVisitId, SubjectId = inCommand.SubjectId }); } else { //为了稽查 先删除 再添加 await _studySubjectVisitRepository.DeleteAsync(otherFind); if (!_studySubjectVisitRepository.Any(t => t.SubjectVisitId == otherFind.SubjectVisitId && t.SCPStudyId != inCommand.SCPStudyId)) { var updateVisit = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == otherFind.SubjectVisitId); updateVisit.SubmitState = SubmitStateEnum.None; } //otherFind.SubjectVisitId = inCommand.SubjectVisitId; await _studySubjectVisitRepository.AddAsync(new SCPStudySubjectVisit() { TrialId = inCommand.TrialId, SCPStudyId = inCommand.SCPStudyId, SubjectVisitId = subjectVisitId, SubjectId = inCommand.SubjectId }); } subjectVisit.SubmitState = SubmitStateEnum.ToSubmit; await _studySubjectVisitRepository.SaveChangesAsync(); #region 处理自动绑定 //触发自动绑定后续检查和访视的绑定关系 //预先处理1:找到所有未提交的检查访视绑定 var studyList = await _studySubjectVisitRepository.Where(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted) .Select(t => new VerifyVisitStudyDTO() { SubjectVisitId = t.SubjectVisitId, StudyTime = t.SCPStudy.StudyTime, VisitNum = t.SubjectVisit.VisitNum, SCPStudyId = t.SCPStudyId }).ToListAsync(); var needDealStudyList = studyList.Where(t => t.VisitNum > subjectVisit.VisitNum).Select(t => new AuToBindingStudyInfo() { SCPStudyId = t.SCPStudyId, StudyTime = t.StudyTime }).ToList(); if (needDealStudyList.Count > 0) { //没有该访视的检查 var findVisitStudy = studyList.Where(t => t.SubjectVisitId == subjectVisitId).FirstOrDefault(); //就从当前访视开始绑定,否则就从下一个访视开始绑定 var startBindingVisitNum = findVisitStudy == null ? subjectVisit.VisitNum : subjectVisit.VisitNum + 1; // 预先处理2: 删除未提交的所有绑定的检查记录,所有检查一起考虑绑定 await _studySubjectVisitRepository.DeleteFromQueryAsync(t => t.SubjectId == subjectId && t.SubjectVisit.VisitNum >= startBindingVisitNum); await DealAutoBindingStudyAsync(inCommand.TrialId, subjectId, needDealStudyList, startBindingVisitNum); } #endregion } else { var find = await _studySubjectVisitRepository.FirstOrDefaultAsync(t => t.SCPStudyId == inCommand.SCPStudyId && t.SubjectVisitId == subjectVisitId); await _studySubjectVisitRepository.DeleteAsync(find); if (!_studySubjectVisitRepository.Any(t => t.SubjectVisitId == subjectVisitId && t.SCPStudyId != find.SCPStudyId)) { subjectVisit.SubmitState = SubmitStateEnum.None; } await _studySubjectVisitRepository.SaveChangesAsync(); //预处理1 找到该subjecct 已提交的访视的最大检查时间,绑定的检查时间要比这个时间要大 var maxStudyTime = _subjectVisitRepository.Where(t => t.SubjectId == subjectId && t.SubmitState == SubmitStateEnum.Submitted).SelectMany(t => t.SCPStudySubjectVisitList).MaxOrDefault(t => t.SCPStudy.StudyTime); //预先处理2:找到所有未提交的检查访视绑定 大于当前访视 var studyList = await _studySubjectVisitRepository.Where(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted && t.SubjectVisit.VisitNum > subjectVisit.VisitNum) .WhereIf(maxStudyTime != null, t => t.SCPStudy.StudyTime > maxStudyTime) .Select(t => new AuToBindingStudyInfo() { StudyTime = t.SCPStudy.StudyTime, SCPStudyId = t.SCPStudyId }).Distinct().ToListAsync(); if (studyList.Count > 0) { //预处理3 删除所有未提交的绑定关系 所有检查一起考虑绑定 排除当前访视 await _studySubjectVisitRepository.DeleteFromQueryAsync(t => t.SubjectId == subjectId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted && t.SubjectVisit.VisitNum > subjectVisit.VisitNum); //没有从当前访视开始绑定,否则就从下一个访视开始绑定 var startBindingVisitNum = !_studySubjectVisitRepository.Any(t => t.SubjectVisitId == subjectVisitId) ? subjectVisit.VisitNum : subjectVisit.VisitNum + 1; await DealAutoBindingStudyAsync(inCommand.TrialId, subjectId, studyList, startBindingVisitNum); } } return ResponseOutput.Ok(); } /// /// 提交 患者检查和访视的绑定 /// /// /// [HttpPost] [UnitOfWork] [TrialGlobalLimit("AfterStopCannNotOpt")] public async Task SubmitVisitStudyBinding(SubmitVisitStudyBindingCommand inCommand, [FromServices] IOptionsMonitor _basicSystemConfigConfig, [FromServices] IRepository _repository ) { //防止访视重复 inCommand.SubjectVisitIdList = inCommand.SubjectVisitIdList.Distinct().ToList(); //确认当前提交的最大的访视之前所有的访视都已提交,并且没有漏的 var curentMaxNum = await _subjectVisitRepository.Where(t => inCommand.SubjectVisitIdList.Contains(t.Id)).Select(t => t.VisitNum).MaxAsync(); var allVisitList = _subjectVisitRepository.Where(t => t.TrialId == inCommand.TrialId && t.SubjectId == inCommand.SubjectId && t.VisitNum <= curentMaxNum).Select(t => new { SubjectVisitId = t.Id, t.Subject.Status, t.VisitNum, t.SubmitState }).OrderBy(t => t.VisitNum).ToList(); //批量提交 if (inCommand.SubjectVisitIdList.Count > 1) { if (allVisitList.Where(t => t.SubmitState != SubmitStateEnum.Submitted).Count() != inCommand.SubjectVisitIdList.Count()) { //当前批量提交的访视中间有遗漏的访视或者前序有访视未提交 return ResponseOutput.NotOk(_localizer["Patient_CurrentBatchSubmitVisitError"]); } } else { if (allVisitList.Any(t => t.VisitNum < curentMaxNum && t.SubmitState != SubmitStateEnum.Submitted)) { //前序有访视未提交,请先提交前序访视 return ResponseOutput.NotOk(_localizer["Patient_BeforeVisitNotSubmit"]); } } if (allVisitList.Any(t => t.Status == SubjectStatus.EndOfVisit)) { //受试者状态为访视结束,不允许提交访视生成任务 return ResponseOutput.NotOk(_localizer["Patient_SubjectEndOfVisitNotSubmit"]); } var list = await _studySubjectVisitRepository.Where(t => inCommand.SubjectVisitIdList.Contains(t.SubjectVisitId)).Select(t => new { t.SCPStudyId, t.SCPStudy.PatientId, t.SubjectVisitId, t.SubjectVisit.VisitNum, t.SubjectVisit.SubjectId, SubjectCode = t.SubjectVisit.Subject.Code, t.SubjectVisit.TrialId, t.SCPStudy.StudyTime, t.StudyId, t.SCPStudy.IsUploadFinished }).OrderBy(t => t.StudyTime).ToListAsync(); if (list.Any(t => t.StudyId != null)) { //有访视和检查处于已绑定关系,不允许再次提交绑定 return ResponseOutput.NotOk(_localizer["Patient_VisitAndStudyHaveBind"]); } if (list.Any(t => t.IsUploadFinished == false)) { //有访视检查正在传输中,不允许提交 return ResponseOutput.NotOk(_localizer["Patient_VisitStudyIsUploading"]); } //判断每个subject 批量提交的是否符合时间要求 foreach (var g in list.GroupBy(t => new { t.SubjectId, t.SubjectCode })) { var visitOrderStudyList = g.OrderBy(t => t.VisitNum).ThenBy(t => t.StudyTime).ToList(); var studyTimeOrderList = visitOrderStudyList.OrderBy(t => t.StudyTime).ToList(); bool arraysEqual = visitOrderStudyList.SequenceEqual(studyTimeOrderList); if (!arraysEqual) { //_localizer[$"{g.Key.SubjectCode}所提交的访视中的检查时间,不符合后续访视的检查时间比前序检查的时间大的要求"] return ResponseOutput.NotOk(_localizer["Patient_SubjectVisitSubmmitTimeVerifyError", g.Key.SubjectCode] ); } if (DateTime.Now < studyTimeOrderList.Max(t => t.StudyTime)) { //$"您当前修改了服务器时间,试图绕过软件授权,请恢复服务器时间,并联系授权方授权才可进行操作" return ResponseOutput.NotOk(_localizer["Patient_ModifyServerTimeError"]); } } var trialConfig = await _trialRepository.Where(t => t.Id == inCommand.TrialId).Select(t => new { t.IsEnrollementQualificationConfirm, t.IsPDProgressView, t.AuthorizationEncrypt }).FirstOrDefaultAsync(); //var decodedText = Cryptography.DecryptString(trialConfig.AuthorizationEncrypt, _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt"); //var authInfo = JsonConvert.DeserializeObject(decodedText); var @lock = _distributedLockProvider.CreateLock($"StudyCode"); using (await @lock.AcquireAsync()) { var dbStudyCodeIntMax = _repository.Where(s => s.TrialId == inCommand.TrialId).Select(t => t.Code).DefaultIfEmpty().Max(); int currentNextCodeInt = dbStudyCodeIntMax + 1; foreach (var item in list) { //提交的访视下没有检查 if (!_studySubjectVisitRepository.Any(t => t.SubjectVisitId == item.SubjectVisitId)) { //提交的访视下必须有影像检查! return ResponseOutput.NotOk(_localizer["Patient_SubmitVisitMustHaveImage"]); } var dbSubjectVisit = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == item.SubjectVisitId); //处理脏数据,可能之前的绑定的数据状态是待上传,但是已经绑定了检查 if (dbSubjectVisit.SubmitState == SubmitStateEnum.ToSubmit || dbSubjectVisit.SubmitState == SubmitStateEnum.None) { dbSubjectVisit.SubmitState = SubmitStateEnum.Submitted; dbSubjectVisit.SubmitTime = DateTime.Now; dbSubjectVisit.SubmitUserId = _userInfo.Id; //维护统一状态 //dbSubjectVisit.ReadingStatus = ReadingStatusEnum.TaskAllocate; dbSubjectVisit.AuditState = AuditStateEnum.QCPassed; dbSubjectVisit.CheckState = CheckStateEnum.CVPassed; dbSubjectVisit.IsEnrollmentConfirm = dbSubjectVisit.IsBaseLine ? trialConfig.IsEnrollementQualificationConfirm : false; dbSubjectVisit.PDState = trialConfig.IsPDProgressView ? PDStateEnum.PDProgress : PDStateEnum.None; } var find = _studyRepository.Where(t => t.Id == item.SCPStudyId).Include(t => t.SeriesList).Include(t => t.InstanceList).FirstOrDefault(); if (find != null) { //重新算Id Guid studyId = IdentifierHelper.CreateGuid(item.TrialId.ToString(), find.StudyInstanceUid); find.Id = studyId; var newStuty = _mapper.Map(find); await _repository.AddAsync(newStuty); //newStuty.Id = NewId.NextSequentialGuid(); newStuty.SeqId = NewId.NextSequentialGuid(); newStuty.Code = currentNextCodeInt; newStuty.StudyCode = AppSettings.GetCodeStr(currentNextCodeInt, nameof(DicomStudy)); newStuty.TrialId = item.TrialId; newStuty.SubjectId = item.SubjectId; newStuty.SubjectVisitId = item.SubjectVisitId; var newSeriesList = _mapper.Map>(find.SeriesList); foreach (var series in newSeriesList) { Guid seriesId = IdentifierHelper.CreateGuid(item.TrialId.ToString(), find.StudyInstanceUid, series.SeriesInstanceUid); //重新算Id series.Id = seriesId; series.StudyId = newStuty.Id; series.SeqId = NewId.NextSequentialGuid(); series.TrialId = item.TrialId; series.SubjectId = item.SubjectId; series.SubjectVisitId = item.SubjectVisitId; } await _repository.AddRangeAsync(newSeriesList); var newInstanceList = _mapper.Map>(find.InstanceList); foreach (var instance in newInstanceList) { Guid seriesId = IdentifierHelper.CreateGuid(item.TrialId.ToString(), find.StudyInstanceUid, instance.SeriesInstanceUid); Guid instanceId = IdentifierHelper.CreateGuid(item.TrialId.ToString(), find.StudyInstanceUid, instance.SeriesInstanceUid, instance.SopInstanceUid); //重新算Id instance.Id = instanceId; instance.SeriesId = seriesId; instance.StudyId = newStuty.Id; instance.SeqId = NewId.NextSequentialGuid(); instance.TrialId = item.TrialId; instance.SubjectId = item.SubjectId; instance.SubjectVisitId = item.SubjectVisitId; } await _repository.AddRangeAsync(newInstanceList); } currentNextCodeInt++; await _studySubjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.SubjectVisitId == item.SubjectVisitId && t.SCPStudyId == item.SCPStudyId, u => new SCPStudySubjectVisit() { StudyId = find.Id }); await _subjectPatientRepository.BatchUpdateNoTrackingAsync(t => t.SubjectId == item.SubjectId && t.PatientId == item.PatientId, u => new SubjectPatient() { IsBinded = true }); } } await _studySubjectVisitRepository.SaveChangesAsync(); return ResponseOutput.Ok(); } #endregion #region 访视基本管理 /// /// 绑定访视 初始化患者检查列表 /// /// /// [HttpPost] public async Task> GetVisitPatientStudyList(PatientStudyQuery inQuery) { var patientQuery = from scpStudy in _studyRepository .Where(t => inQuery.PatientIdList.Contains(t.PatientId)) .WhereIf(inQuery.EarliestStudyTime != null, t => t.StudyTime >= inQuery.EarliestStudyTime) .WhereIf(inQuery.LatestStudyTime != null, t => t.StudyTime <= inQuery.LatestStudyTime) join scpStudySubjectVisit in _studySubjectVisitRepository.Where(t => t.SubjectVisit.TrialId == inQuery.TrialId) on scpStudy.Id equals scpStudySubjectVisit.SCPStudyId into cc from scpStudySubjectVisit in cc.DefaultIfEmpty() select new PatientStudySelectDto() { Description = scpStudy.Description, CalledAE = scpStudy.CalledAE, InstanceCount = scpStudy.InstanceCount, Modalities = scpStudy.Modalities, PatientId = scpStudy.Patient.Id, PatientIdStr = scpStudy.PatientIdStr, PatientAge = scpStudy.PatientAge, PatientBirthDate = scpStudy.PatientBirthDate, PatientSex = scpStudy.PatientSex, PatientName = scpStudy.PatientName, SCPStudyId = scpStudy.Id, SeriesCount = scpStudy.SeriesCount, StudyTime = scpStudy.StudyTime, CallingAE = scpStudy.CallingAE, SubmitState = scpStudySubjectVisit.SubjectVisit.SubmitState, SubjectVisitId = scpStudySubjectVisit.SubjectVisitId } ; var sortField = string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(VisitPatientStudyView.StudyTime) : inQuery.SortField; var orderQuery = inQuery.Asc ? patientQuery.OrderBy(sortField) : patientQuery.OrderBy(sortField + " desc"); var list = await orderQuery.ToListAsync(); return list; } /// /// 访视管理- 获取subject 已存在的访视列表 ,同时获取项目访视的配置 在otherData里 /// /// /// /// [HttpPost] public async Task GetSubjectVisitSelectList(SubjectVisitSelectQuery inQuery, [FromServices] IRepository _subjectVisitReposiotry) { var scpStudyList = await _studySubjectVisitRepository.Where(t => t.TrialId == inQuery.TrialId && t.SubjectId == inQuery.SubjectId && t.SCPStudyId != inQuery.SCPStudyId) .Select(t => new { t.SubjectVisitId, StudyTime = t.SCPStudy.StudyTime }) .ToListAsync(); var result = scpStudyList.GroupBy(t => t.SubjectVisitId) .Select(g => new { SubejctVisitId = g.Key, VisitMaxStudyTime = g.Max(c => c.StudyTime), VisitMinStudyTime = g.Min(c => c.StudyTime) }).ToList(); var list = _subjectVisitReposiotry.Where(t => t.SubjectId == inQuery.SubjectId).ProjectTo(_mapper.ConfigurationProvider).ToList(); foreach (var item in list) { item.VisitMaxStudyTime = result.Where(t => t.SubejctVisitId == item.Id).FirstOrDefault()?.VisitMaxStudyTime; item.VisitMinStudyTime = result.Where(t => t.SubejctVisitId == item.Id).FirstOrDefault()?.VisitMinStudyTime; } var trialconfig = _trialRepository.Where(t => t.Id == inQuery.TrialId).Select(t => new { t.BlindBaseLineName, t.BlindFollowUpPrefix }).FirstOrDefault(); return ResponseOutput.Ok(list, trialconfig); } /// /// 添加或者更新受试者访视 /// /// /// /// [HttpPost] [TrialGlobalLimit("AfterStopCannNotOpt")] public async Task AddOrUpdateSubjectVisit(AddOrUpdateSubjectVisitCommand incommand, [FromServices] IRepository _subjectVisitReposiotry) { //var verifyExp1 = new EntityVerifyExp() //{ // VerifyExp = t => t.VisitNum == incommand.VisitNum && t.SubjectId == incommand.SubjectId, // //---该受试者的访视计划中已经包含一个具有相同访视号的访视。 // VerifyMsg = _localizer["Visit_DuplicateVisitNo"] //}; //var verifyExp3 = new EntityVerifyExp() //{ // VerifyExp = t => t.SubjectId == incommand.SubjectId && t.VisitName == incommand.VisitName, // //---该受试者的访视计划中已经包含一个具有相同访视名称的访视。 // VerifyMsg = _localizer["Visit_DuplicateVisitName"] //}; var find = _subjectVisitRepository.Where(t => t.TrialId == incommand.TrialId && t.SubjectId == incommand.SubjectId && t.VisitNum == incommand.VisitNum && t.VisitName == incommand.VisitName).FirstOrDefault(); if (find != null) { return ResponseOutput.Ok(find.Id); } else { var entity = await _subjectVisitReposiotry.InsertOrUpdateAsync(incommand, true); return ResponseOutput.Ok(entity.Id); } } /// /// 前端利用组件打成压缩包,后端返回路径和压缩包的信息 /// /// /// [HttpGet("{subjectId:guid}/{subjectVisitId:guid}")] public async Task GetSubjectImageZipInfo(Guid subjectId, Guid subjectVisitId) { //var patientQuery = from subject in _subjectRepository.Where(t => t.Id == subjectId) // select new // { // SubjectCode = subject.Code, // PatientList = subject.SubjectPatientList.Select(c => new // { // c.Patient.PatientIdStr, // c.Patient.PatientName, // StudyList = c.Patient.SCPStudyList.Select(u => new // { // u.StudyTime, // SeriesList = u.SeriesList.Select(z => new // { // z.Modality, // InstancePathList = z.SCPInstanceList.Select(k => new // { // k.Path // }) // }) // }) // }) // }; var subject_visit_studyQuery = from subject in _subjectRepository.Where(t => t.Id == subjectId) select new { SubjectCode = subject.Code, PatientList = subject.SubjectPatientList.Select(c => new { c.Patient.PatientIdStr, c.Patient.PatientName }).ToList(), VisitList = subject.SubjectVisitList.Where(t => t.Id == subjectVisitId) .Select(z => new { z.VisitName, StudyList = z.StudyList.Select(u => new { u.PatientIdStr, u.StudyTime, u.StudyCode, SeriesList = u.SeriesList.Select(z => new { z.Modality, InstancePathList = z.DicomInstanceList.Select(k => new { k.Path }) }) }) }), }; #region subject_patient_visit //var testquery = from subject in _subjectRepository.Where(t => t.Id == subjectId) // select new // { // SubjectCode = subject.Code, // PatientList = subject.SubjectPatientList.Select(c => new // { // c.Patient.PatientIdStr, // c.Patient.PatientName, // VisitList = c.Subject.SubjectVisitList.Select(z => new // { // z.VisitName, // StudyList = z.SCPStudySubjectVisitList.Where(t => t.SCPStudy.PatientId == c.PatientId).Select(u => new // { // //u.SCPStudy.PatientId, // u.SCPStudy.PatientIdStr, // u.SCPStudy.StudyTime, // SeriesList = u.SCPStudy.SeriesList.Select(z => new // { // z.Modality, // InstancePathList = z.SCPInstanceList.Select(k => new // { // k.Path // }) // }) // }) // }), // }) // }; #endregion //var subjectVisitQuery = from subject in _subjectRepository.Where(t => t.Id == subjectId) // select new // { // SubjectCode = subject.Code, // VisitList = subject.SubjectVisitList.Select(z => new // { // z.VisitName, // StudyList = z.SCPStudySubjectVisitList.Select(u => new // { // u.SCPStudy.PatientId, // u.SCPStudy.PatientIdStr, // u.SCPStudy.StudyTime, // SeriesList = u.SCPStudy.SeriesList.Select(z => new // { // z.Modality, // InstancePathList = z.SCPInstanceList.Select(k => new // { // k.Path // }) // }) // }) // }) // }; var result = subject_visit_studyQuery.FirstOrDefault(); ; return ResponseOutput.Ok(result); } /// ///访视管理-> 访视列表 (带患者信息,患者信息是数组) /// /// /// [HttpPost] public async Task>> GetPatientSubejctVisitList(PatientSubejctVisitQuery inQuery) { var query = _subjectVisitRepository.Where(t => t.TrialId == inQuery.TrialId) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Subject.Code.Contains(inQuery.SubjectCode)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.Subject.Sex.Contains(inQuery.SubjectSex)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), u => u.Subject.ShortName.Contains(inQuery.SubjectShortName)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.VisitName), u => u.VisitName.Contains(inQuery.VisitName)) .WhereIf(inQuery.SubmitState != null, u => u.SubmitState == inQuery.SubmitState) .WhereIf(inQuery.BeginStudyTime != null, t => t.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime) >= inQuery.BeginStudyTime) .WhereIf(inQuery.EndStudyTime != null, t => t.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime) <= inQuery.EndStudyTime) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientIdStr.Contains(inQuery.PatientIdStr))) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientName.Contains(inQuery.PatientName))) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientSex.Contains(inQuery.PatientSex))) .Select(t => new PatientSubjectVisitView() { PatientList = t.Subject.SubjectPatientList.Select(c => new PatientBasicInfo() { PatientId = c.PatientId, PatientAge = c.Patient.PatientAge, PatientBirthDate = c.Patient.PatientBirthDate, PatientIdStr = c.Patient.PatientIdStr, PatientSex = c.Patient.PatientSex, PatientName = c.Patient.PatientName, }).ToList(), TrialId = t.TrialId, SubjectId = t.SubjectId, SubjectVisitId = t.Id, SubjectAge = t.Subject.Age, SubjectSex = t.Subject.Sex, SubjectShortName = t.Subject.ShortName, SubjectCode = t.Subject.Code, SubmitState = t.SubmitState, SubmitTime = t.SubmitTime, VisitNum = t.VisitNum, VisitName = t.VisitName, VisitEarliestStudyTime = t.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime), VisitLatestStudyTime = t.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime) }); var defalutSortArray = new string[] { nameof(PatientSubjectVisitView.SubjectId), nameof(PatientSubjectVisitView.VisitNum) }; var pageList = await query.ToPagedListAsync(inQuery, defalutSortArray); return ResponseOutput.Ok(pageList); } /// ///访视管理-> 获取当前访视 已绑定的患者检查 (从访视列表 进入修改绑定) /// /// /// [HttpPost] public async Task> GetCurrentVisitPatientStudyList(SubjectVisitStudyQuery inQuery) { var patientQuery = _studySubjectVisitRepository.Where(t => t.SubjectVisitId == inQuery.SujectVisitId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted) //.WhereIf(inQuery.SubmitState != null, u => u.SubjectVisit.SubmitState == inQuery.SubmitState) .ProjectTo(_mapper.ConfigurationProvider); var sortField = string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(VisitPatientStudyView.StudyTime) : inQuery.SortField; var orderQuery = inQuery.Asc ? patientQuery.OrderBy(sortField) : patientQuery.OrderBy(sortField + " desc"); var list = await orderQuery.ToListAsync(); return list; } /// /// 访视管理-> 获取可选访视列表 (从访视列表 进入修改绑定) /// /// /// [HttpPost] public async Task> GetPatientOtherStudyList(PatientStudyOtherQuery inQuery) { var query = from scpStudy in _studyRepository.Where(t => inQuery.PatientIdList.Contains(t.PatientId) && !t.SCPStudySubjectVisitList.Any(t => (t.SubjectVisitId == inQuery.SujectVisitId || t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted) && t.TrialId == inQuery.TrialId)) .WhereIf(inQuery.EarliestStudyTime != null, t => t.StudyTime >= inQuery.EarliestStudyTime) .WhereIf(inQuery.LatestStudyTime != null, t => t.StudyTime <= inQuery.LatestStudyTime) .WhereIf(!string.IsNullOrEmpty(inQuery.PatientIdStr), t => t.PatientIdStr.Contains(inQuery.PatientIdStr)) //不属于该访视的检查 或者未绑定的检查 join scpStudySubjectVisit in _studySubjectVisitRepository.Where(c => c.TrialId == inQuery.TrialId) on scpStudy.Id equals scpStudySubjectVisit.SCPStudyId into dd from scpStudySV in dd.DefaultIfEmpty() select new VisitPatientStudyView() { PatientIdStr = scpStudy.PatientIdStr, PatientBirthDate = scpStudy.PatientBirthDate, PatientAge = scpStudy.PatientAge, PatientName = scpStudy.PatientName, PatientSex = scpStudy.PatientSex, Description = scpStudy.Description, CalledAE = scpStudy.CalledAE, CallingAE = scpStudy.CallingAE, InstanceCount = scpStudy.InstanceCount, Modalities = scpStudy.Modalities, PatientId = scpStudy.PatientId, SCPStudyId = scpStudy.Id, SeriesCount = scpStudy.SeriesCount, StudyTime = scpStudy.StudyTime, SubmitState = scpStudySV.SubjectVisit.SubmitState, SubjectVisitId = scpStudySV.SubjectVisitId, VisitName = scpStudySV.SubjectVisit.VisitName, }; #region 废弃 //var notCurrentVisitQuey = _studySubjectVisitRepository.Where(t => t.SubjectVisitId != inQuery.SujectVisitId && t.SCPStudy.Patient.Id == inQuery.PatientId) // .Select(t => new VisitPatientStudyView() // { // Description = t.SCPStudy.Description, // CalledAE = t.SCPStudy.CalledAE, // InstanceCount = t.SCPStudy.InstanceCount, // Modalities = t.SCPStudy.Modalities, // PatientId = t.SCPStudy.PatientId, // SCPStudyId = t.SCPStudy.PatientId, // SeriesCount = t.SCPStudy.SeriesCount, // StudyTime = t.SCPStudy.StudyTime, // SubjectVisitId = t.SubjectVisitId, // VisitName = t.SubjectVisit.VisitName, // }); //var notBindQuery= _studyRepository.Where(t => t.PatientId == inQuery.PatientId && t.pa) //var patientQuery = query // .ProjectTo(_mapper.ConfigurationProvider); #endregion var sortField = string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(VisitPatientStudyView.StudyTime) : inQuery.SortField; var orderQuery = inQuery.Asc ? query.OrderBy(sortField) : query.OrderBy(sortField + " desc"); var list = await orderQuery.ToListAsync(); return list; } #endregion #region 检查管理 /// ///检查管理-> 检查列表 (同步影像数据之前的) /// /// /// [HttpPost] public async Task>> GetPatientStudyBeforeConfirmList(TrialPatientStudyQuery inQuery) { #region 只查询已绑定的 //var query = _studySubjectVisitRepository.Where(t => t.SubjectVisit.TrialId == inQuery.TrialId) // .WhereIf(inQuery.PatientId != null, t => t.SCPStudy.PatientId == inQuery.PatientId) // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.SCPStudy.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.SCPStudy.Patient.PatientSex.Contains(inQuery.PatientSex)) // .WhereIf(inQuery.SubjectAge != null, t => t.SubjectVisit.Subject.Age == inQuery.SubjectAge) // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.SubjectVisit.Subject.Sex.Contains(inQuery.SubjectSex)) // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.SubjectVisit.Subject.ShortName.Contains(inQuery.SubjectShortName)) // .WhereIf(inQuery.BeginStudyTime != null, t => t.SCPStudy.StudyTime >= inQuery.BeginStudyTime) // .WhereIf(inQuery.EndStudyTime != null, t => t.SCPStudy.StudyTime <= inQuery.EndStudyTime) // .Select(t => new PatientStudyBeforeConfirmView() // { // SubjectId = t.SubjectVisit.SubjectId, // SubjectAge = t.SubjectVisit.Subject.Age, // SubjectSex = t.SubjectVisit.Subject.Sex, // SubjectShortName = t.SubjectVisit.Subject.ShortName, // PatientId = t.SCPStudy.PatientId, // PatientAge = t.SCPStudy.PatientAge, // PatientBirthDate = t.SCPStudy.PatientBirthDate, // PatientIdStr = t.SCPStudy.PatientIdStr, // PatientSex = t.SCPStudy.PatientSex, // //PatientList = t.SubjectVisit.Subject.SubjectPatientList.Select(t => new PatientBasicInfo() // //{ // // PatientId = t.PatientId, // // PatientAge = t.Patient.PatientAge, // // PatientBirthDate = t.Patient.PatientBirthDate, // // PatientIdStr = t.Patient.PatientIdStr, // // PatientSex = t.Patient.PatientSex, // //}).ToList(), // SubjectCode = t.SubjectVisit.Subject.Code, // SubmitState = t.SubjectVisit.SubmitState, // SubmitTime = t.SubjectVisit.SubmitTime, // VisitName = t.SubjectVisit.VisitName, // SubjectVisitId = t.SubjectVisitId, // VisitEarliestStudyTime = t.SubjectVisit.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime), // VisitLatestStudyTime = t.SubjectVisit.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime), // StudyId = t.SCPStudyId, // StudyTime = t.SCPStudy.StudyTime, // CallingAE = t.SCPStudy.CallingAE, // CalledAE = t.SCPStudy.CalledAE // }); #endregion var query = from scpStudy in _studyRepository.Where(t => !t.SCPStudySubjectVisitList.Any(t => t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted && t.TrialId == inQuery.TrialId)) .WhereIf(inQuery.IsBindedVisit == false, t => !t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId)) .WhereIf(inQuery.IsBindedVisit == true, t => t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.VisitName), t => t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId && t.SubjectVisit.VisitName.Contains(inQuery.VisitName))) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Patient.PatientSex.Contains(inQuery.PatientSex)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) join subjectPatient in _subjectPatientRepository.Where(t => t.Subject.TrialId == inQuery.TrialId) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Subject.Code.Contains(inQuery.SubjectCode)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.Subject.Sex.Contains(inQuery.SubjectSex)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.Subject.ShortName.Contains(inQuery.SubjectShortName)) on scpStudy.PatientId equals subjectPatient.PatientId join scpStudySubjectVisit in _studySubjectVisitRepository.Where(t => t.TrialId == inQuery.TrialId) on scpStudy.Id equals scpStudySubjectVisit.SCPStudyId into dd from scpStudySV in dd.DefaultIfEmpty() select new PatientStudyBeforeConfirmView() { SubjectId = subjectPatient.Subject.Id, SubjectAge = subjectPatient.Subject.Age, SubjectSex = subjectPatient.Subject.Sex, SubjectShortName = subjectPatient.Subject.ShortName, SubjectCode = subjectPatient.Subject.Code, PatientId = scpStudy.PatientId, PatientName = scpStudy.PatientName, PatientAge = scpStudy.PatientAge, PatientBirthDate = scpStudy.PatientBirthDate, PatientIdStr = scpStudy.PatientIdStr, PatientSex = scpStudy.PatientSex, //PatientList = t.SubjectVisit.Subject.SubjectPatientList.Select(t => new PatientBasicInfo() //{ // PatientId = t.PatientId, // PatientAge = t.Patient.PatientAge, // PatientBirthDate = t.Patient.PatientBirthDate, // PatientIdStr = t.Patient.PatientIdStr, // PatientSex = t.Patient.PatientSex, //}).ToList(), SubmitState = scpStudySV.SubjectVisit.SubmitState, SubmitTime = scpStudySV.SubjectVisit.SubmitTime, VisitName = scpStudySV.SubjectVisit.VisitName, VisitNum = scpStudySV.SubjectVisit.VisitNum, SubjectVisitId = scpStudySV.SubjectVisit.Id, VisitEarliestStudyTime = scpStudySV.SubjectVisit.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime), VisitLatestStudyTime = scpStudySV.SubjectVisit.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime), StudyId = scpStudy.Id, StudyTime = scpStudy.StudyTime, CallingAE = scpStudy.CallingAE, CalledAE = scpStudy.CalledAE, Description = scpStudy.Description, InstanceCount = scpStudy.InstanceCount, Modalities = scpStudy.Modalities, ModalityForEdit = scpStudy.ModalityForEdit, SeriesCount = scpStudy.SeriesCount }; var defalutSortArray = new string[] { nameof(PatientStudyBeforeConfirmView.SubjectCode), nameof(PatientStudyBeforeConfirmView.VisitNum), nameof(PatientStudyBeforeConfirmView.StudyTime) }; var pageList = await query.ToPagedListAsync(inQuery, defalutSortArray); return ResponseOutput.Ok(pageList); } /// ///检查管理-> 检查列表 (同步影像数据之后的 带患者信息 患者信息是数组) /// /// /// [HttpPost] public async Task>> GetTrialPatientStudyList(TrialPatientStudyQuery inQuery, [FromServices] IRepository _repository) { var query = _repository.Where(t => t.SubjectVisit.TrialId == inQuery.TrialId) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.PatientIdStr.Contains(inQuery.PatientIdStr)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.PatientSex.Contains(inQuery.PatientSex)) .WhereIf(inQuery.SubjectAge != null, t => t.SubjectVisit.Subject.Age == inQuery.SubjectAge) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.SubjectVisit.Subject.Sex.Contains(inQuery.SubjectSex)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.SubjectVisit.Subject.ShortName.Contains(inQuery.SubjectShortName)) .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) .Select(t => new PatientStudyView() { SubjectId = t.SubjectVisit.SubjectId, SubjectAge = t.Subject.Age, SubjectSex = t.Subject.Sex, SubjectShortName = t.Subject.ShortName, //PatientId = Guid.Empty, PatientAge = t.PatientAge, PatientName = t.PatientName, PatientBirthDate = t.PatientBirthDate, PatientIdStr = t.PatientIdStr, PatientSex = t.PatientSex, //PatientList = t.Subject.SubjectPatientList.Select(t => new PatientBasicInfo() //{ // PatientId = t.PatientId, // PatientAge = t.Patient.PatientAge, // PatientBirthDate = t.Patient.PatientBirthDate, // PatientIdStr = t.Patient.PatientIdStr, // PatientSex = t.Patient.PatientSex, //}).ToList(), Modalities = t.Modalities, ModalityForEdit = t.ModalityForEdit, SubjectCode = t.SubjectVisit.Subject.Code, SubmitState = t.SubjectVisit.SubmitState, SubmitTime = t.SubjectVisit.SubmitTime, VisitName = t.SubjectVisit.VisitName, VisitNum = t.SubjectVisit.VisitNum, SubjectVisitId = t.SubjectVisitId, VisitEarliestStudyTime = t.SubjectVisit.StudyList.Min(t => t.StudyTime), VisitLatestStudyTime = t.SubjectVisit.StudyList.Max(t => t.StudyTime), StudyId = t.Id, StudyTime = t.StudyTime, Description = t.Description, SeriesCount = t.SeriesCount, InstanceCount = t.InstanceCount, }); var defalutSortArray = new string[] { nameof(PatientStudyView.SubjectCode), nameof(PatientStudyView.VisitNum), nameof(PatientStudyView.StudyTime) }; var pageList = await query.ToPagedListAsync(inQuery, defalutSortArray); return ResponseOutput.Ok(pageList); } /// /// 获取该项目 患者已绑定subject ,新来了的检查 可能需要新建访视 但是新增的检查未绑定访视的检查列表 /// /// /// [HttpPost] public async Task>> GetTrialUnbindSubjectVisitStudyList(TrialPatientStudyQuery inQuery) { //属于该项目的已绑定患者的检查,同时没有绑定任何访视 var query = from scpStudy in _studyRepository.Where(t => !t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Patient.PatientSex.Contains(inQuery.PatientSex)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) join subjectPatient in _subjectPatientRepository.Where(t => t.Subject.TrialId == inQuery.TrialId) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Subject.Code.Contains(inQuery.SubjectCode)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.Subject.Sex.Contains(inQuery.SubjectSex)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.Subject.ShortName.Contains(inQuery.SubjectShortName)) on scpStudy.PatientId equals subjectPatient.PatientId join scpStudySubjectVisit in _studySubjectVisitRepository.AsQueryable() on scpStudy.Id equals scpStudySubjectVisit.SCPStudyId into dd from scpStudySV in dd.DefaultIfEmpty() select new UnbindStudyView() { PatientIdStr = scpStudy.PatientIdStr, PatientBirthDate = scpStudy.PatientBirthDate, PatientAge = scpStudy.PatientAge, PatientName = scpStudy.PatientName, PatientSex = scpStudy.PatientSex, Description = scpStudy.Description, CalledAE = scpStudy.CalledAE, InstanceCount = scpStudy.InstanceCount, Modalities = scpStudy.Modalities, PatientId = scpStudy.PatientId, SCPStudyId = scpStudy.Id, SeriesCount = scpStudy.SeriesCount, StudyTime = scpStudy.StudyTime, SubjectVisitId = scpStudySV.SubjectVisitId, VisitName = scpStudySV.SubjectVisit.VisitName, SubjectId = subjectPatient.SubjectId, SubjectCode = subjectPatient.Subject.Code, TrialId = subjectPatient.Subject.TrialId, SubjectAge = subjectPatient.Subject.Age, SubjectSex = subjectPatient.Subject.Sex, SubjectShortName = subjectPatient.Subject.ShortName, SubjectBirthDate = subjectPatient.Subject.BirthDate }; #region 废弃 //var query = from subject in _subjectRepository.Where(t => t.TrialId == inQuery.TrialId) // .WhereIf(inQuery.SubjectAge != null, t => t.Age == inQuery.SubjectAge) // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Code.Contains(inQuery.SubjectCode)) // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.Sex.Contains(inQuery.SubjectSex)) // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.ShortName.Contains(inQuery.SubjectShortName)) // join subjectPatient in _subjectPatientRepository.AsQueryable() on subject.Id equals subjectPatient.PatientId // //没有绑定任何访视 // join scpStudy in _studyRepository.AsQueryable() // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Patient.PatientSex.Contains(inQuery.PatientSex)) // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) // .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) // .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) // on subjectPatient.PatientId equals scpStudy.PatientId // select new SubjectPatientStudyView() // { // SubjectId = subject.Id, // SubjectAge = subject.Age, // SubjectSex = subject.Sex, // SubjectShortName = subject.ShortName, // PatientList = subject.SubjectPatientList.Select(t => new PatientBasicInfo() // { // PatientId = t.PatientId, // PatientAge = t.Patient.PatientAge, // PatientBirthDate = t.Patient.PatientBirthDate, // PatientIdStr = t.Patient.PatientIdStr, // PatientSex = t.Patient.PatientSex, // }).ToList(), // SubjectCode = subject.Code, // SeriesCount = scpStudy.SeriesCount, // CalledAE = scpStudy.CalledAE, // InstanceCount = scpStudy.InstanceCount, // Description = scpStudy.Description, // Modalities = scpStudy.Modalities, // PatientId = scpStudy.PatientId, // SCPStudyId = scpStudy.Id, // StudyTime = scpStudy.StudyTime // }; #endregion var pageList = await query.ToPagedListAsync(inQuery, nameof(UnbindStudyView.StudyTime)); return ResponseOutput.Ok(pageList); } /// /// 删除某个项目 未提交的访视检查绑定, 清理数据,方便测试自动绑定 /// /// /// [HttpDelete] public async Task DeleteUnSubmittedStudyBind(Guid trialId, Guid? subjectId, [FromServices] IRepository _visitTaskRepository, [FromServices] IRepository _dicomStudyRepository, [FromServices] IRepository _dicomSeriesRepository, [FromServices] IRepository _dicomInstanceRepository) { if (subjectId != null) { await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.TrialId == trialId && t.SubjectId == subjectId); await _subjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); await _visitTaskRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); await _dicomStudyRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); await _dicomSeriesRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); await _dicomInstanceRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); } else { await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.TrialId == trialId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted); } return ResponseOutput.Ok(); } /// /// 阅片管理-> 任务列表 /// /// /// [HttpPost] public async Task>> GetPatientVisitTaskList([FromServices] IRepository _visitTaskRepository, PatientVisitTaskQuery inQuery) { var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsAnalysisCreate == false) .WhereIf(inQuery.ReadingCategory != null, t => t.ReadingCategory == inQuery.ReadingCategory) .WhereIf(inQuery.ReadingCategory == null, t => t.ReadingCategory != ReadingCategory.Judge) .WhereIf(inQuery.ReadingTaskState != null, t => t.ReadingTaskState == inQuery.ReadingTaskState) .WhereIf(inQuery.TaskState != null, t => t.TaskState == inQuery.TaskState) .WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId) .WhereIf(inQuery.DoctorUserId != null, t => t.DoctorUserId == inQuery.DoctorUserId) .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) .WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.TaskName.Contains(inQuery.TaskName) || t.TaskBlindName.Contains(inQuery.TaskName)) .WhereIf(inQuery.BeginSignTime != null, t => t.SignTime >= inQuery.BeginSignTime) .WhereIf(inQuery.EndSignTime != null, t => t.SignTime <= inQuery.EndSignTime) .WhereIf(inQuery.BeginTaskCreateTime != null, t => t.CreateTime >= inQuery.BeginTaskCreateTime) .WhereIf(inQuery.EndTaskCreateTime != null, t => t.CreateTime <= inQuery.EndTaskCreateTime) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientIdStr.Contains(inQuery.PatientIdStr))) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientName.Contains(inQuery.PatientName))) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientSex.Contains(inQuery.PatientSex))) .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectShortName), t => t.Subject.ShortName.Contains(inQuery.SubjectShortName)) .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => (t.Subject.Code.Contains(inQuery.SubjectCode) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(inQuery.SubjectCode) && t.IsAnalysisCreate)) .ProjectTo(_mapper.ConfigurationProvider); var defalutSortArray = new string[] { nameof(PatientVisitTaskDTO.SubjectId), nameof(PatientVisitTaskDTO.VisitTaskNum) }; var pageList = await visitTaskQueryable.ToPagedListAsync(inQuery, defalutSortArray); return ResponseOutput.Ok(pageList); } #endregion #region 影像下载 /// /// 获取下载的访视检查信息 /// /// /// /// public async Task GetDownloadSubjectVisitStudyInfo(Guid trialId, Guid subjectVisitId) { var query = from sv in _subjectVisitRepository.Where(t => t.Id == subjectVisitId) select new { TrialId = sv.TrialId, SubjectId = sv.SubjectId, SubjectCode = sv.Subject.Code, VisitName = sv.VisitName, StudyList = sv.StudyList .Select(u => new { u.PatientIdStr, u.StudyTime, u.StudyCode, SeriesList = u.SeriesList.Select(z => new { z.Modality, InstanceList = z.DicomInstanceList.Select(k => new { k.Path, k.FileSize }) }) }).ToList(), }; var result = query.FirstOrDefault(); var preDownloadInfo = new SubejctVisitDownload() { Id = NewId.NextSequentialGuid(), IP = _userInfo.IP, SubjectVisitId = subjectVisitId, DownloadStartTime = DateTime.Now, ImageCount = result.StudyList.Sum(s => s.SeriesList.Sum(s => s.InstanceList.Count())), ImageSize = result.StudyList.Sum(t => t.SeriesList.Sum(s => s.InstanceList.Sum(i => i.FileSize))) ?? 0 }; await _subejctVisitDownloadRepository.AddAsync(preDownloadInfo, true); return ResponseOutput.Ok(result, preDownloadInfo.Id); } /// /// 影像下载成功回调 /// /// /// [HttpGet] public async Task DownloadImageSuccess(Guid trialImageDownloadId) { await _subejctVisitDownloadRepository.UpdatePartialFromQueryAsync(t => t.Id == trialImageDownloadId, u => new SubejctVisitDownload() { DownloadEndTime = DateTime.Now, IsSuccess = true }, true); return ResponseOutput.Ok(); } /// /// 访视影像下载记录表 /// /// [HttpPost] public async Task>> GetTrialSubjectVisitDownloadList(VisitImageDownloadQuery inQuery) { var query = _subejctVisitDownloadRepository.Where(t => t.SubjectVisit.TrialId == inQuery.TrialId) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.IP), t => t.IP.Contains(inQuery.IP)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), t => t.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.VisitName), t => t.SubjectVisit.VisitName.Contains(inQuery.VisitName)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Name), t => t.CreateUser.UserName.Contains(inQuery.Name) || t.CreateUser.FullName.Contains(inQuery.Name)) .WhereIf(inQuery.UserTypeEnum != null, t => t.CreateUser.UserTypeRole.UserTypeEnum == inQuery.UserTypeEnum) .WhereIf(inQuery.BeginDownloadTime != null, t => t.CreateTime >= inQuery.BeginDownloadTime) .WhereIf(inQuery.EndDownloadTime != null, t => t.CreateTime <= inQuery.EndDownloadTime) .ProjectTo(_mapper.ConfigurationProvider); var pageList = await query.ToPagedListAsync(inQuery, nameof(VisitImageDownloadQuery.SubjectCode)); return ResponseOutput.Ok(pageList); } #endregion /// /// scp 影像推送记录表 /// /// /// [HttpPost] public async Task>> GetSCPImageUploadList(SCPImageUploadQuery inQuery) { var query = _SCPImageUploadRepository.Where() .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CalledAE), t => t.CalledAE.Contains(inQuery.CalledAE)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAEIP), t => t.CallingAEIP.Contains(inQuery.CallingAEIP)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAE), t => t.CallingAE.Contains(inQuery.CallingAE)) .WhereIf(inQuery.StartTime != null, t => t.StartTime >= inQuery.StartTime) .WhereIf(inQuery.EndTime != null, t => t.EndTime <= inQuery.EndTime) .ProjectTo(_mapper.ConfigurationProvider); var pageList = await query.ToPagedListAsync(inQuery, nameof(SCPImageUploadView.CallingAE)); return ResponseOutput.Ok(pageList); } #region 作为SCU C-find c-move 操作 #region 基础方法 public static DicomCFindRequest CreateStudyRequest(SCUQuery inQuery, string modality/*, List modalityList*/) { var request = new DicomCFindRequest(DicomQueryRetrieveLevel.Study); // always add the encoding request.Dataset.AddOrUpdate(new DicomTag(0x8, 0x5), "ISO_IR 100"); // add the dicom tags with empty values that should be included in the result of the QR Server ////病例号 //request.Dataset.AddOrUpdate(DicomTag.PatientID, ""); //request.Dataset.AddOrUpdate(DicomTag.PatientName, ""); //request.Dataset.AddOrUpdate(DicomTag.PatientSex, ""); //request.Dataset.AddOrUpdate(DicomTag.PatientBirthDate, ""); ////检查号 设备类型 //request.Dataset.AddOrUpdate(DicomTag.StudyID, ""); //request.Dataset.AddOrUpdate(DicomTag.ModalitiesInStudy, ""); //request.Dataset.AddOrUpdate(DicomTag.StudyDate, ""); //request.Dataset.AddOrUpdate(DicomTag.StudyTime, ""); //request.Dataset.AddOrUpdate(DicomTag.BodyPartExamined, ""); //request.Dataset.AddOrUpdate(DicomTag.StudyDescription, ""); //request.Dataset.AddOrUpdate(DicomTag.StudyInstanceUID, ""); // add the dicom tags that contain the filter criterias //request.Dataset.AddOrUpdate(DicomTag.PatientID, "Test*"); //var modalityQuery = string.Join($"\\", (inQuery.ModalitiesInStudyList == null || inQuery.ModalitiesInStudyList?.Count == 0) ? modalityList : inQuery.ModalitiesInStudyList); //病例号 request.Dataset.AddOrUpdate(DicomTag.PatientID, inQuery.PatientID.IsNullOrEmpty() ? "" : inQuery.PatientID); request.Dataset.AddOrUpdate(DicomTag.PatientName, inQuery.PatientName.IsNullOrEmpty() ? "" : inQuery.PatientName); request.Dataset.AddOrUpdate(DicomTag.PatientSex, inQuery.PatientSex.IsNullOrEmpty() ? "" : inQuery.PatientSex); request.Dataset.AddOrUpdate(DicomTag.PatientBirthDate, inQuery.PatientBirthDate.IsNullOrEmpty() ? "" : inQuery.PatientBirthDate); //检查号 设备类型 request.Dataset.AddOrUpdate(DicomTag.StudyID, inQuery.StudyID.IsNullOrEmpty() ? "" : inQuery.StudyID); request.Dataset.AddOrUpdate(DicomTag.AccessionNumber, inQuery.StudyID.IsNullOrEmpty() ? "" : inQuery.AccessionNumber); request.Dataset.AddOrUpdate(DicomTag.Modality, ""); request.Dataset.AddOrUpdate(DicomTag.ModalitiesInStudy, modality); request.Dataset.AddOrUpdate(DicomTag.StudyDate, inQuery.StudyDate.IsNullOrEmpty() ? "" : inQuery.StudyDate); request.Dataset.AddOrUpdate(DicomTag.StudyTime, inQuery.StudyTime.IsNullOrEmpty() ? "" : inQuery.StudyTime); request.Dataset.AddOrUpdate(DicomTag.StudyDescription, inQuery.StudyDescription.IsNullOrEmpty() ? "" : inQuery.StudyDescription); request.Dataset.AddOrUpdate(DicomTag.StudyInstanceUID, inQuery.StudyInstanceUID.IsNullOrEmpty() ? "" : inQuery.StudyInstanceUID); //测试 //request.Dataset.AddOrUpdate(DicomTag.SeriesInstanceUID, ""); //request.Dataset.AddOrUpdate(DicomTag.SeriesDescription, ""); //request.Dataset.AddOrUpdate(DicomTag.BodyPartExamined, inQuery.BodyPartExamined.IsNullOrEmpty() ? "" : inQuery.BodyPartExamined); return request; } public static void DebugStudyResponse(DicomCFindResponse response, ILogger _logger) { if (response.Status == DicomStatus.Pending) { // print the results _logger.LogDebug($"Patient {response.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty)}, {(response.Dataset.TryGetString(DicomTag.ModalitiesInStudy, out var dummy) ? dummy : string.Empty)}-Study from {response.Dataset.GetSingleValueOrDefault(DicomTag.StudyDate, new DateTime())} with UID {response.Dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty)} "); } if (response.Status == DicomStatus.Success) { Console.WriteLine(response.Status.ToString()); } } #endregion private bool CEchoTest(DicomAE find, string clientAE) { var client = DicomClientFactory.Create(find.IP, find.Port, false, clientAE, find.CalledAE); client.NegotiateAsyncOps(); client.ServiceOptions.RequestTimeout = TimeSpan.FromSeconds(3); var request = new DicomCEchoRequest { OnResponseReceived = (req, response) => { if (response.Status == DicomStatus.Success) { find.IsTestOK = true; } else { find.IsTestOK = false; } } }; return find.IsTestOK; } /// /// 获取 检查列表 /// /// /// /// /// [HttpPost] public async Task>> GetCFindStudyList(SCUQuery inQuery, [FromServices] IRepository _dicomAEReposiotry, [FromServices] ILogger _logger) { var result = new List(); var find = await _dicomAEReposiotry.FirstOrDefaultAsync(t => t.Id == inQuery.PacsDicomAEId); var hirClient = await _dicomAEReposiotry.FirstOrDefaultAsync(t => t.PacsTypeEnum == PacsType.HIRClient); if (hirClient == null) { throw new BusinessValidationFailedException(_localizer["Patient_NoPacsClientAE"]); } if (find != null) { //测试失败 if (!CEchoTest(find, hirClient.CalledAE)) { throw new BusinessValidationFailedException(_localizer["Patient_PacsAENotOnline"]); } var @lock = _distributedLockProvider.CreateLock($"CFind"); var client = DicomClientFactory.Create(find.IP, find.Port, false, hirClient.CalledAE, find.CalledAE); client.NegotiateAsyncOps(); DicomCFindRequest.ResponseDelegate responseDelegate = (req, response) => { if (response.HasDataset) { using (@lock.Acquire()) { var maxStudyCount = find.MaxStudyCount > 0 ? find.MaxStudyCount : 50; //查询该项目下的患者,那么不限制总数,否则需要限制总数 if (inQuery.TrialId == null && result.Count >= maxStudyCount) { response.Status = DicomStatus.Cancel; return; } else { var studyTime = response.Dataset.GetDateTime(DicomTag.StudyDate, DicomTag.StudyTime); DateOnly datePart = DateOnly.FromDateTime(studyTime); TimeOnly timePart = TimeOnly.FromDateTime(studyTime); var canParce = DateTime.TryParseExact(response.Dataset?.GetSingleValueOrDefault(DicomTag.PatientBirthDate, ""), "yyyyMMdd", null, System.Globalization.DateTimeStyles.None, out DateTime birthDate); // 格式化为你需要的日期格式,例如:yyyy-MM-dd string formattedBirthDate = canParce ? birthDate.ToString("yyyy-MM-dd") : string.Empty; result.Add(new SCUStudyView() { PatientID = response.Dataset?.GetSingleValueOrDefault(DicomTag.PatientID, ""), PatientName = response.Dataset?.GetSingleValueOrDefault(DicomTag.PatientName, ""), PatientSex = response.Dataset?.GetSingleValueOrDefault(DicomTag.PatientSex, ""), PatientBirthDate = formattedBirthDate, StudyID = response.Dataset?.GetSingleValueOrDefault(DicomTag.StudyID, ""), AccessionNumber = response.Dataset?.GetSingleValueOrDefault(DicomTag.AccessionNumber, ""), ModalitiesInStudy = response.Dataset?.GetSingleValueOrDefault(DicomTag.ModalitiesInStudy, ""), Modality = response.Dataset?.GetSingleValueOrDefault(DicomTag.Modality, ""), StudyDate = datePart.ToString("yyyy-MM-dd"), StudyTime = timePart.ToString("HH:mm:ss"), BodyPartExamined = response.Dataset?.GetSingleValueOrDefault(DicomTag.BodyPartExamined, ""), StudyDescription = response.Dataset?.GetSingleValueOrDefault(DicomTag.StudyDescription, ""), StudyInstanceUID = response.Dataset?.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, "") }); } } } }; //看当前前端有没有传递modality,有的话以前端为准,没有的话以配置为准 构造同样数量的请求 var requestModalityList = (inQuery.ModalitiesInStudyList != null && inQuery.ModalitiesInStudyList.Count > 0) ? inQuery.ModalitiesInStudyList : find.ModalityList; if (inQuery.TrialId == null) { //判断前段有没有传递时间 if (find.PacsSearchMaxDays != 0) { //0 天 + 前端不传递就是不限制 inQuery.StudyDate = inQuery.StudyDate.IsNullOrEmpty() ? $"{DateTime.Now.AddDays(-(find.PacsSearchMaxDays - 1)).ToString("yyyyMMdd")}-{DateTime.Now.ToString("yyyyMMdd")}" : inQuery.StudyDate; } if (find.IsSupportMutiModality) { //支持多模态的Pacs var modality = string.Join($"\\", requestModalityList); var request = CreateStudyRequest(inQuery, modality); request.OnResponseReceived += responseDelegate; await client.AddRequestAsync(request); } else { foreach (var modality in requestModalityList) { var cloneInQuery = inQuery.Clone(); var request = CreateStudyRequest(cloneInQuery, modality); request.OnResponseReceived += responseDelegate; await client.AddRequestAsync(request); } } await client.SendAsync(); } else { //找到该项目下的患者,以及每个患者最新的检查之后的检查 var list = await _subjectRepository.Where(t => t.TrialId == inQuery.TrialId).SelectMany(t => t.SubjectPatientList) .WhereIf(inQuery.PatientID.IsNotNullOrEmpty(), t => t.Patient.PatientIdStr == inQuery.PatientID) .Select(t => new { t.Patient.PatientIdStr, MaxStudyTime = t.Patient.SCPStudyList.Max(t => t.StudyTime), //仅用提交时间 VisitMaxSubmitTime = t.Subject.SubjectVisitList.Max(t => t.SubmitTime) }).ToListAsync(); foreach (var patient in list) { var cloneInQuery = inQuery.Clone(); cloneInQuery.PatientID = patient.PatientIdStr; if (find.PacsSearchMaxDays != 0) { // pacs 支持的时间 // 访视提交的时间 if (patient.VisitMaxSubmitTime != null && patient.VisitMaxSubmitTime > DateTime.Now.AddDays(-(find.PacsSearchMaxDays - 1))) { cloneInQuery.StudyDate = patient.VisitMaxSubmitTime?.ToString("yyyyMMdd") + "-"; cloneInQuery.StudyTime = patient.VisitMaxSubmitTime?.ToString("HHmmss") + "-"; } else { //pacs 限制的时间 cloneInQuery.StudyDate = $"{DateTime.Now.AddDays(-(find.PacsSearchMaxDays - 1)).ToString("yyyyMMdd")}-{DateTime.Now.ToString("yyyyMMdd")}"; } } else { if (patient.VisitMaxSubmitTime != null) { cloneInQuery.StudyDate = patient.VisitMaxSubmitTime?.ToString("yyyyMMdd") + "-"; cloneInQuery.StudyTime = patient.VisitMaxSubmitTime?.ToString("HHmmss") + "-"; } } #region 区分时间查询 ////选择了患者,那么不按照患者最新检查时间之后查询(因为可能存在,先拉去了后面的检查,导致前面的检查查询不到) //if (inQuery.PatientID.IsNotNullOrEmpty()) //{ // // pacs 支持的时间 // // 访视提交的时间 // if (patient.VisitMaxSubmitTime != null) // { // cloneInQuery.StudyDate = patient.VisitMaxSubmitTime?.ToString("yyyyMMdd") + "-"; // cloneInQuery.StudyTime = patient.VisitMaxSubmitTime?.ToString("HHmmss") + "-"; // } //} //else //{ // //没有选择患者,那么按照每个患者的最大检查时间之后的查询 // if (patient.MaxStudyTime != null) // { // cloneInQuery.StudyDate = patient.MaxStudyTime?.ToString("yyyyMMdd") + "-"; // cloneInQuery.StudyTime = patient.MaxStudyTime?.ToString("HHmmss") + "-"; // } //} #endregion var request = CreateStudyRequest(cloneInQuery, find.IsSupportMutiModality ? string.Join($"\\", requestModalityList) : ""); request.OnResponseReceived += responseDelegate; await client.AddRequestAsync(request); } await client.SendAsync(); } var resultInstanceUidList = result.Select(t => t.StudyInstanceUID).ToList(); var existStudyIdList = _studyRepository.Where(t => resultInstanceUidList.Contains(t.StudyInstanceUid)) .Select(t => t.StudyInstanceUid).ToList(); foreach (var item in result) { item.IsStudyExist = existStudyIdList.Any(t => t == item.StudyInstanceUID); } if (inQuery.TrialId != null) { result = result.Where(t => t.IsStudyExist == false).ToList(); } //查询的时候不支持多模态的可以去重一下,支持多模态的本身处理了分组过滤也不影响 result = result.GroupBy(t => t.StudyInstanceUID).Select(g => g.First()).ToList(); } return ResponseOutput.Ok(result, find); } #region 获取项目维度拉取影像数据下拉框 public async Task> GetUserJoinedTrialPatientList() { var trialQuery = _trialRepository .WhereIf(_userInfo.UserTypeEnumInt != (int)UserTypeEnum.SuperAdmin && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.Admin && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.OA , t => t.TrialUserList.Any(t => t.UserId == _userInfo.Id && t.IsDeleted == false) && t.IsDeleted == false) .Select(t => new NewTrialSelectDTO() { TrialId = t.Id, TrialCode = t.TrialCode, ResearchProgramNo = t.ResearchProgramNo, ExperimentName = t.ExperimentName, CreateTime = t.CreateTime, TrialStatusStr = t.TrialStatusStr, PatientList = t.SubjectList.Where(t => t.Status == SubjectStatus.OnVisit).SelectMany(t => t.SubjectPatientList).Select(u => new TrialPatientDTO() { PatientAge = u.Patient.PatientAge, PatientIdStr = u.Patient.PatientIdStr, PatientName = u.Patient.PatientName, PatientSex = u.Patient.PatientSex }).ToList() }); var list = await trialQuery.OrderBy(t => t.CreateTime).ToListAsync(); return list; } #endregion /// /// 批量验证Cmove 检查在系统存在与否 /// /// /// public async Task> CMoveVerify(SCUCmoveCommand inCommand) { var existStudyIdList = _studyRepository.Where(t => inCommand.StudyInstanceUIDList.Contains(t.StudyInstanceUid)) .Select(t => new { t.StudyInstanceUid, t.StudyId, PatientId = t.PatientIdStr, t.AccessionNumber, t.PatientName }).ToList(); var dic = new Dictionary(); foreach (var item in inCommand.StudyInstanceUIDList) { var find = existStudyIdList.FirstOrDefault(t => t.StudyInstanceUid == item); dic.Add(item, find == null ? string.Empty : find); } return dic; } /// /// 拉取影像 /// /// /// public async Task CmoveStudyList(SCUCmoveCommand inCommand) { var result = new List(); var find = await _dicomAEReposiotry.FirstOrDefaultAsync(t => t.Id == inCommand.PacsDicomAEId); var hirServer = await _dicomAEReposiotry.FirstOrDefaultAsync(t => t.PacsTypeEnum == PacsType.HIRServer); var hirClient = await _dicomAEReposiotry.FirstOrDefaultAsync(t => t.PacsTypeEnum == PacsType.HIRClient); if (hirServer == null) { //"未找到服务端AE配置,请核查后再拉取" return ResponseOutput.NotOk(_localizer["Patient_NoPacsAE"]); } if (hirClient == null) { return ResponseOutput.NotOk(_localizer["Patient_NoPacsClientAE"]); } if (find != null) { DicomCMoveRequest.ResponseDelegate responseDelegate = (req, response) => { if (response.Status.State == DicomState.Failure || response.Status.State == DicomState.Cancel) { _logger.LogError($" Cmove Pacs 返回状态: {response.Status.State} {response.SOPClassUID.ToJsonNotIgnoreNull()}"); _fusionCache.Set(CacheKeys.CmoveStudyId(req.Dataset.GetString(DicomTag.StudyInstanceUID)), null, TimeSpan.FromSeconds(1)); } // 判断是否传输完成或中断,执行缓存清理 if (response.Status.State == DicomState.Success && response.Remaining == 0) { _fusionCache.Set(CacheKeys.CmoveStudyId(req.Dataset.GetString(DicomTag.StudyInstanceUID)), null, TimeSpan.FromSeconds(1)); } }; var latestInstanceList = await _scpInstanceRepository.Where(t => inCommand.StudyInstanceUIDList.Contains(t.StudyInstanceUid)) .GroupBy(t => t.StudyInstanceUid).Select(g => g.OrderByDescending(t => t.CreateTime).FirstOrDefault()).ToListAsync(); var task = Task.Run(async () => { var client = DicomClientFactory.Create(find.IP, find.Port, false, hirClient.CalledAE, find.CalledAE); client.NegotiateAsyncOps(); var cmoveRequestList = new List(); foreach (var item in inCommand.StudyInstanceUIDList) { var cmoveConfig = _basicSystemConfigConfig.CurrentValue; var exsitStudyId = await _fusionCache.GetOrDefaultAsync(CacheKeys.CmoveStudyId(item), null); var latestInstance = latestInstanceList.Where(t => t.StudyInstanceUid == item).FirstOrDefault(); //缓存不存在当前检查,或者instance表最新的记录时间与当前时间之差超过1分钟(认为完成归档,可以重复拉取) if (exsitStudyId == null && (latestInstance == null || latestInstance?.CreateTime.AddMinutes(cmoveConfig.CmoveInstanceIntervalMinutes) < DateTime.Now)) { await _fusionCache.SetAsync(CacheKeys.CmoveStudyId(item), item, TimeSpan.FromMinutes(cmoveConfig.CmoveIntervalMinutes)); var cmoveRequest = new DicomCMoveRequest(hirServer.CalledAE, item); cmoveRequest.OnResponseReceived += responseDelegate; cmoveRequestList.Add(cmoveRequest); _logger.LogInformation($"执行C-Move StudyInstanceUID:{item}"); } else { _logger.LogInformation($"忽略C-Move StudyInstanceUID:{item}"); } } await client.AddRequestsAsync(cmoveRequestList); await client.SendAsync(); }); //await task; } return ResponseOutput.Ok(); } /// /// 同济医院 第三方系统 发送命令拉取影像 /// /// /// [HttpPost] [AllowAnonymous] public async Task TjCmoveStudy(TJCmoveStudyCommand inCommand) { var apiUrl = "http://192.168.40.88:8080/dock/userinfo"; var headers = new Dictionary { { "Content-Type", "application/json" } // 根据需要添加其他头部信息 }; var requestData = new { token = inCommand.Token, appId = "third-hirs", }; var tjUserInfo = await RestClientAPI.PostAsync(apiUrl, requestData, headers); if (tjUserInfo != null && tjUserInfo.Data?.Roles != null && tjUserInfo.Data.Roles.Any(role => string.Equals("PM", role, StringComparison.OrdinalIgnoreCase))) { var find = await _dicomAEReposiotry.FirstOrDefaultAsync(t => t.CalledAE == inCommand.CalledAE && t.PacsTypeEnum == PacsType.PacsServer); if (find == null) { return ResponseOutput.NotOk($"{inCommand.CalledAE}在系统中未配置,请联系维护人员核查"); } else { var cmoveCommand = new SCUCmoveCommand() { PacsDicomAEId = find.Id, StudyInstanceUIDList = inCommand.StudyInstanceUIDList }; return await CmoveStudyList(cmoveCommand); } } else { return ResponseOutput.NotOk("未获取到该用户信息或者该用户token 不包含PM 角色"); } } #endregion } }