diff --git a/IRC.Core.SCP/Service/DicomArchiveService.cs b/IRC.Core.SCP/Service/DicomArchiveService.cs index e522f6690..a79319afd 100644 --- a/IRC.Core.SCP/Service/DicomArchiveService.cs +++ b/IRC.Core.SCP/Service/DicomArchiveService.cs @@ -74,7 +74,7 @@ namespace IRaCIS.Core.SCP.Service //using (@lock.Acquire()) { - var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId==trialSiteId ); + var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr /*&& t.TrialSiteId==trialSiteId*/ ); var findStudy = await _studyRepository.FirstOrDefaultAsync(t=>t.Id== studyId); var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId); var findInstance = await _instanceRepository.FirstOrDefaultAsync(t => t.Id == instanceId); @@ -90,8 +90,8 @@ namespace IRaCIS.Core.SCP.Service findPatient = new SCPPatient() { Id = NewId.NextSequentialGuid(), - TrialId=trialId, - TrialSiteId=trialSiteId, + //TrialId=trialId, + //TrialSiteId=trialSiteId, PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty), PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty), PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty), @@ -163,8 +163,8 @@ namespace IRaCIS.Core.SCP.Service PatientId = findPatient.Id, Id = studyId, - TrialId = trialId, - TrialSiteId = trialSiteId, + //TrialId = trialId, + //TrialSiteId = trialSiteId, StudyInstanceUid = studyInstanceUid, StudyTime = studyTime, Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty), diff --git a/IRaCIS.Core.Application/BusinessFilter/_Config/_AppSettings.cs b/IRaCIS.Core.Application/BusinessFilter/_Config/_AppSettings.cs index e2c6fcddb..58f6db3f7 100644 --- a/IRaCIS.Core.Application/BusinessFilter/_Config/_AppSettings.cs +++ b/IRaCIS.Core.Application/BusinessFilter/_Config/_AppSettings.cs @@ -36,6 +36,8 @@ public class ServiceVerifyConfigOption public string ThirdPdfUrl { get; set; } + public string AESKey { get; set; } + } public class SystemEmailSendConfig diff --git a/IRaCIS.Core.Application/Helper/Cryptography.cs b/IRaCIS.Core.Application/Helper/Cryptography.cs new file mode 100644 index 000000000..7e8fa32a4 --- /dev/null +++ b/IRaCIS.Core.Application/Helper/Cryptography.cs @@ -0,0 +1,62 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +public class Cryptography +{ + public static string EncryptString(string plainText, string key, string iv) + { + using (Aes aesAlg = Aes.Create()) + { + aesAlg.Key = GetKeyBytes(key, aesAlg.KeySize / 8); + aesAlg.IV = GetKeyBytes(iv, 16); + + ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); + + using (MemoryStream msEncrypt = new MemoryStream()) + { + using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) + { + byte[] plainBytes = Encoding.UTF8.GetBytes(plainText); + csEncrypt.Write(plainBytes, 0, plainBytes.Length); + csEncrypt.FlushFinalBlock(); + } + return Convert.ToBase64String(msEncrypt.ToArray()); + } + } + } + + public static string DecryptString(string cipherText, string key, string iv) + { + byte[] cipherBytes = Convert.FromBase64String(cipherText); + using (Aes aesAlg = Aes.Create()) + { + aesAlg.Key = GetKeyBytes(key, aesAlg.KeySize / 8); + aesAlg.IV = GetKeyBytes(iv, 16); + + ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); + + using (MemoryStream msDecrypt = new MemoryStream(cipherBytes)) + { + using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) + { + using (StreamReader srDecrypt = new StreamReader(csDecrypt)) + { + return srDecrypt.ReadToEnd(); + } + } + } + } + } + + private static byte[] GetKeyBytes(string key, int keySize) + { + + + using (var deriveBytes = new PasswordDeriveBytes(key, null)) + { + return deriveBytes.GetBytes(keySize); + } + } +} diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml index ec3b62da9..3efdaaf54 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml @@ -863,23 +863,6 @@ - - - 影像下载记录表 - - - - - - - - - - 影像接收记录表 - - - - 国际化导出 @@ -887,25 +870,6 @@ - - - 影像检查列表-患者为维度组织 - - - - - - - - - - 邮件导出 - - - - - - 一致性分析结果导出 7 8 分别是自身 和组件一致性 @@ -12824,42 +12788,20 @@ TrialSiteDicomAEService - + - scp 影像推送记录表 - - - + DicomAEService + - - - 影像检查列表-患者为维度组织 - - - - - + - 影像检查列表-> 获取患者的检查列表 - - - + DicomAEService + - + - 影像访视上传 检查列表 + 测试scp server 是否可以连接 - - - - - - 提交 患者检查和访视的绑定 - - - - - @@ -17655,5 +17597,304 @@ 入组流程-向CRO提交医生[Submit] + + + 获取医院的配置信息 + + + + + + + 配置医院信息,方便测试邮件和授权码的方式 + + + + + + + 获取系统已确认的标准 + + + + + + + 获取项目列表 (PM CRC 共用) + + + + + + + 添加更新项目 + + + + + + + + + + 获取项目授权码 + + + + + + + 获取授权码明文信息 + + + + + + + + 获取项目激活码 + + + + + + + + 激活码获取明文信息 + + + + + + + + + 设置项目授权信息 + + + + + + + + 检查管理-> 检查Tab 患者列表 (带加入的项目信息 以及检查统计) 原型标注错误,不是检查列表 + + + + + + + 检查管理-> 患者加入项目 初始化勾选列表 + + + + + + + 检查管理-> 患者加入项目 下拉框勾选列表 + + + + + + + 检查管理-> 患者已加入的列表(原型有误,应该展示 项目 下的subject 绑定关系) + + + + + + + 检查管理-> 获取患者检查列表(同步影像数据之前的) + + + + + + + 清除该患者绑定的受试者的所有的数据、(subject subjectVisit visitTask dicom) + + + + + + + 受试者管理-> 受试者列表 (带患者信息,患者信息是数组) + + + + + + + 受试者管理-> 患者列表 (subject 列表进入,进行关系绑定初始化列表,排除已绑定的患者和已绑定给其他subject的患者) + + + + + + + 受试者管理->患者列表 Dicom AE 下拉框数据获取 + + + + + + 受试者管理-> 患者列表 模糊搜索下拉 选择subject 排除已绑定并提交的 + + + + + + + 之前患者和subject已绑定后,新来了的检查 可能需要新建访视,或者和已有访视在同一区间,需要自动绑定 + + + + + + + 建立subject与患者绑定 如果是下拉,则传递SubjectId,如果不存在,创建,那么就传递 SubjectCode + + 绑定以后,后台自动创建访视 和检查预先绑定 + + + + + + + 删除 受试者 和患者之间的绑定 + + + + + + + 患者检查 与SubjectVisit 的绑定 + + + + + + + 修改 访视 和检查的绑定关系 IsAdd 区分添加 还是移除 + + + + + + + 提交 患者检查和访视的绑定 + + + + + + + 绑定访视 初始化患者检查列表 + + + + + + + 访视管理- 获取subject 已存在的访视列表 ,同时获取项目访视的配置 在otherData里 + + + + + + + + 添加或者更新受试者访视 + + + + + + + + 前端利用组件打成压缩包,后端返回路径和压缩包的信息 + + + + + + + 访视管理-> 访视列表 (带患者信息,患者信息是数组) + + + + + + + 访视管理-> 获取当前访视 已绑定的患者检查 (从访视列表 进入修改绑定) + + + + + + + 访视管理-> 获取可选访视列表 (从访视列表 进入修改绑定) + + + + + + + 检查管理-> 检查列表 (同步影像数据之前的) + + + + + + + 检查管理-> 检查列表 (同步影像数据之后的 带患者信息 患者信息是数组) + + + + + + + 获取该项目 患者已绑定subject ,新来了的检查 可能需要新建访视 但是新增的检查未绑定访视的检查列表 + + + + + + + 删除某个项目 未提交的访视检查绑定, 清理数据,方便测试自动绑定 + + + + + + + 阅片管理-> 任务列表 + + + + + + + 打包和匿名化影像 默认是匿名化打包,也可以不匿名化打包 + + + + + + + + + 访视影像下载记录表 + + + + + + scp 影像推送记录表 + + + + diff --git a/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs b/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs index f1559db0e..b4b48272b 100644 --- a/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs +++ b/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs @@ -230,7 +230,7 @@ namespace IRaCIS.Core.Application.Service.Common ExperimentName = t.ExperimentName, TrialCode = t.TrialCode, CreateTime = t.CreateTime, - Sponsor = _userInfo.IsEn_Us ? t.Sponsor.SponsorName : t.Sponsor.SponsorNameCN, + Sponsor = t.Sponsor, TrialStatusStr = t.TrialStatusStr, ExpetiedTaskCount = isPM ? t.VisitTaskList.Where(t => t.IsUrgent).Count() : 0, @@ -1213,78 +1213,8 @@ namespace IRaCIS.Core.Application.Service.Common } - /// - /// 影像下载记录表 - /// - /// - /// - /// - /// - /// - [HttpPost] - public async Task GetTrialDownloadList_Export(TrialIamgeDownQuery inQuery, - [FromServices] IRepository _trialImageDownloadRepository, - [FromServices] IDictionaryService _dictionaryService, - [FromServices] IRepository _trialRepository) - { - var query = _trialImageDownloadRepository.Where(t => t.TrialId == inQuery.TrialId) - .WhereIf(inQuery.SubjectCode.IsNotNullOrEmpty(), t => t.SubjectCode.Contains(inQuery.SubjectCode)) - .WhereIf(inQuery.IP.IsNotNullOrEmpty(), t => t.IP.Contains(inQuery.IP)) - .WhereIf(inQuery.Name.IsNotNullOrEmpty(), t => t.CreateUser.UserName.Contains(inQuery.Name) || t.CreateUser.FullName.Contains(inQuery.Name)) - .WhereIf(inQuery.ImageType != null, t => t.ImageType == inQuery.ImageType) - .WhereIf(inQuery.UserType != null, t => t.CreateUser.UserTypeEnum == inQuery.UserType) - .WhereIf(inQuery.IsSuccess != null, t => t.IsSuccess == inQuery.IsSuccess) - .WhereIf(inQuery.DownloadStartTime != null, t => t.DownloadStartTime >= inQuery.DownloadStartTime) - .WhereIf(inQuery.DownloadEndTime != null, t => t.DownloadEndTime <= inQuery.DownloadEndTime) - - .ProjectTo(_mapper.ConfigurationProvider); - - var list = await query.SortToListAsync(inQuery); - var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - - exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); - exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId); - - - return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialImageDownloadList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(TrialImageDownloadExportDto)); - - } - - /// - /// 影像接收记录表 - /// - /// - /// - [HttpPost] - public async Task GetSCPImageUploadList_Export(SCPImageUploadQuery inQuery, - [FromServices] IRepository _scpImageUploadRepository, - [FromServices] IDictionaryService _dictionaryService, - [FromServices] IRepository _trialRepository) - { - var query = _scpImageUploadRepository.Where(t => t.TrialId == inQuery.TrialId) - .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) - .WhereIf(!string.IsNullOrWhiteSpace(inQuery.TrialSiteKeyInfo), t => t.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteKeyInfo) - || t.TrialSite.TrialSiteAliasName.Contains(inQuery.TrialSiteKeyInfo) || t.TrialSite.TrialSiteName.Contains(inQuery.TrialSiteKeyInfo)) - .ProjectTo(_mapper.ConfigurationProvider); - - - var list = await query.SortToListAsync(inQuery); - - var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - - exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); - exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId); - - - return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSCPImageUploadList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(SCPImageUploadExportDTO)); - - } /// /// 国际化导出 @@ -1322,110 +1252,9 @@ namespace IRaCIS.Core.Application.Service.Common } - /// - ///影像检查列表-患者为维度组织 - /// - /// - /// - /// - /// - /// - [HttpPost] - public async Task GetPatientList_Export(PatientTrialQuery inQuery, - [FromServices] IRepository _patientRepository, - [FromServices] IDictionaryService _dictionaryService, - [FromServices] IRepository _trialRepository) - { + - var query = _patientRepository.Where(t => t.TrialId == inQuery.TrialId) - .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.SubejctCode), t => t.Subject.Code.Contains(inQuery.SubejctCode)) - .WhereIf(!string.IsNullOrWhiteSpace(inQuery.TrialSiteKeyInfo), t => t.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteKeyInfo) - || t.TrialSite.TrialSiteAliasName.Contains(inQuery.TrialSiteKeyInfo) || t.TrialSite.TrialSiteName.Contains(inQuery.TrialSiteKeyInfo)) - .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAE), t => t.SCPStudyList.Any(t => t.CallingAE == inQuery.CallingAE)) - .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CalledAE), t => t.SCPStudyList.Any(t => t.CalledAE == inQuery.CalledAE)) - .WhereIf(inQuery.BeginPushTime != null, t => t.LatestPushTime >= inQuery.BeginPushTime) - .WhereIf(inQuery.EndPushTime != null, t => t.LatestPushTime <= inQuery.EndPushTime); - - - var resultQuery = from patient in query - - select new SCPPatientSubjectExportDTO() - { - //CreateUserId = patient.CreateUserId, - //UpdateTime = patient.UpdateTime, - //UpdateUserId = patient.UpdateUserId, - //TrialId = patient.TrialId, - //SubejctId = patient.SubjectId, - //CreateTime = patient.CreateTime, - //PatientId = patient.Id, - - PatientBirthDate = patient.PatientBirthDate, - CalledAEList = patient.SCPStudyList.Select(t => t.CalledAE).Distinct().ToList(), - CallingAEList = patient.SCPStudyList.Select(t => t.CallingAE).Distinct().ToList(), - 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(), - SubjectCode = patient.Subject.Code, - TrialSiteAliasName = patient.TrialSite.TrialSiteAliasName, - TrialSiteCode = patient.TrialSite.TrialSiteCode, - TrialSiteName = patient.TrialSite.TrialSiteName - - }; - - var list = await resultQuery.SortToListAsync(inQuery); - - var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - - exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); - exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId); - - - return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSCPImageUploadPatientList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(SCPPatientSubjectExportDTO)); - - } - - - /// - /// 邮件导出 - /// - /// - /// - /// - /// - [HttpPost] - public async Task GetEmailNoticeConfigList_Export(EmailNoticeConfigQuery inQuery, - [FromServices] IRepository _emailNoticeConfigrepository, - [FromServices] IDictionaryService _dictionaryService) - { - var emailNoticeConfigQueryable = _emailNoticeConfigrepository - //.WhereIf(inQuery.SystemLevel == null, t => t.SystemLevel == SysEmailLevel.not_sys) - //.WhereIf(inQuery.SystemLevel != null, t => t.SystemLevel == inQuery.SystemLevel) - //.WhereIf(inQuery.IsDistinguishCriteria != null, t => t.IsDistinguishCriteria == inQuery.IsDistinguishCriteria) - .WhereIf(inQuery.CriterionTypeEnum != null, t => t.CriterionTypeEnum == inQuery.CriterionTypeEnum) - .WhereIf(inQuery.BusinessScenarioEnum != null, t => t.BusinessScenarioEnum == inQuery.BusinessScenarioEnum) - .WhereIf(inQuery.IsReturnRequired != null, t => t.IsReturnRequired == inQuery.IsReturnRequired) - .WhereIf(inQuery.IsEnable != null, t => t.IsEnable == inQuery.IsEnable) - .ProjectTo(_mapper.ConfigurationProvider); - - var list = await emailNoticeConfigQueryable.SortToListAsync(inQuery); - - var exportInfo = new ExcelExportInfo(); - - exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); - exportInfo.CurrentTime = ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId); - - - return await ExcelExportHelper.DataExportAsync(StaticData.Export.EmailNoticeConfig_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(EmailNoticeConfigExportDto)); - - } #region 导表公用 diff --git a/IRaCIS.Core.Application/Service/Document/EmailSendService.cs b/IRaCIS.Core.Application/Service/Document/EmailSendService.cs index b0b9e02d9..f21040187 100644 --- a/IRaCIS.Core.Application/Service/Document/EmailSendService.cs +++ b/IRaCIS.Core.Application/Service/Document/EmailSendService.cs @@ -53,7 +53,7 @@ namespace IRaCIS.Core.Application.Service t.Trial.ResearchProgramNo, t.Subject.TrialSite.TrialSiteCode, SubjectCode = t.Subject.Code, - t.Trial.Sponsor.SponsorName, + t.Trial.Sponsor, t.SourceSubjectVisit.VisitName, t.TrialId, diff --git a/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs b/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs index 3c85f29a0..54c75c7f4 100644 --- a/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs +++ b/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs @@ -187,7 +187,7 @@ namespace IRaCIS.Core.Application.Service t.Trial.ResearchProgramNo, t.Subject.TrialSite.TrialSiteCode, SubjectCode = t.Subject.Code, - t.Trial.Sponsor.SponsorName, + t.Trial.Sponsor, t.Trial.IsEnrollementQualificationConfirm, t.Trial.IsPDProgressView, @@ -617,7 +617,7 @@ namespace IRaCIS.Core.Application.Service var value = new Dictionary() { - ["SponsorName"] = taskInfo.SponsorName, + ["SponsorName"] = taskInfo.Sponsor, ["ResearchProgramNo"] = taskInfo.ResearchProgramNo, ["TrialSiteCode"] = taskInfo.TrialSiteCode, ["SubjectCode"] = taskInfo.SubjectCode, diff --git a/IRaCIS.Core.Application/Service/Financial/_MapConfig.cs b/IRaCIS.Core.Application/Service/Financial/_MapConfig.cs index 17e9f3de4..79fdbe046 100644 --- a/IRaCIS.Core.Application/Service/Financial/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/Financial/_MapConfig.cs @@ -47,7 +47,7 @@ namespace IRaCIS.Core.Application.Service CreateMap() .ForMember(t => t.TrialCode, u => u.MapFrom(t => t.Trial.Code)) .ForMember(t => t.ReviewMode, u => u.MapFrom(t => t.Trial.ReviewMode.Value)) - .ForMember(t => t.Cro, u => u.MapFrom(t => t.Trial.CRO.CROName)) + .ForMember(t => t.Cro, u => u.MapFrom(t => t.Trial.CRO)) .ForMember(t => t.Indication, u => u.MapFrom(t => t.Trial.Indication)) .ForMember(t => t.Expedited, u => u.MapFrom(t => t.Trial.Expedited)) .ForMember(t => t.DoctorsNames, u => u.MapFrom(t => string.Join(',', t.Trial.EnrollList.Select(t => t.Doctor.ChineseName)))) diff --git a/IRaCIS.Core.Application/Service/QC/QCOperationService.cs b/IRaCIS.Core.Application/Service/QC/QCOperationService.cs index 66a613276..3ed18b0f2 100644 --- a/IRaCIS.Core.Application/Service/QC/QCOperationService.cs +++ b/IRaCIS.Core.Application/Service/QC/QCOperationService.cs @@ -771,7 +771,7 @@ namespace IRaCIS.Core.Application.Image.QA var succeess2 = await _dicomInstanceRepository.BatchDeleteNoTrackingAsync(t => t.StudyId == id); var success3 = await _dicomSeriesrepository.BatchDeleteNoTrackingAsync(t => t.StudyId == id); - await _scpStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == id, u => new SCPStudy() { SubjectVisitId = null }); + //await _scpStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == id, u => new SCPStudy() { SubjectVisitId = null }); @@ -797,17 +797,17 @@ namespace IRaCIS.Core.Application.Image.QA } - var subjectId = waitDeleteStudyList.Select(t => t.SubjectId).FirstOrDefault(); + //var subjectId = waitDeleteStudyList.Select(t => t.SubjectId).FirstOrDefault(); - var patientList = _scpPatientRepository.Where(t => t.SubjectId == subjectId).Select(t => t.Id).ToList(); + //var patientList = _scpPatientRepository.Where(t => t.SubjectId == subjectId).Select(t => t.Id).ToList(); - foreach (var patientId in patientList) - { - if (_scpPatientRepository.Where(t => t.Id == patientId).Any(t => t.SCPStudyList.Count() == t.SCPStudyList.Where(t => t.SubjectVisitId == null).Count())) - { - await _scpPatientRepository.BatchUpdateNoTrackingAsync(t => t.Id == patientId, u => new SCPPatient() { SubjectId = null }); - } - } + //foreach (var patientId in patientList) + //{ + // if (_scpPatientRepository.Where(t => t.Id == patientId).Any(t => t.SCPStudyList.Count() == t.SCPStudyList.Where(t => t.SubjectVisitId == null).Count())) + // { + // await _scpPatientRepository.BatchUpdateNoTrackingAsync(t => t.Id == patientId, u => new SCPPatient() { SubjectId = null }); + // } + //} diff --git a/IRaCIS.Core.Application/Service/SiteSurvey/_MapConfig.cs b/IRaCIS.Core.Application/Service/SiteSurvey/_MapConfig.cs index fdf43fac5..b5d7918eb 100644 --- a/IRaCIS.Core.Application/Service/SiteSurvey/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/SiteSurvey/_MapConfig.cs @@ -58,7 +58,7 @@ namespace IRaCIS.Core.Application.AutoMapper CreateMap() - .ForMember(d => d.Sponsor, u => u.MapFrom(s => s.Sponsor.SponsorName)) + .ForMember(d => d.Sponsor, u => u.MapFrom(s => s.Sponsor)) .ForMember(d => d.IndicationType, u => u.MapFrom(s => s.IndicationType.Value)) .ForMember(d => d.TrialSiteSelectList, u => u.MapFrom(s => s.TrialSiteList)) .ForMember(d => d.TrialId, u => u.MapFrom(s => s.Id)); diff --git a/IRaCIS.Core.Application/Service/Third-partyProject/UltrasonicDicomService.cs b/IRaCIS.Core.Application/Service/Third-partyProject/UltrasonicDicomService.cs index 7e6c03113..2440021b7 100644 --- a/IRaCIS.Core.Application/Service/Third-partyProject/UltrasonicDicomService.cs +++ b/IRaCIS.Core.Application/Service/Third-partyProject/UltrasonicDicomService.cs @@ -49,7 +49,7 @@ namespace IRaCIS.Core.Application.Service.Third_partyProject ExperimentName = t.ExperimentName, TrialCode = t.TrialCode, CreateTime = t.CreateTime, - Sponsor = _userInfo.IsEn_Us ? t.Sponsor.SponsorName : t.Sponsor.SponsorNameCN, + Sponsor = _userInfo.IsEn_Us ? t.Sponsor : t.Sponsor, TrialStatusStr = t.TrialStatusStr }); diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs index d41bdbca1..494368863 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs @@ -1389,7 +1389,7 @@ namespace IRaCIS.Core.Application ExperimentName = t.ExperimentName, TrialCode = t.TrialCode, CreateTime = t.CreateTime, - Sponsor = _userInfo.IsEn_Us ? t.Sponsor.SponsorName : t.Sponsor.SponsorNameCN, + Sponsor = t.Sponsor, TrialStatusStr = t.TrialStatusStr, ExpetiedTaskCount = isPM ? t.VisitTaskList.Where(t => t.IsUrgent).Count() : 0, diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs index f94b5ae18..e6b698027 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs @@ -67,11 +67,11 @@ namespace IRaCIS.Core.Application.Service .ForMember(x => x.CriterionList, y => y.MapFrom(z => z.TrialReadingCriterionList.Where(n => n.IsConfirm).Select(m => m.CriterionName))) .ForMember(d => d.DictionaryList, u => u.MapFrom(s => s.TrialDicList.Select(t => t.Dictionary).OrderBy(t => t.ShowOrder))) //.ForMember(d => d.Code, u => u.MapFrom(s => s.TrialCode)) - .ForMember(d => d.Sponsor, u => u.MapFrom(s => s.Sponsor.SponsorName)) + .ForMember(d => d.Sponsor, u => u.MapFrom(s => s.Sponsor)) .ForMember(d => d.Phase, u => u.MapFrom(s => isEn_Us ? s.Phase.Value : s.Phase.ValueCN)) //.ForMember(d => d.DeclarationType, u => u.MapFrom(s => s.DeclarationType.MappedValue)) .ForMember(d => d.IndicationType, u => u.MapFrom(s => isEn_Us ? s.IndicationType.Value : s.IndicationType.ValueCN)) - .ForMember(d => d.CRO, u => u.MapFrom(s => s.CRO.CROName)) + .ForMember(d => d.CRO, u => u.MapFrom(s => s.CRO)) .ForMember(d => d.ReviewMode, u => u.MapFrom(s => isEn_Us ? s.ReviewMode.Value : s.ReviewMode.ValueCN)) //.ForMember(d => d.ReviewType, u => u.MapFrom(s => s.ReviewType.Value)) .ForMember(d => d.IsLocked, u => u.MapFrom(s => s.WorkloadList.Any(u => u.DataFrom == (int)WorkLoadFromStatus.FinalConfirm))) diff --git a/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs b/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs index 8b8fa455f..72b1ee836 100644 --- a/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs +++ b/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs @@ -1,7 +1,10 @@ using IRaCIS.Core.Application.ViewModel; using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure.Extention; using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Web; namespace IRaCIS.Application.Contracts { @@ -219,83 +222,34 @@ namespace IRaCIS.Application.Contracts public class PatientTrialQuery : PageInput { - [NotDefault] - public Guid TrialId { get; set; } - public string? PatientIdStr { get; set; } public string? PatientName { get; set; } - //public List CalledAEList { get; set; } = new List(); + public List CalledAEList { get; set; } = new List(); public string? CallingAE { get; set; } - public string? CalledAE { get; set; } + + public string? ExperimentName { get; set; } = string.Empty; public DateTime? BeginPushTime { get; set; } public DateTime? EndPushTime { get; set; } - public string SubejctCode { get; set; } - - public string TrialSiteKeyInfo { get; set; } - } - public class PatientSubjectView : PatientQueryView + public class PatientTrialView : PatientQueryView { public int? StudyCount { get; set; } - - - public Guid? SubejctId { get; set; } - - public Guid TrialId { get; set; } - - public string? SubjectCode { get; set; } - - public string? TrialSiteCode { get; set; } - - public string? TrialSiteName { get; set; } - - public string? TrialSiteAliasName { get; set; } - - - + public List TrialList { get; set; } } - - public class SCPPatientSubjectExportDTO + public class PatientTrialStatInfo { - public int? StudyCount { get; set; } + public int? VisitCount { get; set; } - public string? SubjectCode { get; set; } - - public string? TrialSiteCode { get; set; } - - public string? TrialSiteName { get; set; } - - public string? TrialSiteAliasName { get; set; } - - public string PatientIdStr { get; set; } = string.Empty; - public string PatientName { get; set; } = string.Empty; - public string PatientAge { get; set; } = string.Empty; - public string PatientSex { get; set; } = string.Empty; - public string PatientBirthDate { get; set; } = string.Empty; - - public DateTime? EarliestStudyTime { get; set; } - - public DateTime? LatestStudyTime { get; set; } - - public DateTime LatestPushTime { get; set; } - - public List CallingAEList { get; set; } = new List(); - - public List CalledAEList { get; set; } = new List(); - - public string CallingAEListStr => string.Join(",", CallingAEList); - - public string CalledAEListStr => string.Join(",", CalledAEList); + public string ExperimentName { get; set; } } - public class PatientQuery : PageInput { [NotDefault] @@ -415,9 +369,7 @@ namespace IRaCIS.Application.Contracts } - - - public class VerifyPacsImageCommand + public class SubmitVisitStudyBindingCommand { [NotDefault] public Guid TrialId { get; set; } @@ -425,17 +377,7 @@ namespace IRaCIS.Application.Contracts [NotDefault] public Guid SubjectId { get; set; } - [NotDefault] - public Guid SubjectVisitId { get; set; } - - public List SCPStudyIdList { get; set; } - } - - public class SubmitVisitStudyBindingCommand : VerifyPacsImageCommand - { - - - public List ReUploadSCPStudyIdList { get; set; } + public List SubjectVisitIdList { get; set; } } public class SubjectVisitSelectQuery @@ -996,10 +938,6 @@ namespace IRaCIS.Application.Contracts public class SCPImageUploadQuery : PageInput { - [NotDefault] - public Guid TrialId { get; set; } - public string TrialSiteKeyInfo { get; set; } - public string? CallingAE { get; set; } public string? CalledAE { get; set; } @@ -1028,28 +966,7 @@ namespace IRaCIS.Application.Contracts public long FileSize { get; set; } public int StudyCount { get; set; } - - - public Guid TrialId { get; set; } - public Guid TrialSiteId { get; set; } - - - public string TrialSiteCode { get; set; } - - public string TrialSiteName { get; set; } - - public string TrialSiteAliasName { get; set; } - - public string UploadIntervalStr - { - get - { - var uploadTimeSpan = EndTime - StartTime; - - return $" {uploadTimeSpan.Hours}:{uploadTimeSpan.Minutes}:{uploadTimeSpan.Seconds}.{uploadTimeSpan.Milliseconds}"; - } - } - + } public class VisitPatientStudyView : PatientStudySelectDto { @@ -1073,51 +990,8 @@ namespace IRaCIS.Application.Contracts } - public class VisitPatientStudyFilterQuery : PageInput - { - [NotDefault] - public Guid SubjectId { get; set; } - - [NotDefault] - public Guid SubjectVisitId { get; set; } - - public DateTime? EarliestStudyTime { get; set; } - - public DateTime? LatestStudyTime { get; set; } - public string? Modalities { get; set; } - - public string? PatientInfo { get; set; } - } - public class VisitPatientStudyFilterView - { - public Guid SCPStudyId { get; set; } - - public Guid PatientId { get; set; } - - public DateTime? StudyTime { get; set; } - public string Modalities { get; set; } = string.Empty; - - public string Description { get; set; } = string.Empty; - public int SeriesCount { get; set; } = 0; - public int InstanceCount { get; set; } = 0; - - public string CalledAE { get; set; } = string.Empty; - - public string CallingAE { get; set; } = string.Empty; - - public string BodyPartExamined { get; set; } = string.Empty; - public string AccessionNumber { get; set; } = string.Empty; - public string PatientIdStr { get; set; } = string.Empty; - public string PatientName { get; set; } = string.Empty; - public string PatientAge { get; set; } = string.Empty; - public string PatientSex { get; set; } = string.Empty; - - public string PatientBirthDate { get; set; } = string.Empty; - } - public class PatientStudySimpleView { - public Guid SCPStudyId { get; set; } public Guid PatientId { get; set; } @@ -1133,25 +1007,8 @@ namespace IRaCIS.Application.Contracts public string CallingAE { get; set; } = string.Empty; - public Guid? SubjectVisitId { get; set; } - public string? VisitName { get; set; } - - public string? BlindName { get; set; } = string.Empty; - - - //public SubjectVisitInfo SubejectVisit { get; set; } - } - public class SubjectVisitInfo - { - public Guid Id { get; set; } - - public string VisitName { get; set; } - - public decimal VisitNum { get; set; } - public string BlindName { get; set; } = string.Empty; - } public class PatientSeriesDTO diff --git a/IRaCIS.Core.Application/Service/Visit/DicomAEService.cs b/IRaCIS.Core.Application/Service/Visit/DicomAEService.cs new file mode 100644 index 000000000..ac11e0f0b --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/DicomAEService.cs @@ -0,0 +1,120 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-03-22 15:44:31 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using FellowOakDicom.Network.Client; +using FellowOakDicom.Network; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Share; +namespace IRaCIS.Core.Application.Service +{ + /// + /// DicomAEService + /// + [ApiExplorerSettings(GroupName = "Common")] + public class DicomAEService (IRepository _dicomAERepository, IMapper _mapper, IUserInfo _userInfo) : BaseService, IDicomAEService + { + + + + [HttpPost] + public async Task>> GetDicomAEList(DicomAEQuery inQuery) + { + + var dicomAEQueryable = _dicomAERepository + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.IP), t => t.IP.Contains(inQuery.IP)) + .WhereIf(inQuery.Port != null, t => t.Port == inQuery.Port) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CalledAE), t => t.CalledAE.Contains(inQuery.CalledAE)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Description), t => t.Description.Contains(inQuery.Description)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Modality), t => t.Modality.Contains(inQuery.Modality)) + .ProjectTo(_mapper.ConfigurationProvider); + + + + + var pageList = await dicomAEQueryable.ToPagedListAsync(inQuery, nameof(DicomAEView.CalledAE)); + + + return ResponseOutput.Ok(pageList); + } + + + public async Task AddOrUpdateDicomAE(DicomAEAddOrEdit addOrEditDicomAE) + { + var verifyExp1 = new EntityVerifyExp() + { + VerifyExp = u => u.IP == addOrEditDicomAE.IP && u.Port == addOrEditDicomAE.Port, + + VerifyMsg = "不允许添加相同的IP和端口的记录" + }; + + // 在此处拷贝automapper 映射 + var entity = await _dicomAERepository.InsertOrUpdateAsync(addOrEditDicomAE, true, verifyExp1); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + + [HttpDelete("{dicomAEId:guid}")] + public async Task DeleteDicomAE(Guid dicomAEId) + { + var success = await _dicomAERepository.DeleteFromQueryAsync(t => t.Id == dicomAEId, true); + return ResponseOutput.Ok(); + } + + + /// + /// 测试scp server 是否可以连接 + /// + /// + [HttpGet("{dicomAEId:guid}")] + public async Task TestSCPServerConnect(Guid dicomAEId) + { + var find = await _dicomAERepository.FirstOrDefaultAsync(t => t.Id == dicomAEId); + + if (find == null) + { + + return false; + } + else + { + find.LatestTestTime = DateTime.Now; + + try + { + var client = DicomClientFactory.Create(find.IP, find.Port, false, "test-callingAE", find.CalledAE); + + client.NegotiateAsyncOps(); + + await client.AddRequestAsync(new DicomCEchoRequest()); + + await client.SendAsync(); + + find.IsTestOK = true; + await _dicomAERepository.SaveChangesAsync(); + + return true; + } + catch (Exception ex) + { + find.IsTestOK = false; + await _dicomAERepository.SaveChangesAsync(); + + return false; + } + } + + + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/Visit/PatientService.cs b/IRaCIS.Core.Application/Service/Visit/PatientService.cs index c8397ba82..ec1ef8db8 100644 --- a/IRaCIS.Core.Application/Service/Visit/PatientService.cs +++ b/IRaCIS.Core.Application/Service/Visit/PatientService.cs @@ -1,130 +1,558 @@ -using FellowOakDicom; +using IRaCIS.Application.Interfaces; using IRaCIS.Application.Contracts; -using IRaCIS.Core.Application.Contracts; -using IRaCIS.Core.Application.Contracts.Dicom.DTO; using IRaCIS.Core.Application.Filter; using IRaCIS.Core.Domain.Share; -using IRaCIS.Core.Infrastructure; using Microsoft.AspNetCore.Mvc; -using Pipelines.Sockets.Unofficial.Arenas; -using System.Linq.Dynamic.Core; -using IDistributedLockProvider = Medallion.Threading.IDistributedLockProvider; +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 Pipelines.Sockets.Unofficial.Arenas; +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; -namespace IRaCIS.Core.Application.Service +namespace IRaCIS.Application.Services { [ApiExplorerSettings(GroupName = "Trial")] - public class PatientService(IRepository _trialRepository, - IRepository _patientRepository, - IRepository _scpStudyRepository, - IRepository _subjectRepository, - IRepository _subjectVisitRepository, - IRepository _dictionaryRepository, - IRepository _dicomStudyRepository, - IRepository _scpPatientRepository, - IRepository _scpImageUploadRepository, - IDistributedLockProvider _distributedLockProvider, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService + public class PatientService(IRepository _studySubjectVisitRepository, + IRepository _subjectPatientRepository, + IRepository _trialRepository, + IRepository _patientRepository, + IRepository _studyRepository, + IRepository _subjectRepository, + IRepository _subjectVisitRepository, + IRepository _subejctVisitDownloadRepository, + IRepository _SCPImageUploadRepository, + IRepository _userRepository, + IDistributedLockProvider _distributedLockProvider, IMapper _mapper, IUserInfo _userInfo, IWebHostEnvironment _hostEnvironment, IStringLocalizer _localizer + + + ) : BaseService { + #region 医院信息管理 - [HttpGet] - public async Task>> GetPatientSeriesList(Guid scpStudyId, - [FromServices] IRepository _seriesRepository, - [FromServices] IRepository _instanceRepository - ) + /// + /// 获取医院的配置信息 + /// + /// + /// + [AllowAnonymous] + public async Task GetHospitalInfo([FromServices] IOptionsMonitor options) { + return options.CurrentValue; + } - 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(); + /// + /// 配置医院信息,方便测试邮件和授权码的方式 + /// + /// + /// + [HttpPost] + public async Task UpdateHospitalInfo(SystemHospitalOption systemHospitalOption) + { + var path = $"appsettings.{_hostEnvironment.EnvironmentName}.json"; - 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); + string text = System.IO.File.ReadAllText(path); - foreach (var series in seriesList) - { + // 修改 + JObject obj = JObject.Parse(text); + obj["SystemHospitalConfig"][nameof(SystemHospitalOption.HospitalName)] = systemHospitalOption.HospitalName; + obj["SystemHospitalConfig"][nameof(SystemHospitalOption.HospitalAliasName)] = systemHospitalOption.HospitalAliasName; + obj["SystemHospitalConfig"][nameof(SystemHospitalOption.Country)] = systemHospitalOption.Country; + obj["SystemHospitalConfig"][nameof(SystemHospitalOption.City)] = systemHospitalOption.City; + obj["SystemHospitalConfig"][nameof(SystemHospitalOption.Phone)] = systemHospitalOption.Phone; + obj["SystemHospitalConfig"][nameof(SystemHospitalOption.Province)] = systemHospitalOption.Province; + obj["SystemHospitalConfig"][nameof(SystemHospitalOption.Address)] = systemHospitalOption.Address; + obj["SystemHospitalConfig"][nameof(SystemHospitalOption.IsCanConnectInternet)] = systemHospitalOption.IsCanConnectInternet; - 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, + obj["SystemHospitalConfig"][nameof(SystemHospitalOption.HospitalCode)] = systemHospitalOption.HospitalCode; + obj["SystemHospitalConfig"][nameof(SystemHospitalOption.HospitalLogoPath)] = systemHospitalOption.HospitalLogoPath; + obj["SystemHospitalConfig"][nameof(SystemHospitalOption.TrialKeepCount)] = systemHospitalOption.TrialKeepCount; - }).ToList(); - } + // 重新写入appsettings.json + string result = obj.ToString(); + System.IO.File.WriteAllText(path, result); - var study = await _scpStudyRepository.FindAsync(scpStudyId); + await _userRepository.BatchUpdateNoTrackingAsync(t => t.IsZhiZhun, t => new User() { OrganizationName = systemHospitalOption.HospitalName }); + + 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); - return ResponseOutput.Ok(seriesList, study); } + /// - /// scp 影像推送记录表 + /// 获取项目列表 (PM CRC 共用) /// /// /// [HttpPost] - public async Task>> GetSCPImageUploadList(SCPImageUploadQuery inQuery) + public async Task>> GetTrialList(NewTrialQuery inQuery) { - var query = _scpImageUploadRepository.Where(t => t.TrialId == inQuery.TrialId) - .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) - .WhereIf(!string.IsNullOrWhiteSpace(inQuery.TrialSiteKeyInfo), t => t.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteKeyInfo) - || t.TrialSite.TrialSiteAliasName.Contains(inQuery.TrialSiteKeyInfo) || t.TrialSite.TrialSiteName.Contains(inQuery.TrialSiteKeyInfo)) - .ProjectTo(_mapper.ConfigurationProvider); + 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 query.ToPagedListAsync(inQuery); + 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 = Cryptography.DecryptString(trial.AuthorizationEncrypt, _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt"); + + var authInfo = JsonConvert.DeserializeObject(decodedText); + + return ResponseOutput.Ok(trial, authInfo); + } + catch (Exception) + { + + return ResponseOutput.NotOk("授权信息无法解密,请勿人为修改授权信息,如果有误,请联系开发解决"); + } + } + return ResponseOutput.Ok(trial); + } + + /// + /// 添加更新项目 + /// + /// + /// + /// + /// + /// + [HttpPost] + [UnitOfWork] + public async Task AddOrUpdateTrial(AddOrUpdateTrialCommand inCommand, + [FromServices] IOptionsMonitor _systemEmailSendConfig, + [FromServices] IOptionsMonitor _basicSystemConfigConfig, + [FromServices] IOptionsMonitor _systemHospitalOption, + IRepository _trialDictionaryRepository, + 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}|"))) + { + throw new BusinessValidationFailedException($"已存在研究方案号为{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(), JsonConvert.SerializeObject(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($"已存在研究方案号为{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(), JsonConvert.SerializeObject(caheInfo), TimeSpan.FromDays(7)); + + var success = await _trialRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(trial); + + } + } + + #endregion + + #region 授权和激活 + + + + public async Task> GetTrialAuthorizationInfo(Guid trialId, [FromServices] IOptionsMonitor _hospitalOption) + { + 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 = _hospitalOption.CurrentValue.HospitalName, + HospitalCode = _hospitalOption.CurrentValue.HospitalCode, + }; + + return ResponseOutput.Ok(authInfo); + } + + /// + /// 获取项目授权码 + /// + /// + /// + [HttpPost] + public async Task GetTrialAuthorizationCode(TrialAuthorizationInfo authInfo, [FromServices] IOptionsMonitor _hospitalOption) + { + + // 将明文信息转换成 Base64 编码 + string base64EncodedText = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(authInfo))); + + return ResponseOutput.Ok(base64EncodedText); + + } + + /// + /// 获取授权码明文信息 + /// + /// + /// + /// + [AllowAnonymous] + public async Task GetAuthorizationCodeInfo(string authorizationCode, [FromServices] IOptionsMonitor _hospitalOption) + { + // 解密 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 = Cryptography.EncryptString($"{JsonConvert.SerializeObject(authorizationInfo)}", _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt"); + + return ResponseOutput.Ok(info); + + } + + /// + /// 激活码获取明文信息 + /// + /// + /// + /// + /// + public async Task GetActivationCodeInfo(string activationCode, + [FromServices] IOptionsMonitor _hospitalOption, + [FromServices] IOptionsMonitor _basicSystemConfigConfig) + { + + var decodedText = string.Empty; + + try + { + decodedText = Cryptography.DecryptString(activationCode, _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt"); + + + } + catch (Exception) + { + + return ResponseOutput.NotOk("激活码有误,请核对,无法激活"); + } + + var authInfo = JsonConvert.DeserializeObject(decodedText); + + if (authInfo != null) + { + return ResponseOutput.Ok(authInfo); + } + else + { + return ResponseOutput.NotOk("激活码信息有误!"); + } + + } + + + /// + /// 设置项目授权信息 + /// + /// + /// + /// + [HttpPut] + public async Task ActivateTrial(Guid trialId, string activationCode, + [FromServices] IOptionsMonitor _basicSystemConfigConfig, + [FromServices] IFusionCache _provider, + [FromServices] IOptionsMonitor _hospitalOption) + { + var hospitalCode = _hospitalOption.CurrentValue.HospitalCode; + var trialInfo = _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).FirstOrDefault(); + + var decodedText = string.Empty; + try + { + decodedText = Cryptography.DecryptString(activationCode, _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt"); + + + } + catch (Exception) + { + + return ResponseOutput.NotOk("激活码有误,请核对,无法激活"); + } + + 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("该授权码与该项目不匹配"); + } + + if (DateTime.Now > authInfo.ActiveDeadLineDate) + { + return ResponseOutput.NotOk($"当前时间{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 = Cryptography.EncryptString($"{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 _provider.SetAsync(trialInfo.Id.ToString(), JsonConvert.SerializeObject(caheInfo), TimeSpan.FromDays(7)); + } + else + { + return ResponseOutput.NotOk("该授权码与该项目不匹配"); + + } + + return ResponseOutput.Ok(); + + } + + + + #endregion #region 患者检查管理 /// - ///影像检查列表-患者为维度组织 + ///检查管理-> 检查Tab 患者列表 (带加入的项目信息 以及检查统计) 原型标注错误,不是检查列表 /// /// /// [HttpPost] - public async Task>> GetPatientList(PatientTrialQuery inQuery) + 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.Where(t => t.TrialId == inQuery.TrialId) + 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.SubejctCode), t => t.Subject.Code.Contains(inQuery.SubejctCode)) - .WhereIf(!string.IsNullOrWhiteSpace(inQuery.TrialSiteKeyInfo), t => t.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteKeyInfo) - || t.TrialSite.TrialSiteAliasName.Contains(inQuery.TrialSiteKeyInfo) || t.TrialSite.TrialSiteName.Contains(inQuery.TrialSiteKeyInfo)) + .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(!string.IsNullOrWhiteSpace(inQuery.CalledAE), t => t.SCPStudyList.Any(t => t.CalledAE == inQuery.CalledAE)) .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)); - //} + 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 PatientSubjectView() + select new PatientTrialView() { PatientId = patient.Id, PatientBirthDate = patient.PatientBirthDate, @@ -145,19 +573,18 @@ namespace IRaCIS.Core.Application.Service StudyCount = patient.SCPStudyList.Count(), - TrialId = patient.TrialId, - SubejctId = patient.SubjectId, - SubjectCode = patient.Subject.Code, - TrialSiteAliasName = patient.TrialSite.TrialSiteAliasName, - TrialSiteCode = patient.TrialSite.TrialSiteCode, - TrialSiteName = patient.TrialSite.TrialSiteName + 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); + var pageList = await resultQuery.ToPagedListAsync(inQuery, nameof(PatientQueryView.PatientIdStr)); #endregion @@ -165,18 +592,80 @@ namespace IRaCIS.Core.Application.Service } - - - /// - /// 影像检查列表-> 获取患者的检查列表 + /// 检查管理-> 患者加入项目 初始化勾选列表 /// /// /// [HttpPost] - public async Task> GetPatientStudyList(PatientStudyInfoQuery inQuery) + public async Task>> GetPatientJoinTrialInitList(PatientJoinTrialInitQuery inQuery) { - var query = from scpStudy in _scpStudyRepository.Where(t => t.PatientId == inQuery.PatientId) + + //排除已参与的项目列表 + 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.ToListAsync(); + + 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)) @@ -191,70 +680,1599 @@ namespace IRaCIS.Core.Application.Service SCPStudyId = scpStudy.Id, SeriesCount = scpStudy.SeriesCount, StudyTime = scpStudy.StudyTime, - - - - - SubjectVisitId = scpStudy.SubjectVisitId, - VisitName = scpStudy.SubjectVisit.VisitName, - BlindName = scpStudy.SubjectVisit.BlindName }; - //var sortField = string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(PatientStudySimpleView.StudyTime) : inQuery.SortField; - //var orderQuery = inQuery.Asc ? query.OrderBy(sortField) : query.OrderBy(sortField + " desc"); + var sortField = string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(PatientStudySimpleView.StudyTime) : inQuery.SortField; + var orderQuery = inQuery.Asc ? query.OrderBy(sortField) : query.OrderBy(sortField + " desc"); - //var list = await orderQuery.ToListAsync(); - - var pageList = await query.ToPagedListAsync(inQuery, nameof(PatientStudySimpleView.StudyTime)); - - return pageList; - } - - - public async Task> GetDicomCalledAEList(Guid trialId) - { - var list = await _scpStudyRepository.Where(t => t.TrialId == trialId).Select(t => t.CalledAE).Distinct().ToListAsync(); + var list = await orderQuery.ToListAsync(); return list; } - public async Task> GetDicomCallingAEList(Guid trialId) + + + public async Task>> GetPatientSeriesList(Guid scpStudyId, + [FromServices] IRepository _seriesRepository, + [FromServices] IRepository _instanceRepository + ) { - var list = await _scpStudyRepository.Where(t => t.TrialId == trialId).Select(t => t.CallingAE).Distinct().ToListAsync(); + + 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 idList = 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 item in seriesList) + { + item.InstanceList = idList.Where(s => s.SeriesId == item.Id).Select(u => u.Id).ToList(); + + //处理多帧 + item.InstancePathList = idList.Where(s => s.SeriesId == item.Id).OrderBy(t => t.InstanceNumber) + .SelectMany(u => + { + + if (u.NumberOfFrames > 1) + { + var pathList = new List(); + + for (int i = 1; i <= u.NumberOfFrames; i++) + { + pathList.Add(u.Path + "?frame=" + (i - 1)); + } + return pathList; + } + else + { + return new List { u.Path }; + + } + }) + .ToList(); + } + + + var study = await _studyRepository.FindAsync(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 受试者管理 + + + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "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> GetDicomModalityList(Guid trialId) + public async Task> GetDicomCallingAEList() { - var list = await _scpStudyRepository.Where(t => t.TrialId == trialId).SelectMany(t => t.SeriesList).Select(t => t.Modality).Distinct().ToListAsync(); + var list = await _studyRepository.Select(t => t.CallingAE).Distinct().ToListAsync(); return list; } /// - /// 影像访视上传 检查列表 + ///受试者管理-> 患者列表 模糊搜索下拉 选择subject 排除已绑定并提交的 /// /// /// [HttpPost] - public async Task> GetVisitPatientStudyFilterList(VisitPatientStudyFilterQuery inQuery) + 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] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "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) + { + return ResponseOutput.NotOk($"项目中已存在编号为“{find.Code}”的受试者,状态为:{(find.Status == SubjectStatus.OnVisit ? "访视中" : "访视结束")},不允许添加。", ApiResponseCodeEnum.NeedTips); + + } + return ResponseOutput.Ok(); + + } + + /// + /// 建立subject与患者绑定 如果是下拉,则传递SubjectId,如果不存在,创建,那么就传递 SubjectCode + /// + /// 绑定以后,后台自动创建访视 和检查预先绑定 + /// + /// + /// + [HttpPost] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "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 + { + + return ResponseOutput.NotOk($"项目中已存在编号为“{find.Code}”的受试者,状态为:{(find.Status == SubjectStatus.OnVisit ? "访视中" : "访视结束")},不允许添加。"); + //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] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "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["该受试者有访视已提交,不允许解除受试者和该患者的绑定关系"]); + } + + + + //当前是最后移除的患者 + 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] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + [UnitOfWork] + [Obsolete] + public async Task AddSubjectPatientStudyBinding(List inCommandList) { - var trialSiteId = _subjectRepository.Where(t => t.Id == inQuery.SubjectId).Select(t => t.TrialSiteId).FirstOrDefault(); - var query = from scpStudy in _scpStudyRepository - //未绑定的患者,或者自己已绑定但是未绑定访视的 - .Where(t => t.Patient.SubjectId == null || (t.Patient.SubjectId == inQuery.SubjectId && t.SubjectVisitId == null)) - //中心 - .Where(t => t.TrialSiteId == trialSiteId) + 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 区分添加 还是移除 + /// + /// + /// + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "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["受试者状态为访视结束,不允许绑定访视和检查"]); + } + + 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) + { + return ResponseOutput.NotOk(_localizer[$"{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] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "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["当前批量提交的访视中间有遗漏的访视或者前序有访视未提交"]); + } + } + + else + { + if (allVisitList.Any(t => t.VisitNum < curentMaxNum && t.SubmitState != SubmitStateEnum.Submitted)) + { + return ResponseOutput.NotOk(_localizer["前序有访视未提交,请先提交前序访视"]); + } + } + + if (allVisitList.Any(t => t.Status == SubjectStatus.EndOfVisit)) + { + return ResponseOutput.NotOk(_localizer["受试者状态为访视结束,不允许提交访视生成任务"]); + } + + 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["有访视和检查处于已绑定关系,不允许再次提交绑定"]); + } + + if (list.Any(t => t.IsUploadFinished == false)) + { + return ResponseOutput.NotOk(_localizer["有访视检查正在传输中,不允许提交"]); + } + + + //判断每个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) + { + return ResponseOutput.NotOk(_localizer[$"{g.Key.SubjectCode}所提交的访视中的检查时间,不符合后续访视的检查时间比前序检查的时间大的要求"]); + } + + if (DateTime.Now < studyTimeOrderList.Max(t => t.StudyTime)) + { + return ResponseOutput.NotOk(_localizer[$"您当前修改了服务器时间,试图绕过软件授权,请恢复服务器时间,并联系授权方授权才可进行操作"]); + } + } + + 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["提交的访视下必须有影像检查!"]); + } + + 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 = Guid.Empty; + 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 = Guid.Empty; + 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 = Guid.Empty; + 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] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "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), + VisitImageZipPath = t.VisitImageZipPath, + PackState = t.PackState, + }); + + 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.IsNullOrWhiteSpace(inQuery.Modalities), t => t.Modalities.Contains(inQuery.Modalities)) - .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientInfo), t => t.PatientIdStr.Contains(inQuery.PatientInfo) || t.PatientName.Contains(inQuery.PatientInfo) || t.PatientSex.Contains(inQuery.PatientInfo)) - select new VisitPatientStudyFilterView() + .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, @@ -264,352 +2282,515 @@ namespace IRaCIS.Core.Application.Service SCPStudyId = scpStudy.Id, SeriesCount = scpStudy.SeriesCount, StudyTime = scpStudy.StudyTime, - BodyPartExamined = scpStudy.BodyPartExamined, - AccessionNumber = scpStudy.AccessionNumber, - PatientBirthDate = scpStudy.PatientBirthDate, - PatientAge = scpStudy.PatientAge, - PatientIdStr = scpStudy.PatientIdStr, - PatientName = scpStudy.PatientName, - PatientSex = scpStudy.PatientSex, + 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 pageList = await query.ToPagedListAsync(inQuery, nameof(PatientStudySimpleView.StudyTime)); + //var notBindQuery= _studyRepository.Where(t => t.PatientId == inQuery.PatientId && t.pa) - return pageList; + //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); } - - public async Task> VerifyPacsImage(VerifyPacsImageCommand inCommand) + /// + ///检查管理-> 检查列表 (同步影像数据之后的 带患者信息 患者信息是数组) + /// + /// + /// + [HttpPost] + public async Task>> GetTrialPatientStudyList(TrialPatientStudyQuery inQuery, [FromServices] IRepository _repository) { - var trialId = inCommand.TrialId; - - var subjectId = inCommand.SubjectId; - - var isVerifyVisitImageDate = await _trialRepository.Where(t => t.Id == inCommand.TrialId).Select(t => t.IsVerifyVisitImageDate).FirstNotNullAsync(); - - var result = new List(); - - var visitList = _subjectVisitRepository.Where(t => t.SubjectId == inCommand.SubjectId).Select(t => new { t.VisitNum, t.EarliestScanDate, t.LatestScanDate, t.Id }).ToList(); - - var currentVisitNum = visitList.First(t => t.Id == inCommand.SubjectVisitId).VisitNum; - - var scpStudyList = _scpStudyRepository.Where(t => inCommand.SCPStudyIdList.Contains(t.Id)).Select(t => new { StudyDate = t.StudyTime, t.Id }).ToList(); - - foreach (var waitUploadItem in scpStudyList) - { - - if (isVerifyVisitImageDate) - { - //小于当前访视 最近的最晚拍片 - var before = visitList.Where(u => u.VisitNum < currentVisitNum).Max(k => k.LatestScanDate); - - if (before != null && waitUploadItem.StudyDate != null && before > waitUploadItem.StudyDate) - { - - // $"当前访视检查时间{waitUploadItem.StudyDate?.ToString("yyyy-MM-dd")}不能早于前序访视检查时间{before?.ToString("yyyy-MM-dd")},请核对检查数据是否有误", - result.Add(new VerifySCPStudyUploadResult() { SCPStudyId = waitUploadItem.Id, ErrorMesseage = _localizer["Study_VisitBeforePrevError", waitUploadItem.StudyDate?.ToString("yyyy-MM-dd")!, before?.ToString("yyyy-MM-dd")!] }); - continue; // 跳过当前迭代 - } - - //大于当前访视 最近的最早拍片日期 - var after = visitList.Where(u => u.VisitNum > currentVisitNum).Min(k => k.EarliestScanDate); - - if (after != null && waitUploadItem.StudyDate != null && after < waitUploadItem.StudyDate) - { - // $"当前访视检查时间{waitUploadItem.StudyDate?.ToString("yyyy-MM-dd")}不能晚于该访视之后的检查时间{after?.ToString("yyyy-MM-dd")},请核对检查数据是否有误" - result.Add(new VerifySCPStudyUploadResult() { SCPStudyId = waitUploadItem.Id, ErrorMesseage = _localizer["Study_VisitAfterSubseqError", waitUploadItem.StudyDate?.ToString("yyyy-MM-dd")!, after?.ToString("yyyy-MM-dd")!] }); - continue; // 跳过当前迭代 - } - } - - var verifyStudyInfo = _dicomStudyRepository.Where(t => t.TrialId == trialId && t.Id == waitUploadItem.Id).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); - - var currentStudyResult = new VerifySCPStudyUploadResult() { SCPStudyId = waitUploadItem.Id }; + 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, - //数据库不存在该检查 允许上传 - if (verifyStudyInfo == null) - { - currentStudyResult.AllowUpload = true; - } - //数据库该项目有该检查 看是否支持重传 - else - { - //是同一个受试者 支持重传 - if (verifyStudyInfo.SubjectId == subjectId && verifyStudyInfo.SubjectVisitId == inCommand.SubjectVisitId) - { - currentStudyResult.AllowReUpload = true; - } - //不是同一个受试者 - else - { - //有默认值,其实不用写,这里为了好理解 - currentStudyResult.AllowUpload = false; + //PatientId = Guid.Empty, + PatientAge = t.PatientAge, + PatientName = t.PatientName, + PatientBirthDate = t.PatientBirthDate, + PatientIdStr = t.PatientIdStr, + PatientSex = t.PatientSex, - currentStudyResult.AllowReUpload = false; + //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(), - //$"此处不可以上传。当前影像检查已经上传给受试者{verifyStudyInfo.SubjectCode}的{verifyStudyInfo.VisitName}" - currentStudyResult.ErrorMesseage = _localizer["Study_ImgAlreadyUploaded", verifyStudyInfo.SubjectCode, verifyStudyInfo.VisitName]; - } - } + 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), - result.Add(currentStudyResult); - } - - - return result; + 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] - [UnitOfWork] - [TrialGlobalLimit( "AfterStopCannNotOpt" )] - public async Task SubmitVisitStudyBinding(SubmitVisitStudyBindingCommand inCommand, - [FromServices] IRepository _dicomstudyRepository, + 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) { - - //这里要做校验 + 同时验证本地系统里面的影像是否存在pacs推过来的 - - var copyCommand = inCommand.Clone(); - copyCommand.SCPStudyIdList = inCommand.SCPStudyIdList.Union(inCommand.ReUploadSCPStudyIdList).ToList(); - var verifyResult = await VerifyPacsImage(copyCommand); - - var allowUploadList = verifyResult.Where(u => u.AllowUpload == true).Select(t => t.SCPStudyId).ToList(); - var allowReUploadList = verifyResult.Where(u => u.AllowReUpload == true).Select(t => t.SCPStudyId).ToList(); - - if (!(inCommand.SCPStudyIdList.All(t => allowUploadList.Contains(t)) && inCommand.ReUploadSCPStudyIdList.All(t => allowReUploadList.Contains(t)))) + if (subjectId != null) { - throw new BusinessValidationFailedException("对接提示: 前端提交的检查有不能上传的,请刷新页面调用验证接口重试!"); - } + await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.TrialId == trialId && t.SubjectId == subjectId); - var subjectId = inCommand.SubjectId; - var subjectVisitId = inCommand.SubjectVisitId; - var trialId = inCommand.TrialId; + await _subjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); - var @lock = _distributedLockProvider.CreateLock($"StudyCode"); - - using (await @lock.AcquireAsync()) - { - var dbStudyCodeIntMax = _dicomStudyRepository.Where(s => s.TrialId == inCommand.TrialId).Select(t => t.Code).DefaultIfEmpty().Max(); - - int currentNextCodeInt = dbStudyCodeIntMax + 1; - - //新增的,上传的 - foreach (var scpStudyId in inCommand.SCPStudyIdList) - { - - var find = _scpStudyRepository.Where(t => t.Id == scpStudyId).Select(t => new { SCPStudy = t, t.SeriesList, t.InstanceList }).FirstOrDefault(); - - if (find != null) - { - var newStuty = _mapper.Map(find.SCPStudy); - - await _dicomStudyRepository.AddAsync(newStuty); - - newStuty.SeqId = Guid.Empty; - newStuty.Code = currentNextCodeInt; - newStuty.StudyCode = AppSettings.GetCodeStr(currentNextCodeInt, nameof(DicomStudy)); - newStuty.IsFromPACS = true; - newStuty.TrialId = trialId; - newStuty.SubjectId = subjectId; - newStuty.SubjectVisitId = subjectVisitId; - - var newSeriesList = _mapper.Map>(find.SeriesList); - - foreach (var series in newSeriesList) - { - - series.SeqId = Guid.Empty; - series.TrialId = trialId; - series.SubjectId = subjectId; - series.SubjectVisitId = subjectVisitId; - } - - await _dicomSeriesRepository.AddRangeAsync(newSeriesList); - - var newInstanceList = _mapper.Map>(find.InstanceList); - - foreach (var instance in newInstanceList) - { - instance.SeqId = Guid.Empty; - instance.TrialId = trialId; - instance.SubjectId = subjectId; - instance.SubjectVisitId = subjectVisitId; - - } - await _dicomInstanceRepository.AddRangeAsync(newInstanceList); - } - - currentNextCodeInt++; - - await _scpPatientRepository.BatchUpdateNoTrackingAsync(t => t.Id == find.SCPStudy.PatientId, u => new SCPPatient() { SubjectId = subjectId }); - await _scpStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == scpStudyId, u => new SCPStudy() { SubjectVisitId = subjectVisitId }); - - - } - - foreach (var scpStudyId in inCommand.ReUploadSCPStudyIdList) - { - - var study = await _dicomstudyRepository.FirstOrDefaultAsync(t => t.Id == scpStudyId); - - var instanceIdList = _dicomInstanceRepository.Where(t => t.Id == scpStudyId).Select(t => t.Id).ToList(); - - var scpStudy = _scpStudyRepository.Where(t => t.Id == scpStudyId).Include(t => t.SeriesList).ThenInclude(t => t.SCPInstanceList).FirstOrDefault(); - - //以最后一次为准 - study.IsFromPACS = true; - //特殊处理逻辑 - study.Modalities = string.Join("、", scpStudy.SeriesList.Select(t => t.Modality).Union(study.Modalities.Split("、", StringSplitOptions.RemoveEmptyEntries)).Distinct()); - - SpecialArchiveStudyDeal(study); - - - // 少了整个序列 - - //某个序列下少了instance - foreach (var seriesItem in scpStudy.SeriesList) - { - var seriesId = IdentifierHelper.CreateGuid(seriesItem.StudyInstanceUid, seriesItem.SeriesInstanceUid, trialId.ToString()); - - DicomSeries dicomSeries = await _dicomSeriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId); - - //判断重复 - if (dicomSeries == null) - { - var series = _mapper.Map(seriesItem); - - series.SeqId = Guid.Empty; - series.TrialId = trialId; - series.SubjectId = subjectId; - series.SubjectVisitId = subjectVisitId; - - - dicomSeries = await _dicomSeriesRepository.AddAsync(series); - - - - foreach (var instanceItem in seriesItem.SCPInstanceList) - { - var instance = _mapper.Map(instanceItem); - - instance.SeqId = Guid.Empty; - instance.TrialId = trialId; - instance.SubjectId = subjectId; - instance.SubjectVisitId = subjectVisitId; - - await _dicomInstanceRepository.AddAsync(instance); - } - - //新的序列 那么 检查的序列数量+1 - study.SeriesCount += 1; - - study.InstanceCount += seriesItem.SCPInstanceList.Count; - } - else - { - //该序列掉了instance - dicomSeries.InstanceCount += seriesItem.SCPInstanceList.Count; - - var newInstanceList = seriesItem.SCPInstanceList.Where(t => !instanceIdList.Contains(t.Id)); - - foreach (var instanceItem in newInstanceList) - { - var instance = _mapper.Map(instanceItem); - - instance.SeqId = Guid.Empty; - instance.TrialId = trialId; - instance.SubjectId = subjectId; - instance.SubjectVisitId = subjectVisitId; - - await _dicomInstanceRepository.AddAsync(instance); - } - - study.InstanceCount += newInstanceList.Count(); - - } - } - - await _scpPatientRepository.BatchUpdateNoTrackingAsync(t => t.Id == scpStudy.PatientId, u => new SCPPatient() { SubjectId = subjectId }); - await _scpStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == scpStudyId, u => new SCPStudy() { SubjectVisitId = subjectVisitId }); - } + 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); - - - await _scpStudyRepository.SaveChangesAsync(); + } return ResponseOutput.Ok(); } - private void SpecialArchiveStudyDeal(DicomStudy study) + /// + /// 阅片管理-> 任务列表 + /// + /// + /// + [HttpPost] + public async Task>> GetPatientVisitTaskList([FromServices] IRepository _visitTaskRepository, PatientVisitTaskQuery inQuery) { - #region 特殊逻辑 + 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) - if (study.PatientBirthDate.Length == 8) - { - study.PatientBirthDate = $"{study.PatientBirthDate[0]}{study.PatientBirthDate[1]}{study.PatientBirthDate[2]}{study.PatientBirthDate[3]}-{study.PatientBirthDate[4]}{study.PatientBirthDate[5]}-{study.PatientBirthDate[6]}{study.PatientBirthDate[7]}"; - } + .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) - var dicModalityList = _dictionaryRepository.Where(t => t.Code == "Modality").SelectMany(t => t.ChildList.Select(c => c.Value)).ToList(); + .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 modality = study.Modalities; + var defalutSortArray = new string[] { nameof(PatientVisitTaskDTO.SubjectId), nameof(PatientVisitTaskDTO.VisitTaskNum) }; - var modalityForEdit = dicModalityList.Contains(modality) ? modality : String.Empty; + var pageList = await visitTaskQueryable.ToPagedListAsync(inQuery, defalutSortArray); - if (modality == "MR") - { - modalityForEdit = "MRI"; - } - - if (modality == "PT") - { - modalityForEdit = "PET"; - } - if (modality == "PT、CT") - { - modalityForEdit = "PET-CT"; - } - - study.ModalityForEdit = modalityForEdit; - #endregion + return ResponseOutput.Ok(pageList); } #endregion + /// + /// 打包和匿名化影像 默认是匿名化打包,也可以不匿名化打包 + /// + /// + /// + /// + /// + + public async Task RequestPackageAndAnonymizImage(Guid trialId, Guid subjectVisitId, [FromServices] IOSSService _oSSService, bool isAnonymize = true) + { + var subjectVisit = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId); + + if (subjectVisit.PackState == PackState.WaitPack) + { + HangfireJobHelper.NotImmediatelyOnceOnlyJob(t => t.RequestPackageAndAnonymizImage(trialId, subjectVisitId, isAnonymize), TimeSpan.FromSeconds(1)); + + subjectVisit.PackState = PackState.Packing; + + await _subjectVisitRepository.SaveChangesAsync(); + + } + + var zipPath = subjectVisit.VisitImageZipPath; + + if (!string.IsNullOrEmpty(zipPath)) + { + //记录下载的记录 + + await _subejctVisitDownloadRepository.AddAsync(new SubejctVisitDownload() { IP = _userInfo.IP, SubjectVisitId = subjectVisitId }, true); + } + + return ResponseOutput.Ok(zipPath); + + } + + /// + /// 访视影像下载记录表 + /// + /// + [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); + } + /// + /// 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); + } @@ -617,5 +2798,191 @@ namespace IRaCIS.Core.Application.Service } + public interface IDownloadAndUploadService + { + Task RequestPackageAndAnonymizImage(Guid trialId, Guid subjectVisitId, bool isAnonymize = true); + } + [ApiExplorerSettings(GroupName = "Trial")] + public class DownloadAndUploadService : BaseService, IDownloadAndUploadService + { + private readonly IRepository _systemAnonymizationRepository; + private readonly IRepository _subjectVisitRepository; + private readonly IOSSService _oSSService; + public DownloadAndUploadService(IRepository systemAnonymizationRepository, IRepository subjectVisitRepository, IOSSService oSSService) + { + _systemAnonymizationRepository = systemAnonymizationRepository; + + _subjectVisitRepository = subjectVisitRepository; + + _oSSService = oSSService; + } + + + public async Task RequestPackageAndAnonymizImage(Guid trialId, Guid subjectVisitId, bool isAnonymize = true) + { + + var subjectVisit = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId); + + try + { + var addOrUpdateFixedFieldList = new List(); + + var ircFieldList = new List(); + + if (isAnonymize) + { + var systemAnonymizationList = _systemAnonymizationRepository.Where(t => t.IsEnable).ToList(); + + addOrUpdateFixedFieldList = systemAnonymizationList.Where(t => t.IsFixed).ToList(); + + ircFieldList = systemAnonymizationList.Where(t => t.IsFixed == false).ToList(); + } + + var subjectAndVisitInfo = _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new { SubjectCode = t.Subject.Code, t.Trial.TrialCode, t.VisitNum }).FirstOrDefault(); + + var query = from sv in _subjectVisitRepository.Where(t => t.Id == subjectVisitId) + + select new + { + 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, + + InstancePathList = z.DicomInstanceList.Select(k => new + { + k.Path + }) + }) + + }) + }; + + var info = query.FirstOrDefault(); + + if (info != null) + { + // 创建一个临时文件夹来存放文件 + string tempFolderPath = Path.Combine(Directory.GetCurrentDirectory(), $"DownloadTemp_{NewId.NextGuid()}"); + Directory.CreateDirectory(tempFolderPath); + + // 遍历查询结果 + foreach (var studyInfo in info.StudyList) + { + // 遍历 Series + foreach (var seriesInfo in studyInfo.SeriesList) + { + string studyFolderPath = Path.Combine(tempFolderPath, $"{info.SubjectCode}_{info.VisitName}", $"{studyInfo.StudyCode}_{studyInfo.StudyTime?.ToString("yyyy-MM-dd")}_{seriesInfo.Modality}"); + + // 创建 影像 文件夹 + Directory.CreateDirectory(studyFolderPath); + + // 遍历 InstancePathList + foreach (var instanceInfo in seriesInfo.InstancePathList) + { + // 复制文件到相应的文件夹 + string destinationPath = Path.Combine(studyFolderPath, Path.GetFileName(instanceInfo.Path)); + + //下载到当前目录 + await _oSSService.DownLoadFromOSSAsync(instanceInfo.Path, destinationPath); + + #region 匿名化逻辑 + + + if (isAnonymize) + { + DicomFile dicomFile = await DicomFile.OpenAsync(destinationPath, Encoding.Default); + + DicomDataset dataset = dicomFile.Dataset; + + foreach (var item in addOrUpdateFixedFieldList) + { + + var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16)); + + dataset.AddOrUpdate(dicomTag, item.ReplaceValue); + } + + foreach (var item in ircFieldList) + { + + var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16)); + + if (dicomTag == DicomTag.ClinicalTrialProtocolID) + { + dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.TrialCode); + + } + if (dicomTag == DicomTag.ClinicalTrialSiteID) + { + //dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.TrialSiteCode); + + } + if (dicomTag == DicomTag.ClinicalTrialSubjectID) + { + dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.SubjectCode); + + } + if (dicomTag == DicomTag.ClinicalTrialTimePointID) + { + dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.VisitNum.ToString()); + + } + if (dicomTag == DicomTag.PatientID) + { + dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.TrialCode + "_" + subjectAndVisitInfo.SubjectCode); + + } + + } + } + #endregion + } + } + } + + var zipPath = Path.Combine(Directory.GetCurrentDirectory(), $"{info.SubjectCode}_{info.VisitName}_ImageStudy.zip"); + + ZipFile.CreateFromDirectory(tempFolderPath, zipPath); + + //上传到Oss + var relativePath = await _oSSService.UploadToOSSAsync(zipPath, $"download_zip/{subjectVisitId}", false); + + + //subjectVisit.PackState = PackState.Packed; + //subjectVisit.VisitImageZipPath = relativePath; + //await _subjectVisitRepository.SaveChangesAsync(); + + var zipSize = File.OpenRead(zipPath).Length; + var fileCount = info.StudyList.SelectMany(t => t.SeriesList).SelectMany(t => t.InstancePathList).Count(); + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == subjectVisitId, u => new SubjectVisit() { PackState = PackState.Packed, VisitImageZipPath = relativePath, VisitImageFileCount = fileCount, VisitImageZipSize = zipSize }); + + + //清理文件夹 + Directory.Delete(tempFolderPath, true); + File.Delete(zipPath); + + } + } + catch (Exception ex) + { + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == subjectVisitId, u => new SubjectVisit() { PackState = PackState.WaitPack }); + + } + + + + + + } + + } } diff --git a/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs b/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs index cf927ee17..05a50ef5d 100644 --- a/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs @@ -1,7 +1,7 @@ using AutoMapper; using IRaCIS.Application.Contracts; using IRaCIS.Core.Application.Contracts; -using IRaCIS.Core.Application.Contracts.Dicom.DTO; +using IRaCIS.Core.Application.ViewModel; using IRaCIS.Core.Domain.Share; namespace IRaCIS.Core.Application.Service @@ -22,8 +22,20 @@ namespace IRaCIS.Core.Application.Service .ForMember(d => d.VisitStageId, t => t.MapFrom(u => u.Id)); + CreateMap() + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.SubjectVisit.Subject.Code)) + .ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName)) + .ForMember(d => d.VisitImageFileCount, u => u.MapFrom(s => s.SubjectVisit.VisitImageFileCount)) + .ForMember(d => d.VisitImageZipPath, u => u.MapFrom(s => s.SubjectVisit.VisitImageZipPath)) + .ForMember(d => d.VisitImageZipSize, u => u.MapFrom(s => s.SubjectVisit.VisitImageZipSize)) + .ForMember(d => d.DownloadTime, u => u.MapFrom(s => s.CreateTime)) + .ForMember(d => d.DownloadUserName, u => u.MapFrom(s => s.CreateUser.UserName)) + .ForMember(d => d.StudyCount, u => u.MapFrom(s => s.SubjectVisit.StudyList.Count())) + .ForMember(d => d.DownLoadUserFullName, u => u.MapFrom(s => s.CreateUser.FullName)) + .ForMember(d => d.UserTypeEnum, u => u.MapFrom(s => s.CreateUser.UserTypeEnum)); - + CreateMap(); + CreateMap() .ForMember(d => d.CreateUser, u => u.MapFrom(g => g.CreateUser.LastName + " / " + g.CreateUser.FirstName)); @@ -66,7 +78,6 @@ namespace IRaCIS.Core.Application.Service .ForMember(d => d.MissingSubmmitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.VisitNum < s.LatestSubjectVisit.VisitNum && t.SubmitState != SubmitStateEnum.Submitted && t.IsLostVisit == false))) - .ForMember(d => d.IsMissingImages, u => u.MapFrom(s => s.SubjectVisitList.Any(t => t.VisitNum < s.LatestSubjectVisit.VisitNum && t.SubmitState != SubmitStateEnum.Submitted && t.IsLostVisit == false))) .ForMember(d => d.InPlanVisitSubmmitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.SubmitState == SubmitStateEnum.Submitted && t.InPlan == true))) .ForMember(d => d.LostVisitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.IsLostVisit))) .ForMember(d => d.InPlanVisitSubmmitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.SubmitState == SubmitStateEnum.Submitted && t.InPlan == true))) @@ -77,8 +88,6 @@ namespace IRaCIS.Core.Application.Service //.ForMember(d => d.OutPlanVisitUploadCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t => t.VisitExecuted == VisitExecutedEnum.Executed && t.InPlan == false))); - //审计信息 这里不用IncludeMembers 也可以识别 是以导航属性名称开头 - // 还有 外键? COALESCE([t0].[SubjectId], '00000000-0000-0000-0000-000000000000') 因为destination 是Guid CreateMap().ForAllMembers(opt => opt.Condition((src, dest, srcMember) => srcMember != null)); @@ -90,7 +99,7 @@ namespace IRaCIS.Core.Application.Service .ForMember(d => d.SubjectVisitId, u => u.MapFrom(s => s.Id)) .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) .ForMember(d => d.TrialCode, u => u.MapFrom(s => s.Trial.TrialCode)) - .ForMember(d => d.Sponsor, u => u.MapFrom(s => s.Trial.Sponsor.SponsorName)); + .ForMember(d => d.Sponsor, u => u.MapFrom(s => s.Trial.Sponsor)); CreateMap(); CreateMap() @@ -100,31 +109,99 @@ namespace IRaCIS.Core.Application.Service .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.SubjectVisit.TrialSite.TrialSiteCode)); - CreateMap() - .ForMember(d => d.StudyId, u => u.MapFrom(s => s.Id)) - .ForMember(d => d.SeriesList, u => u.MapFrom(s => s.SeriesList)); + CreateMap() + .ForMember(d => d.SCPStudyId, u => u.MapFrom(s => s.Id)); - CreateMap() - .ForMember(d => d.InstanceInfoList, u => u.MapFrom(s => s.InstanceList)); + CreateMap() + .ForMember(d => d.CalledAEList, u => u.MapFrom(s => s.SCPStudyList.Select(t => t.CalledAE).Distinct())) + .ForMember(d => d.CallingAEList, u => u.MapFrom(s => s.SCPStudyList.Select(t => t.CallingAE).Distinct())) + .ForMember(d => d.PatientId, u => u.MapFrom(s => s.Id)); - CreateMap(); + CreateMap().IncludeMembers(t=>t.SCPStudy) + .ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName)); + + CreateMap() + .ForMember(d => d.PatientList, u => u.MapFrom(s => s.SubjectPatientList)) + .ForMember(d => d.VisitCount, u => u.MapFrom(s => s.SubjectVisitList.Count())) + .ForMember(d => d.LatestVisitName, u => u.MapFrom(s => s.SubjectVisitList.OrderByDescending(t => t.VisitNum).First().VisitName)) + ; + + CreateMap() + .ForMember(d => d.PatientId, u => u.MapFrom(s => s.Patient.Id)) + .ForMember(d => d.PatientSex, u => u.MapFrom(s => s.Patient.PatientSex)) + .ForMember(d => d.PatientIdStr, u => u.MapFrom(s => s.Patient.PatientIdStr)) + .ForMember(d => d.PatientAge, u => u.MapFrom(s => s.Patient.PatientAge)) + .ForMember(d => d.PatientName, u => u.MapFrom(s => s.Patient.PatientName)) + .ForMember(d => d.PatientBirthDate, u => u.MapFrom(s => s.Patient.PatientBirthDate)); + + CreateMap() + .ForMember(d => d.TrialId, u => u.MapFrom(s => s.Id)); + + CreateMap() + .ForMember(d => d.TrialId, u => u.MapFrom(s => s.Id)) + .ForMember(d => d.UnSubmitCount, u => u.MapFrom(s => s.SubjectVisitList.Count(t=>t.SubmitState==SubmitStateEnum.ToSubmit))) + .ForMember(d => d.UnReadCount, u => u.MapFrom(s => s.VisitTaskList.Count(t=>t.TaskState==TaskState.Effect && t.SignTime==null))) + ; + + CreateMap() + .ForMember(d => d.DictionaryList, u => u.MapFrom(s => s.TrialDicList.Select(t => t.Dictionary).OrderBy(t => t.ShowOrder))); + + + CreateMap() + .ForMember(d => d.SCPStudyId, u => u.MapFrom(s => s.Id)); + + CreateMap(); + CreateMap().ReverseMap(); + + CreateMap().IncludeMembers(t => t.Subject) + .ForMember(d => d.TrialId, u => u.MapFrom(s => s.Subject.TrialId)) + .ForMember(d => d.ResearchProgramNo, u => u.MapFrom(s => s.Subject.Trial.ResearchProgramNo)) + .ForMember(d => d.TrialStatusStr, u => u.MapFrom(s => s.Subject.Trial.TrialStatusStr)) + .ForMember(d => d.TrialType, u => u.MapFrom(s => s.Subject.Trial.TrialType)) + .ForMember(d => d.Sponsor, u => u.MapFrom(s => s.Subject.Trial.Sponsor)) + .ForMember(d => d.CreateTime, u => u.MapFrom(s => s.CreateTime)) + .ForMember(d => d.TrialCode, u => u.MapFrom(s => s.Subject.Trial.TrialCode)) + .ForMember(d => d.ExperimentName, u => u.MapFrom(s => s.Subject.Trial.ExperimentName)); + + CreateMap(); + + + CreateMap(); + + CreateMap() + .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code)) + .ForMember(d => d.SubjectShortName, u => u.MapFrom(s => s.Subject.ShortName)) + .ForMember(d => d.TrialReadingCriterionName, u => u.MapFrom(s => s.TrialReadingCriterion.CriterionName)) + .ForMember(d => d.CriterionType, u => u.MapFrom(s => s.TrialReadingCriterion.CriterionType)) + .ForMember(d => d.PatientList, u => u.MapFrom(s => s.Subject.SubjectPatientList)) + .ForMember(d => d.VisitImageZipPath, u => u.MapFrom(s => s.SourceSubjectVisit.VisitImageZipPath)) + .ForMember(d => d.PackState, u => u.MapFrom(s => s.SourceSubjectVisit.PackState)); + + CreateMap().ReverseMap(); + + CreateMap(); - CreateMap() - .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) - .ForMember(d => d.TrialSiteAliasName, u => u.MapFrom(s => s.TrialSite.TrialSiteAliasName)) - .ForMember(d => d.TrialSiteName, u => u.MapFrom(s => s.TrialSite.TrialSiteName)); + CreateMap() + .ForMember(d => d.SeriesList, u => u.Ignore()) + /* .ForMember(d => d.SeriesList, u => u.Ignore())*/; - CreateMap() - .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) - .ForMember(d => d.TrialSiteAliasName, u => u.MapFrom(s => s.TrialSite.TrialSiteAliasName)) - .ForMember(d => d.TrialSiteName, u => u.MapFrom(s => s.TrialSite.TrialSiteName)); - - - CreateMap(); CreateMap(); CreateMap(); + CreateMap() + .ForMember(d => d.TrialId, u => u.MapFrom(s => s.Id)); + + CreateMap().ReverseMap(); + + CreateMap(); + + CreateMap().ReverseMap(); + + + CreateMap(); + + } } diff --git a/IRaCIS.Core.Domain.Share/Trial/TrialExpedited.cs b/IRaCIS.Core.Domain.Share/Trial/TrialExpedited.cs index 1c7d99f54..e023561b2 100644 --- a/IRaCIS.Core.Domain.Share/Trial/TrialExpedited.cs +++ b/IRaCIS.Core.Domain.Share/Trial/TrialExpedited.cs @@ -22,7 +22,12 @@ namespace IRaCIS.Core.Domain.Share NoneOfficial = 0, - Training = 2 + Training = 2, + + //临床研究 + ClinicalResearch = 3, + + ScientificResearch = 4 } diff --git a/IRaCIS.Core.Domain.Share/User/UserType.cs b/IRaCIS.Core.Domain.Share/User/UserType.cs index b38f45d76..2dc5e933c 100644 --- a/IRaCIS.Core.Domain.Share/User/UserType.cs +++ b/IRaCIS.Core.Domain.Share/User/UserType.cs @@ -4,43 +4,65 @@ public enum UserTypeEnum { - + SuperAdmin = 1, + + Admin = 2, + + + TA = 3, + + + //PM - ProjectManager=1, + ProjectManager = 4, + + //CRC - ClinicalResearchCoordinator=2, + ClinicalResearchCoordinator = 5, + + CRA = 6, //IQC - IQC = 3, + IQC = 7, - ReviewerCoordinator = 4, - // 大屏展示 - Dashboard = 6, - - // 超级管理员用户类型,用于取代 SuperAdmin字段 数据库不内置这个用户类型和角色的配置,因为只允许有一个 - SuperAdmin=7, - - Admin = 8, + PI = 8, - CRA =9, + SR = 9, - SPM=10, - - APM=11, - - CPM=12, - - IndependentReviewer=13, - - // 医学影像经理 - MIM = 14, + MIM = 10, + + + IM = 11, + + + QA = 12, + + OP = 13, + + OA = 14, + + SPM = 20, + APM = 21, + CPM = 22, + + IndependentReviewer = 18, + + AIR = 21, + + //医生用户类型暂不处理 + + ShareImage = 125, + + + Undefined = 0, + + - QA=15, EA=16, @@ -52,7 +74,6 @@ CMM=19, - AIR=21, ZYSS=26, @@ -60,16 +81,6 @@ MC=30, - OP=31, - - //医生用户类型暂不处理 - - ShareImage = 125, - - - - - Undefined=0 } diff --git a/IRaCIS.Core.Domain/Allocation/VisitTask.cs b/IRaCIS.Core.Domain/Allocation/VisitTask.cs index 4efb9a1dd..78e86e852 100644 --- a/IRaCIS.Core.Domain/Allocation/VisitTask.cs +++ b/IRaCIS.Core.Domain/Allocation/VisitTask.cs @@ -281,4 +281,67 @@ public class VisitTask : BaseFullAuditEntity [Comment("通知IR加急阅片时间")] public DateTime? ExpetidEmailNoticeTime { get; set; } + + #region HIR 新增字段 + + [JsonIgnore] + public User FirstAuditUser { get; set; } + + [JsonIgnore] + public User LatestReplyUser { get; set; } + + [JsonIgnore] + public User SubjectCriterionClaimUser { get; set; } + + + [NotMapped] + public List PIAuditImagePathList + { + get + { + + try + { + return this.PIAuditImagePath?.Trim().Split('|', StringSplitOptions.RemoveEmptyEntries).ToList(); + } + catch (Exception) + { + + return new List(); + } + + } + } + + public string PIAuditNote { get; set; } = string.Empty; + + public string PIAuditImagePath { get; set; } = string.Empty; + + public string NotAgreeReason { get; set; } = string.Empty; + + public PIAuditState PIAuditState { get; set; } + + public bool? IsEnrollment { get; set; } + + public bool? IsPDConfirm { get; set; } + + public Guid? FirstAuditUserId { get; set; } + public DateTime? FirstAuditTime { get; set; } + + public Guid? LatestReplyUserId { get; set; } + public DateTime? LatestReplyTime { get; set; } + + public Guid? SubjectCriterionClaimUserId { get; set; } + + public DateTime? ReportExportDate { get; set; } + + public int? ReportExportNum { get; set; } + + public string ReportExportUrl { get; set; } = string.Empty; + + /// + /// 是否确认提醒 + /// + public bool IsConfirmReminder { get; set; } = false; + #endregion } diff --git a/IRaCIS.Core.Domain/HIR/DicomAE.cs b/IRaCIS.Core.Domain/HIR/DicomAE.cs new file mode 100644 index 000000000..4d5145511 --- /dev/null +++ b/IRaCIS.Core.Domain/HIR/DicomAE.cs @@ -0,0 +1,40 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-03-22 15:44:11 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + + + [Comment("医院dicomAE 配置")] + [Table("DicomAE")] + public class DicomAE : BaseFullAuditEntity + { + + public string CalledAE { get; set; } + + + public string IP { get; set; } + + + public int Port { get; set; } + + + public string Modality { get; set; } + + + public string Description { get; set; } + + + public DateTime? LatestTestTime { get; set; } + + public bool IsTestOK { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/HIR/PIAudit.cs b/IRaCIS.Core.Domain/HIR/PIAudit.cs new file mode 100644 index 000000000..e885401c7 --- /dev/null +++ b/IRaCIS.Core.Domain/HIR/PIAudit.cs @@ -0,0 +1,87 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2023-07-10 15:14:16 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +using System.Linq; + +namespace IRaCIS.Core.Domain.Models +{ + /// + ///PIAudit + /// + [Table("PIAudit")] + public class PIAudit : BaseAddAuditEntity + { + + [JsonIgnore] + public VisitTask VisitTask { get; set; } + + public Guid VisitTaskId { get; set; } + + public string ReplyContent { get; set; }=string.Empty; + + public bool? IsEnrollment { get; set; } + + public bool? IsPDConfirm { get; set; } + + public string PIAuditNote { get; set; } = string.Empty; + + + public string NotAgreeReason { get; set; } = string.Empty; + + public PIAuditState? PIAuditState { get; set; } + + + public string PIAuditImagePath { get; set; } = string.Empty; + + + + [NotMapped] + public List PIAuditImagePathList + { + get + { + + try + { + return this.PIAuditImagePath?.Trim().Split('|', StringSplitOptions.RemoveEmptyEntries).ToList(); + } + catch (Exception) + { + + return new List(); + } + + } + } + + } + + /// + /// PI 审核状态 + /// + public enum PIAuditState + { + + /// + /// PI未审核 + /// + PInotAutit = 0, + + /// + /// PI不认同 + /// + PINotAgree = 1, + + /// + /// PI认同 + /// + PIAgree = 2 + } +} diff --git a/IRaCIS.Core.Domain/HIR/SCPStudySubjectVisit.cs b/IRaCIS.Core.Domain/HIR/SCPStudySubjectVisit.cs new file mode 100644 index 000000000..86e160727 --- /dev/null +++ b/IRaCIS.Core.Domain/HIR/SCPStudySubjectVisit.cs @@ -0,0 +1,46 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-03-21 13:54:49 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + + [Comment("访视检查关联表")] + [Table("SCPStudySubjectVisit")] + public class SCPStudySubjectVisit : BaseFullAuditEntity + { + [ForeignKey("SCPStudyId")] + [JsonIgnore] + public SCPStudy SCPStudy { get; set; } + + [ForeignKey("SubjectVisitId")] + [JsonIgnore] + public SubjectVisit SubjectVisit { get; set; } + + [ForeignKey("SubjectId")] + [JsonIgnore] + public Subject Subject { get; set; } + + + [Required] + public Guid SubjectVisitId { get; set; } + + public Guid TrialId { get; set; } + + public Guid SubjectId { get; set; } + + [Required] + public Guid SCPStudyId { get; set; } + + public Guid? StudyId { get; set; } + + + } + + +} diff --git a/IRaCIS.Core.Domain/HIR/SubejctVisitDownload.cs b/IRaCIS.Core.Domain/HIR/SubejctVisitDownload.cs new file mode 100644 index 000000000..222ecffd1 --- /dev/null +++ b/IRaCIS.Core.Domain/HIR/SubejctVisitDownload.cs @@ -0,0 +1,33 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-05-24 14:31:45 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + + [Comment("访视下载记录")] + [Table("SubejctVisitDownload")] + public class SubejctVisitDownload : BaseAddAuditEntity + { + + + [Required] + public string IP { get; set; } + + + [Required] + public Guid SubjectVisitId { get; set; } + + [ForeignKey("SubjectVisitId")] + [JsonIgnore] + public SubjectVisit SubjectVisit { get; set; } + + + } + +} diff --git a/IRaCIS.Core.Domain/HIR/SubjectPatient.cs b/IRaCIS.Core.Domain/HIR/SubjectPatient.cs new file mode 100644 index 000000000..1b6a73b41 --- /dev/null +++ b/IRaCIS.Core.Domain/HIR/SubjectPatient.cs @@ -0,0 +1,34 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-03-20 17:53:43 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +namespace IRaCIS.Core.Domain.Models +{ + [Comment("受试者患者绑定关系")] + [Table("SubjectPatient")] + public class SubjectPatient : BaseFullDeleteAuditEntity + { + [JsonIgnore] + public SCPPatient Patient { get; set; } + [JsonIgnore] + public Subject Subject { get; set; } + + [Required] + public Guid SubjectId { get; set; } + + + [Required] + public Guid PatientId { get; set; } + + //检查和访视绑定 已提交 + public bool IsBinded { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/HIR/SubjectPatientSCPStudy.cs b/IRaCIS.Core.Domain/HIR/SubjectPatientSCPStudy.cs new file mode 100644 index 000000000..0e32d45ce --- /dev/null +++ b/IRaCIS.Core.Domain/HIR/SubjectPatientSCPStudy.cs @@ -0,0 +1,32 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-04-16 17:06:22 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + [Comment("受试者患者检查绑定关系")] + [Table("SubjectPatientSCPStudy")] + public class SubjectPatientSCPStudy : BaseFullDeleteAuditEntity + { + + public Guid SubjectId { get; set; } + + + public Guid PatientId { get; set; } + + + public Guid TrialId { get; set; } + + + public Guid SCPStudyId { get; set; } + + + + } + +} diff --git a/IRaCIS.Core.Domain/Image/DicomStudy.cs b/IRaCIS.Core.Domain/Image/DicomStudy.cs index 1ca22dc30..331c12351 100644 --- a/IRaCIS.Core.Domain/Image/DicomStudy.cs +++ b/IRaCIS.Core.Domain/Image/DicomStudy.cs @@ -92,4 +92,7 @@ public class DicomStudy : BaseFullDeleteAuditEntity, IEntitySeqId public DateTime? UploadedTime { get; set; } public string Uploader { get; set; } = null!; + + + public string PatientIdStr { get; set; } = string.Empty; } diff --git a/IRaCIS.Core.Domain/Image/SCPPatient.cs b/IRaCIS.Core.Domain/Image/SCPPatient.cs index caeb1828d..225350c69 100644 --- a/IRaCIS.Core.Domain/Image/SCPPatient.cs +++ b/IRaCIS.Core.Domain/Image/SCPPatient.cs @@ -5,14 +5,16 @@ public class SCPPatient : BaseFullAuditEntity { #region 导航属性 + + public List SubjectPatientList { get; set; } [JsonIgnore] public List SCPStudyList { get; set; } - [JsonIgnore] - public Subject Subject { get; set; } - [JsonIgnore] - public Trial Trial { get; set; } - [JsonIgnore] - public TrialSite TrialSite { get; set; } + //[JsonIgnore] + //public Subject Subject { get; set; } + //[JsonIgnore] + //public Trial Trial { get; set; } + //[JsonIgnore] + //public TrialSite TrialSite { get; set; } #endregion public string PatientIdStr { get; set; } = string.Empty; public string PatientName { get; set; } = string.Empty; @@ -25,7 +27,12 @@ public class SCPPatient : BaseFullAuditEntity public DateTime? LatestStudyTime { get; set; } public DateTime LatestPushTime { get; set; } - public Guid? SubjectId { get; set; } - public Guid TrialId { get; set; } - public Guid TrialSiteId { get; set; } + + + #region HIR 注释 + //public Guid TrialId { get; set; } + //public Guid TrialSiteId { get; set; } + + //public Guid? SubjectVisitId { get; set; } + #endregion } diff --git a/IRaCIS.Core.Domain/Image/SCPStudy.cs b/IRaCIS.Core.Domain/Image/SCPStudy.cs index a14710bb4..cab99e1ac 100644 --- a/IRaCIS.Core.Domain/Image/SCPStudy.cs +++ b/IRaCIS.Core.Domain/Image/SCPStudy.cs @@ -6,6 +6,9 @@ public class SCPStudy : BaseFullDeleteAuditEntity, IEntitySeqId { #region 导航属性 + + [JsonIgnore] + public List SCPStudySubjectVisitList { get; set; } [JsonIgnore] public List InstanceList { get; set; } @@ -60,8 +63,13 @@ public class SCPStudy : BaseFullDeleteAuditEntity, IEntitySeqId public string CalledAE { get; set; } = string.Empty; public bool IsUploadFinished { get; set; } - public Guid TrialId { get; set; } - public Guid TrialSiteId { get; set; } - public Guid? SubjectVisitId { get; set; } + + #region HIR 注释 + //public Guid TrialId { get; set; } + //public Guid TrialSiteId { get; set; } + + //public Guid? SubjectVisitId { get; set; } + #endregion + } diff --git a/IRaCIS.Core.Domain/Management/User.cs b/IRaCIS.Core.Domain/Management/User.cs index 087a418bd..a8f833977 100644 --- a/IRaCIS.Core.Domain/Management/User.cs +++ b/IRaCIS.Core.Domain/Management/User.cs @@ -85,4 +85,9 @@ public class User : BaseFullAuditEntity public UserTypeEnum UserTypeEnum { get; set; } public Guid UserTypeId { get; set; } + + #region HIR + public string CheckCode { get; set; } = string.Empty; + + #endregion } diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterion/ReadingQuestionCriterionTrial.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterion/ReadingQuestionCriterionTrial.cs index 3a2d77ac4..2a1c049ff 100644 --- a/IRaCIS.Core.Domain/Reading/ReadingCriterion/ReadingQuestionCriterionTrial.cs +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterion/ReadingQuestionCriterionTrial.cs @@ -156,6 +156,10 @@ public class ReadingQuestionCriterionTrial : BaseAddAuditEntity [Comment("是否影像筛选")] public bool IsImageFilter { get; set; } + + public ReadingDivisionEnum ReadingDivisionEnum { get; set; } = ReadingDivisionEnum.OnlySR; + + public PIReadingScopenEnum PIReadingScopenEnum { get; set; } } public enum ReadingImageDownload @@ -184,3 +188,41 @@ public enum ReadingOrder SubjectRandom = 2, } +/// +/// 阅片分工 +/// +public enum ReadingDivisionEnum +{ + + /// + /// 仅SR阅片 + /// + OnlySR = 1, + + /// + /// PI和SR + /// + PIandSR = 2 +} + +/// +/// PI阅片范围 +/// +public enum PIReadingScopenEnum +{ + + /// + /// 全部基线 + /// + AllBaseline = 1, + + /// + /// 全部随访 + /// + AllVisit = 2, + + /// + /// 全部基线和随访 + /// + AllBaselineandVisit = 3 +} \ No newline at end of file diff --git a/IRaCIS.Core.Domain/Trial/Trial.cs b/IRaCIS.Core.Domain/Trial/Trial.cs index 2178c8a44..613a6d5aa 100644 --- a/IRaCIS.Core.Domain/Trial/Trial.cs +++ b/IRaCIS.Core.Domain/Trial/Trial.cs @@ -9,12 +9,12 @@ namespace IRaCIS.Core.Domain.Models; public partial class Trial : BaseFullDeleteAuditEntity { #region 导航属性 - [JsonIgnore] - [ForeignKey("SponsorId")] - public Sponsor Sponsor { get; set; } - [JsonIgnore] - [ForeignKey("CROId")] - public CRO CRO { get; set; } + //[JsonIgnore] + //[ForeignKey("SponsorId")] + //public Sponsor Sponsor { get; set; } + //[JsonIgnore] + //[ForeignKey("CROId")] + //public CRO CRO { get; set; } [JsonIgnore] public List TrialBodyPartList { get; set; } [JsonIgnore] @@ -130,7 +130,7 @@ public partial class Trial : BaseFullDeleteAuditEntity [StringLength(1000)] public string TrialExtraConfigJsonStr { get; set; } = string.Empty; - public bool VisitPlanConfirmed { get; set; } + public bool VisitPlanConfirmed { get; set; } = true; [Comment("受试者编号具体规则")] public string SubjectCodeRule { get; set; } = I18n.T("Trial_number"); @@ -149,16 +149,16 @@ public partial class Trial : BaseFullDeleteAuditEntity [Comment("是否 验证拍片日期")] public bool IsVerifyVisitImageDate { get; set; } = true; [Comment("临床信息传输 1:系统录入 2:系统录入+PDF 0:无")] - public int ClinicalInformationTransmissionEnum { get; set; } = 1; + public int ClinicalInformationTransmissionEnum { get; set; } = 0; [Comment("是否审核 临床信息")] public bool IsCRAAuditClinicalInformation { get; set; } = false; [Comment("QC流程 0 不审,1 单审,2双审")] - public TrialQCProcess QCProcessEnum { get; set; } = TrialQCProcess.DoubleAudit; + public TrialQCProcess QCProcessEnum { get; set; } = TrialQCProcess.NotAudit; [Comment("影像一致性核查")] - public bool IsImageConsistencyVerification { get; set; } = true; + public bool IsImageConsistencyVerification { get; set; } = false; [Comment("影像导出")] public bool IsImageExport { get; set; } = false; @@ -222,11 +222,11 @@ public partial class Trial : BaseFullDeleteAuditEntity public int? DigitPlaces { get; set; } = 1; - public bool IsTrialProcessConfirmed { get; set; } - public bool IsTrialBasicLogicConfirmed { get; set; } - public bool IsTrialUrgentConfirmed { get; set; } + public bool IsTrialProcessConfirmed { get; set; } = true; + public bool IsTrialBasicLogicConfirmed { get; set; } = true; + public bool IsTrialUrgentConfirmed { get; set; } = true; - public bool IsQCQuestionConfirmed { get; set; } + public bool IsQCQuestionConfirmed { get; set; } = true; [Comment("同步临床数据时间")] public DateTime? SyncClinicalDataTime { get; set; } public string BlindBaseLineName { get; set; } = "Baseline"; @@ -264,6 +264,47 @@ public partial class Trial : BaseFullDeleteAuditEntity [StringLength(2000)] public List TrialObjectNameList { get; set; } + + #region HIR 增加 + + public UserTypeEnum? EnrollConfirmDefaultUserType { get; set; } + + public UserTypeEnum? PDProgressDefaultUserType { get; set; } + + public bool IsDeclaration { get; set; } + public bool IsMedicalReview { get; set; } = false; + + public string VisitBaseDataDes { get; set; } = I18n.T("Trial_VisitBaseDataDes"); + + public string CRO { get; set; } = string.Empty; + + public string Sponsor { get; set; } = string.Empty; + + //药物名称 + public string MedicineName { get; set; } = string.Empty; + + + + //阅片标准 + + //联系人 + public string ContactUser { get; set; } = string.Empty; + //联系电话 + public string ContactPhone { get; set; } = string.Empty; + + public int AuthorizationDuration { get; set; } + + public string AuthorizationEncrypt { get; set; } = string.Empty; + + public DateTime? AuthorizationDate { get; set; } + + public string CriterionTypes { get; set; } = string.Empty; + + [NotMapped] + public List CriterionTypeList => CriterionTypes.Split('|', StringSplitOptions.RemoveEmptyEntries) + .Select(s => Enum.Parse(typeof(CriterionType), s)).Cast().ToList(); + #endregion + } [ComplexType] diff --git a/IRaCIS.Core.Domain/Visit/Subject.cs b/IRaCIS.Core.Domain/Visit/Subject.cs index d65f21d83..149867132 100644 --- a/IRaCIS.Core.Domain/Visit/Subject.cs +++ b/IRaCIS.Core.Domain/Visit/Subject.cs @@ -8,6 +8,8 @@ public class Subject : BaseFullDeleteAuditEntity { #region 导航属性 [JsonIgnore] + public List SubjectPatientList { get; set; } + [JsonIgnore] public List TaskStudyList { get; set; } = new List(); [JsonIgnore] diff --git a/IRaCIS.Core.Domain/Visit/SubjectVisit.cs b/IRaCIS.Core.Domain/Visit/SubjectVisit.cs index 751ea0be7..ccb6d6aa5 100644 --- a/IRaCIS.Core.Domain/Visit/SubjectVisit.cs +++ b/IRaCIS.Core.Domain/Visit/SubjectVisit.cs @@ -7,6 +7,8 @@ public class SubjectVisit : BaseFullDeleteAuditEntity { #region 导航属性 + [JsonIgnore] + public List SCPStudySubjectVisitList { get; set; } [JsonIgnore] public TrialSite TrialSite { get; set; } @@ -180,5 +182,19 @@ public class SubjectVisit : BaseFullDeleteAuditEntity public Guid? SubmitUserId { get; set; } public ReadingStatusEnum ReadingStatus { get; set; } + + + #region HIR 废弃 + //文件数 + public long VisitImageZipSize { get; set; } + + //文件大小 + public int VisitImageFileCount { get; set; } + + public string VisitImageZipPath { get; set; } = string.Empty; + + //路径 + public PackState PackState { get; set; } + #endregion } diff --git a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContextFactory.cs b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContextFactory.cs index f897b6b6c..2a2043e1f 100644 --- a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContextFactory.cs +++ b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContextFactory.cs @@ -13,7 +13,7 @@ public class IRaCISDBContextFactory : IDesignTimeDbContextFactory(); - optionsBuilder.UseSqlServer("Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true", contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure()); + optionsBuilder.UseSqlServer("Server=106.14.89.110,1435;Database=Test_HIR_New;User ID=sa;Password=xc@123456;TrustServerCertificate=true", contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure()); //迁移的时候,不生成外键 optionsBuilder.ReplaceService(); return new IRaCISDBContext(optionsBuilder.Options);