From 7c3b0e9e72f33f51832953441962f691a109456f Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Tue, 2 Jul 2024 09:45:18 +0800 Subject: [PATCH 1/9] =?UTF-8?q?=E6=B5=8F=E8=A7=88=E5=99=A8=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E6=8E=A8=E8=8D=90=20--=E8=BF=81=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/DTO/ExploreRecommendViewModel.cs | 67 ++++++++++++++ .../Service/Common/ExploreRecommendService.cs | 92 +++++++++++++++++++ .../Interface/IExploreRecommendService.cs | 24 +++++ .../Service/Common/_MapConfig.cs | 3 +- IRaCIS.Core.Domain/Common/ExploreRecommend.cs | 53 +++++++++++ .../Context/IRaCISDBContext.cs | 3 + IRaCIS.Core.Test/DbHelper.ttinclude | 2 +- .../TT_Template/IRaCIS .Core.ServiceAsync.tt | 5 +- 8 files changed, 245 insertions(+), 4 deletions(-) create mode 100644 IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs create mode 100644 IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs create mode 100644 IRaCIS.Core.Application/Service/Common/Interface/IExploreRecommendService.cs create mode 100644 IRaCIS.Core.Domain/Common/ExploreRecommend.cs diff --git a/IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs new file mode 100644 index 000000000..b87cfc9e7 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs @@ -0,0 +1,67 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 09:29:36 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +namespace IRaCIS.Core.Application.ViewModel +{ + /// ExploreRecommendView 列表视图模型 + public class ExploreRecommendView + { + public Guid Id { get; set; } + public string Version { get; set; } + public string Title { get; set; } + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public bool IsDeleted { get; set; } + public string DownloadUrl { get; set; } + public string Path { get; set; } + public string FileName { get; set; } + public DateTime? DeleteTime { get; set; } + public Guid? DeleteUserId { get; set; } + } + + ///ExploreRecommendQuery 列表查询参数模型 + public class ExploreRecommendQuery:PageInput + { + + public string? Version { get; set; } + + + public string? Title { get; set; } + + + public string? DownloadUrl { get; set; } + + + public string? FileName { get; set; } + + public bool? IsDeleted { get; set; } + + } + + /// ExploreRecommendAddOrEdit 列表查询参数模型 + public class ExploreRecommendAddOrEdit + { + public Guid Id { get; set; } + public string Version { get; set; } + public string Title { get; set; } + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + public bool IsDeleted { get; set; } + public string DownloadUrl { get; set; } + public string Path { get; set; } + public string FileName { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs b/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs new file mode 100644 index 000000000..76d181268 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs @@ -0,0 +1,92 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 09:26:59 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using Microsoft.AspNetCore.Authorization; +namespace IRaCIS.Core.Application.Service +{ + /// + /// ExploreRecommendService + /// + [ApiExplorerSettings(GroupName = "Common")] + public class ExploreRecommendService : BaseService, IExploreRecommendService + { + + private readonly IRepository _exploreRecommendRepository; + + public ExploreRecommendService(IRepository exploreRecommendRepository) + { + _exploreRecommendRepository = exploreRecommendRepository; + } + + [HttpPost] + public async Task> GetExploreRecommendList(ExploreRecommendQuery inQuery) + { + + var exploreRecommendQueryable = + + _exploreRecommendRepository + .WhereIf(string.IsNullOrEmpty(inQuery.Title), t => t.Title.Contains(inQuery.Title)) + .WhereIf(string.IsNullOrEmpty(inQuery.FileName), t => t.Title.Contains(inQuery.FileName)) + .WhereIf(string.IsNullOrEmpty(inQuery.DownloadUrl), t => t.Title.Contains(inQuery.DownloadUrl)) + .WhereIf(string.IsNullOrEmpty(inQuery.Version), t => t.Title.Contains(inQuery.Version)) + .WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == t.IsDeleted) + .ProjectTo(_mapper.ConfigurationProvider); + + var pageList = await exploreRecommendQueryable + .ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(ExploreRecommendView.Id) : inQuery.SortField, + inQuery.Asc); + + return pageList; + } + + + public async Task AddOrUpdateExploreRecommend(ExploreRecommendAddOrEdit addOrEditExploreRecommend) + { + var verifyExp2 = new EntityVerifyExp() + { + VerifyExp = u => u.IsDeleted == addOrEditExploreRecommend.IsDeleted, + + VerifyMsg = "当前启用版本只允许有一个", + + IsVerify = addOrEditExploreRecommend.IsDeleted == false + }; + + var entity = await _exploreRecommendRepository.InsertOrUpdateAsync(addOrEditExploreRecommend, true, verifyExp2); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + + [HttpDelete("{exploreRecommendId:guid}")] + public async Task DeleteExploreRecommend(Guid exploreRecommendId) + { + var success = await _exploreRecommendRepository.DeleteFromQueryAsync(t => t.Id == exploreRecommendId, true); + return ResponseOutput.Ok(); + } + + [AllowAnonymous] + public async Task GetExploreRecommentInfo() + { + + + var result = await _exploreRecommendRepository.Where(t => t.IsDeleted == false).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); + + if (result == null) + { + throw new QueryBusinessObjectNotExistException("系统浏览器版本推荐未维护,请联系维护人员"); + } + + return result; + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/Common/Interface/IExploreRecommendService.cs b/IRaCIS.Core.Application/Service/Common/Interface/IExploreRecommendService.cs new file mode 100644 index 000000000..2dc5556b2 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/Interface/IExploreRecommendService.cs @@ -0,0 +1,24 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 09:27:36 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// IExploreRecommendService + /// + public interface IExploreRecommendService + { + + Task> GetExploreRecommendList(ExploreRecommendQuery inQuery); + + Task AddOrUpdateExploreRecommend(ExploreRecommendAddOrEdit addOrEditExploreRecommend); + + Task DeleteExploreRecommend(Guid exploreRecommendId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/Common/_MapConfig.cs b/IRaCIS.Core.Application/Service/Common/_MapConfig.cs index 7312de9f9..916cabf1f 100644 --- a/IRaCIS.Core.Application/Service/Common/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/Common/_MapConfig.cs @@ -80,8 +80,9 @@ namespace IRaCIS.Core.Application.Service CreateMap().ReverseMap(); CreateMap(); - + CreateMap(); + CreateMap().ReverseMap(); } } diff --git a/IRaCIS.Core.Domain/Common/ExploreRecommend.cs b/IRaCIS.Core.Domain/Common/ExploreRecommend.cs new file mode 100644 index 000000000..db000e174 --- /dev/null +++ b/IRaCIS.Core.Domain/Common/ExploreRecommend.cs @@ -0,0 +1,53 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 09:26:43 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///ExploreRecommend + /// + [Table("ExploreRecommend")] + public class ExploreRecommend : Entity, IAuditUpdate, IAuditAdd,ISoftDelete + { + + public string Version { get; set; }=string.Empty; + + + public string Title { get; set; } = string.Empty; + + + public DateTime CreateTime { get; set; } + + + public Guid CreateUserId { get; set; } + + + public Guid UpdateUserId { get; set; } + + + public DateTime UpdateTime { get; set; } + + + public bool IsDeleted { get; set; } + + + public string DownloadUrl { get; set; } = string.Empty; + + + public string Path { get; set; } = string.Empty; + + public string FileName { get; set; } = string.Empty; + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + } + +} diff --git a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs index 913ae7a94..c5e8715c7 100644 --- a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs +++ b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs @@ -481,6 +481,9 @@ namespace IRaCIS.Core.Infra.EFCore public virtual DbSet TrialBodyPart { get; set; } + public virtual DbSet ExploreRecommend { get; set; } + + public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) { // 采用触发器的方式 设置 CreateUserId CreateTime UpdateTime UpdateUserId 稽查实体里面没有这四个字段的值 因为先后顺序的原因 diff --git a/IRaCIS.Core.Test/DbHelper.ttinclude b/IRaCIS.Core.Test/DbHelper.ttinclude index 93d6bd2d6..649ef03e0 100644 --- a/IRaCIS.Core.Test/DbHelper.ttinclude +++ b/IRaCIS.Core.Test/DbHelper.ttinclude @@ -4,7 +4,7 @@ public static readonly string ConnectionString = "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true"; public static readonly string DbDatabase = "Test_IRC"; //表名称用字符串,拼接 - public static readonly string TableName = "TrialBodyPart"; + public static readonly string TableName = "ExploreRecommend"; //具体文件里面 例如service 可以配置是否分页 } #> diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.ServiceAsync.tt b/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.ServiceAsync.tt index fa5c367b7..5a1998a61 100644 --- a/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.ServiceAsync.tt +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.ServiceAsync.tt @@ -65,6 +65,7 @@ namespace IRaCIS.Core.Application.Service } <# if(isPage){#> + [HttpPost] public async TaskView>> Get<#=tableName#>List(<#=tableName#>Query inQuery) { @@ -74,13 +75,13 @@ namespace IRaCIS.Core.Application.Service .ProjectTo<<#=tableName#>View>(_mapper.ConfigurationProvider); var pageList= await <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Queryable - .ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? "Id" : inQuery.SortField, + .ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(<#=tableName#>View.Id) : inQuery.SortField, inQuery.Asc); return pageList; } <# } else {#> - + [HttpPost] public async TaskView>> Get<#=tableName#>List(<#=tableName#>Query inQuery) { From 27b99eb3be2eaac262f2945ec0744ea961c08338 Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Tue, 2 Jul 2024 11:08:11 +0800 Subject: [PATCH 2/9] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E8=B7=AF=E5=BE=84=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IRaCIS.Core.Application.xml | 19 +++++++++++ .../ImageAndDoc/DownloadAndUploadService.cs | 33 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml index ed3163fab..dc0ac3d68 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml @@ -707,6 +707,11 @@ + + + ExploreRecommendService + + InternationalizationService @@ -9960,6 +9965,15 @@ CommonDocumentAddOrEdit 列表查询参数模型 + + ExploreRecommendView 列表视图模型 + + + ExploreRecommendQuery 列表查询参数模型 + + + ExploreRecommendAddOrEdit 列表查询参数模型 + FrontAuditConfigView 列表视图模型 @@ -11002,6 +11016,11 @@ ICommonDocumentService + + + IExploreRecommendService + + IFrontAuditConfigService diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs index b733298cb..f8e3c2606 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs @@ -506,6 +506,39 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc } + + public async Task GetSubejectVisitPathInfo(Guid subjectVisitId) + { + 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.PatientId, + u.StudyTime, + u.StudyCode, + + SeriesList = u.SeriesList.Select(z => new + { + z.Modality, + + InstancePathList = z.DicomInstanceList.Select(k => new + { + k.Path + }) + }) + + }) + }; + + var info = query.FirstOrDefault(); + + return ResponseOutput.Ok(info); + } + /// /// 后台任务调用,前端忽略该接口 /// From 5cc41b3b0f86599bfb8fa73fb3764ed8714e51cd Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Tue, 2 Jul 2024 17:16:46 +0800 Subject: [PATCH 3/9] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E8=87=B4?= =?UTF-8?q?=E6=80=A7=E6=A0=B8=E6=9F=A5=E9=A2=9C=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_MediatR/Handlers/ConsistencyVerificationHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IRaCIS.Core.Application/_MediatR/Handlers/ConsistencyVerificationHandler.cs b/IRaCIS.Core.Application/_MediatR/Handlers/ConsistencyVerificationHandler.cs index b2cb55016..975656660 100644 --- a/IRaCIS.Core.Application/_MediatR/Handlers/ConsistencyVerificationHandler.cs +++ b/IRaCIS.Core.Application/_MediatR/Handlers/ConsistencyVerificationHandler.cs @@ -213,7 +213,7 @@ namespace IRaCIS.Core.Application.MediatR.Handlers else { //"Problems are as follows: - dialogMsg.AppendLine($"
{_localizer["ConsistencyVerification_Prob"]}
"); + dialogMsg.AppendLine($"
{_localizer["ConsistencyVerification_Prob"]}
"); num = 0; foreach (var item in dbExceptExcel) From d094f22ab21a51d262fb5d7bfaad2a0471afaede Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Tue, 2 Jul 2024 17:20:32 +0800 Subject: [PATCH 4/9] =?UTF-8?q?pacs=20=E7=9B=B4=E8=BF=9E=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/InspectionController.cs | 13 ++ .../IRaCIS.Core.Application.xml | 58 +++++++ .../Common/DTO/ExploreRecommendViewModel.cs | 16 +- .../TrialSiteUser/DTO/DicomAEViewModel.cs | 66 ++++++++ .../TrialSiteUser/DTO/TrialConfigDTO.cs | 10 ++ .../DTO/TrialSiteDicomAEViewModel.cs | 55 +++++++ .../Interface/IDicomAEService.cs | 24 +++ .../Interface/ITrialConfigService.cs | 2 +- .../Interface/ITrialSiteDicomAEService.cs | 24 +++ .../TrialSiteUser/TrialConfigService.cs | 25 ++- .../TrialSiteUser/TrialDicomAEService.cs | 149 ++++++++++++++++++ .../TrialSiteUser/TrialSiteDicomAEService.cs | 74 +++++++++ .../Service/TrialSiteUser/_MapConfig2.cs | 6 +- IRaCIS.Core.Domain/Image/SCPImageUpload.cs | 61 +++++++ IRaCIS.Core.Domain/Image/SCPInstance.cs | 53 +++++++ IRaCIS.Core.Domain/Image/SCPPatient.cs | 37 +++++ IRaCIS.Core.Domain/Image/SCPSeries.cs | 57 +++++++ IRaCIS.Core.Domain/Image/SCPStudy.cs | 95 +++++++++++ IRaCIS.Core.Domain/Image/TrialDicomAE.cs | 52 ++++++ IRaCIS.Core.Domain/Trial/Trial.cs | 2 + .../TrialSiteUser/TrialSiteDicomAE.cs | 59 +++++++ .../Context/IRaCISDBContext.cs | 12 ++ IRaCIS.Core.Test/DbHelper.ttinclude | 2 +- 23 files changed, 934 insertions(+), 18 deletions(-) create mode 100644 IRaCIS.Core.Application/Service/TrialSiteUser/DTO/DicomAEViewModel.cs create mode 100644 IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialSiteDicomAEViewModel.cs create mode 100644 IRaCIS.Core.Application/Service/TrialSiteUser/Interface/IDicomAEService.cs create mode 100644 IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialSiteDicomAEService.cs create mode 100644 IRaCIS.Core.Application/Service/TrialSiteUser/TrialDicomAEService.cs create mode 100644 IRaCIS.Core.Application/Service/TrialSiteUser/TrialSiteDicomAEService.cs create mode 100644 IRaCIS.Core.Domain/Image/SCPImageUpload.cs create mode 100644 IRaCIS.Core.Domain/Image/SCPInstance.cs create mode 100644 IRaCIS.Core.Domain/Image/SCPPatient.cs create mode 100644 IRaCIS.Core.Domain/Image/SCPSeries.cs create mode 100644 IRaCIS.Core.Domain/Image/SCPStudy.cs create mode 100644 IRaCIS.Core.Domain/Image/TrialDicomAE.cs create mode 100644 IRaCIS.Core.Domain/TrialSiteUser/TrialSiteDicomAE.cs diff --git a/IRaCIS.Core.API/Controllers/InspectionController.cs b/IRaCIS.Core.API/Controllers/InspectionController.cs index 7de33aaae..e9deab588 100644 --- a/IRaCIS.Core.API/Controllers/InspectionController.cs +++ b/IRaCIS.Core.API/Controllers/InspectionController.cs @@ -402,6 +402,19 @@ namespace IRaCIS.Core.API.Controllers return result; } + + [HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialPACSInfoConfirm")] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })] + public async Task ConfigTrialPACSInfoConfirm(DataInspectionDto opt) + { + opt.Data.IsTrialPACSConfirmed = true; + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _trialConfigService.ConfigTrialPACSInfo(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + /// /// 签名确认 /// diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml index dc0ac3d68..8232128a0 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml @@ -9813,6 +9813,24 @@ + + + DicomAEService + + + + + 获取项目dicom AE 配置信息,otherinfo里面有IsPACSConnect IsTrialPACSConfirmed + + + + + + + 测试scp server 是否可以连接 + + + 项目外部人员 录入流程相关 @@ -9825,6 +9843,11 @@ + + + TrialSiteDicomAEService + + TaskAllocationRuleView 列表视图模型 @@ -10973,6 +10996,15 @@ UserWLTemplateAddOrEdit 列表查询参数模型 + + DicomAEView 列表视图模型 + + + DicomAEQuery 列表查询参数模型 + + + DicomAEAddOrEdit 列表查询参数模型 + TrialExternalUserView 列表视图模型 @@ -10982,6 +11014,15 @@ TrialExternalUserAddOrEdit 列表查询参数模型 + + TrialSiteDicomAEView 列表视图模型 + + + TrialSiteDicomAEQuery 列表查询参数模型 + + + TrialSiteDicomAEAddOrEdit 列表查询参数模型 + TrialUserPreparation View 列表视图模型 @@ -11071,11 +11112,21 @@ IOrganInfoService + + + IDicomAEService + + ITrialExternalUserService + + + ITrialSiteDicomAEService + + EmailNoticeConfigView 列表视图模型 @@ -13059,6 +13110,13 @@ + + + 配置pacs信息 + + + + 更新项目状态 diff --git a/IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs index b87cfc9e7..382271cf1 100644 --- a/IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs +++ b/IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs @@ -9,19 +9,14 @@ using System.Collections.Generic; namespace IRaCIS.Core.Application.ViewModel { /// ExploreRecommendView 列表视图模型 - public class ExploreRecommendView + public class ExploreRecommendView: ExploreRecommendAddOrEdit { - public Guid Id { get; set; } - public string Version { get; set; } - public string Title { get; set; } + public DateTime CreateTime { get; set; } public Guid CreateUserId { get; set; } public Guid UpdateUserId { get; set; } public DateTime UpdateTime { get; set; } - public bool IsDeleted { get; set; } - public string DownloadUrl { get; set; } - public string Path { get; set; } - public string FileName { get; set; } + public DateTime? DeleteTime { get; set; } public Guid? DeleteUserId { get; set; } } @@ -51,10 +46,7 @@ namespace IRaCIS.Core.Application.ViewModel public Guid Id { get; set; } public string Version { get; set; } public string Title { get; set; } - public DateTime CreateTime { get; set; } - public Guid CreateUserId { get; set; } - public Guid UpdateUserId { get; set; } - public DateTime UpdateTime { get; set; } + public bool IsDeleted { get; set; } public string DownloadUrl { get; set; } public string Path { get; set; } diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/DicomAEViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/DicomAEViewModel.cs new file mode 100644 index 000000000..6e335b36d --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/DicomAEViewModel.cs @@ -0,0 +1,66 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-03-22 15:44:37 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +namespace IRaCIS.Core.Application.ViewModel +{ + /// DicomAEView 列表视图模型 + public class DicomAEView : DicomAEAddOrEdit + { + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + public DateTime? LatestTestTime { get; set; } + + public bool IsTestOK { get; set; } + + public bool IsPACSConnect { get; set; } + + public bool IsTrialPACSConfirmed { get; set; } + + } + + ///DicomAEQuery 列表查询参数模型 + public class DicomAEQuery : PageInput + { + public Guid? TrialId { get; set; } + + 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; } + } + + /// DicomAEAddOrEdit 列表查询参数模型 + public class DicomAEAddOrEdit + { + public Guid? Id { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + + public string CalledAE { get; set; } + public string IP { get; set; } + public int Port { get; set; } + public string Modality { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs index dc0d0ded9..2508214e5 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs @@ -313,6 +313,16 @@ namespace IRaCIS.Core.Application.Contracts } + public class TrialPACSConfig + { + [NotDefault] + public Guid TrialId { get; set; } + + public bool IsPACSConnect { get; set; } + + public bool IsTrialPACSConfirmed { get; set; } = true; + } + public class TrialStateChangeDTO { public Guid Id { get; set; } diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialSiteDicomAEViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialSiteDicomAEViewModel.cs new file mode 100644 index 000000000..94ac1e703 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialSiteDicomAEViewModel.cs @@ -0,0 +1,55 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 16:53:52 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +namespace IRaCIS.Core.Application.ViewModel +{ + /// TrialSiteDicomAEView 列表视图模型 + public class TrialSiteDicomAEView : TrialSiteDicomAEAddOrEdit + { + + public Guid UpdateUserId { get; set; } + public Guid? DeleteUserId { get; set; } + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + } + + ///TrialSiteDicomAEQuery 列表查询参数模型 + public class TrialSiteDicomAEQuery : PageInput + { + public string? CallingAE { get; set; } + + public string? IP { get; set; } + + public string? Port { get; set; } + + public string? Description { get; set; } + + } + + /// TrialSiteDicomAEAddOrEdit 列表查询参数模型 + public class TrialSiteDicomAEAddOrEdit + { + public Guid? Id { get; set; } + public Guid TrialId { get; set; } + + + public Guid TrialSiteId { get; set; } + public string CallingAE { get; set; } + public string IP { get; set; } + public string Port { get; set; } + public string Description { get; set; } + + //public bool IsDeleted { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/IDicomAEService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/IDicomAEService.cs new file mode 100644 index 000000000..71f56aeb4 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/IDicomAEService.cs @@ -0,0 +1,24 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-03-22 15:44:27 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// IDicomAEService + /// + public interface IDicomAEService + { + + Task>> GetDicomAEList(DicomAEQuery inQuery); + + Task AddOrUpdateDicomAE(DicomAEAddOrEdit addOrEditDicomAE); + + Task DeleteDicomAE(Guid dicomAEId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialConfigService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialConfigService.cs index 7a06d0fb5..1de70d035 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialConfigService.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialConfigService.cs @@ -18,7 +18,7 @@ namespace IRaCIS.Application.Interfaces Task ConfigTrialUrgentInfo(TrialUrgentConfig trialConfig); - + Task ConfigTrialPACSInfo(TrialPACSConfig trialConfig); Task TrialConfigSignatureConfirm(SignConfirmDTO signConfirmDTO); Task AsyncTrialCriterionDictionary(AsyncTrialCriterionDictionaryInDto inDto); diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialSiteDicomAEService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialSiteDicomAEService.cs new file mode 100644 index 000000000..a6c2323a6 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialSiteDicomAEService.cs @@ -0,0 +1,24 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 16:53:55 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// ITrialSiteDicomAEService + /// + public interface ITrialSiteDicomAEService + { + + Task> GetTrialSiteDicomAEList(TrialSiteDicomAEQuery inQuery); + + Task AddOrUpdateTrialSiteDicomAE(TrialSiteDicomAEAddOrEdit addOrEditTrialSiteDicomAE); + + Task DeleteTrialSiteDicomAE(Guid trialSiteDicomAEId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs index 573b0b300..4be695071 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs @@ -604,7 +604,7 @@ namespace IRaCIS.Core.Application await _readingQuestionCriterionTrialRepository.UpdatePartialFromQueryAsync(inDto.TrialReadingCriterionId, x => new ReadingQuestionCriterionTrial() { - IsImageFilter=inDto.IsImageFilter, + IsImageFilter = inDto.IsImageFilter, ImageDownloadEnum = inDto.ImageDownloadEnum, ImageUploadEnum = inDto.ImageUploadEnum, CriterionModalitys = inDto.CriterionModalitys, @@ -954,7 +954,7 @@ namespace IRaCIS.Core.Application trialInfo.UpdateTime = DateTime.Now; - //await _readingQuestionCriterionTrialRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == trialConfig.TrialId && t.IsSigned == false, u => new ReadingQuestionCriterionTrial() { CriterionModalitys = trialConfig.Modalitys }); + //await _readingQuestionCriterionTrialRepository.BatchUpdateNoTrackingAsync(t => t.TrialId == trialConfig.TrialId && t.IsSigned == false, u => new ReadingQuestionCriterionTrial() { CriterionModalitys = trialConfig.Modalitys }); return ResponseOutput.Ok(await _repository.SaveChangesAsync()); } @@ -1151,6 +1151,25 @@ namespace IRaCIS.Core.Application return ResponseOutput.Ok(await _repository.SaveChangesAsync()); } + /// + /// 配置pacs信息 + /// + /// + /// + [HttpPut] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + public async Task ConfigTrialPACSInfo(TrialPACSConfig trialConfig) + { + var trialInfo = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialConfig.TrialId)).IfNullThrowException(); + trialInfo.IsPACSConnect = trialConfig.IsPACSConnect; + trialConfig.IsTrialPACSConfirmed = trialConfig.IsTrialPACSConfirmed; + trialInfo.UpdateTime = DateTime.Now; + await _trialRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(await _repository.SaveChangesAsync()); + } + + [HttpGet("{trialId:guid}")] public async Task IfTrialCanOngoing(Guid trialId) { @@ -1318,7 +1337,7 @@ namespace IRaCIS.Core.Application public async Task>> GetTrialBodyPartList(Guid trialId) { - var list = await _trialRepository.Where(t => t.Id == trialId).SelectMany(t => t.TrialBodyPartList).Select(t => new TrialBodyPartView() { Code = t.Code, Name = _userInfo.IsEn_Us ? t.Name : t.NameCN ,Id=t.Id,IsHandAdd=t.IsHandAdd}).ToListAsync(); + var list = await _trialRepository.Where(t => t.Id == trialId).SelectMany(t => t.TrialBodyPartList).Select(t => new TrialBodyPartView() { Code = t.Code, Name = _userInfo.IsEn_Us ? t.Name : t.NameCN, Id = t.Id, IsHandAdd = t.IsHandAdd }).ToListAsync(); return ResponseOutput.Ok(list); } diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialDicomAEService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialDicomAEService.cs new file mode 100644 index 000000000..035be1e3e --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialDicomAEService.cs @@ -0,0 +1,149 @@ +//-------------------------------------------------------------------- +// 此代码由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 = "Trial")] + public class TrialDicomAEService : BaseService, IDicomAEService + { + + private readonly IRepository _dicomAERepository; + private readonly IRepository _trialRepository; + + public TrialDicomAEService(IRepository dicomAERepository, IRepository trialRepository) + { + _trialRepository = trialRepository; + _dicomAERepository = dicomAERepository; + } + + [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.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(DicomAEView.CalledAE) : inQuery.SortField, inQuery.Asc); + + + return ResponseOutput.Ok(pageList); + } + + + /// + /// 获取项目dicom AE 配置信息,otherinfo里面有IsPACSConnect IsTrialPACSConfirmed + /// + /// + /// + public async Task> GetTrialDicomAEList(Guid trialId) + { + var dicomAE = _dicomAERepository.Where(t => t.TrialId == trialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); + var trialConfig = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.IsPACSConnect, t.IsTrialPACSConfirmed }); + return ResponseOutput.Ok(dicomAE, trialConfig); + + } + + + public async Task AddOrUpdateDicomAE(DicomAEAddOrEdit addOrEditDicomAE) + { + var verifyExp1 = new EntityVerifyExp() + { + VerifyExp = u => u.IP == addOrEditDicomAE.IP && u.Port == addOrEditDicomAE.Port && u.TrialId == addOrEditDicomAE.TrialId, + + VerifyMsg = "不允许添加相同的IP和端口的记录" + }; + + //var verifyExp2 = new EntityVerifyExp() + //{ + // VerifyExp = u => u.TrialId == addOrEditDicomAE.TrialId, + + // VerifyMsg = "只允许配置一条记录", + // IsVerify=addOrEditDicomAE.Id==null + //}; + + // 在此处拷贝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/TrialSiteUser/TrialSiteDicomAEService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialSiteDicomAEService.cs new file mode 100644 index 000000000..2cfec8f8f --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialSiteDicomAEService.cs @@ -0,0 +1,74 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 16:53:58 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Service +{ + /// + /// TrialSiteDicomAEService + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class TrialSiteDicomAEService : BaseService, ITrialSiteDicomAEService + { + + private readonly IRepository _trialSiteDicomAERepository; + + public TrialSiteDicomAEService(IRepository trialSiteDicomAERepository) + { + _trialSiteDicomAERepository = trialSiteDicomAERepository; + } + + [HttpPost] + public async Task> GetTrialSiteDicomAEList(TrialSiteDicomAEQuery inQuery) + { + + var trialSiteDicomAEQueryable = + + _trialSiteDicomAERepository + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.IP), t => t.IP.Contains(inQuery.IP)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Port), t => t.Port.Contains(inQuery.Port)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Description), t => t.Description.Contains(inQuery.Description)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAE), t => t.CallingAE.Contains(inQuery.CallingAE)) + .ProjectTo(_mapper.ConfigurationProvider); + + var pageList = await trialSiteDicomAEQueryable + .ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(TrialSiteDicomAEView.Id) : inQuery.SortField, + inQuery.Asc); + + return pageList; + } + + + public async Task AddOrUpdateTrialSiteDicomAE(TrialSiteDicomAEAddOrEdit addOrEditTrialSiteDicomAE) + { + var verifyExp1 = new EntityVerifyExp() + { + VerifyExp = u => u.IP == addOrEditTrialSiteDicomAE.IP && u.Port == addOrEditTrialSiteDicomAE.Port &&u.CallingAE==addOrEditTrialSiteDicomAE.CallingAE && u.TrialId == addOrEditTrialSiteDicomAE.TrialId, + + VerifyMsg = "不允许添加相同的IP和端口的记录" + }; + + + var entity = await _trialSiteDicomAERepository.InsertOrUpdateAsync(addOrEditTrialSiteDicomAE, true, verifyExp1); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + + [HttpDelete("{trialSiteDicomAEId:guid}")] + public async Task DeleteTrialSiteDicomAE(Guid trialSiteDicomAEId) + { + var success = await _trialSiteDicomAERepository.DeleteFromQueryAsync(t => t.Id == trialSiteDicomAEId, true); + return ResponseOutput.Ok(); + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig2.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig2.cs index b608fb229..96041148e 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig2.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig2.cs @@ -45,7 +45,11 @@ namespace IRaCIS.Core.Application.Service CreateMap(); - + + CreateMap(); + CreateMap().ReverseMap(); + + } } diff --git a/IRaCIS.Core.Domain/Image/SCPImageUpload.cs b/IRaCIS.Core.Domain/Image/SCPImageUpload.cs new file mode 100644 index 000000000..a2982fba0 --- /dev/null +++ b/IRaCIS.Core.Domain/Image/SCPImageUpload.cs @@ -0,0 +1,61 @@ + +//-------------------------------------------------------------------- +// 此代码由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; +using System.Collections.Generic; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///SCPImageUpload + /// + [Table("SCPImageUpload")] + public class SCPImageUpload : Entity, IAuditAdd + { + + + + [Required] + public DateTime CreateTime { get; set; } + + + [Required] + public Guid CreateUserId { get; set; } + + + [Required] + public string CallingAE { get; set; }=string.Empty; + + [Required] + public string CalledAE { get; set; } = string.Empty; + + + [Required] + public string CallingAEIP { get; set; } = string.Empty; + + + [Required] + public DateTime StartTime { get; set; } + + [Required] + public DateTime EndTime { get; set; } + + + [Required] + public int FileCount { get; set; } + + + [Required] + public long FileSize { get; set; } + + + public int StudyCount { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/Image/SCPInstance.cs b/IRaCIS.Core.Domain/Image/SCPInstance.cs new file mode 100644 index 000000000..f0eb643f6 --- /dev/null +++ b/IRaCIS.Core.Domain/Image/SCPInstance.cs @@ -0,0 +1,53 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("SCPInstance")] + public class SCPInstance : Entity, IAuditAdd, IAuditUpdate + { + [JsonIgnore] + [ForeignKey("SeriesId")] + public SCPSeries SCPSeries { get; set; } + + [JsonIgnore] + [ForeignKey("StudyId")] + public SCPStudy SCPStudy { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + + public Guid SeqId { get; set; } + public Guid StudyId { get; set; } + public Guid SeriesId { get; set; } + public string StudyInstanceUid { get; set; } + public string SeriesInstanceUid { get; set; } + public string SopInstanceUid { get; set; } + public int InstanceNumber { get; set; } + public DateTime? InstanceTime { get; set; } + public bool CPIStatus { get; set; } + public int ImageRows { get; set; } + public int ImageColumns { get; set; } + public int SliceLocation { get; set; } + + + public string SliceThickness { get; set; } + public int NumberOfFrames { get; set; } + public string PixelSpacing { get; set; } + + public string ImagerPixelSpacing { get; set; } + public string FrameOfReferenceUID { get; set; } + public string WindowCenter { get; set; } + public string WindowWidth { get; set; } + + public bool Anonymize { get; set; } + public string Path { get; set; } = string.Empty; + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + + + } +} diff --git a/IRaCIS.Core.Domain/Image/SCPPatient.cs b/IRaCIS.Core.Domain/Image/SCPPatient.cs new file mode 100644 index 000000000..21eda7f87 --- /dev/null +++ b/IRaCIS.Core.Domain/Image/SCPPatient.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("SCPPatient")] + public class SCPPatient : Entity, IAuditUpdate, IAuditAdd + { + public List SCPStudyList { 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 Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + + + public DateTime? EarliestStudyTime { get; set; } + + public DateTime? LatestStudyTime { get; set; } + + public DateTime LatestPushTime { get; set; } + + + [JsonIgnore] + public Subject Subject { get; set; } + public Guid? SubjectId { get; set; } + + } +} diff --git a/IRaCIS.Core.Domain/Image/SCPSeries.cs b/IRaCIS.Core.Domain/Image/SCPSeries.cs new file mode 100644 index 000000000..5c2213c20 --- /dev/null +++ b/IRaCIS.Core.Domain/Image/SCPSeries.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("SCPSeries")] + public class SCPSeries : Entity, IAuditAdd, IAuditUpdate, ISoftDelete + { + [JsonIgnore] + [ForeignKey("StudyId")] + public SCPStudy SCPStudy { get; set; } + + [JsonIgnore] + public List SCPInstanceList { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid SeqId { get; set; } + public Guid StudyId { get; set; } + public string StudyInstanceUid { get; set; } + public string SeriesInstanceUid { get; set; } + public int SeriesNumber { get; set; } + public DateTime? SeriesTime { get; set; } + public string Modality { get; set; } + public string Description { get; set; } + public int InstanceCount { get; set; } + public string SliceThickness { get; set; } + + public string ImagePositionPatient { get; set; } + public string ImageOrientationPatient { get; set; } + public string BodyPartExamined { get; set; } + public string SequenceName { get; set; } + public string ProtocolName { get; set; } + public string ImagerPixelSpacing { get; set; } + + public string AcquisitionTime { get; set; } = string.Empty; + public string AcquisitionNumber { get; set; } = string.Empty; + public string TriggerTime { get; set; } = string.Empty; + + public string BodyPartForEdit { get; set; } = string.Empty; + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + public bool IsDeleted {get;set;} + public bool IsReading { get; set; } = true; + + public string ImageResizePath { get; set; }=string.Empty; + + } +} diff --git a/IRaCIS.Core.Domain/Image/SCPStudy.cs b/IRaCIS.Core.Domain/Image/SCPStudy.cs new file mode 100644 index 000000000..c78222617 --- /dev/null +++ b/IRaCIS.Core.Domain/Image/SCPStudy.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("SCPStudy")] + public class SCPStudy : Entity, IAuditUpdate, IAuditAdd, ISoftDelete + { + + + + + + //0 未知 1 单重 2 双重 + public bool IsDoubleReview { get; set; } + + [JsonIgnore] + public List InstanceList { get; set; } + + [JsonIgnore] + public List SeriesList { get; set; } + + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid SeqId { get; set; } + + [ForeignKey("PatientId")] + [JsonIgnore] + public SCPPatient Patient { get; set; } + public Guid PatientId { get; set; } + + + + + public string StudyInstanceUid { get; set; } = string.Empty; + 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 InstitutionName { 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 StudyId { get; set; } = string.Empty; + public string AccessionNumber { get; set; } = string.Empty; + public string PatientBirthDate { get; set; } = string.Empty; + public string AcquisitionTime { get; set; } = string.Empty; + public string AcquisitionNumber { get; set; } = string.Empty; + public string TriggerTime { get; set; } = string.Empty; + + public string BodyPartExamined { get; set; } = string.Empty; + + public string BodyPartForEdit { get; set; } = string.Empty; + + public string ModalityForEdit { get; set; } = string.Empty; + + + + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + + + //软删除 + public bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + public string CallingAE { get; set; } = string.Empty; + + public string CalledAE { get; set; } = string.Empty; + + public bool IsUploadFinished { get; set; } + + + + + public Guid? SubjectVisitId { get; set; } + + [JsonIgnore] + public SubjectVisit SubjectVisit { get; set; } + + } +} diff --git a/IRaCIS.Core.Domain/Image/TrialDicomAE.cs b/IRaCIS.Core.Domain/Image/TrialDicomAE.cs new file mode 100644 index 000000000..0d91a9c77 --- /dev/null +++ b/IRaCIS.Core.Domain/Image/TrialDicomAE.cs @@ -0,0 +1,52 @@ + +//-------------------------------------------------------------------- +// 此代码由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 +{ + /// + ///DicomAE + /// + [Table("TrialDicomAE")] + public class TrialDicomAE : Entity, IAuditUpdate, IAuditAdd + { + + public Guid TrialId { get; set; } + + public DateTime CreateTime { get; set; } + + + public Guid CreateUserId { get; set; } + + + public Guid UpdateUserId { get; set; } + + + public DateTime UpdateTime { get; set; } + + public string CalledAE { get; set; } = string.Empty; + + public string IP { get; set; } + + + public int Port { get; set; } + + + public string Modality { get; set; } = string.Empty; + + + public string Description { get; set; }=string.Empty; + + + public DateTime? LatestTestTime { get; set; } + + public bool IsTestOK { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Trial/Trial.cs b/IRaCIS.Core.Domain/Trial/Trial.cs index e63ca4315..d00d43d55 100644 --- a/IRaCIS.Core.Domain/Trial/Trial.cs +++ b/IRaCIS.Core.Domain/Trial/Trial.cs @@ -401,7 +401,9 @@ namespace IRaCIS.Core.Domain.Models #endregion + public bool IsPACSConnect { get; set; } + public bool IsTrialPACSConfirmed { get; set; } ///// ///// 图像是否有标注 diff --git a/IRaCIS.Core.Domain/TrialSiteUser/TrialSiteDicomAE.cs b/IRaCIS.Core.Domain/TrialSiteUser/TrialSiteDicomAE.cs new file mode 100644 index 000000000..a23e4d5b6 --- /dev/null +++ b/IRaCIS.Core.Domain/TrialSiteUser/TrialSiteDicomAE.cs @@ -0,0 +1,59 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 16:53:49 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialSiteDicomAE + /// + [Table("TrialSiteDicomAE")] + public class TrialSiteDicomAE : Entity, IAuditUpdate, IAuditAdd + { + + + + public DateTime? DeletedTime { get; set; } + + + public Guid TrialId { get; set; } + + + public Guid UpdateUserId { get; set; } + + + public Guid? DeleteUserId { get; set; } + + + public DateTime CreateTime { get; set; } + + + public Guid CreateUserId { get; set; } + + public bool IsDeleted { get; set; } + + public DateTime UpdateTime { get; set; } + + + public Guid TrialSiteId { get; set; } + + + public string CallingAE { get; set; } + + + public string IP { get; set; } + + + public string Port { get; set; } + + + public string Description { get; set; } + + } + +} diff --git a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs index c5e8715c7..f4dcffdd9 100644 --- a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs +++ b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs @@ -483,6 +483,18 @@ namespace IRaCIS.Core.Infra.EFCore public virtual DbSet ExploreRecommend { get; set; } + public virtual DbSet SCPPatient { get; set; } + public virtual DbSet SCPStudy { get; set; } + public virtual DbSet SCPSeries { get; set; } + public virtual DbSet SCPInstance { get; set; } + public virtual DbSet TrialDicomAE { get; set; } + + + public virtual DbSet TrialSiteDicomAE { get; set; } + + + public virtual DbSet SCPImageUpload { get; set; } + public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) { diff --git a/IRaCIS.Core.Test/DbHelper.ttinclude b/IRaCIS.Core.Test/DbHelper.ttinclude index 649ef03e0..8790fcb50 100644 --- a/IRaCIS.Core.Test/DbHelper.ttinclude +++ b/IRaCIS.Core.Test/DbHelper.ttinclude @@ -4,7 +4,7 @@ public static readonly string ConnectionString = "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true"; public static readonly string DbDatabase = "Test_IRC"; //表名称用字符串,拼接 - public static readonly string TableName = "ExploreRecommend"; + public static readonly string TableName = "TrialSiteDicomAE"; //具体文件里面 例如service 可以配置是否分页 } #> From 9f66a932fcdde4d6fd8cded40988b3c4335bbd00 Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Wed, 3 Jul 2024 14:00:29 +0800 Subject: [PATCH 5/9] =?UTF-8?q?=E5=A2=9E=E5=8A=A0pacs=20=E7=9B=B4=E8=BF=9E?= =?UTF-8?q?=20=20scp=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BusinessFilter/ModelActionFilter .cs | 37 + IRC.Core.SCP/BusinessFilter/ModelBinding.cs | 28 + .../BusinessFilter/ProjectExceptionFilter.cs | 61 + .../BusinessFilter/UnifiedApiResultFilter.cs | 120 ++ IRC.Core.SCP/Helper/ImageHelper.cs | 62 + IRC.Core.SCP/HostConfig/AutofacModuleSetup.cs | 57 + IRC.Core.SCP/HostConfig/EFSetup.cs | 44 + .../HostConfig/NewtonsoftJsonSetup.cs | 59 + .../HostConfig/NullToEmptyStringResolver.cs | 36 + .../NullToEmptyStringValueProvider.cs | 42 + IRC.Core.SCP/IRC.Core.SCP.csproj | 38 + IRC.Core.SCP/Program.cs | 208 +++ IRC.Core.SCP/Properties/launchSettings.json | 31 + IRC.Core.SCP/Service/BaseService.cs | 118 ++ IRC.Core.SCP/Service/CStoreSCPService.cs | 368 +++++ IRC.Core.SCP/Service/DicomArchiveService.cs | 333 +++++ .../Service/Interface/IDicomArchiveService.cs | 11 + IRC.Core.SCP/Service/OSSService.cs | 427 ++++++ IRC.Core.SCP/appsettings.IRC_Prod_SCP.json | 63 + IRC.Core.SCP/appsettings.IRC_Test_SCP.json | 79 + IRC.Core.SCP/appsettings.IRC_US_SCP.json | 76 + IRC.Core.SCP/appsettings.IRC_Uat_SCP.json | 76 + IRC.Core.SCP/appsettings.json | 9 + IRaCIS.Core.API.sln | 6 + IRaCIS.Core.API/IRaCIS.Core.API.csproj | 9 +- .../Service/Common/ExploreRecommendService.cs | 2 +- .../Service/Visit/DTO/PatientViewModel.cs | 1083 ++++++++++++++ .../Service/Visit/PatientService.cs | 1323 +++++++++++++++++ .../IRaCIS.Core.Infrastructure.csproj | 10 +- 29 files changed, 4805 insertions(+), 11 deletions(-) create mode 100644 IRC.Core.SCP/BusinessFilter/ModelActionFilter .cs create mode 100644 IRC.Core.SCP/BusinessFilter/ModelBinding.cs create mode 100644 IRC.Core.SCP/BusinessFilter/ProjectExceptionFilter.cs create mode 100644 IRC.Core.SCP/BusinessFilter/UnifiedApiResultFilter.cs create mode 100644 IRC.Core.SCP/Helper/ImageHelper.cs create mode 100644 IRC.Core.SCP/HostConfig/AutofacModuleSetup.cs create mode 100644 IRC.Core.SCP/HostConfig/EFSetup.cs create mode 100644 IRC.Core.SCP/HostConfig/NewtonsoftJsonSetup.cs create mode 100644 IRC.Core.SCP/HostConfig/NullToEmptyStringResolver.cs create mode 100644 IRC.Core.SCP/HostConfig/NullToEmptyStringValueProvider.cs create mode 100644 IRC.Core.SCP/IRC.Core.SCP.csproj create mode 100644 IRC.Core.SCP/Program.cs create mode 100644 IRC.Core.SCP/Properties/launchSettings.json create mode 100644 IRC.Core.SCP/Service/BaseService.cs create mode 100644 IRC.Core.SCP/Service/CStoreSCPService.cs create mode 100644 IRC.Core.SCP/Service/DicomArchiveService.cs create mode 100644 IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs create mode 100644 IRC.Core.SCP/Service/OSSService.cs create mode 100644 IRC.Core.SCP/appsettings.IRC_Prod_SCP.json create mode 100644 IRC.Core.SCP/appsettings.IRC_Test_SCP.json create mode 100644 IRC.Core.SCP/appsettings.IRC_US_SCP.json create mode 100644 IRC.Core.SCP/appsettings.IRC_Uat_SCP.json create mode 100644 IRC.Core.SCP/appsettings.json create mode 100644 IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs create mode 100644 IRaCIS.Core.Application/Service/Visit/PatientService.cs diff --git a/IRC.Core.SCP/BusinessFilter/ModelActionFilter .cs b/IRC.Core.SCP/BusinessFilter/ModelActionFilter .cs new file mode 100644 index 000000000..2f0ec4873 --- /dev/null +++ b/IRC.Core.SCP/BusinessFilter/ModelActionFilter .cs @@ -0,0 +1,37 @@ +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Localization; +using Newtonsoft.Json; + + +namespace IRaCIS.Core.SCP.Filter +{ + + + public class ModelActionFilter : ActionFilterAttribute, IActionFilter + { + public IStringLocalizer _localizer; + public ModelActionFilter(IStringLocalizer localizer) + { + _localizer = localizer; + } + + public override void OnActionExecuting(ActionExecutingContext context) + { + if (!context.ModelState.IsValid) + { + + var validationErrors = context.ModelState + .Keys + .SelectMany(k => context.ModelState[k]!.Errors) + .Select(e => e.ErrorMessage) + .ToArray(); + + //---提供给接口的参数无效。 + context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ModelAction_InvalidAPIParameter"] + JsonConvert.SerializeObject( validationErrors))); + } + } + } + +} diff --git a/IRC.Core.SCP/BusinessFilter/ModelBinding.cs b/IRC.Core.SCP/BusinessFilter/ModelBinding.cs new file mode 100644 index 000000000..56e18b418 --- /dev/null +++ b/IRC.Core.SCP/BusinessFilter/ModelBinding.cs @@ -0,0 +1,28 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace IRaCIS.Core.SCP.Filter +{ + #region snippet_DisableFormValueModelBindingAttribute + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter + { + public void OnResourceExecuting(ResourceExecutingContext context) + { + + + var factories = context.ValueProviderFactories; + //factories.RemoveType(); + factories.RemoveType(); + factories.RemoveType(); + context.HttpContext.Request.EnableBuffering(); + } + + public void OnResourceExecuted(ResourceExecutedContext context) + { + } + } + #endregion +} diff --git a/IRC.Core.SCP/BusinessFilter/ProjectExceptionFilter.cs b/IRC.Core.SCP/BusinessFilter/ProjectExceptionFilter.cs new file mode 100644 index 000000000..0952bc457 --- /dev/null +++ b/IRC.Core.SCP/BusinessFilter/ProjectExceptionFilter.cs @@ -0,0 +1,61 @@ +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Logging; + +namespace IRaCIS.Core.SCP.Filter +{ + public class ProjectExceptionFilter : Attribute, IExceptionFilter + { + private readonly ILogger _logger; + + public IStringLocalizer _localizer; + + public ProjectExceptionFilter(IStringLocalizer localizer, ILogger logger) + { + _logger = logger; + _localizer = localizer; + } + public void OnException(ExceptionContext context) + { + //context.ExceptionHandled;//记录当前这个异常是否已经被处理过了 + + if (!context.ExceptionHandled) + { + if (context.Exception.GetType().Name == "DbUpdateConcurrencyException") + { + //---并发更新,当前不允许该操作 + context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + context.Exception.Message)); + } + + if (context.Exception.GetType() == typeof(BusinessValidationFailedException)) + { + var error = context.Exception as BusinessValidationFailedException; + + context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, error!.Code)); + } + else if(context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException)) + { + context.Result = new JsonResult(ResponseOutput.NotOk( context.Exception.Message, ApiResponseCodeEnum.DataNotExist)); + } + else + { + context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (context.Exception.InnerException is null ? (context.Exception.Message /*+ context.Exception.StackTrace*/) + : (context.Exception.InnerException?.Message /*+ context.Exception.InnerException?.StackTrace*/)), ApiResponseCodeEnum.ProgramException)); + } + + + _logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message + context.Exception.StackTrace) : (context.Exception.InnerException?.Message + context.Exception.InnerException?.StackTrace)); + + + } + else + { + //继续 + } + context.ExceptionHandled = true;//标记当前异常已经被处理过了 + } + } +} diff --git a/IRC.Core.SCP/BusinessFilter/UnifiedApiResultFilter.cs b/IRC.Core.SCP/BusinessFilter/UnifiedApiResultFilter.cs new file mode 100644 index 000000000..7609ae4bc --- /dev/null +++ b/IRC.Core.SCP/BusinessFilter/UnifiedApiResultFilter.cs @@ -0,0 +1,120 @@ +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Services.BusinessFilter +{ + /// + /// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码 + /// by zhouhang 2021.09.12 周末 + /// + public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter + { + /// + /// 异步版本 + /// + /// + /// + /// + public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + + if (context.Result is ObjectResult objectResult) + { + var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode; + + //是200 并且没有包装 那么包装结果 + if (statusCode == 200 && !(objectResult.Value is IResponseOutput)) + { + //if (objectResult.Value == null) + //{ + // var apiResponse = ResponseOutput.DBNotExist(); + + // objectResult.Value = apiResponse; + // objectResult.DeclaredType = apiResponse.GetType(); + //} + //else + //{ + + var type = objectResult.Value?.GetType(); + + + if ( type!=null&& type.IsGenericType&&(type.GetGenericTypeDefinition()==typeof(ValueTuple<,>)|| type.GetGenericTypeDefinition()==typeof(Tuple<,>))) + { + + //报错 + //var tuple = (object, object))objectResult.Value; + + //var (val1, val2) = ((dynamic, dynamic))objectResult.Value; + //var apiResponse = ResponseOutput.Ok(val1, val2); + + //OK + var tuple = (dynamic)objectResult.Value; + var apiResponse = ResponseOutput.Ok(tuple.Item1, tuple.Item2); + + + objectResult.Value = apiResponse; + objectResult.DeclaredType = apiResponse.GetType(); + } + else + { + var apiResponse = ResponseOutput.Ok(objectResult.Value); + + objectResult.Value = apiResponse; + objectResult.DeclaredType = apiResponse.GetType(); + } + + + //} + + } + //如果不是200 是IResponseOutput 不处理 + else if (statusCode != 200 && (objectResult.Value is IResponseOutput)) + { + } + + else if(statusCode != 200&&!(objectResult.Value is IResponseOutput)) + { + //---程序错误,请联系开发人员。 + var apiResponse = ResponseOutput.NotOk(StaticData.International("UnifiedAPI_ProgramError")); + + objectResult.Value = apiResponse; + objectResult.DeclaredType = apiResponse.GetType(); + } + + } + + await next.Invoke(); + + } + + public static bool IsTupleType(Type type, bool checkBaseTypes = false) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + + if (type == typeof(Tuple)) + return true; + + while (type != null) + { + if (type.IsGenericType) + { + var genType = type.GetGenericTypeDefinition(); + if (genType == typeof(Tuple<>) + || genType == typeof(Tuple<,>) + || genType == typeof(Tuple<,>)) + return true; + } + + if (!checkBaseTypes) + break; + + type = type.BaseType; + } + + return false; + } + } +} diff --git a/IRC.Core.SCP/Helper/ImageHelper.cs b/IRC.Core.SCP/Helper/ImageHelper.cs new file mode 100644 index 000000000..50f0ab4f6 --- /dev/null +++ b/IRC.Core.SCP/Helper/ImageHelper.cs @@ -0,0 +1,62 @@ + +using FellowOakDicom.Imaging; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; +using System.Security.Cryptography; +using System.Text; + +namespace IRaCIS.Core.Application.Helper; + +public static class ImageHelper +{ + + // 采用ImageSharp组件 跨平台,不依赖windows 平台图像处理组件,这里大坑(net 6 下,之前由于Dicom处理最新组件 依赖了这个库的一个旧版本,导致缩略图bug,单独升级依赖组件才解决) + public static void ResizeSave(string filePath, string? fileStorePath) + { + + fileStorePath = fileStorePath ?? filePath + ".preview.jpeg"; + + using (var image = SixLabors.ImageSharp.Image.Load(filePath)) + { + + image.Mutate(x => x.Resize(500, 500)); + + image.Save(fileStorePath); + + } + } + + + + public static Stream RenderPreviewJpeg(string filePath) + { + string jpegPath = filePath + ".preview.jpg"; + + if (!File.Exists(jpegPath)) + { + using (Stream stream = new FileStream(jpegPath, FileMode.Create)) + { + DicomImage image = new DicomImage(filePath); + + var sharpimage = image.RenderImage().AsSharpImage(); + + sharpimage.Save(stream, new JpegEncoder()); + + } + } + + return new FileStream(jpegPath, FileMode.Open); + } + + public static void RemovePreviewJpeg(string filePath) + { + string jpegPath = filePath + ".preview.jpg"; + if (File.Exists(jpegPath)) File.Delete(jpegPath); + } + + +} + + + diff --git a/IRC.Core.SCP/HostConfig/AutofacModuleSetup.cs b/IRC.Core.SCP/HostConfig/AutofacModuleSetup.cs new file mode 100644 index 000000000..183eb235b --- /dev/null +++ b/IRC.Core.SCP/HostConfig/AutofacModuleSetup.cs @@ -0,0 +1,57 @@ +using Autofac; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Http; +using Panda.DynamicWebApi; +using System; +using System.Linq; +using System.Reflection; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Application.Services; +using AutoMapper; +using IRaCIS.Core.SCP.Service; + +namespace IRaCIS.Core.SCP +{ + // ReSharper disable once IdentifierTypo + public class AutofacModuleSetup : Autofac.Module + { + protected override void Load(ContainerBuilder containerBuilder) + { + + #region byzhouhang 20210917 此处注册泛型仓储 可以减少Domain层 和Infra.EFcore 两层 空的仓储接口定义和 仓储文件定义 + + containerBuilder.RegisterGeneric(typeof(Repository<>)) + .As(typeof(IRepository<>)).InstancePerLifetimeScope();//注册泛型仓储 + + containerBuilder.RegisterType().As().InstancePerLifetimeScope(); + + + #endregion + + #region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html + + //获取所有控制器类型并使用属性注入 + containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly) + .Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type)) + .PropertiesAutowired(); + + #endregion + + + + Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "IRaCIS.Core.SCP.dll"); + containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service")) + .PropertiesAutowired().AsImplementedInterfaces(); + + + containerBuilder.RegisterType().As().SingleInstance(); + containerBuilder.RegisterType().As().InstancePerLifetimeScope(); + + + + + + } + } +} \ No newline at end of file diff --git a/IRC.Core.SCP/HostConfig/EFSetup.cs b/IRC.Core.SCP/HostConfig/EFSetup.cs new file mode 100644 index 000000000..b53095cbd --- /dev/null +++ b/IRC.Core.SCP/HostConfig/EFSetup.cs @@ -0,0 +1,44 @@ +using IRaCIS.Core.Infra.EFCore; +using Medallion.Threading; +using Medallion.Threading.SqlServer; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace IRaCIS.Core.SCP +{ + public static class EFSetup + { + public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration) + { + //services.AddScoped(); + + //这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext + //Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点 + services.AddDbContext(options => + { + options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, + contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure()); + + options.EnableSensitiveDataLogging(); + + options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor()); + + options.UseProjectables(); + + + + }); + + //注意区分 easy caching 也有 IDistributedLockProvider + services.AddSingleton(sp => + { + //var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!); + + return new SqlDistributedSynchronizationProvider(configuration.GetSection("ConnectionStrings:RemoteNew").Value); + }); + + //services.AddAssemblyTriggers(typeof(SubjectVisitImageDateTrigger).Assembly); + } + } +} diff --git a/IRC.Core.SCP/HostConfig/NewtonsoftJsonSetup.cs b/IRC.Core.SCP/HostConfig/NewtonsoftJsonSetup.cs new file mode 100644 index 000000000..00d5ae329 --- /dev/null +++ b/IRC.Core.SCP/HostConfig/NewtonsoftJsonSetup.cs @@ -0,0 +1,59 @@ + + +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; + +namespace IRaCIS.Core.SCP +{ + public static class NewtonsoftJsonSetup + { + public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services) + { + services.AddHttpContextAccessor(); + services.AddScoped(); + + builder.AddNewtonsoftJson(options => + { + //options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects; + // 忽略循环引用 + options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + //options.SerializerSettings.TypeNameHandling = TypeNameHandling.All; + + //处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0 + options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver(); //new DefaultContractResolver();// new NullToEmptyStringResolver(); + // 设置时间格式 + options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + + options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind; + + //options.SerializerSettings.Converters.Add(new JSONCustomDateConverter()) ; + + //options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService()); + + + + }) + .AddControllersAsServices()//动态webApi属性注入需要 + .ConfigureApiBehaviorOptions(o => + { + o.SuppressModelStateInvalidFilter = true; //自己写验证 + + }); + + + Newtonsoft.Json.JsonSerializerSettings setting = new Newtonsoft.Json.JsonSerializerSettings(); + JsonConvert.DefaultSettings = new Func(() => + { + //日期类型默认格式化处理 + setting.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + setting.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + + return setting; + }); + + + } + } +} diff --git a/IRC.Core.SCP/HostConfig/NullToEmptyStringResolver.cs b/IRC.Core.SCP/HostConfig/NullToEmptyStringResolver.cs new file mode 100644 index 000000000..f4bd469a6 --- /dev/null +++ b/IRC.Core.SCP/HostConfig/NullToEmptyStringResolver.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace IRaCIS.Core.SCP +{ + public class NullToEmptyStringResolver : DefaultContractResolver + { + + + protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) + { + IList properties = base.CreateProperties(type, memberSerialization); + + var list= type.GetProperties() + .Select(p => + { + var jp = base.CreateProperty(p, memberSerialization); + jp.ValueProvider = new NullToEmptyStringValueProvider(p); + return jp; + }).ToList(); + + var uu = list.Select(t => t.PropertyName).ToList(); + + //获取复杂对象属性 + properties = properties.TakeWhile(t => !uu.Contains(t.PropertyName)).ToList(); + + list.AddRange(properties); + return list; + } + + + } +} diff --git a/IRC.Core.SCP/HostConfig/NullToEmptyStringValueProvider.cs b/IRC.Core.SCP/HostConfig/NullToEmptyStringValueProvider.cs new file mode 100644 index 000000000..10e7e613d --- /dev/null +++ b/IRC.Core.SCP/HostConfig/NullToEmptyStringValueProvider.cs @@ -0,0 +1,42 @@ +using System; +using System.Reflection; +using Newtonsoft.Json.Serialization; + +namespace IRaCIS.Core.SCP +{ + + public class NullToEmptyStringValueProvider : IValueProvider + { + PropertyInfo _MemberInfo; + public NullToEmptyStringValueProvider(PropertyInfo memberInfo) + { + _MemberInfo = memberInfo; + } + public object GetValue(object target) + { + object result = _MemberInfo.GetValue(target); + if (_MemberInfo.PropertyType == typeof(string) && result == null) result = ""; + else if (_MemberInfo.PropertyType == typeof(String[]) && result == null) result = new string[] { }; + //else if (_MemberInfo.PropertyType == typeof(Nullable) && result == null) result = 0; + else if (_MemberInfo.PropertyType == typeof(Nullable) && result == null) result = 0.00M; + + return result; + } + public void SetValue(object target, object value) + { + + if(_MemberInfo.PropertyType == typeof(string)) + { + //去掉前后空格 + _MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value.ToString().Trim()); + + } + else + { + _MemberInfo.SetValue(target, value); + } + + } + } + +} diff --git a/IRC.Core.SCP/IRC.Core.SCP.csproj b/IRC.Core.SCP/IRC.Core.SCP.csproj new file mode 100644 index 000000000..9407aa2ba --- /dev/null +++ b/IRC.Core.SCP/IRC.Core.SCP.csproj @@ -0,0 +1,38 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + diff --git a/IRC.Core.SCP/Program.cs b/IRC.Core.SCP/Program.cs new file mode 100644 index 000000000..5ad53894d --- /dev/null +++ b/IRC.Core.SCP/Program.cs @@ -0,0 +1,208 @@ + +using Autofac; +using Autofac.Extensions.DependencyInjection; +using AutoMapper.EquivalencyExpression; +using FellowOakDicom; +using FellowOakDicom.Imaging; +using FellowOakDicom.Imaging.NativeCodec; +using FellowOakDicom.Network; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.SCP; +using IRaCIS.Core.SCP.Filter; +using IRaCIS.Core.SCP.Service; +using MassTransit; +using MassTransit.NewIdProviders; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.Extensions.DependencyInjection; +using Panda.DynamicWebApi; +using Serilog; +using Serilog.Events; +using System.Runtime.InteropServices; + + +//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数) +var config = new ConfigurationBuilder() + .AddEnvironmentVariables() + .Build(); + +var enviromentName = config["ASPNETCORE_ENVIRONMENT"]; + +var builder = WebApplication.CreateBuilder(new WebApplicationOptions +{ + EnvironmentName = enviromentName +}); + +#region 兼容windows 服务命令行的方式 + +int urlsIndex = Array.FindIndex(args, arg => arg != null && arg.StartsWith("--port")); + +if (urlsIndex > -1) +{ + var port = args[urlsIndex].Substring("--port=".Length); + Console.WriteLine(port); + builder.WebHost.UseUrls($"http://0.0.0.0:{port}"); +} + +#endregion + +#region 主机配置 + +NewId.SetProcessIdProvider(new CurrentProcessIdProvider()); + +builder.Configuration.AddJsonFile("appsettings.json", false, true) + .AddJsonFile($"appsettings.{enviromentName}.json", false, true); +builder.Host + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .ConfigureContainer(containerBuilder => + { + containerBuilder.RegisterModule(); + }) + .UseSerilog(); +#endregion + +#region 配置服务 +var _configuration = builder.Configuration; + +//健康检查 +builder.Services.AddHealthChecks(); + +//本地化 +builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources"); + + +// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim() +builder.Services.AddControllers(options => +{ + //options.Filters.Add(); + options.Filters.Add(); + options.Filters.Add(); + options.Filters.Add(); + + +}) + .AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理 + + +builder.Services.AddOptions().Configure(_configuration.GetSection("AliyunOSS")); +builder.Services.AddOptions().Configure(_configuration.GetSection("ObjectStoreService")); +builder.Services.AddOptions().Configure(_configuration.GetSection("DicomSCPServiceConfig")); + + +//动态WebApi + UnifiedApiResultFilter 省掉控制器代码 +//动态webApi 目前存在的唯一小坑是生成api上服务上的动态代理AOP失效 间接掉用不影响 +builder.Services + .AddDynamicWebApi(dynamicWebApiOption => + { + //默认是 api + dynamicWebApiOption.DefaultApiPrefix = ""; + //首字母小写 + dynamicWebApiOption.GetRestFulActionName = (actionName) => char.ToLower(actionName[0]) + actionName.Substring(1); + //删除 Service后缀 + dynamicWebApiOption.RemoveControllerPostfixes.Add("Service"); + + }); + +//AutoMapper +builder.Services.AddAutoMapper(automapper => +{ + + automapper.AddCollectionMappers(); + + +}, typeof(BaseService).Assembly); + +//EF ORM QueryWithNoLock +builder.Services.AddEFSetup(_configuration); + + +//转发头设置 获取真实IP +builder.Services.Configure(options => +{ + options.ForwardedHeaders = + ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; +}); + +//Dicom影像渲染图片 跨平台 +//builder.Services.AddDicomSetup(); +new DicomSetupBuilder() + .RegisterServices(s => + s.AddFellowOakDicom() + .AddTranscoderManager() + //.AddTranscoderManager() + .AddImageManager()) + .SkipValidation() + .Build(); + + + +#endregion + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +//if (app.Environment.IsDevelopment()) +//{ +app.UseSwagger(); +app.UseSwaggerUI(); +//} + +app.UseAuthorization(); + +app.MapControllers(); + +#region 日志 + +Log.Logger = new LoggerConfiguration() + .MinimumLevel.Information() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + // Filter out ASP.NET Core infrastructre logs that are Information and below 日志太多了 一个请求 记录好几条 + .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) + .MinimumLevel.Override("Hangfire", LogEventLevel.Warning) + .MinimumLevel.Override("System.Net.Http.HttpClient.HttpReports", LogEventLevel.Warning) + .Enrich.WithClientIp() + + .Enrich.FromLogContext() + + //控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型 + .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning, + outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}") + + .WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day, + outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}") + .CreateLogger(); + + +#endregion + + +#region 运行环境 部署平台 + +Log.Logger.Warning($"当前环境:{enviromentName}"); + +if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) +{ + Log.Logger.Warning($"当前部署平台环境:windows"); +} +else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) +{ + Log.Logger.Warning($"当前部署平台环境:linux"); +} +else +{ + Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD"); +} + +#endregion + + +var server = DicomServerFactory.Create(_configuration.GetSection("DicomSCPServiceConfig").GetValue("ServerPort"), userState: app.Services); + + +app.Run(); diff --git a/IRC.Core.SCP/Properties/launchSettings.json b/IRC.Core.SCP/Properties/launchSettings.json new file mode 100644 index 000000000..ec782f999 --- /dev/null +++ b/IRC.Core.SCP/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:36358", + "sslPort": 0 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5243", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/IRC.Core.SCP/Service/BaseService.cs b/IRC.Core.SCP/Service/BaseService.cs new file mode 100644 index 000000000..a10e9bf9c --- /dev/null +++ b/IRC.Core.SCP/Service/BaseService.cs @@ -0,0 +1,118 @@ +using AutoMapper; +using IRaCIS.Application.Services.BusinessFilter; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Localization; +using Panda.DynamicWebApi; +using Panda.DynamicWebApi.Attributes; +using System.Diagnostics.CodeAnalysis; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure.Extention; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.SCP.Service +{ + +#pragma warning disable CS8618 + + + #region 非泛型版本 + + [Authorize, DynamicWebApi, UnifiedApiResultFilter] + public class BaseService : IBaseService, IDynamicWebApi + { + public IMapper _mapper { get; set; } + + public IUserInfo _userInfo { get; set; } + + public IRepository _repository { get; set; } + + public IStringLocalizer _localizer { get; set; } + + public IWebHostEnvironment _hostEnvironment { get; set; } + + + + + public static IResponseOutput Null404NotFound(TEntity? businessObject) where TEntity : class + { + return new ResponseOutput() + .NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist); + } + } + + + public interface IBaseService + { + [MemberNotNull(nameof(_mapper))] + public IMapper _mapper { get; set; } + + [MemberNotNull(nameof(_userInfo))] + public IUserInfo _userInfo { get; set; } + + [MemberNotNull(nameof(_repository))] + public IRepository _repository { get; set; } + + [MemberNotNull(nameof(_localizer))] + public IStringLocalizer _localizer { get; set; } + + [MemberNotNull(nameof(_hostEnvironment))] + public IWebHostEnvironment _hostEnvironment { get; set; } + + } + #endregion + + + #region 泛型版本测试 + + + public interface IBaseServiceTest where T : Entity + { + [MemberNotNull(nameof(_mapper))] + public IMapper _mapper { get; set; } + + [MemberNotNull(nameof(_userInfo))] + public IUserInfo _userInfo { get; set; } + + [MemberNotNull(nameof(_repository))] + public IRepository _repository { get; set; } + + [MemberNotNull(nameof(_localizer))] + public IStringLocalizer _localizer { get; set; } + + + + } + + + [Authorize, DynamicWebApi, UnifiedApiResultFilter] + public class BaseServiceTest : IBaseServiceTest, IDynamicWebApi where T : Entity + { + public IMapper _mapper { get; set; } + + public IUserInfo _userInfo { get; set; } + + public IRepository _repository { get; set; } + + public IStringLocalizer _localizer { get; set; } + + public static IResponseOutput Null404NotFound(TEntity? businessObject) where TEntity : class + { + return new ResponseOutput() + .NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist); + } + + + } + + + #endregion + + + + + + + +} diff --git a/IRC.Core.SCP/Service/CStoreSCPService.cs b/IRC.Core.SCP/Service/CStoreSCPService.cs new file mode 100644 index 000000000..a81e11ec1 --- /dev/null +++ b/IRC.Core.SCP/Service/CStoreSCPService.cs @@ -0,0 +1,368 @@ +using FellowOakDicom.Network; +using FellowOakDicom; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using IRaCIS.Core.SCP.Service; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infra.EFCore; +using Medallion.Threading; +using IRaCIS.Core.Domain.Share; +using Serilog; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; +using Microsoft.Extensions.Options; +using System.Data; +using FellowOakDicom.Imaging; +using SharpCompress.Common; +using SixLabors.ImageSharp.Formats.Jpeg; +using IRaCIS.Core.Infrastructure; + +namespace IRaCIS.Core.SCP.Service +{ + /// + /// 后台托管服务的方式运行 + /// + //public class CStoreSCPHostedService : IHostedService + //{ + // private readonly ILogger _logger; + // private readonly IDicomServerFactory _dicomServerFactory; + // private IDicomServer? _server; + + // public CStoreSCPHostedService(ILogger logger, IDicomServerFactory dicomServerFactory) + // { + // _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + // _dicomServerFactory = dicomServerFactory ?? throw new ArgumentNullException(nameof(dicomServerFactory)); + // } + + // public async Task StartAsync(CancellationToken cancellationToken) + // { + // _logger.LogInformation("Starting DICOM server"); + // _server = _dicomServerFactory.Create(104); + // _logger.LogInformation("DICOM server is running"); + // } + + // public Task StopAsync(CancellationToken cancellationToken) + // { + // if (_server != null) + // { + // _server.Stop(); + // _server.Dispose(); + // _server = null; + // } + // return Task.CompletedTask; + // } + //} + public class DicomSCPServiceOption + { + public List CalledAEList { get; set; } + + public string ServerPort { get; set; } + } + + + + + public class CStoreSCPService : DicomService, IDicomServiceProvider, IDicomCStoreProvider, IDicomCEchoProvider + { + private IServiceProvider _serviceProvider { get; set; } + + private List _SCPStudyIdList { get; set; } = new List(); + + private SCPImageUpload _upload { get; set; } + + + private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[] + { + DicomTransferSyntax.ExplicitVRLittleEndian, + DicomTransferSyntax.ExplicitVRBigEndian, + DicomTransferSyntax.ImplicitVRLittleEndian + }; + + private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[] + { + // Lossless + DicomTransferSyntax.JPEGLSLossless, + DicomTransferSyntax.JPEG2000Lossless, + DicomTransferSyntax.JPEGProcess14SV1, + DicomTransferSyntax.JPEGProcess14, + DicomTransferSyntax.RLELossless, + // Lossy + DicomTransferSyntax.JPEGLSNearLossless, + DicomTransferSyntax.JPEG2000Lossy, + DicomTransferSyntax.JPEGProcess1, + DicomTransferSyntax.JPEGProcess2_4, + // Uncompressed + DicomTransferSyntax.ExplicitVRLittleEndian, + DicomTransferSyntax.ExplicitVRBigEndian, + DicomTransferSyntax.ImplicitVRLittleEndian + }; + + + public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies) + : base(stream, fallbackEncoding, log, dependencies) + { + } + + + + + public Task OnReceiveAssociationRequestAsync(DicomAssociation association) + { + + _upload = new SCPImageUpload() { StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost }; + + + Log.Logger.Warning($"接收到来自{association.CallingAE}的连接"); + + _serviceProvider = (IServiceProvider)this.UserState; + + var option = _serviceProvider.GetService>().CurrentValue; + + + + var calledAEList = option.CalledAEList; + + if (!calledAEList.Contains(association.CalledAE)) + + //if (association.CalledAE != "STORESCP") + { + + Log.Logger.Warning($"拒绝CalledAE:{association.CalledAE}的连接"); + + return SendAssociationRejectAsync( + DicomRejectResult.Permanent, + DicomRejectSource.ServiceUser, + DicomRejectReason.CalledAENotRecognized); + } + + foreach (var pc in association.PresentationContexts) + { + if (pc.AbstractSyntax == DicomUID.Verification) + { + pc.AcceptTransferSyntaxes(_acceptedTransferSyntaxes); + } + else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None) + { + pc.AcceptTransferSyntaxes(_acceptedImageTransferSyntaxes); + } + } + + return SendAssociationAcceptAsync(association); + } + + + public async Task OnReceiveAssociationReleaseRequestAsync() + { + await DataMaintenanceAsaync(); + + //记录监控 + + var _SCPImageUploadRepository = _serviceProvider.GetService>(); + + _upload.EndTime = DateTime.Now; + _upload.StudyCount = _SCPStudyIdList.Count; + + await _SCPImageUploadRepository.AddAsync(_upload,true); + + await SendAssociationReleaseResponseAsync(); + } + + + private async Task DataMaintenanceAsaync() + { + Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality 以及自动创建访视,绑定检查"); + + //var patientStudyService = _serviceProvider.GetService(); + + //await patientStudyService.AutoBindingPatientStudyVisitAsync(_SCPStudyIdList); + + //处理检查Modality + var _dictionaryRepository = _serviceProvider.GetService>(); + var _seriesRepository = _serviceProvider.GetService>(); + var _studyRepository = _serviceProvider.GetService>(); + + var dicModalityList = _dictionaryRepository.Where(t => t.Code == "Modality").SelectMany(t => t.ChildList.Select(c => c.Value)).ToList(); + var seriesModalityList = _seriesRepository.Where(t => _SCPStudyIdList.Contains(t.StudyId)).Select(t => new { SCPStudyId = t.StudyId, t.Modality }).ToList(); + + foreach (var g in seriesModalityList.GroupBy(t => t.SCPStudyId)) + { + var modality = string.Join('、', g.Select(t => t.Modality).Distinct().ToList()); + + //特殊逻辑 + var modalityForEdit = dicModalityList.Contains(modality) ? modality : String.Empty; + + if (modality == "MR") + { + modalityForEdit = "MRI"; + } + + if (modality == "PT") + { + modalityForEdit = "PET"; + } + if (modality == "PT、CT" || modality == "CT、PT") + { + modalityForEdit = "PET-CT"; + } + + await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == g.Key, u => new SCPStudy() { Modalities = modality, ModalityForEdit = modalityForEdit }); + + } + + Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}维护数据结束"); + } + + public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason) + { + Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}接收中断,中断原因:{source.ToString() + reason.ToString()}"); + /* nothing to do here */ + } + + + public async void OnConnectionClosed(Exception exception) + { + /* nothing to do here */ + + //奇怪的bug 上传的时候,用王捷修改的影像,会关闭,重新连接,导致检查id 丢失,然后状态不一致 + if (exception == null) + { + var _studyRepository = _serviceProvider.GetService>(); + //将检查设置为传输结束 + await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true }); + + await _studyRepository.SaveChangesAndClearAllTrackingAsync(); + } + + Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}"); + } + + + + + public async Task OnCStoreRequestAsync(DicomCStoreRequest request) + { + + string studyInstanceUid = request.Dataset.GetString(DicomTag.StudyInstanceUID); + string seriesInstanceUid = request.Dataset.GetString(DicomTag.SeriesInstanceUID); + string sopInstanceUid = request.Dataset.GetString(DicomTag.SOPInstanceUID); + + Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid); + Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid); + + + var ossService = _serviceProvider.GetService(); + var dicomArchiveService = _serviceProvider.GetService(); + var _seriesRepository = _serviceProvider.GetService>(); + + var _distributedLockProvider = _serviceProvider.GetService(); + + var storeRelativePath = string.Empty; + var ossFolderPath = $"Dicom/{studyInstanceUid}"; + + + long fileSize = 0; + try + { + + using (MemoryStream ms = new MemoryStream()) + { + await request.File.SaveAsync(ms); + + //irc 从路径最后一截取Guid + storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false); + + fileSize = ms.Length; + } + + Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 "); + + } + catch (Exception ec) + { + Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 上传异常 {ec.Message}"); + } + + + + var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}"); + + using (await @lock.AcquireAsync()) + { + try + { + var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, storeRelativePath, Association.CallingAE, Association.CalledAE); + + if (!_SCPStudyIdList.Contains(scpStudyId)) + { + _SCPStudyIdList.Add(scpStudyId); + } + + var series = await _seriesRepository.FindAsync(seriesId); + + //没有缩略图 + if (series != null && string.IsNullOrEmpty(series.ImageResizePath)) + { + + // 生成缩略图 + using (var memoryStream = new MemoryStream()) + { + DicomImage image = new DicomImage(request.Dataset); + + var sharpimage = image.RenderImage().AsSharpImage(); + sharpimage.Save(memoryStream, new JpegEncoder()); + + // 上传缩略图到 OSS + + var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, seriesId.ToString() + ".preview.jpg", false); + + Console.WriteLine(seriesPath + " Id: " + seriesId); + + series.ImageResizePath = seriesPath; + + + + //await _seriesRepository.BatchUpdateNoTrackingAsync(t => t.Id == seriesId, u => new SCPSeries() { ImageResizePath = seriesPath }); + + + } + } + + await _seriesRepository.SaveChangesAsync(); + } + catch (Exception ex) + { + + Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}"); + + } + + } + + + + //监控信息设置 + _upload.FileCount++; + _upload.FileSize= _upload.FileSize+ fileSize; + return new DicomCStoreResponse(request, DicomStatus.Success); + } + + + public Task OnCStoreRequestExceptionAsync(string tempFileName, Exception e) + { + // let library handle logging and error response + return Task.CompletedTask; + } + + + public Task OnCEchoRequestAsync(DicomCEchoRequest request) + { + return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success)); + } + + } +} diff --git a/IRC.Core.SCP/Service/DicomArchiveService.cs b/IRC.Core.SCP/Service/DicomArchiveService.cs new file mode 100644 index 000000000..439741952 --- /dev/null +++ b/IRC.Core.SCP/Service/DicomArchiveService.cs @@ -0,0 +1,333 @@ +using IRaCIS.Core.Domain.Share; +using System.Text; +using Microsoft.AspNetCore.Hosting; +using IRaCIS.Core.Infrastructure; +using Medallion.Threading; +using FellowOakDicom; +using FellowOakDicom.Imaging.Codec; +using System.Data; +using IRaCIS.Core.Domain.Models; +using FellowOakDicom.Network; +using IRaCIS.Core.SCP.Service; +using IRaCIS.Core.Infra.EFCore; +using MassTransit; + +namespace IRaCIS.Core.SCP.Service +{ + public class DicomArchiveService : BaseService, IDicomArchiveService + { + private readonly IRepository _patientRepository; + private readonly IRepository _studyRepository; + private readonly IRepository _seriesRepository; + private readonly IRepository _instanceRepository; + private readonly IRepository _dictionaryRepository; + private readonly IDistributedLockProvider _distributedLockProvider; + + + private List _instanceIdList = new List(); + + public DicomArchiveService(IRepository patientRepository, IRepository studyRepository, + IRepository seriesRepository, + IRepository instanceRepository, + IRepository dictionaryRepository, + IDistributedLockProvider distributedLockProvider) + { + _distributedLockProvider = distributedLockProvider; + _studyRepository = studyRepository; + _patientRepository = patientRepository; + _seriesRepository = seriesRepository; + _instanceRepository = instanceRepository; + _dictionaryRepository = dictionaryRepository; + + } + + + + + /// + /// 单个文件接收 归档 + /// + /// + /// + /// + public async Task ArchiveDicomFileAsync(DicomDataset dataset, string fileRelativePath, string callingAE, string calledAE) + { + string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID); + string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID); + string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID); + + string patientIdStr = dataset.GetString(DicomTag.PatientID); + + //Guid patientId= IdentifierHelper.CreateGuid(patientIdStr); + Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid); + Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid); + Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid); + + var isStudyNeedAdd = false; + var isSeriesNeedAdd = false; + var isInstanceNeedAdd = false; + var isPatientNeedAdd = false; + + //var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}"); + + //using (@lock.Acquire()) + { + var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr); + var findStudy = await _studyRepository.FindAsync(studyId); + var findSerice = await _seriesRepository.FindAsync(seriesId); + var findInstance = await _instanceRepository.FindAsync(instanceId); + + DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue(DicomTag.StudyTime).TimeOfDay); + + //先传输了修改了患者编号的,又传输了没有修改患者编号的,导致后传输的没有修改患者编号的下面的检查为0 + if (findPatient == null && findStudy==null) + { + isPatientNeedAdd = true; + + + findPatient = new SCPPatient() + { + Id = NewId.NextSequentialGuid(), + PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty), + PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty), + PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty), + PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty), + PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty), + + EarliestStudyTime = studyTime, + LatestStudyTime = studyTime, + LatestPushTime = DateTime.Now, + }; + + if (findPatient.PatientBirthDate.Length == 8) + { + var birthDateStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}-{findPatient.PatientBirthDate[4]}{findPatient.PatientBirthDate[5]}-{findPatient.PatientBirthDate[6]}{findPatient.PatientBirthDate[7]}"; + + var yearStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}"; + + int year = 0; + + var canParse = int.TryParse(yearStr, out year); + + if (canParse && year > 1900) + { + findPatient.PatientBirthDate = birthDateStr; + + } + else + { + findPatient.PatientBirthDate = string.Empty; + } + + } + } + else + { + if (studyTime < findPatient.EarliestStudyTime) + { + findPatient.EarliestStudyTime = studyTime; + } + if (studyTime > findPatient.LatestStudyTime) + { + findPatient.LatestStudyTime = studyTime; + } + + findPatient.LatestPushTime = DateTime.Now; + } + + if (findStudy == null) + { + isStudyNeedAdd = true; + findStudy = new SCPStudy + { + CalledAE = calledAE, + CallingAE = callingAE, + + PatientId = findPatient.Id, + Id = studyId, + StudyInstanceUid = studyInstanceUid, + StudyTime = studyTime, + Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty), + //ModalityForEdit = modalityForEdit, + Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty), + InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty), + PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty), + PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty), + PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty), + PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty), + BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty), + + StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty), + AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty), + + //需要特殊处理 + PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty), + + + AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty), + AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty), + TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty), + + + + //IsDoubleReview = addtionalInfo.IsDoubleReview, + SeriesCount = 0, + InstanceCount = 0 + }; + + + if (findStudy.PatientBirthDate.Length == 8) + { + findStudy.PatientBirthDate = $"{findStudy.PatientBirthDate[0]}{findStudy.PatientBirthDate[1]}{findStudy.PatientBirthDate[2]}{findStudy.PatientBirthDate[3]}-{findStudy.PatientBirthDate[4]}{findStudy.PatientBirthDate[5]}-{findStudy.PatientBirthDate[6]}{findStudy.PatientBirthDate[7]}"; + } + } + + + if (findSerice == null) + { + isSeriesNeedAdd = true; + + findSerice = new SCPSeries + { + Id = seriesId, + StudyId = findStudy.Id, + + StudyInstanceUid = findStudy.StudyInstanceUid, + SeriesInstanceUid = seriesInstanceUid, + SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1), + //SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay), + //SeriesTime = DateTime.TryParse(dataset.GetSingleValue(DicomTag.SeriesDate) + dataset.GetSingleValue(DicomTag.SeriesTime), out DateTime dt) ? dt : null, + SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue(DicomTag.SeriesDate).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue(DicomTag.SeriesTime).TimeOfDay), + Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty), + Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty), + SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty), + + ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty), + ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty), + BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty), + SequenceName = dataset.GetSingleValueOrDefault(DicomTag.SequenceName, string.Empty), + ProtocolName = dataset.GetSingleValueOrDefault(DicomTag.ProtocolName, string.Empty), + ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty), + + AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty), + AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty), + TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty), + + + InstanceCount = 0 + }; + + ++findStudy.SeriesCount; + } + + + if (findInstance == null) + { + isInstanceNeedAdd = true; + findInstance = new SCPInstance + { + Id = instanceId, + StudyId = findStudy.Id, + SeriesId = findSerice.Id, + StudyInstanceUid = findStudy.StudyInstanceUid, + SeriesInstanceUid = findSerice.SeriesInstanceUid, + + SopInstanceUid = sopInstanceUid, + InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1), + InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue(DicomTag.ContentDate).Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue(DicomTag.ContentTime).TimeOfDay), + //InstanceTime = DateTime.TryParse(dataset.GetSingleValue(DicomTag.ContentDate) + dataset.GetSingleValue(DicomTag.ContentTime), out DateTime dt) ? dt : null, + //InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate,(DateTime?)null)?.Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, TimeSpan.Zero)), + //dataset.GetSingleValueOrDefault(DicomTag.ContentDate,DateTime.Now);//, DicomTag.ContentTime) + CPIStatus = false, + ImageRows = dataset.GetSingleValueOrDefault(DicomTag.Rows, 0), + ImageColumns = dataset.GetSingleValueOrDefault(DicomTag.Columns, 0), + SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0), + + SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty), + NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0), + PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty), + ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty), + FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty), + WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty), + WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty), + + Path = fileRelativePath + }; + + ++findStudy.InstanceCount; + ++findSerice.InstanceCount; + } + + if (isPatientNeedAdd) + { + var ss = await _patientRepository.AddAsync(findPatient); + } + if (isStudyNeedAdd) + { + var dd = await _studyRepository.AddAsync(findStudy); + } + else + { + await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == findStudy.Id, t => new SCPStudy() { IsUploadFinished = false }); + } + + if (isSeriesNeedAdd) + { + await _seriesRepository.AddAsync(findSerice); + } + if (isInstanceNeedAdd) + { + await _instanceRepository.AddAsync(findInstance); + } + else + { + await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath }); + } + + //await _studyRepository.SaveChangesAsync(); + + return findStudy.Id; + } + + } + + + // 从DICOM文件中获取使用的字符集 + private string GetEncodingVaulueFromDicomFile(DicomDataset dataset, DicomTag dicomTag) + { + + // 获取DICOM文件的特定元素,通常用于指示使用的字符集 + var charset = dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty); + + var dicomEncoding = DicomEncoding.GetEncoding(charset); + + + var dicomStringElement = dataset.GetDicomItem(dicomTag); + + var bytes = dicomStringElement.Buffer.Data; + + + return dicomEncoding.GetString(bytes); + + + //// 从DICOM文件中获取使用的字符集 + //string filePath = "C:\\Users\\hang\\Documents\\WeChat Files\\wxid_r2imdzb7j3q922\\FileStorage\\File\\2024-05\\1.2.840.113619.2.80.169103990.5390.1271401378.4.dcm"; + //DicomFile dicomFile = DicomFile.Open(filePath); + + //// 获取DICOM文件的特定元素,通常用于指示使用的字符集 + //var charset = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty); + + //var dicomEncoding = DicomEncoding.GetEncoding(charset); + + //var value = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty); + + //var dicomStringElement = dicomFile.Dataset.GetDicomItem(DicomTag.PatientName); + + //var bytes = dicomStringElement.Buffer.Data; + + //var aa= dicomEncoding.GetString(bytes); + + + } + } +} diff --git a/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs b/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs new file mode 100644 index 000000000..970b3e146 --- /dev/null +++ b/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs @@ -0,0 +1,11 @@ +using FellowOakDicom; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; + +namespace IRaCIS.Core.SCP.Service +{ + public interface IDicomArchiveService + { + Task ArchiveDicomFileAsync(DicomDataset dicomDataset,string fileRelativePath,string callingAE,string calledAE); + + } +} diff --git a/IRC.Core.SCP/Service/OSSService.cs b/IRC.Core.SCP/Service/OSSService.cs new file mode 100644 index 000000000..cfbe4b3f8 --- /dev/null +++ b/IRC.Core.SCP/Service/OSSService.cs @@ -0,0 +1,427 @@ +using Aliyun.OSS; +using IRaCIS.Core.Infrastructure; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Minio.DataModel.Args; +using Minio; +using SharpCompress.Common; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Security.AccessControl; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.SCP +{ + public class MinIOOptions : AWSOptions + { + public int port { get; set; } + + } + + public class AWSOptions + { + public string endPoint { get; set; } + public bool useSSL { get; set; } + public string accessKey { get; set; } + public string secretKey { get; set; } + public string bucketName { get; set; } + public string viewEndpoint { get; set; } + } + + public class AliyunOSSOptions + { + public string regionId { get; set; } + public string accessKeyId { get; set; } + public string accessKeySecret { get; set; } + public string endPoint { get; set; } + public string bucketName { get; set; } + + public string roleArn { get; set; } + + public string region { get; set; } + + public string viewEndpoint { get; set; } + + } + + public class ObjectStoreServiceOptions + { + public string ObjectStoreUse { get; set; } + public AliyunOSSOptions AliyunOSS { get; set; } + public MinIOOptions MinIO { get; set; } + + public AWSOptions AWS { get; set; } + + } + + public class ObjectStoreDTO + { + public string ObjectStoreUse { get; set; } + + public AliyunOSSOptions AliyunOSS { get; set; } + + public MinIOOptions MinIO { get; set; } + + public AWSOptions AWS { get; set; } + + } + + public class AliyunOSSTempToken + { + public string AccessKeyId { get; set; } + public string AccessKeySecret { get; set; } + public string SecurityToken { get; set; } + public string Expiration { get; set; } + + public string Region { get; set; } + public string BucketName { get; set; } + public string ViewEndpoint { get; set; } + } + + + + public enum ObjectStoreUse + { + AliyunOSS = 0, + MinIO = 1, + AWS = 2, + } + + public interface IOSSService + { + public Task UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName,bool isFileNameAddGuid=true); + public Task UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true); + + public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath); + + public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; } + + public Task GetSignedUrl(string ossRelativePath); + + } + + + public class OSSService : IOSSService + { + public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; } + + + public OSSService(IOptionsMonitor options) + { + ObjectStoreServiceOptions = options.CurrentValue; + } + + /// + /// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder + /// + /// + /// + /// + /// + public async Task UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true) + { + + + var ossRelativePath = isFileNameAddGuid? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}": $"{oosFolderPath}/{fileRealName}"; + //var ossRelativePath = oosFolderPath + "/" + fileRealName; + + try + { + using (var memoryStream = new MemoryStream()) + { + fileStream.Seek(0, SeekOrigin.Begin); + + fileStream.CopyTo(memoryStream); + + memoryStream.Seek(0, SeekOrigin.Begin); + + + if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS") + { + var aliConfig = ObjectStoreServiceOptions.AliyunOSS; + + var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); + + + + // 上传文件 + var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, memoryStream); + + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO") + { + var minIOConfig = ObjectStoreServiceOptions.MinIO; + + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + var putObjectArgs = new PutObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithStreamData(memoryStream) + .WithObjectSize(memoryStream.Length); + + await minioClient.PutObjectAsync(putObjectArgs); + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS") + { + var minIOConfig = ObjectStoreServiceOptions.AWS; + + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + var putObjectArgs = new PutObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithStreamData(memoryStream) + .WithObjectSize(memoryStream.Length); + + await minioClient.PutObjectAsync(putObjectArgs); + } + else + { + throw new BusinessValidationFailedException("未定义的存储介质类型"); + } + } + } + catch (Exception ex) + { + + throw new BusinessValidationFailedException($"上传发生异常:{ex.Message}"); ; + } + + + + + return "/" + ossRelativePath; + + } + + + + /// + /// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder + /// + /// + /// + /// + /// + public async Task UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true) + { + var localFileName = Path.GetFileName(localFilePath); + + var ossRelativePath = isFileNameAddGuid? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}"; + + //var ossRelativePath = oosFolderPath + "/" + localFileName; + + + if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS") + { + var aliConfig = ObjectStoreServiceOptions.AliyunOSS; + + var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); + + // 上传文件 + var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, localFilePath); + + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO") + { + var minIOConfig = ObjectStoreServiceOptions.MinIO; + + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + var putObjectArgs = new PutObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithFileName(localFilePath); + + await minioClient.PutObjectAsync(putObjectArgs); + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS") + { + var minIOConfig = ObjectStoreServiceOptions.AWS; + + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + var putObjectArgs = new PutObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithFileName(localFilePath); + + await minioClient.PutObjectAsync(putObjectArgs); + } + else + { + throw new BusinessValidationFailedException("未定义的存储介质类型"); + } + return "/" + ossRelativePath; + + } + + public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath) + { + + ossRelativePath = ossRelativePath.TrimStart('/'); + try + { + + + if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS") + { + var aliConfig = ObjectStoreServiceOptions.AliyunOSS; + + var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); + + // 上传文件 + var result = _ossClient.GetObject(aliConfig.bucketName, ossRelativePath); + + // 将下载的文件流保存到本地文件 + using (var fs = File.OpenWrite(localFilePath)) + { + result.Content.CopyTo(fs); + fs.Close(); + } + + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO") + { + var minIOConfig = ObjectStoreServiceOptions.MinIO; + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + var getObjectArgs = new GetObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithFile(localFilePath); + + await minioClient.GetObjectAsync(getObjectArgs); + + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS") + { + var minIOConfig = ObjectStoreServiceOptions.AWS; + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + var getObjectArgs = new GetObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithFile(localFilePath); + + await minioClient.GetObjectAsync(getObjectArgs); + } + else + { + throw new BusinessValidationFailedException("未定义的存储介质类型"); + } + } + catch (Exception ex) + { + + throw new BusinessValidationFailedException("oss下载失败!" + ex.Message); + } + + + + + + } + + public async Task GetSignedUrl(string ossRelativePath) + { + ossRelativePath = ossRelativePath.TrimStart('/'); + try + { + + + if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS") + { + var aliConfig = ObjectStoreServiceOptions.AliyunOSS; + + var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); + + // 生成签名URL。 + var req = new GeneratePresignedUriRequest(aliConfig.bucketName, ossRelativePath, SignHttpMethod.Get) + { + // 设置签名URL过期时间,默认值为3600秒。 + Expiration = DateTime.Now.AddHours(1), + }; + var uri = _ossClient.GeneratePresignedUri(req); + + return uri.PathAndQuery; + + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO") + { + var minIOConfig = ObjectStoreServiceOptions.MinIO; + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + //var reqParams = new Dictionary(StringComparer.Ordinal) + // { + // { "response-content-type", "application/json" } + // }; + + var args = new PresignedGetObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithExpiry(3600) + /*.WithHeaders(reqParams)*/; + + var presignedUrl = await minioClient.PresignedGetObjectAsync(args); + + Uri uri = new Uri(presignedUrl); + + string relativePath = uri.PathAndQuery; + + + return relativePath; + + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS") + { + var minIOConfig = ObjectStoreServiceOptions.AWS; + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + return string.Empty; + } + else + { + throw new BusinessValidationFailedException("未定义的存储介质类型"); + } + } + catch (Exception ex) + { + + throw new BusinessValidationFailedException("oss授权url失败!" + ex.Message); + } + + } + } + + +} diff --git a/IRC.Core.SCP/appsettings.IRC_Prod_SCP.json b/IRC.Core.SCP/appsettings.IRC_Prod_SCP.json new file mode 100644 index 000000000..32ee00af6 --- /dev/null +++ b/IRC.Core.SCP/appsettings.IRC_Prod_SCP.json @@ -0,0 +1,63 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "ObjectStoreService": { + "ObjectStoreUse": "AliyunOSS", + "AliyunOSS": { + "regionId": "cn-shanghai", + "endpoint": "https://oss-cn-shanghai.aliyuncs.com", + "accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ", + "accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio", + "bucketName": "zy-irc-store", + "roleArn": "acs:ram::1899121822495495:role/oss-upload", + "viewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com", + "region": "oss-cn-shanghai" + }, + + "MinIO": { + "endpoint": "http://192.168.3.68", + "port": "8001", + "useSSL": false, + "accessKey": "IDFkwEpWej0b4DtiuThL", + "secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h", + "bucketName": "test" + } + }, + + "ConnectionStrings": { + //"RemoteNew": "Server=47.117.165.18,1434;Database=Prod_Study;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true", + //"Hangfire": "Server=47.117.165.18,1434;Database=Prod_Study_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true", + "RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_Study;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true", + "Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_Study_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true" + }, + + "BasicSystemConfig": { + + "OpenUserComplexPassword": true, + + "OpenSignDocumentBeforeWork": true, + + "OpenTrialRelationDelete": true, + + "OpenLoginLimit": true, + "LoginMaxFailCount": 5, + + "LoginFailLockMinutes": 30 + + }, + + "SystemEmailSendConfig": { + "Port": 465, + "Host": "smtp.qiye.aliyun.com", + "FromEmail": "study@extimaging.com", + "FromName": "研究单位阅片系统", + "AuthorizationCode": "zhanying123", + "SiteUrl": "https://study.extimaging.com/login" + } + +} diff --git a/IRC.Core.SCP/appsettings.IRC_Test_SCP.json b/IRC.Core.SCP/appsettings.IRC_Test_SCP.json new file mode 100644 index 000000000..5e93530c7 --- /dev/null +++ b/IRC.Core.SCP/appsettings.IRC_Test_SCP.json @@ -0,0 +1,79 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "ObjectStoreService": { + "ObjectStoreUse": "AliyunOSS", + "AliyunOSS": { + "regionId": "cn-shanghai", + "endPoint": "https://oss-cn-shanghai.aliyuncs.com", + "accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ", + "accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio", + "bucketName": "zy-irc-test-store", + "roleArn": "acs:ram::1899121822495495:role/oss-upload", + "viewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com", + "region": "oss-cn-shanghai" + }, + + "MinIO": { + "endPoint": "106.14.89.110", + "port": "9001", + "useSSL": false, + "accessKey": "fbStsVYCIPKHQneeqMwD", + "secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy", + "bucketName": "hir-test", + "viewEndpoint": "http://106.14.89.110:9001/hir-test/" + }, + + "AWS": { + "endPoint": "s3.us-east-1.amazonaws.com", + "useSSL": false, + "accessKey": "AKIAZQ3DRSOHFPJJ6FEU", + "secretKey": "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", + "bucketName": "ei-irc-test-store", + "viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/" + } + }, + + "ConnectionStrings": { + "RemoteNew": "Server=106.14.89.110,1435;Database=Test_HIR;User ID=sa;Password=xc@123456;TrustServerCertificate=true", + "Hangfire": "Server=106.14.89.110,1435;Database=Test_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true" + }, + "BasicSystemConfig": { + + "OpenUserComplexPassword": false, + + "OpenSignDocumentBeforeWork": false, + + "OpenTrialRelationDelete": true, + + "OpenLoginLimit": false, + "LoginMaxFailCount": 5, + + "LoginFailLockMinutes": 30 + + }, + + "SystemEmailSendConfig": { + "Port": 465, + "Host": "smtp.qiye.aliyun.com", + "FromEmail": "test-study@extimaging.com", + "FromName": "Test_Study", + "AuthorizationCode": "zhanying123", + + "SiteUrl": "http://study.test.extimaging.com/login" + }, + "DicomSCPServiceConfig": { + "CalledAEList": [ + "STORESCP", + "Value1", + "Value2", + "Value3" + ], + "ServerPort": 11112 + } +} diff --git a/IRC.Core.SCP/appsettings.IRC_US_SCP.json b/IRC.Core.SCP/appsettings.IRC_US_SCP.json new file mode 100644 index 000000000..7821a16b8 --- /dev/null +++ b/IRC.Core.SCP/appsettings.IRC_US_SCP.json @@ -0,0 +1,76 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "ObjectStoreService": { + "ObjectStoreUse": "MinIO", + "AliyunOSS": { + "regionId": "cn-shanghai", + "endpoint": "https://oss-cn-shanghai.aliyuncs.com", + "accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ", + "accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio", + "bucketName": "zy-sir-test-store", + "roleArn": "acs:ram::1899121822495495:role/oss-upload", + "viewEndpoint": "https://zy-sir-test-store.oss-cn-shanghai.aliyuncs.com", + "region": "oss-cn-shanghai" + }, + + "MinIO": { + "endPoint": "44.218.11.19", + "port": "9001", + "useSSL": false, + "accessKey": "lH8DkKskLuDqPaiubuSQ", + "secretKey": "pdPdicvvLeH7xAC5yFUrI7odMyBfOXxvVWMvKYV4", + "bucketName": "hir-us", + "viewEndpoint": "http://hir.us.extimaging.com/oss/hir-us" + }, + + "AWS": { + "endPoint": "s3.us-east-1.amazonaws.com", + "useSSL": false, + "accessKey": "AKIAZQ3DRSOHFPJJ6FEU", + "secretKey": "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", + "bucketName": "ei-irc-test-store", + "viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/" + } + }, + "ConnectionStrings": { + "RemoteNew": "Server=44.218.11.19,1435;Database=US_HIR;User ID=sa;Password=xc@123456;TrustServerCertificate=true", + "Hangfire": "Server=44.218.11.19,1435;Database=US_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true" + }, + "BasicSystemConfig": { + + "OpenUserComplexPassword": false, + + "OpenSignDocumentBeforeWork": false, + + "OpenTrialRelationDelete": true, + + "OpenLoginLimit": false, + "LoginMaxFailCount": 5, + + "LoginFailLockMinutes": 30 + }, + + "SystemEmailSendConfig": { + "Port": 465, + "Host": "smtp.qiye.aliyun.com", + "FromEmail": "uat-study@extimaging.com", + "FromName": "Uat_Study", + "AuthorizationCode": "zhanying123", + "SiteUrl": "http://study.uat.extimaging.com/login" + }, + "DicomSCPServiceConfig": { + "CalledAEList": [ + "STORESCP", + "Value1", + "Value2", + "Value3" + ], + "ServerPort": 11112 + } +} diff --git a/IRC.Core.SCP/appsettings.IRC_Uat_SCP.json b/IRC.Core.SCP/appsettings.IRC_Uat_SCP.json new file mode 100644 index 000000000..f50eedf66 --- /dev/null +++ b/IRC.Core.SCP/appsettings.IRC_Uat_SCP.json @@ -0,0 +1,76 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "ObjectStoreService": { + "ObjectStoreUse": "MinIO", + "AliyunOSS": { + "regionId": "cn-shanghai", + "endpoint": "https://oss-cn-shanghai.aliyuncs.com", + "accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ", + "accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio", + "bucketName": "zy-irc-uat-store", + "roleArn": "acs:ram::1899121822495495:role/oss-upload", + "viewEndpoint": "https://zy-irc-uat-store.oss-cn-shanghai.aliyuncs.com", + "region": "oss-cn-shanghai" + }, + + "MinIO": { + "endPoint": "47.117.164.182", + "port": "9001", + "useSSL": false, + "accessKey": "b9Ul0e98xPzt6PwRXA1Q", + "secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ", + "bucketName": "hir-uat", + "viewEndpoint": "http://hir.uat.extimaging.com/oss/hir-uat" + }, + + "AWS": { + "endPoint": "s3.us-east-1.amazonaws.com", + "useSSL": false, + "accessKey": "AKIAZQ3DRSOHFPJJ6FEU", + "secretKey": "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", + "bucketName": "ei-irc-test-store", + "viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/" + } + }, + "ConnectionStrings": { + "RemoteNew": "Server=47.117.164.182,1434;Database=Uat_HIR;User ID=sa;Password=xc@123456;TrustServerCertificate=true", + "Hangfire": "Server=47.117.164.182,1434;Database=Uat_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true" + }, + "BasicSystemConfig": { + + "OpenUserComplexPassword": false, + + "OpenSignDocumentBeforeWork": false, + + "OpenTrialRelationDelete": true, + + "OpenLoginLimit": false, + "LoginMaxFailCount": 5, + + "LoginFailLockMinutes": 30 + }, + + "SystemEmailSendConfig": { + "Port": 465, + "Host": "smtp.qiye.aliyun.com", + "FromEmail": "uat-study@extimaging.com", + "FromName": "Uat_Study", + "AuthorizationCode": "zhanying123", + "SiteUrl": "http://study.uat.extimaging.com/login" + }, + "DicomSCPServiceConfig": { + "CalledAEList": [ + "STORESCP", + "Value1", + "Value2", + "Value3" + ], + "ServerPort": 11112 + } +} diff --git a/IRC.Core.SCP/appsettings.json b/IRC.Core.SCP/appsettings.json new file mode 100644 index 000000000..10f68b8c8 --- /dev/null +++ b/IRC.Core.SCP/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/IRaCIS.Core.API.sln b/IRaCIS.Core.API.sln index d5afa55fa..9511e66e3 100644 --- a/IRaCIS.Core.API.sln +++ b/IRaCIS.Core.API.sln @@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infra.EFCore", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infrastructure", "IRaCIS.Core.Infrastructure\IRaCIS.Core.Infrastructure.csproj", "{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IRC.Core.SCP", "IRC.Core.SCP\IRC.Core.SCP.csproj", "{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -51,6 +53,10 @@ Global {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|Any CPU.Build.0 = Debug|Any CPU {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.ActiveCfg = Release|Any CPU {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.Build.0 = Release|Any CPU + {ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/IRaCIS.Core.API/IRaCIS.Core.API.csproj b/IRaCIS.Core.API/IRaCIS.Core.API.csproj index f6e1614ef..a434c6d2b 100644 --- a/IRaCIS.Core.API/IRaCIS.Core.API.csproj +++ b/IRaCIS.Core.API/IRaCIS.Core.API.csproj @@ -64,22 +64,21 @@ - true - + - + - + - + diff --git a/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs b/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs index 76d181268..b67c4f25f 100644 --- a/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs +++ b/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs @@ -31,7 +31,7 @@ namespace IRaCIS.Core.Application.Service var exploreRecommendQueryable = - _exploreRecommendRepository + _exploreRecommendRepository.Where().IgnoreQueryFilters() .WhereIf(string.IsNullOrEmpty(inQuery.Title), t => t.Title.Contains(inQuery.Title)) .WhereIf(string.IsNullOrEmpty(inQuery.FileName), t => t.Title.Contains(inQuery.FileName)) .WhereIf(string.IsNullOrEmpty(inQuery.DownloadUrl), t => t.Title.Contains(inQuery.DownloadUrl)) diff --git a/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs b/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs new file mode 100644 index 000000000..72b1ee836 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs @@ -0,0 +1,1083 @@ +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 +{ + public class SystemHospitalOption + { + public string HospitalName { get; set; } + public string HospitalAliasName { get; set; } + public string Country { get; set; } + public string City { get; set; } + public string Province { get; set; } + public string Address { get; set; } + public string Phone { get; set; } + + public bool IsCanConnectInternet { get; set; } + + public string HospitalCode { get; set; } + public string HospitalLogoPath { get; set; } + public int TrialKeepCount { get; set; } + + } + + + public class NewTrialView : PatientJoinTrialInitView + { + public Guid Id { get; set; } + //授权年限 + public int AuthorizationDuration { get; set; } + + public DateTime? AuthorizationDate { get; set; } + + public string AuthorizationEncrypt { get; set; } + + public string CriterionTypes { get; set; } + public List CriterionTypeList => CriterionTypes.Split('|', StringSplitOptions.RemoveEmptyEntries) + .Select(s => Enum.Parse(typeof(CriterionType), s)).Cast().ToList(); + + public int? UnSubmitCount { get; set; } + + public int? UnReadCount { get; set; } + } + + public class NewTrialQuery : PageInput + { + public string? ExperimentName { get; set; } + public string? ResearchProgramNo { get; set; } + public TrialType? TrialType { get; set; } + + public string? SponsorName { get; set; } + + public string? TrialCode { get; set; } + } + + public class TrialInfoDTO : AddOrUpdateTrialCommand + { + [JsonIgnore] + public List DictionaryList { get; set; } = new List(); + + public new List ModalityIds => DictionaryList.Where(t => t.ParentCode == StaticData.Modality).OrderBy(t => t.ShowOrder).Select(t => t.Id).ToList(); + + public string CriterionTypes { get; set; } = string.Empty; + + + public new List CriterionTypeList => CriterionTypes.Split('|', StringSplitOptions.RemoveEmptyEntries) + .Select(s => Enum.Parse(typeof(CriterionType), s)).Cast().ToList(); + + public string AuthorizationEncrypt { get; set; } + + public TrialAuthorizationInfo AuthorizationInfo { get; set; } + + public string TrialStatusStr { get; set; } + + public DateTime CreateTime { get; set; } + } + + public class AddOrUpdateTrialCommand + { + + public Guid? Id { get; set; } + public string TrialCode { get; set; } = string.Empty; + + public TrialType TrialType { get; set; } + + + //研究方案号 + public string ResearchProgramNo { get; set; } = string.Empty; + + //实验名称 + public string ExperimentName { get; set; } = string.Empty; + + // 负责人PI + public string HeadPI { get; set; } = string.Empty; + + public string CRO { get; set; } = string.Empty; + + public string Sponsor { get; set; } = string.Empty; + + //药物名称 + public string MedicineName { get; set; } = string.Empty; + + + //临床分期 + public Guid? PhaseId { get; set; } + + //适应症 + public string Indication { get; set; } = string.Empty; + + //检查技术 + public List ModalityIds { get; set; } = new List(); + + //阅片标准 + public List CriterionTypeList { get; set; } + + //联系人 + public string ContactUser { get; set; } = string.Empty; + //联系电话 + public string ContactPhone { get; set; } = string.Empty; + + //授权年限 + public int AuthorizationDuration { get; set; } + + public DateTime? AuthorizationDate { get; set; } + + } + + public class PatientJoinTrialInitQuery : PageInput + { + [NotDefault] + public Guid PatientId { get; set; } + + public string? Filter { get; set; } + + } + + public class PatientJoinedTrialQuery + { + [NotDefault] + public Guid PatientId { get; set; } + } + + public class PatientJoinedTrialView : PatientJoinTrialInitView + { + public Guid SubjectId { get; set; } + public string Code { get; set; } + + public string ShortName { get; set; } + + public string Sex { get; set; } + + public int? Age { get; set; } + } + + public class PatientJoinTrialSelectView + { + public Guid TrialId { get; set; } + public string ExperimentName { get; set; } + public string ResearchProgramNo { get; set; } + public string Sponsor { get; set; } + } + + + public class TrialCacheInfo + { + public Guid TrialId { get; set; } + public Guid CreateUserId { get; set; } + public string TrialCode { get; set; } + + public DateTime? AuthorizationDate { get; set; } + public string TrialStatusStr { get; set; } + + public string AuthorizationEncrypt { get; set; } + + public string CriterionTypes { get; set; } + public List CriterionTypeList => CriterionTypes.Split('|', StringSplitOptions.RemoveEmptyEntries) + .Select(s => Enum.Parse(typeof(CriterionType), s)).Cast().ToList(); + } + public class TrialAuthorizationInfo + { + public Guid TrialId { get; set; } + public Guid CreateUserId { get; set; } + public string TrialCode { get; set; } + + public string HospitalName { get; set; } + + public string HospitalCode { get; set; } + + public int PurchaseDuration { get; set; } + + + + public List CriterionTypeList { get; set; } + + public DateTime? AuthorizationDeadLineDate { get; set; } + + public DateTime? ActiveDeadLineDate { get; set; } + + public DateTime? ActiveTime { get; set; } + + } + public class PatientJoinTrialInitView + { + public Guid TrialId { get; set; } + + public string TrialCode { get; set; } + public string ExperimentName { get; set; } + public string ResearchProgramNo { get; set; } + public TrialType TrialType { get; set; } + + public string Sponsor { get; set; } + + public string TrialStatusStr { get; set; } + + public DateTime CreateTime { get; set; } + } + + + public class PatientTrialQuery : PageInput + { + public string? PatientIdStr { get; set; } + public string? PatientName { get; set; } + public List CalledAEList { get; set; } = new List(); + + public string? CallingAE { get; set; } + + public string? ExperimentName { get; set; } = string.Empty; + + public DateTime? BeginPushTime { get; set; } + public DateTime? EndPushTime { get; set; } + + } + + public class PatientTrialView : PatientQueryView + { + + public int? StudyCount { get; set; } + + public List TrialList { get; set; } + } + + public class PatientTrialStatInfo + { + public int? VisitCount { get; set; } + + public string ExperimentName { get; set; } + } + + public class PatientQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + public Guid? SubjectId { get; set; } + public string? PatientIdStr { get; set; } + public string? PatientName { get; set; } + public List CalledAEList { get; set; } = new List(); + + public string? CallingAE { get; set; } + + public DateTime? EarliestStudyTime { get; set; } + + public DateTime? LatestStudyTime { get; set; } + + } + + public class PatientQueryView + { + public Guid PatientId { 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 Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + } + + public class UpdateSubjectVisitStudyBindingCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid SubjectId { get; set; } + [NotDefault] + public Guid SCPStudyId { get; set; } + + [NotDefault] + public Guid SubjectVisitId { get; set; } + + public bool IsAdd { get; set; } + + + } + + public class DeleteSubejctPatientCommand + { + [NotDefault] + public Guid SubjectId { get; set; } + [NotDefault] + public Guid PatientId { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + } + + + public class VisitStudyVerifyTimeQuery + { + public Guid SCPStudyId { get; set; } + + public Guid SubjectId { get; set; } + public Guid TrialId { get; set; } + } + + public class VerifyTrialSubjectCommand + { + [NotDefault] + public Guid TrialId { get; set; } + public string SubjectCode { get; set; } = string.Empty; + } + public class AddSubjectPatientCommand + { + [NotDefault] + public Guid TrialId { get; set; } + public string SubjectCode { get; set; } = string.Empty; + + public Guid? SubjectId { get; set; } + + public List PatientIdList { get; set; } + } + + public class VerifyVisitStudyDTO + { + public Guid SubjectVisitId { get; set; } + public decimal VisitNum { get; set; } + public DateTime? StudyTime { get; set; } + public Guid SCPStudyId { get; set; } + } + public class AddSubjectPatientStudyVisitCommand + { + [NotDefault] + public Guid TrialId { get; set; } + [NotDefault] + public Guid SubjectId { get; set; } + [NotDefault] + public Guid SubjectVisitId { get; set; } + [NotDefault] + public Guid SCPStudyId { get; set; } + + //public string VisitName { get; set; } + + } + + public class SubmitVisitStudyBindingCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid SubjectId { get; set; } + + public List SubjectVisitIdList { get; set; } + } + + public class SubjectVisitSelectQuery + { + [NotDefault] + public Guid TrialId { get; set; } + [NotDefault] + public Guid SubjectId { get; set; } + + public Guid? SCPStudyId { get; set; } + } + + public class SubjectVisitSelectDto + { + public Guid Id { get; set; } + + public Guid TrialId { get; set; } + public Guid SubjectId { get; set; } + + public string VisitNum { get; set; } + + public string VisitName { get; set; } + + public SubmitStateEnum SubmitState { get; set; } + + public DateTime? VisitMaxStudyTime { get; set; } + + public DateTime? VisitMinStudyTime { get; set; } + } + public class AddOrUpdateSubjectVisitCommand + { + public Guid? Id { get; set; } + public Guid TrialId { get; set; } + public Guid SubjectId { get; set; } + + public decimal VisitNum { get; set; } + + public string VisitName { get; set; } + } + public class SubjectSelectQuery + { + public string? SubjectCode { get; set; } + + + public Guid? SubjectId { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + } + + public class SubjectPatientSelectQuery + { + public string? SubjectCode { get; set; } + public string? PatientIdStr { get; set; } + + public Guid? SubjectId { get; set; } + + public Guid? PatientId { get; set; } + [NotDefault] + public Guid TrialId { get; set; } + } + public class SubjectSelectDto + { + public Guid SubejctId { get; set; } + + public string SubjectCode { get; set; } + + public string ShortName { get; set; } + + public string Sex { get; set; } + + public int? Age { get; set; } + + public DateTime? BirthDate { get; set; } + + public SubjectStatus Status { get; set; } + + public List PatientList { get; set; } + } + + public class PatienBasicInfo + { + public Guid PatientId { get; set; } + public string PatientIdStr { get; set; } + } + + public class SubjectPatientSelectDto + { + public Guid Id { get; set; } + public Guid PatientId { get; set; } + public Guid TrialId { get; set; } + public Guid SubejctId { get; set; } + public string? PatientIdStr { get; set; } + public string SubjectCode { get; set; } + } + + public class PatientStudyInfoQuery : PageInput + { + [NotDefault] + public Guid PatientId { get; set; } + + public DateTime? EarliestStudyTime { get; set; } + + public DateTime? LatestStudyTime { get; set; } + public string? Modalities { get; set; } + } + + public class PatientStudyQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public List PatientIdList { get; set; } + + + + public DateTime? EarliestStudyTime { get; set; } + + public DateTime? LatestStudyTime { get; set; } + } + + + public class PatientVisitTaskQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + + public Guid? SubjectId { get; set; } + + public string SubjectCode { get; set; } = String.Empty; + + public string? SubjectShortName { get; set; } + + public string TaskName { get; set; } = String.Empty; + + public Guid? DoctorUserId { get; set; } + + public ReadingCategory? ReadingCategory { get; set; } + + public ReadingTaskState? ReadingTaskState { get; set; } + + public TaskState? TaskState { get; set; } + + + public string? TaskCode { get; set; } + + public Arm? ArmEnum { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + + + public DateTime? BeginSignTime { get; set; } + public DateTime? EndSignTime { get; set; } + + public DateTime? BeginTaskCreateTime { get; set; } + public DateTime? EndTaskCreateTime { get; set; } + + + public string? PatientName { get; set; } + public string? PatientIdStr { get; set; } + + public string? PatientSex { get; set; } + + } + + public class PatientVisitTaskDTO + { + public List PatientList { get; set; } + + public Guid Id { get; set; } + public Guid TrialId { get; set; } + + public string TaskCode { get; set; } + + public string TaskName { get; set; } + public string TaskBlindName { get; set; } + + public decimal VisitTaskNum { get; set; } + + public Guid? SourceSubjectVisitId { get; set; } + + public ReadingCategory ReadingCategory { get; set; } + + + public TaskState TaskState { get; set; } + + public DateTime? SignTime { get; set; } + + public DateTime? AllocateTime { get; set; } + public Guid SubjectId { get; set; } + + public string SubjectCode { get; set; } = String.Empty; + + public string SubjectShortName { get; set; } + + public string VisitImageZipPath { get; set; } + + public PackState PackState { get; set; } + + public Arm ArmEnum { get; set; } + public Guid? DoctorUserId { get; set; } + + + public Guid TrialReadingCriterionId { get; set; } + + public string TrialReadingCriterionName { get; set; } + + public CriterionType CriterionType { get; set; } + + public DateTime CreateTime { get; set; } + + public UserSimpleInfo DoctorUser { get; set; } + + //任务阅片状态 + public ReadingTaskState ReadingTaskState { get; set; } + + //public bool IsUrgent { get; set; } + + ///// + ///// 加急类型 + ///// + //public TaskUrgentType? TaskUrgentType { get; set; } + + + //public string TaskUrgentRemake { get; set; } = string.Empty; + + + //public TaskAllocationState TaskAllocationState { get; set; } + //public bool IsNeedClinicalDataSign { get; set; } + + //public bool IsClinicalDataSign { get; set; } + + } + + public class AutoBindingPatientStudyVisitCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + + } + public class TrialPatientStudyQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public string? SubjectCode { get; set; } + + + public Guid? PatientId { get; set; } + + public string? PatientIdStr { get; set; } + + public string? PatientSex { get; set; } + + public string? SubjectShortName { get; set; } + + public int? SubjectAge { get; set; } + public string? SubjectSex { get; set; } + + public DateTime? BeginStudyTime { get; set; } + + public DateTime? EndStudyTime { get; set; } + + public string? VisitName { get; set; } + + public bool? IsBindedVisit { get; set; } + } + + public class AddOrUpdateSubjectCommand + { + + public Guid? Id { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + public string Code { get; set; } = String.Empty; + + public int? Age { get; set; } + public string Sex { get; set; } = string.Empty; + + public string ShortName { get; set; } = string.Empty; + + + public DateTime? BirthDate { get; set; } + + + } + public class PatientSubjectQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public string? Code { get; set; } + + public string? ShortName { get; set; } + + public string? Sex { get; set; } + + public SubjectStatus? Status { get; set; } + + public string? PatientName { get; set; } + public string? PatientIdStr { get; set; } + + public string? PatientSex { get; set; } + + } + + public class PatienSubejctView + { + public List PatientList { get; set; } + + public Guid TrialId { get; set; } + public Guid Id { get; set; } + public string Code { get; set; } + + + public string ShortName { get; set; } + + public string Sex { get; set; } + + public int? Age { get; set; } + + public SubjectStatus Status { get; set; } + + public DateTime? BirthDate { get; set; } + + public int? VisitCount { get; set; } + + public DateTime CreateTime { get; set; } + + public string LatestVisitName { get; set; } + + public DateTime? VisitOverTime { get; set; } + + public Guid? FinalSubjectVisitId { get; set; } + + public string Reason { get; set; } + } + + public class PatientSubejctVisitQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + public string? SubjectCode { get; set; } + + + public string? SubjectShortName { get; set; } + + public string? SubjectSex { get; set; } + + public string? PatientName { get; set; } + public string? PatientIdStr { get; set; } + + public string? PatientSex { get; set; } + + public string? VisitName { get; set; } + + public SubmitStateEnum? SubmitState { get; set; } + + public DateTime? BeginStudyTime { get; set; } + + public DateTime? EndStudyTime { get; set; } + } + + public class PatientStudyBeforeConfirmView : PatientStudyView + { + public Guid PatientId { get; set; } + public string CalledAE { get; set; } = string.Empty; + + public string CallingAE { get; set; } = string.Empty; + } + + public class PatientStudyView + { + + public string PatientName { get; set; } + public string PatientIdStr { get; set; } + + public string PatientSex { get; set; } + + public string PatientAge { get; set; } = string.Empty; + public string PatientBirthDate { get; set; } = string.Empty; + + + public Guid StudyId { get; set; } + public string Modalities { get; set; } + + public string ModalityForEdit { get; set; } + + public DateTime? StudyTime { get; set; } + + public string Description { get; set; } = string.Empty; + public int SeriesCount { get; set; } = 0; + public int InstanceCount { get; set; } = 0; + + + + public Guid SubjectId { get; set; } + + public string SubjectCode { get; set; } + + + public string SubjectShortName { get; set; } = String.Empty; + + public int? SubjectAge { get; set; } + public string SubjectSex { get; set; } = string.Empty; + + public Guid? SubjectVisitId { get; set; } + public string? VisitName { get; set; } + + public decimal? VisitNum { get; set; } + + public DateTime? VisitEarliestStudyTime { get; set; } + + public DateTime? VisitLatestStudyTime { get; set; } + + public DateTime? SubmitTime { get; set; } + + public SubmitStateEnum? SubmitState { get; set; } + + } + + public class PatientBasicInfo + { + public Guid PatientId { get; set; } + + public string PatientIdStr { get; set; } + + public string PatientSex { get; set; } + + public string PatientName { get; set; } + + public string PatientAge { get; set; } = string.Empty; + public string PatientBirthDate { get; set; } = string.Empty; + } + + + public class PatientSubjectVisitView + { + public Guid TrialId { get; set; } + public Guid SubjectId { get; set; } + + public string SubjectCode { get; set; } + + public List PatientList { get; set; } + + public string SubjectShortName { get; set; } = String.Empty; + + public int? SubjectAge { get; set; } + public string SubjectSex { get; set; } = string.Empty; + + public Guid SubjectVisitId { get; set; } + public string VisitName { get; set; } + + public decimal VisitNum { get; set; } + + public DateTime? VisitEarliestStudyTime { get; set; } + + public DateTime? VisitLatestStudyTime { get; set; } + + public DateTime? SubmitTime { get; set; } + + public SubmitStateEnum SubmitState { get; set; } + + public string VisitImageZipPath { get; set; } + public PackState PackState { get; set; } + } + + public class SubjectPatientStudyView : PatientStudySimpleView + { + public Guid SubjectId { get; set; } + + public string SubjectCode { get; set; } + + public List PatientList { get; set; } + + public string SubjectShortName { get; set; } = String.Empty; + + public int? SubjectAge { get; set; } + public string SubjectSex { get; set; } = string.Empty; + + } + + public class SubjectVisitStudyQuery : PageInput + { + [NotDefault] + public Guid SujectVisitId { get; set; } + + public SubmitStateEnum? SubmitState { get; set; } + } + + public class PatientStudyOtherQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + public List PatientIdList { get; set; } + + [NotDefault] + public Guid SujectVisitId { get; set; } + + public DateTime? EarliestStudyTime { get; set; } + + public DateTime? LatestStudyTime { get; set; } + + public string? PatientIdStr { get; set; } + + } + + public class VisitImageDownloadQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public string? IP { get; set; } + + public string? SubjectCode { get; set; } + + public string? VisitName { get; set; } + + public string? Name { get; set; } + + public DateTime? BeginDownloadTime { get; set; } + + public DateTime? EndDownloadTime { get; set; } + + public UserTypeEnum? UserTypeEnum { get; set; } + } + + public class VisitImageDownloadView + { + public string SubjectCode { get; set; } + + public string VisitName { get; set; } + + public DateTime DownloadTime { get; set; } + + public string DownloadUserName { get; set; } + + public string DownLoadUserFullName { get; set; } + + public string IP { get; set; } + + public long VisitImageZipSize { get; set; } + + //文件大小 + public int VisitImageFileCount { get; set; } + + public string VisitImageZipPath { get; set; } = string.Empty; + + public int? StudyCount { get; set; } + + public UserTypeEnum UserTypeEnum { get; set; } + + } + + + public class SCPImageUploadQuery : PageInput + { + public string? CallingAE { get; set; } + + public string? CalledAE { get; set; } + + public string? CallingAEIP { get; set; } + + public DateTime? StartTime { get; set; } + + public DateTime? EndTime { get; set; } + } + + public class SCPImageUploadView + { + public string CallingAE { get; set; } = string.Empty; + + public string CalledAE { get; set; } = string.Empty; + + public string CallingAEIP { get; set; } = string.Empty; + + public DateTime StartTime { get; set; } + + public DateTime EndTime { get; set; } + + public int FileCount { get; set; } + + public long FileSize { get; set; } + + public int StudyCount { get; set; } + + } + public class VisitPatientStudyView : PatientStudySelectDto + { + + public string VisitName { get; set; } + } + + public class UnbindStudyView : VisitPatientStudyView + { + public Guid TrialId { get; set; } + + public Guid SubjectId { get; set; } + public string SubjectCode { get; set; } + + public string SubjectShortName { get; set; } = String.Empty; + + public int? SubjectAge { get; set; } + public string SubjectSex { get; set; } = string.Empty; + + public DateTime? SubjectBirthDate { get; set; } + + } + + public class PatientStudySimpleView + { + 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 class PatientSeriesDTO + { + + public bool IsDicom { get; set; } = true; + + + public Guid Id { get; set; } + public Guid StudyId { get; set; } + public string StudyInstanceUid { get; set; } = String.Empty; + public string SeriesInstanceUid { get; set; } = String.Empty; + public int SeriesNumber { get; set; } + public DateTime? SeriesTime { get; set; } + public string Modality { get; set; } = String.Empty; + public string Description { get; set; } = String.Empty; + public int InstanceCount { get; set; } + public string SliceThickness { get; set; } = String.Empty; + + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + + public bool IsDeleted { get; set; } + public bool IsReading { get; set; } = true; + + public List InstanceList { get; set; } = new List(); + + public List InstancePathList { get; set; } = new List(); + + public string ImageResizePath { get; set; } + } + + public class PatientStudySelectDto + { + public Guid SCPStudyId { get; set; } + + public Guid PatientId { 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? 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; } + public Guid? SubjectVisitId { get; set; } + + public SubmitStateEnum? SubmitState { get; set; } + + //public Guid? SubjectVisitId { get; set; } + + //public string CallingAE { get; set; } = string.Empty; + + //public string BodyPartExamined { get; set; } = string.Empty; + + //public string BodyPartForEdit { get; set; } = string.Empty; + + //public string ModalityForEdit { get; set; } = string.Empty; + + + + } + +} diff --git a/IRaCIS.Core.Application/Service/Visit/PatientService.cs b/IRaCIS.Core.Application/Service/Visit/PatientService.cs new file mode 100644 index 000000000..c4bef2800 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/PatientService.cs @@ -0,0 +1,1323 @@ +//using IRaCIS.Application.Interfaces; +//using IRaCIS.Application.Contracts; +//using IRaCIS.Core.Application.Filter; +//using IRaCIS.Core.Domain.Share; +//using Microsoft.AspNetCore.Mvc; +//using Microsoft.AspNetCore.Authorization; +//using IRaCIS.Core.Application.Auth; +//using MassTransit; +//using Panda.DynamicWebApi.Attributes; +//using DocumentFormat.OpenXml.Spreadsheet; +//using AutoMapper.EntityFrameworkCore; +//using IRaCIS.Core.Domain.Models; +//using IRaCIS.Core.Application.Service.Reading.Dto; +//using Microsoft.Extensions.Options; +//using Microsoft.Extensions.Configuration; +//using Microsoft.Extensions.Configuration.Json; +//using Newtonsoft.Json; +//using Newtonsoft.Json.Linq; +//using SharpCompress.Common; +//using System.Reactive.Subjects; +//using Subject = IRaCIS.Core.Domain.Models.Subject; +//using IRaCIS.Core.Application.ViewModel; +//using Medallion.Threading; +//using IRaCIS.Core.Infrastructure; +//using EasyCaching.Core; +//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 EasyCaching.Core.DistributedLock; +//using IDistributedLockProvider = Medallion.Threading.IDistributedLockProvider; +//using DocumentFormat.OpenXml.InkML; + +//namespace IRaCIS.Application.Services +//{ +// [ApiExplorerSettings(GroupName = "Trial")] +// public class PatientService : BaseService +// { + +// private readonly IRepository _trialRepository; +// private readonly IRepository _patientRepository; +// private readonly IRepository _studyRepository; +// private readonly IRepository _subjectRepository; +// private readonly IRepository _subjectVisitRepository; +// private readonly IDistributedLockProvider _distributedLockProvider; + +// public PatientService(IRepository studyRepository, IRepository trialRepository, IRepository patientRepository, IRepository subjectRepository, IRepository subjectVisitRepository, IDistributedLockProvider distributedLockProvider) +// { +// _studyRepository = studyRepository; +// _trialRepository = trialRepository; +// _patientRepository = patientRepository; +// _subjectRepository = subjectRepository; +// _subjectVisitRepository = subjectVisitRepository; +// _distributedLockProvider = distributedLockProvider; +// } + + + + + +// #region 患者检查管理 + +// /// +// ///检查管理-> 检查Tab 患者列表 (带加入的项目信息 以及检查统计) 原型标注错误,不是检查列表 +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetPatientList(PatientTrialQuery inQuery) +// { + +// var isAdminOrOA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.Admin || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.OA || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin; + +// #region new ok +// var query = _patientRepository +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.PatientIdStr.Contains(inQuery.PatientIdStr)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.PatientName.Contains(inQuery.PatientName)) +// //.WhereIf(!string.IsNullOrWhiteSpace(inQuery.ExperimentName), t => t.SubjectPatientList.Any(t => t.Subject.Trial.ExperimentName.Contains(inQuery.ExperimentName))) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAE), t => t.SCPStudyList.Any(t => t.CallingAE == inQuery.CallingAE)) +// .WhereIf(inQuery.BeginPushTime != null, t => t.LatestPushTime >= inQuery.BeginPushTime) +// .WhereIf(inQuery.EndPushTime != null, t => t.LatestPushTime <= inQuery.EndPushTime); + +// foreach (var calledAE in inQuery.CalledAEList) +// { +// query = query.Where(t => t.SCPStudyList.Select(c => c.CalledAE).Contains(calledAE)); +// } + + +// var resultQuery = from patient in query + +// select new PatientTrialView() +// { +// PatientId = patient.Id, +// PatientBirthDate = patient.PatientBirthDate, +// CreateTime = patient.CreateTime, +// CalledAEList = patient.SCPStudyList.Select(t => t.CalledAE).Distinct().ToList(), +// CallingAEList = patient.SCPStudyList.Select(t => t.CallingAE).Distinct().ToList(), +// CreateUserId = patient.CreateUserId, +// UpdateTime = patient.UpdateTime, +// UpdateUserId = patient.UpdateUserId, + +// EarliestStudyTime = patient.EarliestStudyTime, +// LatestStudyTime = patient.LatestStudyTime, +// LatestPushTime = patient.LatestPushTime, +// PatientAge = patient.PatientAge, +// PatientName = patient.PatientName, +// PatientIdStr = patient.PatientIdStr, +// PatientSex = patient.PatientSex, + +// StudyCount = patient.SCPStudyList.Count(), + +// TrialList = patient.SubjectPatientList.Where(t => isAdminOrOA ? true : t.Subject.Trial.TrialUserList.Any(t => t.UserId == _userInfo.Id)).Select(c => new PatientTrialStatInfo() +// { +// ExperimentName = c.Subject.Trial.ExperimentName, +// VisitCount = c.Subject.SubjectVisitList.Count() +// }).ToList(), + +// }; + + + + +// var pageList = await resultQuery.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(PatientQueryView.PatientIdStr) : inQuery.SortField, inQuery.Asc); +// #endregion + + +// return ResponseOutput.Ok(pageList); +// } + + + + + +// /// +// /// 检查管理-> 获取患者检查列表(同步影像数据之前的) +// /// +// /// +// /// +// [HttpPost] +// public async Task> GetPatientStudyList(PatientStudyInfoQuery inQuery) +// { +// var query = from scpStudy in _studyRepository.Where(t => t.PatientId == inQuery.PatientId) +// .WhereIf(inQuery.EarliestStudyTime != null, t => t.StudyTime >= inQuery.EarliestStudyTime) +// .WhereIf(inQuery.LatestStudyTime != null, t => t.StudyTime <= inQuery.LatestStudyTime) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Modalities), t => t.Modalities.Contains(inQuery.Modalities)) +// select new PatientStudySimpleView() +// { +// Description = scpStudy.Description, +// CalledAE = scpStudy.CalledAE, +// CallingAE = scpStudy.CallingAE, +// InstanceCount = scpStudy.InstanceCount, +// Modalities = scpStudy.Modalities, +// PatientId = scpStudy.PatientId, +// SCPStudyId = scpStudy.Id, +// SeriesCount = scpStudy.SeriesCount, +// StudyTime = scpStudy.StudyTime, +// }; + + +// 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(); + +// return list; +// } + + + +// public async Task>> GetPatientSeriesList(Guid scpStudyId, +// [FromServices] IRepository _seriesRepository, +// [FromServices] IRepository _instanceRepository +// ) +// { + +// var seriesList = await _seriesRepository.Where(s => s.StudyId == scpStudyId).OrderBy(s => s.SeriesNumber). +// ThenBy(s => s.SeriesTime).ThenBy(s => s.CreateTime) +// .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + +// var 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); +// } + + + + + +// /// +// /// 清除该患者绑定的受试者的所有的数据、(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 _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.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(PatienSubejctView.Code) : inQuery.SortField, inQuery.Asc); + + +// 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.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(PatientQueryView.PatientIdStr) : inQuery.SortField, inQuery.Asc); + + +// return ResponseOutput.Ok(pageList); +// } + +// /// +// /// 受试者管理->患者列表 Dicom AE 下拉框数据获取 +// /// +// /// +// public async Task> GetDicomCalledAEList() +// { +// var list = await _studyRepository.Select(t => t.CalledAE).Distinct().ToListAsync(); + +// return list; +// } + +// public async Task> GetDicomCallingAEList() +// { +// var list = await _studyRepository.Select(t => t.CallingAE).Distinct().ToListAsync(); + +// return list; +// } + +// /// +// ///受试者管理-> 患者列表 模糊搜索下拉 选择subject 排除已绑定并提交的 +// /// +// /// +// /// +// [HttpPost] +// public async Task> GetTrialSubejctSelectList(SubjectSelectQuery inQuery) +// { +// var list = await _subjectRepository.Where(t => t.TrialId == inQuery.TrialId && t.Status == SubjectStatus.OnVisit) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Code.Contains(inQuery.SubjectCode)) +// .WhereIf(inQuery.SubjectId != null, t => t.Id == inQuery.SubjectId) +// //.Where(t => !t.SubjectVisitList.SelectMany(t => t.SCPStudySubjectVisitList).Any(c => c.StudyId != null)) +// .Select(t => new SubjectSelectDto() +// { +// SubejctId = t.Id, +// SubjectCode = t.Code, +// Status = t.Status, +// Sex = t.Sex, +// ShortName = t.ShortName, +// Age = t.Age, +// BirthDate = t.BirthDate, +// //PatientList = t.SubjectPatientList.Select(c => new PatienBasicInfo() { PatientId = c.PatientId, PatientIdStr = c.Patient.PatientIdStr }).ToList() +// }) +// .ToListAsync(); + +// return list; +// } + +// #endregion + + + +// #region 患者和受试者绑定,生成访视,预先绑定检查和访视 + + + + + + + +// /// +// /// 提交 患者检查和访视的绑定 +// /// +// /// +// /// +// [HttpPost] +// [UnitOfWork] +// [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] +// public async Task SubmitVisitStudyBinding(SubmitVisitStudyBindingCommand inCommand, [FromServices] IOptionsMonitor _basicSystemConfigConfig) +// { + +// //防止访视重复 +// 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) +// { + + +// 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] +// 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.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); + + + +// return ResponseOutput.Ok(pageList); +// } + + +// /// +// ///访视管理-> 获取当前访视 已绑定的患者检查 (从访视列表 进入修改绑定) +// /// +// /// +// /// +// [HttpPost] +// public async Task> GetCurrentVisitPatientStudyList(SubjectVisitStudyQuery inQuery) +// { +// var patientQuery = _studySubjectVisitRepository.Where(t => t.SubjectVisitId == inQuery.SujectVisitId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted) +// //.WhereIf(inQuery.SubmitState != null, u => u.SubjectVisit.SubmitState == inQuery.SubmitState) +// .ProjectTo(_mapper.ConfigurationProvider); + +// var sortField = string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(VisitPatientStudyView.StudyTime) : inQuery.SortField; +// var orderQuery = inQuery.Asc ? patientQuery.OrderBy(sortField) : patientQuery.OrderBy(sortField + " desc"); + +// var list = await orderQuery.ToListAsync(); + +// return list; +// } + + +// /// +// /// 访视管理-> 获取可选访视列表 (从访视列表 进入修改绑定) +// /// +// /// +// /// +// [HttpPost] +// public async Task> GetPatientOtherStudyList(PatientStudyOtherQuery inQuery) +// { +// var query = from scpStudy in _studyRepository.Where(t => inQuery.PatientIdList.Contains(t.PatientId) && !t.SCPStudySubjectVisitList.Any(t => (t.SubjectVisitId == inQuery.SujectVisitId || t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted) && t.TrialId == inQuery.TrialId)) +// .WhereIf(inQuery.EarliestStudyTime != null, t => t.StudyTime >= inQuery.EarliestStudyTime) +// .WhereIf(inQuery.LatestStudyTime != null, t => t.StudyTime <= inQuery.LatestStudyTime) +// .WhereIf(!string.IsNullOrEmpty(inQuery.PatientIdStr), t => t.PatientIdStr.Contains(inQuery.PatientIdStr)) +// //不属于该访视的检查 或者未绑定的检查 +// join scpStudySubjectVisit in _studySubjectVisitRepository.Where(c => c.TrialId == inQuery.TrialId) on scpStudy.Id equals scpStudySubjectVisit.SCPStudyId +// into dd +// from scpStudySV in dd.DefaultIfEmpty() +// select new VisitPatientStudyView() +// { +// PatientIdStr = scpStudy.PatientIdStr, +// PatientBirthDate = scpStudy.PatientBirthDate, +// PatientAge = scpStudy.PatientAge, +// PatientName = scpStudy.PatientName, +// PatientSex = scpStudy.PatientSex, +// Description = scpStudy.Description, +// CalledAE = scpStudy.CalledAE, +// CallingAE = scpStudy.CallingAE, +// InstanceCount = scpStudy.InstanceCount, +// Modalities = scpStudy.Modalities, +// PatientId = scpStudy.PatientId, +// SCPStudyId = scpStudy.Id, +// SeriesCount = scpStudy.SeriesCount, +// StudyTime = scpStudy.StudyTime, + +// SubmitState = scpStudySV.SubjectVisit.SubmitState, +// SubjectVisitId = scpStudySV.SubjectVisitId, +// VisitName = scpStudySV.SubjectVisit.VisitName, +// }; + +// #region 废弃 +// //var notCurrentVisitQuey = _studySubjectVisitRepository.Where(t => t.SubjectVisitId != inQuery.SujectVisitId && t.SCPStudy.Patient.Id == inQuery.PatientId) +// // .Select(t => new VisitPatientStudyView() +// // { +// // Description = t.SCPStudy.Description, +// // CalledAE = t.SCPStudy.CalledAE, +// // InstanceCount = t.SCPStudy.InstanceCount, +// // Modalities = t.SCPStudy.Modalities, +// // PatientId = t.SCPStudy.PatientId, +// // SCPStudyId = t.SCPStudy.PatientId, +// // SeriesCount = t.SCPStudy.SeriesCount, +// // StudyTime = t.SCPStudy.StudyTime, +// // SubjectVisitId = t.SubjectVisitId, +// // VisitName = t.SubjectVisit.VisitName, +// // }); + +// //var notBindQuery= _studyRepository.Where(t => t.PatientId == inQuery.PatientId && t.pa) + +// //var patientQuery = query + +// // .ProjectTo(_mapper.ConfigurationProvider); +// #endregion +// var sortField = string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(VisitPatientStudyView.StudyTime) : inQuery.SortField; +// var orderQuery = inQuery.Asc ? query.OrderBy(sortField) : query.OrderBy(sortField + " desc"); + +// var list = await orderQuery.ToListAsync(); + +// return list; +// } + +// #endregion + +// #region 检查管理 + + + +// /// +// ///检查管理-> 检查列表 (同步影像数据之前的) +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetPatientStudyBeforeConfirmList(TrialPatientStudyQuery inQuery) +// { +// #region 只查询已绑定的 +// //var query = _studySubjectVisitRepository.Where(t => t.SubjectVisit.TrialId == inQuery.TrialId) +// // .WhereIf(inQuery.PatientId != null, t => t.SCPStudy.PatientId == inQuery.PatientId) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.SCPStudy.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.SCPStudy.Patient.PatientSex.Contains(inQuery.PatientSex)) +// // .WhereIf(inQuery.SubjectAge != null, t => t.SubjectVisit.Subject.Age == inQuery.SubjectAge) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.SubjectVisit.Subject.Sex.Contains(inQuery.SubjectSex)) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.SubjectVisit.Subject.ShortName.Contains(inQuery.SubjectShortName)) +// // .WhereIf(inQuery.BeginStudyTime != null, t => t.SCPStudy.StudyTime >= inQuery.BeginStudyTime) +// // .WhereIf(inQuery.EndStudyTime != null, t => t.SCPStudy.StudyTime <= inQuery.EndStudyTime) +// // .Select(t => new PatientStudyBeforeConfirmView() +// // { +// // SubjectId = t.SubjectVisit.SubjectId, +// // SubjectAge = t.SubjectVisit.Subject.Age, +// // SubjectSex = t.SubjectVisit.Subject.Sex, +// // SubjectShortName = t.SubjectVisit.Subject.ShortName, + + +// // PatientId = t.SCPStudy.PatientId, +// // PatientAge = t.SCPStudy.PatientAge, +// // PatientBirthDate = t.SCPStudy.PatientBirthDate, +// // PatientIdStr = t.SCPStudy.PatientIdStr, +// // PatientSex = t.SCPStudy.PatientSex, + +// // //PatientList = t.SubjectVisit.Subject.SubjectPatientList.Select(t => new PatientBasicInfo() +// // //{ +// // // PatientId = t.PatientId, +// // // PatientAge = t.Patient.PatientAge, +// // // PatientBirthDate = t.Patient.PatientBirthDate, +// // // PatientIdStr = t.Patient.PatientIdStr, +// // // PatientSex = t.Patient.PatientSex, +// // //}).ToList(), + +// // SubjectCode = t.SubjectVisit.Subject.Code, +// // SubmitState = t.SubjectVisit.SubmitState, +// // SubmitTime = t.SubjectVisit.SubmitTime, +// // VisitName = t.SubjectVisit.VisitName, +// // SubjectVisitId = t.SubjectVisitId, +// // VisitEarliestStudyTime = t.SubjectVisit.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime), +// // VisitLatestStudyTime = t.SubjectVisit.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime), + +// // StudyId = t.SCPStudyId, +// // StudyTime = t.SCPStudy.StudyTime, +// // CallingAE = t.SCPStudy.CallingAE, +// // CalledAE = t.SCPStudy.CalledAE + +// // }); +// #endregion + +// var query = from scpStudy in _studyRepository.Where(t => !t.SCPStudySubjectVisitList.Any(t => t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted && t.TrialId == inQuery.TrialId)) +// .WhereIf(inQuery.IsBindedVisit == false, t => !t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId)) +// .WhereIf(inQuery.IsBindedVisit == true, t => t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.VisitName), t => t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId && t.SubjectVisit.VisitName.Contains(inQuery.VisitName))) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Patient.PatientSex.Contains(inQuery.PatientSex)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) +// .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) +// .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) +// join subjectPatient in _subjectPatientRepository.Where(t => t.Subject.TrialId == inQuery.TrialId) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Subject.Code.Contains(inQuery.SubjectCode)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.Subject.Sex.Contains(inQuery.SubjectSex)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.Subject.ShortName.Contains(inQuery.SubjectShortName)) +// on scpStudy.PatientId equals subjectPatient.PatientId +// join scpStudySubjectVisit in _studySubjectVisitRepository.Where(t => t.TrialId == inQuery.TrialId) +// on scpStudy.Id equals scpStudySubjectVisit.SCPStudyId into dd +// from scpStudySV in dd.DefaultIfEmpty() +// select new PatientStudyBeforeConfirmView() +// { +// SubjectId = subjectPatient.Subject.Id, +// SubjectAge = subjectPatient.Subject.Age, +// SubjectSex = subjectPatient.Subject.Sex, +// SubjectShortName = subjectPatient.Subject.ShortName, +// SubjectCode = subjectPatient.Subject.Code, + +// PatientId = scpStudy.PatientId, +// PatientName = scpStudy.PatientName, +// PatientAge = scpStudy.PatientAge, +// PatientBirthDate = scpStudy.PatientBirthDate, +// PatientIdStr = scpStudy.PatientIdStr, +// PatientSex = scpStudy.PatientSex, + +// //PatientList = t.SubjectVisit.Subject.SubjectPatientList.Select(t => new PatientBasicInfo() +// //{ +// // PatientId = t.PatientId, +// // PatientAge = t.Patient.PatientAge, +// // PatientBirthDate = t.Patient.PatientBirthDate, +// // PatientIdStr = t.Patient.PatientIdStr, +// // PatientSex = t.Patient.PatientSex, +// //}).ToList(), + + +// SubmitState = scpStudySV.SubjectVisit.SubmitState, +// SubmitTime = scpStudySV.SubjectVisit.SubmitTime, +// VisitName = scpStudySV.SubjectVisit.VisitName, +// VisitNum = scpStudySV.SubjectVisit.VisitNum, +// SubjectVisitId = scpStudySV.SubjectVisit.Id, +// VisitEarliestStudyTime = scpStudySV.SubjectVisit.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime), +// VisitLatestStudyTime = scpStudySV.SubjectVisit.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime), + +// StudyId = scpStudy.Id, +// StudyTime = scpStudy.StudyTime, +// CallingAE = scpStudy.CallingAE, +// CalledAE = scpStudy.CalledAE, +// Description = scpStudy.Description, +// InstanceCount = scpStudy.InstanceCount, +// Modalities = scpStudy.Modalities, +// ModalityForEdit = scpStudy.ModalityForEdit, +// SeriesCount = scpStudy.SeriesCount + +// }; + +// var defalutSortArray = new string[] { nameof(PatientStudyBeforeConfirmView.SubjectCode), nameof(PatientStudyBeforeConfirmView.VisitNum), nameof(PatientStudyBeforeConfirmView.StudyTime) }; +// var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); + + + +// return ResponseOutput.Ok(pageList); +// } + + +// /// +// ///检查管理-> 检查列表 (同步影像数据之后的 带患者信息 患者信息是数组) +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetTrialPatientStudyList(TrialPatientStudyQuery inQuery) +// { +// var query = _repository.Where(t => t.SubjectVisit.TrialId == inQuery.TrialId) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.PatientIdStr.Contains(inQuery.PatientIdStr)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.PatientSex.Contains(inQuery.PatientSex)) +// .WhereIf(inQuery.SubjectAge != null, t => t.SubjectVisit.Subject.Age == inQuery.SubjectAge) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.SubjectVisit.Subject.Sex.Contains(inQuery.SubjectSex)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.SubjectVisit.Subject.ShortName.Contains(inQuery.SubjectShortName)) +// .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) +// .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) +// .Select(t => new PatientStudyView() +// { +// SubjectId = t.SubjectVisit.SubjectId, +// SubjectAge = t.Subject.Age, +// SubjectSex = t.Subject.Sex, +// SubjectShortName = t.Subject.ShortName, + + +// //PatientId = Guid.Empty, +// PatientAge = t.PatientAge, +// PatientName = t.PatientName, +// PatientBirthDate = t.PatientBirthDate, +// PatientIdStr = t.PatientIdStr, +// PatientSex = t.PatientSex, + +// //PatientList = t.Subject.SubjectPatientList.Select(t => new PatientBasicInfo() +// //{ +// // PatientId = t.PatientId, +// // PatientAge = t.Patient.PatientAge, +// // PatientBirthDate = t.Patient.PatientBirthDate, +// // PatientIdStr = t.Patient.PatientIdStr, +// // PatientSex = t.Patient.PatientSex, +// //}).ToList(), + +// Modalities = t.Modalities, +// ModalityForEdit = t.ModalityForEdit, +// SubjectCode = t.SubjectVisit.Subject.Code, +// SubmitState = t.SubjectVisit.SubmitState, +// SubmitTime = t.SubjectVisit.SubmitTime, +// VisitName = t.SubjectVisit.VisitName, +// VisitNum = t.SubjectVisit.VisitNum, +// SubjectVisitId = t.SubjectVisitId, +// VisitEarliestStudyTime = t.SubjectVisit.StudyList.Min(t => t.StudyTime), +// VisitLatestStudyTime = t.SubjectVisit.StudyList.Max(t => t.StudyTime), + +// StudyId = t.Id, +// StudyTime = t.StudyTime, +// Description = t.Description, +// SeriesCount = t.SeriesCount, +// InstanceCount = t.InstanceCount, +// }); + +// var defalutSortArray = new string[] { nameof(PatientStudyView.SubjectCode), nameof(PatientStudyView.VisitNum), nameof(PatientStudyView.StudyTime) }; + +// var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); + + +// return ResponseOutput.Ok(pageList); +// } + + + +// /// +// /// 获取该项目 患者已绑定subject ,新来了的检查 可能需要新建访视 但是新增的检查未绑定访视的检查列表 +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetTrialUnbindSubjectVisitStudyList(TrialPatientStudyQuery inQuery) +// { +// //属于该项目的已绑定患者的检查,同时没有绑定任何访视 +// var query = from scpStudy in _studyRepository.Where(t => !t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Patient.PatientSex.Contains(inQuery.PatientSex)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) +// .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) +// .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) +// join subjectPatient in _subjectPatientRepository.Where(t => t.Subject.TrialId == inQuery.TrialId) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Subject.Code.Contains(inQuery.SubjectCode)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.Subject.Sex.Contains(inQuery.SubjectSex)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.Subject.ShortName.Contains(inQuery.SubjectShortName)) +// on scpStudy.PatientId equals subjectPatient.PatientId +// join scpStudySubjectVisit in _studySubjectVisitRepository.AsQueryable() on scpStudy.Id equals scpStudySubjectVisit.SCPStudyId +// into dd +// from scpStudySV in dd.DefaultIfEmpty() +// select new UnbindStudyView() +// { +// PatientIdStr = scpStudy.PatientIdStr, +// PatientBirthDate = scpStudy.PatientBirthDate, +// PatientAge = scpStudy.PatientAge, +// PatientName = scpStudy.PatientName, +// PatientSex = scpStudy.PatientSex, +// Description = scpStudy.Description, +// CalledAE = scpStudy.CalledAE, +// InstanceCount = scpStudy.InstanceCount, +// Modalities = scpStudy.Modalities, +// PatientId = scpStudy.PatientId, +// SCPStudyId = scpStudy.Id, +// SeriesCount = scpStudy.SeriesCount, +// StudyTime = scpStudy.StudyTime, + +// SubjectVisitId = scpStudySV.SubjectVisitId, +// VisitName = scpStudySV.SubjectVisit.VisitName, + +// SubjectId = subjectPatient.SubjectId, +// SubjectCode = subjectPatient.Subject.Code, +// TrialId = subjectPatient.Subject.TrialId, +// SubjectAge = subjectPatient.Subject.Age, +// SubjectSex = subjectPatient.Subject.Sex, +// SubjectShortName = subjectPatient.Subject.ShortName, +// SubjectBirthDate = subjectPatient.Subject.BirthDate +// }; + +// #region 废弃 +// //var query = from subject in _subjectRepository.Where(t => t.TrialId == inQuery.TrialId) +// // .WhereIf(inQuery.SubjectAge != null, t => t.Age == inQuery.SubjectAge) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Code.Contains(inQuery.SubjectCode)) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.Sex.Contains(inQuery.SubjectSex)) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.ShortName.Contains(inQuery.SubjectShortName)) +// // join subjectPatient in _subjectPatientRepository.AsQueryable() on subject.Id equals subjectPatient.PatientId +// // //没有绑定任何访视 +// // join scpStudy in _studyRepository.AsQueryable() +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Patient.PatientSex.Contains(inQuery.PatientSex)) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) +// // .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) +// // .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) +// // on subjectPatient.PatientId equals scpStudy.PatientId +// // select new SubjectPatientStudyView() +// // { +// // SubjectId = subject.Id, +// // SubjectAge = subject.Age, +// // SubjectSex = subject.Sex, +// // SubjectShortName = subject.ShortName, + +// // PatientList = subject.SubjectPatientList.Select(t => new PatientBasicInfo() +// // { +// // PatientId = t.PatientId, +// // PatientAge = t.Patient.PatientAge, +// // PatientBirthDate = t.Patient.PatientBirthDate, +// // PatientIdStr = t.Patient.PatientIdStr, +// // PatientSex = t.Patient.PatientSex, +// // }).ToList(), + +// // SubjectCode = subject.Code, + +// // SeriesCount = scpStudy.SeriesCount, +// // CalledAE = scpStudy.CalledAE, +// // InstanceCount = scpStudy.InstanceCount, +// // Description = scpStudy.Description, +// // Modalities = scpStudy.Modalities, +// // PatientId = scpStudy.PatientId, + +// // SCPStudyId = scpStudy.Id, +// // StudyTime = scpStudy.StudyTime + +// // }; +// #endregion + + + + + + +// var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(UnbindStudyView.StudyTime) : inQuery.SortField, inQuery.Asc); + + +// return ResponseOutput.Ok(pageList); +// } + +// /// +// /// 删除某个项目 未提交的访视检查绑定, 清理数据,方便测试自动绑定 +// /// +// /// +// /// +// [HttpDelete] +// public async Task DeleteUnSubmittedStudyBind(Guid trialId, Guid? subjectId, +// [FromServices] IRepository _visitTaskRepository, +// [FromServices] IRepository _dicomStudyRepository, +// [FromServices] IRepository _dicomSeriesRepository, +// [FromServices] IRepository _dicomInstanceRepository) +// { +// if (subjectId != null) +// { +// await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.TrialId == trialId && t.SubjectId == subjectId); + +// await _subjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); + +// await _visitTaskRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); + +// await _dicomStudyRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); +// await _dicomSeriesRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); +// await _dicomInstanceRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); + +// } +// else +// { +// await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.TrialId == trialId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted); + +// } + + +// return ResponseOutput.Ok(); +// } + + +// /// +// /// 阅片管理-> 任务列表 +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetPatientVisitTaskList([FromServices] IRepository _visitTaskRepository, PatientVisitTaskQuery inQuery) +// { +// var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsAnalysisCreate == false) +// .WhereIf(inQuery.ReadingCategory != null, t => t.ReadingCategory == inQuery.ReadingCategory) +// .WhereIf(inQuery.ReadingCategory == null, t => t.ReadingCategory != ReadingCategory.Judge) + +// .WhereIf(inQuery.ReadingTaskState != null, t => t.ReadingTaskState == inQuery.ReadingTaskState) +// .WhereIf(inQuery.TaskState != null, t => t.TaskState == inQuery.TaskState) +// .WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId) +// .WhereIf(inQuery.DoctorUserId != null, t => t.DoctorUserId == inQuery.DoctorUserId) +// .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) + +// .WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.TaskName.Contains(inQuery.TaskName) || t.TaskBlindName.Contains(inQuery.TaskName)) +// .WhereIf(inQuery.BeginSignTime != null, t => t.SignTime >= inQuery.BeginSignTime) +// .WhereIf(inQuery.EndSignTime != null, t => t.SignTime <= inQuery.EndSignTime) + +// .WhereIf(inQuery.BeginTaskCreateTime != null, t => t.CreateTime >= inQuery.BeginTaskCreateTime) +// .WhereIf(inQuery.EndTaskCreateTime != null, t => t.CreateTime <= inQuery.EndTaskCreateTime) + +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientIdStr.Contains(inQuery.PatientIdStr))) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientName.Contains(inQuery.PatientName))) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientSex.Contains(inQuery.PatientSex))) +// .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectShortName), t => t.Subject.ShortName.Contains(inQuery.SubjectShortName)) +// .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => (t.Subject.Code.Contains(inQuery.SubjectCode) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(inQuery.SubjectCode) && t.IsAnalysisCreate)) +// .ProjectTo(_mapper.ConfigurationProvider); + +// var defalutSortArray = new string[] { nameof(PatientVisitTaskDTO.SubjectId), nameof(PatientVisitTaskDTO.VisitTaskNum) }; + +// var pageList = await visitTaskQueryable.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); + +// return ResponseOutput.Ok(pageList); +// } + + +// #endregion + + + + + +// /// +// /// scp 影像推送记录表 +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetSCPImageUploadList(SCPImageUploadQuery inQuery) +// { +// var query = _repository.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.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(SCPImageUploadView.CallingAE) : inQuery.SortField, inQuery.Asc); + + +// return ResponseOutput.Ok(pageList); +// } + + + + +// } + + + +//} diff --git a/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj b/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj index f2772d9ce..f2b268e49 100644 --- a/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj +++ b/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj @@ -9,13 +9,13 @@ - - + + - - + + - + From a615b5cefdcb965be7c6e6825f9cbd6aba5c3bab Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Wed, 3 Jul 2024 14:45:24 +0800 Subject: [PATCH 6/9] =?UTF-8?q?site=20dicomAE=20=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TrialSiteUser/DTO/TrialSiteDicomAEViewModel.cs | 7 ++++++- .../TrialSiteUser/DTO/UserTrialViewModel.cs | 2 +- .../Interface/ITrialSiteDicomAEService.cs | 2 +- .../Service/TrialSiteUser/TrialDicomAEService.cs | 2 +- .../TrialSiteUser/TrialSiteDicomAEService.cs | 14 ++++++++------ .../Service/TrialSiteUser/_MapConfig.cs | 3 ++- IRaCIS.Core.Domain/TrialSiteUser/TrialSite.cs | 2 ++ .../TrialSiteUser/TrialSiteDicomAE.cs | 4 ++++ 8 files changed, 25 insertions(+), 11 deletions(-) diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialSiteDicomAEViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialSiteDicomAEViewModel.cs index 94ac1e703..568583c26 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialSiteDicomAEViewModel.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialSiteDicomAEViewModel.cs @@ -6,6 +6,7 @@ using System; using IRaCIS.Core.Domain.Share; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; namespace IRaCIS.Core.Application.ViewModel { /// TrialSiteDicomAEView 列表视图模型 @@ -21,8 +22,12 @@ namespace IRaCIS.Core.Application.ViewModel } ///TrialSiteDicomAEQuery 列表查询参数模型 - public class TrialSiteDicomAEQuery : PageInput + public class TrialSiteDicomAEQuery /*: PageInput*/ { + [NotDefault] + + public Guid TrialSiteId { get; set; } + public string? CallingAE { get; set; } public string? IP { get; set; } diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs index a3cd5fa36..a2fdd4c03 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs @@ -259,7 +259,7 @@ namespace IRaCIS.Application.Contracts //public string ContactPhone { get; set; } = String.Empty; //public string Address { get; set; } = String.Empty; - + public List CallingAEList { get; set; } public List UserNameList { get; set; } = new List(); public int? VisitCount { get; set; } diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialSiteDicomAEService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialSiteDicomAEService.cs index a6c2323a6..c6c6fc713 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialSiteDicomAEService.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialSiteDicomAEService.cs @@ -13,7 +13,7 @@ namespace IRaCIS.Core.Application.Interfaces public interface ITrialSiteDicomAEService { - Task> GetTrialSiteDicomAEList(TrialSiteDicomAEQuery inQuery); + Task> GetTrialSiteDicomAEList(TrialSiteDicomAEQuery inQuery); Task AddOrUpdateTrialSiteDicomAE(TrialSiteDicomAEAddOrEdit addOrEditTrialSiteDicomAE); diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialDicomAEService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialDicomAEService.cs index 035be1e3e..982bab572 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialDicomAEService.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialDicomAEService.cs @@ -57,7 +57,7 @@ namespace IRaCIS.Core.Application.Service /// /// /// - public async Task> GetTrialDicomAEList(Guid trialId) + public async Task> GetTrialDicomAE(Guid trialId) { var dicomAE = _dicomAERepository.Where(t => t.TrialId == trialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); var trialConfig = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.IsPACSConnect, t.IsTrialPACSConfirmed }); diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialSiteDicomAEService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialSiteDicomAEService.cs index 2cfec8f8f..7b5318d44 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialSiteDicomAEService.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialSiteDicomAEService.cs @@ -25,23 +25,25 @@ namespace IRaCIS.Core.Application.Service } [HttpPost] - public async Task> GetTrialSiteDicomAEList(TrialSiteDicomAEQuery inQuery) + public async Task> GetTrialSiteDicomAEList(TrialSiteDicomAEQuery inQuery) { var trialSiteDicomAEQueryable = - _trialSiteDicomAERepository + _trialSiteDicomAERepository.Where(t=>t.TrialSiteId==inQuery.TrialSiteId) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.IP), t => t.IP.Contains(inQuery.IP)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Port), t => t.Port.Contains(inQuery.Port)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Description), t => t.Description.Contains(inQuery.Description)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAE), t => t.CallingAE.Contains(inQuery.CallingAE)) .ProjectTo(_mapper.ConfigurationProvider); - var pageList = await trialSiteDicomAEQueryable - .ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(TrialSiteDicomAEView.Id) : inQuery.SortField, - inQuery.Asc); + //var pageList = await trialSiteDicomAEQueryable + //.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(TrialSiteDicomAEView.Id) : inQuery.SortField, + //inQuery.Asc); - return pageList; + var list = await trialSiteDicomAEQueryable.ToListAsync(); + + return list; } diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs index 2e38d4476..461523bfa 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs @@ -141,7 +141,8 @@ namespace IRaCIS.Core.Application.Service .ForMember(d => d.UserCount, u => u.MapFrom(s => s.CRCUserList.Count())) .ForMember(d => d.VisitCount, u => u.MapFrom(s => s.SubjectVisitList.Count())) .ForMember(d => d.SubjectCount, u => u.MapFrom(s => s.SubjectList.Count())) - .ForMember(d => d.UserNameList, u => u.MapFrom(s => s.CRCUserList.Where(t => t.IsDeleted == false).Select(u => u.User.FullName))); + .ForMember(d => d.UserNameList, u => u.MapFrom(s => s.CRCUserList.Where(t => t.IsDeleted == false).Select(u => u.User.FullName))) + .ForMember(d => d.CallingAEList, u => u.MapFrom(s => s.TrialSiteDicomAEList.Select(u => u.CallingAE))); //CreateMap(); diff --git a/IRaCIS.Core.Domain/TrialSiteUser/TrialSite.cs b/IRaCIS.Core.Domain/TrialSiteUser/TrialSite.cs index 63da78434..8812dd62b 100644 --- a/IRaCIS.Core.Domain/TrialSiteUser/TrialSite.cs +++ b/IRaCIS.Core.Domain/TrialSiteUser/TrialSite.cs @@ -55,7 +55,9 @@ namespace IRaCIS.Core.Domain.Models [JsonIgnore] public List SubjectList { get; set; } + [JsonIgnore] + public List TrialSiteDicomAEList { get; set; } } } \ No newline at end of file diff --git a/IRaCIS.Core.Domain/TrialSiteUser/TrialSiteDicomAE.cs b/IRaCIS.Core.Domain/TrialSiteUser/TrialSiteDicomAE.cs index a23e4d5b6..1ef1446f5 100644 --- a/IRaCIS.Core.Domain/TrialSiteUser/TrialSiteDicomAE.cs +++ b/IRaCIS.Core.Domain/TrialSiteUser/TrialSiteDicomAE.cs @@ -53,6 +53,10 @@ namespace IRaCIS.Core.Domain.Models public string Description { get; set; } + + + + public TrialSite TrialSite { get; set; } } From 0c7495870b392ad5329c53387c79f2bbc80d616f Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Thu, 4 Jul 2024 13:12:10 +0800 Subject: [PATCH 7/9] =?UTF-8?q?pacs=20=20=E7=9B=B4=E8=BF=9E=20=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IRC.Core.SCP/Service/CStoreSCPService.cs | 86 +- IRC.Core.SCP/Service/DicomArchiveService.cs | 12 +- .../Service/Interface/IDicomArchiveService.cs | 2 +- .../IRaCIS.Core.Application.xml | 37 +- .../Service/Visit/DTO/PatientViewModel.cs | 88 +- .../Service/Visit/PatientService.cs | 1581 ++++------------- .../Service/Visit/_MapConfig.cs | 8 + IRaCIS.Core.Domain/Image/DicomStudy.cs | 3 + IRaCIS.Core.Domain/Image/SCPImageUpload.cs | 13 + IRaCIS.Core.Domain/Image/SCPPatient.cs | 10 +- IRaCIS.Core.Domain/Image/SCPStudy.cs | 10 +- 11 files changed, 516 insertions(+), 1334 deletions(-) diff --git a/IRC.Core.SCP/Service/CStoreSCPService.cs b/IRC.Core.SCP/Service/CStoreSCPService.cs index a81e11ec1..261ca563b 100644 --- a/IRC.Core.SCP/Service/CStoreSCPService.cs +++ b/IRC.Core.SCP/Service/CStoreSCPService.cs @@ -24,39 +24,7 @@ using IRaCIS.Core.Infrastructure; namespace IRaCIS.Core.SCP.Service { - /// - /// 后台托管服务的方式运行 - /// - //public class CStoreSCPHostedService : IHostedService - //{ - // private readonly ILogger _logger; - // private readonly IDicomServerFactory _dicomServerFactory; - // private IDicomServer? _server; - // public CStoreSCPHostedService(ILogger logger, IDicomServerFactory dicomServerFactory) - // { - // _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - // _dicomServerFactory = dicomServerFactory ?? throw new ArgumentNullException(nameof(dicomServerFactory)); - // } - - // public async Task StartAsync(CancellationToken cancellationToken) - // { - // _logger.LogInformation("Starting DICOM server"); - // _server = _dicomServerFactory.Create(104); - // _logger.LogInformation("DICOM server is running"); - // } - - // public Task StopAsync(CancellationToken cancellationToken) - // { - // if (_server != null) - // { - // _server.Stop(); - // _server.Dispose(); - // _server = null; - // } - // return Task.CompletedTask; - // } - //} public class DicomSCPServiceOption { public List CalledAEList { get; set; } @@ -75,6 +43,11 @@ namespace IRaCIS.Core.SCP.Service private SCPImageUpload _upload { get; set; } + private Guid _trialId { get; set; } + + private Guid _trialSiteId { get; set; } + + private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[] { @@ -121,15 +94,37 @@ namespace IRaCIS.Core.SCP.Service _serviceProvider = (IServiceProvider)this.UserState; - var option = _serviceProvider.GetService>().CurrentValue; + var _trialDicomAERepository = _serviceProvider.GetService>(); - var calledAEList = option.CalledAEList; - if (!calledAEList.Contains(association.CalledAE)) + var trialDicomAEList = _trialDicomAERepository.Select(t => new { t.CalledAE, t.TrialId }).ToList(); + var trialCalledAEList = trialDicomAEList.Select(t => t.CalledAE).ToList(); - //if (association.CalledAE != "STORESCP") + var findCalledAE = trialDicomAEList.Where(t => t.CalledAE == association.CalledAE).FirstOrDefault(); + + var isCanReceiveIamge = false; + + if (findCalledAE!=null) + { + _trialId = findCalledAE.TrialId; + + var _trialSiteDicomAERepository = _serviceProvider.GetService>(); + + + var findTrialSiteAE = _trialSiteDicomAERepository.Where(t=>t.CallingAE==association.CallingAE ).FirstOrDefault(); + + if (findTrialSiteAE!=null) + { + _trialSiteId= findTrialSiteAE.TrialSiteId; + + isCanReceiveIamge = true; + } + } + + + if (!trialCalledAEList.Contains(association.CalledAE) || isCanReceiveIamge==false) { Log.Logger.Warning($"拒绝CalledAE:{association.CalledAE}的连接"); @@ -152,6 +147,8 @@ namespace IRaCIS.Core.SCP.Service } } + + return SendAssociationAcceptAsync(association); } @@ -167,7 +164,7 @@ namespace IRaCIS.Core.SCP.Service _upload.EndTime = DateTime.Now; _upload.StudyCount = _SCPStudyIdList.Count; - await _SCPImageUploadRepository.AddAsync(_upload,true); + await _SCPImageUploadRepository.AddAsync(_upload, true); await SendAssociationReleaseResponseAsync(); } @@ -175,11 +172,9 @@ namespace IRaCIS.Core.SCP.Service private async Task DataMaintenanceAsaync() { - Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality 以及自动创建访视,绑定检查"); + Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality"); - //var patientStudyService = _serviceProvider.GetService(); - //await patientStudyService.AutoBindingPatientStudyVisitAsync(_SCPStudyIdList); //处理检查Modality var _dictionaryRepository = _serviceProvider.GetService>(); @@ -262,7 +257,7 @@ namespace IRaCIS.Core.SCP.Service var _distributedLockProvider = _serviceProvider.GetService(); var storeRelativePath = string.Empty; - var ossFolderPath = $"Dicom/{studyInstanceUid}"; + var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}{studyInstanceUid}"; long fileSize = 0; @@ -295,7 +290,7 @@ namespace IRaCIS.Core.SCP.Service { try { - var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, storeRelativePath, Association.CallingAE, Association.CalledAE); + var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset,_trialId,_trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE); if (!_SCPStudyIdList.Contains(scpStudyId)) { @@ -324,11 +319,6 @@ namespace IRaCIS.Core.SCP.Service series.ImageResizePath = seriesPath; - - - //await _seriesRepository.BatchUpdateNoTrackingAsync(t => t.Id == seriesId, u => new SCPSeries() { ImageResizePath = seriesPath }); - - } } @@ -347,7 +337,7 @@ namespace IRaCIS.Core.SCP.Service //监控信息设置 _upload.FileCount++; - _upload.FileSize= _upload.FileSize+ fileSize; + _upload.FileSize = _upload.FileSize + fileSize; return new DicomCStoreResponse(request, DicomStatus.Success); } diff --git a/IRC.Core.SCP/Service/DicomArchiveService.cs b/IRC.Core.SCP/Service/DicomArchiveService.cs index 439741952..6eb9a092a 100644 --- a/IRC.Core.SCP/Service/DicomArchiveService.cs +++ b/IRC.Core.SCP/Service/DicomArchiveService.cs @@ -50,7 +50,7 @@ namespace IRaCIS.Core.SCP.Service /// /// /// - public async Task ArchiveDicomFileAsync(DicomDataset dataset, string fileRelativePath, string callingAE, string calledAE) + public async Task ArchiveDicomFileAsync(DicomDataset dataset, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE) { string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID); string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID); @@ -59,9 +59,9 @@ namespace IRaCIS.Core.SCP.Service string patientIdStr = dataset.GetString(DicomTag.PatientID); //Guid patientId= IdentifierHelper.CreateGuid(patientIdStr); - Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid); - Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid); - Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid); + Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid,trialId.ToString()); + Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString()); + Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString()); var isStudyNeedAdd = false; var isSeriesNeedAdd = false; @@ -88,6 +88,8 @@ namespace IRaCIS.Core.SCP.Service findPatient = new SCPPatient() { Id = NewId.NextSequentialGuid(), + 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), @@ -145,6 +147,8 @@ namespace IRaCIS.Core.SCP.Service PatientId = findPatient.Id, Id = studyId, + TrialId = trialId, + TrialSiteId = trialSiteId, StudyInstanceUid = studyInstanceUid, StudyTime = studyTime, Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty), diff --git a/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs b/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs index 970b3e146..1c107e94b 100644 --- a/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs +++ b/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs @@ -5,7 +5,7 @@ namespace IRaCIS.Core.SCP.Service { public interface IDicomArchiveService { - Task ArchiveDicomFileAsync(DicomDataset dicomDataset,string fileRelativePath,string callingAE,string calledAE); + Task ArchiveDicomFileAsync(DicomDataset dicomDataset,Guid trialId,Guid trialSiteId, string fileRelativePath,string callingAE,string calledAE); } } diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml index 8232128a0..ebd172a4c 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml @@ -9818,7 +9818,7 @@ DicomAEService - + 获取项目dicom AE 配置信息,otherinfo里面有IsPACSConnect IsTrialPACSConfirmed @@ -15048,6 +15048,41 @@ 9-拒绝入组,10-确认入组 + + + scp 影像推送记录表 + + + + + + + 影像检查列表-患者为维度组织 + + + + + + + 影像检查列表-> 获取患者的检查列表 + + + + + + + 影像访视上传 检查列表 + + + + + + + 提交 患者检查和访视的绑定 + + + + 添加或更新受试者信息[New] diff --git a/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs b/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs index 72b1ee836..0c3c515f8 100644 --- a/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs +++ b/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs @@ -224,31 +224,41 @@ namespace IRaCIS.Application.Contracts { 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? ExperimentName { get; set; } = string.Empty; + public string? CalledAE { get; set; } public DateTime? BeginPushTime { get; set; } public DateTime? EndPushTime { get; set; } + public string SubejctCode { get; set; } + + public string TrialSiteKeyInfo { get; set; } + } - public class PatientTrialView : PatientQueryView + public class PatientSubjectView : PatientQueryView { public int? StudyCount { get; set; } - public List TrialList { 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 class PatientTrialStatInfo - { - public int? VisitCount { get; set; } - public string ExperimentName { get; set; } - } public class PatientQuery : PageInput { @@ -377,7 +387,10 @@ namespace IRaCIS.Application.Contracts [NotDefault] public Guid SubjectId { get; set; } - public List SubjectVisitIdList { get; set; } + [NotDefault] + public Guid SubjectVisitId { get; set; } + + public List SCPStudyIdList { get; set; } } public class SubjectVisitSelectQuery @@ -938,6 +951,8 @@ namespace IRaCIS.Application.Contracts public class SCPImageUploadQuery : PageInput { + public string TrialSiteKeyInfo { get; set; } + public string? CallingAE { get; set; } public string? CalledAE { get; set; } @@ -966,7 +981,18 @@ 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 class VisitPatientStudyView : PatientStudySelectDto { @@ -990,7 +1016,20 @@ namespace IRaCIS.Application.Contracts } - public class PatientStudySimpleView + 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 class VisitPatientStudyFilterView { public Guid SCPStudyId { get; set; } @@ -1006,9 +1045,32 @@ namespace IRaCIS.Application.Contracts public string CalledAE { get; set; } = string.Empty; public string CallingAE { get; set; } = string.Empty; + } + + public class PatientStudySimpleView: VisitPatientStudyFilterView + { + + + + 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/PatientService.cs b/IRaCIS.Core.Application/Service/Visit/PatientService.cs index c4bef2800..1f0468c0e 100644 --- a/IRaCIS.Core.Application/Service/Visit/PatientService.cs +++ b/IRaCIS.Core.Application/Service/Visit/PatientService.cs @@ -1,1323 +1,374 @@ -//using IRaCIS.Application.Interfaces; -//using IRaCIS.Application.Contracts; -//using IRaCIS.Core.Application.Filter; -//using IRaCIS.Core.Domain.Share; -//using Microsoft.AspNetCore.Mvc; -//using Microsoft.AspNetCore.Authorization; -//using IRaCIS.Core.Application.Auth; -//using MassTransit; -//using Panda.DynamicWebApi.Attributes; -//using DocumentFormat.OpenXml.Spreadsheet; -//using AutoMapper.EntityFrameworkCore; -//using IRaCIS.Core.Domain.Models; -//using IRaCIS.Core.Application.Service.Reading.Dto; -//using Microsoft.Extensions.Options; -//using Microsoft.Extensions.Configuration; -//using Microsoft.Extensions.Configuration.Json; -//using Newtonsoft.Json; -//using Newtonsoft.Json.Linq; -//using SharpCompress.Common; -//using System.Reactive.Subjects; -//using Subject = IRaCIS.Core.Domain.Models.Subject; -//using IRaCIS.Core.Application.ViewModel; -//using Medallion.Threading; -//using IRaCIS.Core.Infrastructure; -//using EasyCaching.Core; -//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 EasyCaching.Core.DistributedLock; -//using IDistributedLockProvider = Medallion.Threading.IDistributedLockProvider; -//using DocumentFormat.OpenXml.InkML; - -//namespace IRaCIS.Application.Services -//{ -// [ApiExplorerSettings(GroupName = "Trial")] -// public class PatientService : BaseService -// { - -// private readonly IRepository _trialRepository; -// private readonly IRepository _patientRepository; -// private readonly IRepository _studyRepository; -// private readonly IRepository _subjectRepository; -// private readonly IRepository _subjectVisitRepository; -// private readonly IDistributedLockProvider _distributedLockProvider; - -// public PatientService(IRepository studyRepository, IRepository trialRepository, IRepository patientRepository, IRepository subjectRepository, IRepository subjectVisitRepository, IDistributedLockProvider distributedLockProvider) -// { -// _studyRepository = studyRepository; -// _trialRepository = trialRepository; -// _patientRepository = patientRepository; -// _subjectRepository = subjectRepository; -// _subjectVisitRepository = subjectVisitRepository; -// _distributedLockProvider = distributedLockProvider; -// } - - - - - -// #region 患者检查管理 - -// /// -// ///检查管理-> 检查Tab 患者列表 (带加入的项目信息 以及检查统计) 原型标注错误,不是检查列表 -// /// -// /// -// /// -// [HttpPost] -// public async Task>> GetPatientList(PatientTrialQuery inQuery) -// { - -// var isAdminOrOA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.Admin || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.OA || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin; - -// #region new ok -// var query = _patientRepository -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.PatientIdStr.Contains(inQuery.PatientIdStr)) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.PatientName.Contains(inQuery.PatientName)) -// //.WhereIf(!string.IsNullOrWhiteSpace(inQuery.ExperimentName), t => t.SubjectPatientList.Any(t => t.Subject.Trial.ExperimentName.Contains(inQuery.ExperimentName))) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAE), t => t.SCPStudyList.Any(t => t.CallingAE == inQuery.CallingAE)) -// .WhereIf(inQuery.BeginPushTime != null, t => t.LatestPushTime >= inQuery.BeginPushTime) -// .WhereIf(inQuery.EndPushTime != null, t => t.LatestPushTime <= inQuery.EndPushTime); - -// foreach (var calledAE in inQuery.CalledAEList) -// { -// query = query.Where(t => t.SCPStudyList.Select(c => c.CalledAE).Contains(calledAE)); -// } - - -// var resultQuery = from patient in query - -// select new PatientTrialView() -// { -// PatientId = patient.Id, -// PatientBirthDate = patient.PatientBirthDate, -// CreateTime = patient.CreateTime, -// CalledAEList = patient.SCPStudyList.Select(t => t.CalledAE).Distinct().ToList(), -// CallingAEList = patient.SCPStudyList.Select(t => t.CallingAE).Distinct().ToList(), -// CreateUserId = patient.CreateUserId, -// UpdateTime = patient.UpdateTime, -// UpdateUserId = patient.UpdateUserId, - -// EarliestStudyTime = patient.EarliestStudyTime, -// LatestStudyTime = patient.LatestStudyTime, -// LatestPushTime = patient.LatestPushTime, -// PatientAge = patient.PatientAge, -// PatientName = patient.PatientName, -// PatientIdStr = patient.PatientIdStr, -// PatientSex = patient.PatientSex, - -// StudyCount = patient.SCPStudyList.Count(), - -// TrialList = patient.SubjectPatientList.Where(t => isAdminOrOA ? true : t.Subject.Trial.TrialUserList.Any(t => t.UserId == _userInfo.Id)).Select(c => new PatientTrialStatInfo() -// { -// ExperimentName = c.Subject.Trial.ExperimentName, -// VisitCount = c.Subject.SubjectVisitList.Count() -// }).ToList(), - -// }; +using IRaCIS.Application.Interfaces; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using IRaCIS.Core.Application.Auth; +using MassTransit; +using Panda.DynamicWebApi.Attributes; +using DocumentFormat.OpenXml.Spreadsheet; +using AutoMapper.EntityFrameworkCore; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Application.Service.Reading.Dto; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.Json; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using SharpCompress.Common; +using System.Reactive.Subjects; +using Subject = IRaCIS.Core.Domain.Models.Subject; +using IRaCIS.Core.Application.ViewModel; +using Medallion.Threading; +using IRaCIS.Core.Infrastructure; +using EasyCaching.Core; +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 EasyCaching.Core.DistributedLock; +using IDistributedLockProvider = Medallion.Threading.IDistributedLockProvider; +using DocumentFormat.OpenXml.InkML; + +namespace IRaCIS.Application.Services +{ + [ApiExplorerSettings(GroupName = "Trial")] + public class PatientService : BaseService + { + + private readonly IRepository _trialRepository; + private readonly IRepository _patientRepository; + private readonly IRepository _scpStudyRepository; + private readonly IRepository _subjectRepository; + private readonly IRepository _subjectVisitRepository; + private readonly IDistributedLockProvider _distributedLockProvider; + + public PatientService(IRepository studyRepository, IRepository trialRepository, IRepository patientRepository, IRepository subjectRepository, IRepository subjectVisitRepository, IDistributedLockProvider distributedLockProvider) + { + _scpStudyRepository = studyRepository; + _trialRepository = trialRepository; + _patientRepository = patientRepository; + _subjectRepository = subjectRepository; + _subjectVisitRepository = subjectVisitRepository; + _distributedLockProvider = distributedLockProvider; + } + + + /// + /// scp 影像推送记录表 + /// + /// + /// + [HttpPost] + public async Task>> GetSCPImageUploadList(SCPImageUploadQuery inQuery) + { + var query = _repository.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) + .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 pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(SCPImageUploadView.CallingAE) : inQuery.SortField, inQuery.Asc); + + + return ResponseOutput.Ok(pageList); + } + + + #region 患者检查管理 + + /// + ///影像检查列表-患者为维度组织 + /// + /// + /// + [HttpPost] + public async Task>> GetPatientList(PatientTrialQuery inQuery) + { + + + #region new ok + var query = _patientRepository + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.PatientIdStr.Contains(inQuery.PatientIdStr)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.PatientName.Contains(inQuery.PatientName)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.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); + + //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() + { + PatientId = patient.Id, + PatientBirthDate = patient.PatientBirthDate, + CreateTime = patient.CreateTime, + CalledAEList = patient.SCPStudyList.Select(t => t.CalledAE).Distinct().ToList(), + CallingAEList = patient.SCPStudyList.Select(t => t.CallingAE).Distinct().ToList(), + CreateUserId = patient.CreateUserId, + UpdateTime = patient.UpdateTime, + UpdateUserId = patient.UpdateUserId, + + EarliestStudyTime = patient.EarliestStudyTime, + LatestStudyTime = patient.LatestStudyTime, + LatestPushTime = patient.LatestPushTime, + PatientAge = patient.PatientAge, + PatientName = patient.PatientName, + PatientIdStr = patient.PatientIdStr, + PatientSex = patient.PatientSex, + + StudyCount = patient.SCPStudyList.Count(), + + TrialId=patient.TrialId, + SubejctId=patient.SubjectId, + SubjectCode=patient.Subject.Code, + TrialSiteAliasName=patient.TrialSite.TrialSiteAliasName, + TrialSiteCode=patient.TrialSite.TrialSiteCode, + TrialSiteName=patient.TrialSite.TrialSiteName + + }; + + + + + var pageList = await resultQuery.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(PatientQueryView.PatientIdStr) : inQuery.SortField, inQuery.Asc); + #endregion + + + return ResponseOutput.Ok(pageList); + } + + + + + + /// + /// 影像检查列表-> 获取患者的检查列表 + /// + /// + /// + [HttpPost] + public async Task> GetPatientStudyList(PatientStudyInfoQuery inQuery) + { + var query = from scpStudy in _scpStudyRepository.Where(t => t.PatientId == inQuery.PatientId) + .WhereIf(inQuery.EarliestStudyTime != null, t => t.StudyTime >= inQuery.EarliestStudyTime) + .WhereIf(inQuery.LatestStudyTime != null, t => t.StudyTime <= inQuery.LatestStudyTime) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Modalities), t => t.Modalities.Contains(inQuery.Modalities)) + select new PatientStudySimpleView() + { + Description = scpStudy.Description, + CalledAE = scpStudy.CalledAE, + CallingAE = scpStudy.CallingAE, + InstanceCount = scpStudy.InstanceCount, + Modalities = scpStudy.Modalities, + PatientId = scpStudy.PatientId, + 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 list = await orderQuery.ToListAsync(); + var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(PatientStudySimpleView.StudyTime) : inQuery.SortField, inQuery.Asc); + + return pageList; + } + + + public async Task> GetDicomCalledAEList() + { + var list = await _scpStudyRepository.Select(t => t.CalledAE).Distinct().ToListAsync(); + return list; + } -// var pageList = await resultQuery.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(PatientQueryView.PatientIdStr) : inQuery.SortField, inQuery.Asc); -// #endregion + public async Task> GetDicomCallingAEList() + { + var list = await _scpStudyRepository.Select(t => t.CallingAE).Distinct().ToListAsync(); + return list; + } -// return ResponseOutput.Ok(pageList); -// } + /// + /// 影像访视上传 检查列表 + /// + /// + /// + [HttpPost] + public async Task> GetVisitPatientStudyFilterList(VisitPatientStudyFilterQuery inQuery) + { + 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) + .WhereIf(inQuery.EarliestStudyTime != null, t => t.StudyTime >= inQuery.EarliestStudyTime) + .WhereIf(inQuery.LatestStudyTime != null, t => t.StudyTime <= inQuery.LatestStudyTime) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Modalities), t => t.Modalities.Contains(inQuery.Modalities)) + select new VisitPatientStudyFilterView() + { + Description = scpStudy.Description, + CalledAE = scpStudy.CalledAE, + CallingAE = scpStudy.CallingAE, + InstanceCount = scpStudy.InstanceCount, + Modalities = scpStudy.Modalities, + PatientId = scpStudy.PatientId, + SCPStudyId = scpStudy.Id, + SeriesCount = scpStudy.SeriesCount, + StudyTime = scpStudy.StudyTime, + }; + var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(PatientStudySimpleView.StudyTime) : inQuery.SortField, inQuery.Asc); -// /// -// /// 检查管理-> 获取患者检查列表(同步影像数据之前的) -// /// -// /// -// /// -// [HttpPost] -// public async Task> GetPatientStudyList(PatientStudyInfoQuery inQuery) -// { -// var query = from scpStudy in _studyRepository.Where(t => t.PatientId == inQuery.PatientId) -// .WhereIf(inQuery.EarliestStudyTime != null, t => t.StudyTime >= inQuery.EarliestStudyTime) -// .WhereIf(inQuery.LatestStudyTime != null, t => t.StudyTime <= inQuery.LatestStudyTime) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Modalities), t => t.Modalities.Contains(inQuery.Modalities)) -// select new PatientStudySimpleView() -// { -// Description = scpStudy.Description, -// CalledAE = scpStudy.CalledAE, -// CallingAE = scpStudy.CallingAE, -// InstanceCount = scpStudy.InstanceCount, -// Modalities = scpStudy.Modalities, -// PatientId = scpStudy.PatientId, -// SCPStudyId = scpStudy.Id, -// SeriesCount = scpStudy.SeriesCount, -// StudyTime = scpStudy.StudyTime, -// }; + return pageList; + } -// 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(); - -// return list; -// } - + /// + /// 提交 患者检查和访视的绑定 + /// + /// + /// + [HttpPost] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SubmitVisitStudyBinding(SubmitVisitStudyBindingCommand inCommand) + { -// public async Task>> GetPatientSeriesList(Guid scpStudyId, -// [FromServices] IRepository _seriesRepository, -// [FromServices] IRepository _instanceRepository -// ) -// { + var subjectId = inCommand.SubjectId; + var subjectVisitId=inCommand.SubjectVisitId; + var trialId = inCommand.TrialId; + + -// 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 @lock = _distributedLockProvider.CreateLock($"StudyCode"); -// 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); + using (await @lock.AcquireAsync()) + { + var dbStudyCodeIntMax = _repository.Where(s => s.TrialId == inCommand.TrialId).Select(t => t.Code).DefaultIfEmpty().Max(); -// foreach (var item in seriesList) -// { -// item.InstanceList = idList.Where(s => s.SeriesId == item.Id).Select(u => u.Id).ToList(); + int currentNextCodeInt = dbStudyCodeIntMax + 1; -// //处理多帧 -// item.InstancePathList = idList.Where(s => s.SeriesId == item.Id).OrderBy(t => t.InstanceNumber) -// .SelectMany(u => -// { + foreach (var scpStudyId in inCommand.SCPStudyIdList) + { -// if (u.NumberOfFrames > 1) -// { -// var pathList = new List(); + var find = _scpStudyRepository.Where(t => t.Id == scpStudyId).Include(t => t.SeriesList).Include(t => t.InstanceList).FirstOrDefault(); -// for (int i = 1; i <= u.NumberOfFrames; i++) -// { -// pathList.Add(u.Path + "?frame=" + (i - 1)); -// } -// return pathList; -// } -// else -// { -// return new List { u.Path }; + if (find != null) + { -// } -// }) -// .ToList(); -// } + var newStuty = _mapper.Map(find); + await _repository.AddAsync(newStuty); -// var study = await _studyRepository.FindAsync(scpStudyId); + 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; -// return ResponseOutput.Ok(seriesList, study); -// } + 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 _repository.AddRangeAsync(newSeriesList); + var newInstanceList = _mapper.Map>(find.InstanceList); + foreach (var instance in newInstanceList) + { -// /// -// /// 清除该患者绑定的受试者的所有的数据、(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("当前患者有检查正在上传,不允许清理数据"); -// } + instance.SeqId = Guid.Empty; + instance.TrialId = trialId; + instance.SubjectId = subjectId; + instance.SubjectVisitId = subjectVisitId; + + } + await _repository.AddRangeAsync(newInstanceList); + } -// foreach (var item in subjectPatientList) -// { -// var subjectId = item.SubjectId; + currentNextCodeInt++; -// await _subjectRepository.BatchDeleteNoTrackingAsync(t => t.Id == 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 _repository.SaveChangesAsync(); -// await _patientRepository.BatchDeleteNoTrackingAsync(t => t.Id == patientId); + return ResponseOutput.Ok(); + } -// 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); -// } + #endregion -// 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.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(PatienSubejctView.Code) : inQuery.SortField, inQuery.Asc); - - -// 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.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(PatientQueryView.PatientIdStr) : inQuery.SortField, inQuery.Asc); - - -// return ResponseOutput.Ok(pageList); -// } - -// /// -// /// 受试者管理->患者列表 Dicom AE 下拉框数据获取 -// /// -// /// -// public async Task> GetDicomCalledAEList() -// { -// var list = await _studyRepository.Select(t => t.CalledAE).Distinct().ToListAsync(); - -// return list; -// } - -// public async Task> GetDicomCallingAEList() -// { -// var list = await _studyRepository.Select(t => t.CallingAE).Distinct().ToListAsync(); - -// return list; -// } - -// /// -// ///受试者管理-> 患者列表 模糊搜索下拉 选择subject 排除已绑定并提交的 -// /// -// /// -// /// -// [HttpPost] -// public async Task> GetTrialSubejctSelectList(SubjectSelectQuery inQuery) -// { -// var list = await _subjectRepository.Where(t => t.TrialId == inQuery.TrialId && t.Status == SubjectStatus.OnVisit) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Code.Contains(inQuery.SubjectCode)) -// .WhereIf(inQuery.SubjectId != null, t => t.Id == inQuery.SubjectId) -// //.Where(t => !t.SubjectVisitList.SelectMany(t => t.SCPStudySubjectVisitList).Any(c => c.StudyId != null)) -// .Select(t => new SubjectSelectDto() -// { -// SubejctId = t.Id, -// SubjectCode = t.Code, -// Status = t.Status, -// Sex = t.Sex, -// ShortName = t.ShortName, -// Age = t.Age, -// BirthDate = t.BirthDate, -// //PatientList = t.SubjectPatientList.Select(c => new PatienBasicInfo() { PatientId = c.PatientId, PatientIdStr = c.Patient.PatientIdStr }).ToList() -// }) -// .ToListAsync(); - -// return list; -// } - -// #endregion - - - -// #region 患者和受试者绑定,生成访视,预先绑定检查和访视 - - - - - - - -// /// -// /// 提交 患者检查和访视的绑定 -// /// -// /// -// /// -// [HttpPost] -// [UnitOfWork] -// [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] -// public async Task SubmitVisitStudyBinding(SubmitVisitStudyBindingCommand inCommand, [FromServices] IOptionsMonitor _basicSystemConfigConfig) -// { - -// //防止访视重复 -// 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) -// { - - -// 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] -// 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.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); - - - -// return ResponseOutput.Ok(pageList); -// } - - -// /// -// ///访视管理-> 获取当前访视 已绑定的患者检查 (从访视列表 进入修改绑定) -// /// -// /// -// /// -// [HttpPost] -// public async Task> GetCurrentVisitPatientStudyList(SubjectVisitStudyQuery inQuery) -// { -// var patientQuery = _studySubjectVisitRepository.Where(t => t.SubjectVisitId == inQuery.SujectVisitId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted) -// //.WhereIf(inQuery.SubmitState != null, u => u.SubjectVisit.SubmitState == inQuery.SubmitState) -// .ProjectTo(_mapper.ConfigurationProvider); - -// var sortField = string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(VisitPatientStudyView.StudyTime) : inQuery.SortField; -// var orderQuery = inQuery.Asc ? patientQuery.OrderBy(sortField) : patientQuery.OrderBy(sortField + " desc"); - -// var list = await orderQuery.ToListAsync(); - -// return list; -// } - - -// /// -// /// 访视管理-> 获取可选访视列表 (从访视列表 进入修改绑定) -// /// -// /// -// /// -// [HttpPost] -// public async Task> GetPatientOtherStudyList(PatientStudyOtherQuery inQuery) -// { -// var query = from scpStudy in _studyRepository.Where(t => inQuery.PatientIdList.Contains(t.PatientId) && !t.SCPStudySubjectVisitList.Any(t => (t.SubjectVisitId == inQuery.SujectVisitId || t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted) && t.TrialId == inQuery.TrialId)) -// .WhereIf(inQuery.EarliestStudyTime != null, t => t.StudyTime >= inQuery.EarliestStudyTime) -// .WhereIf(inQuery.LatestStudyTime != null, t => t.StudyTime <= inQuery.LatestStudyTime) -// .WhereIf(!string.IsNullOrEmpty(inQuery.PatientIdStr), t => t.PatientIdStr.Contains(inQuery.PatientIdStr)) -// //不属于该访视的检查 或者未绑定的检查 -// join scpStudySubjectVisit in _studySubjectVisitRepository.Where(c => c.TrialId == inQuery.TrialId) on scpStudy.Id equals scpStudySubjectVisit.SCPStudyId -// into dd -// from scpStudySV in dd.DefaultIfEmpty() -// select new VisitPatientStudyView() -// { -// PatientIdStr = scpStudy.PatientIdStr, -// PatientBirthDate = scpStudy.PatientBirthDate, -// PatientAge = scpStudy.PatientAge, -// PatientName = scpStudy.PatientName, -// PatientSex = scpStudy.PatientSex, -// Description = scpStudy.Description, -// CalledAE = scpStudy.CalledAE, -// CallingAE = scpStudy.CallingAE, -// InstanceCount = scpStudy.InstanceCount, -// Modalities = scpStudy.Modalities, -// PatientId = scpStudy.PatientId, -// SCPStudyId = scpStudy.Id, -// SeriesCount = scpStudy.SeriesCount, -// StudyTime = scpStudy.StudyTime, - -// SubmitState = scpStudySV.SubjectVisit.SubmitState, -// SubjectVisitId = scpStudySV.SubjectVisitId, -// VisitName = scpStudySV.SubjectVisit.VisitName, -// }; - -// #region 废弃 -// //var notCurrentVisitQuey = _studySubjectVisitRepository.Where(t => t.SubjectVisitId != inQuery.SujectVisitId && t.SCPStudy.Patient.Id == inQuery.PatientId) -// // .Select(t => new VisitPatientStudyView() -// // { -// // Description = t.SCPStudy.Description, -// // CalledAE = t.SCPStudy.CalledAE, -// // InstanceCount = t.SCPStudy.InstanceCount, -// // Modalities = t.SCPStudy.Modalities, -// // PatientId = t.SCPStudy.PatientId, -// // SCPStudyId = t.SCPStudy.PatientId, -// // SeriesCount = t.SCPStudy.SeriesCount, -// // StudyTime = t.SCPStudy.StudyTime, -// // SubjectVisitId = t.SubjectVisitId, -// // VisitName = t.SubjectVisit.VisitName, -// // }); - -// //var notBindQuery= _studyRepository.Where(t => t.PatientId == inQuery.PatientId && t.pa) - -// //var patientQuery = query - -// // .ProjectTo(_mapper.ConfigurationProvider); -// #endregion -// var sortField = string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(VisitPatientStudyView.StudyTime) : inQuery.SortField; -// var orderQuery = inQuery.Asc ? query.OrderBy(sortField) : query.OrderBy(sortField + " desc"); - -// var list = await orderQuery.ToListAsync(); - -// return list; -// } - -// #endregion - -// #region 检查管理 - - - -// /// -// ///检查管理-> 检查列表 (同步影像数据之前的) -// /// -// /// -// /// -// [HttpPost] -// public async Task>> GetPatientStudyBeforeConfirmList(TrialPatientStudyQuery inQuery) -// { -// #region 只查询已绑定的 -// //var query = _studySubjectVisitRepository.Where(t => t.SubjectVisit.TrialId == inQuery.TrialId) -// // .WhereIf(inQuery.PatientId != null, t => t.SCPStudy.PatientId == inQuery.PatientId) -// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) -// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.SCPStudy.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) -// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.SCPStudy.Patient.PatientSex.Contains(inQuery.PatientSex)) -// // .WhereIf(inQuery.SubjectAge != null, t => t.SubjectVisit.Subject.Age == inQuery.SubjectAge) -// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) -// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.SubjectVisit.Subject.Sex.Contains(inQuery.SubjectSex)) -// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.SubjectVisit.Subject.ShortName.Contains(inQuery.SubjectShortName)) -// // .WhereIf(inQuery.BeginStudyTime != null, t => t.SCPStudy.StudyTime >= inQuery.BeginStudyTime) -// // .WhereIf(inQuery.EndStudyTime != null, t => t.SCPStudy.StudyTime <= inQuery.EndStudyTime) -// // .Select(t => new PatientStudyBeforeConfirmView() -// // { -// // SubjectId = t.SubjectVisit.SubjectId, -// // SubjectAge = t.SubjectVisit.Subject.Age, -// // SubjectSex = t.SubjectVisit.Subject.Sex, -// // SubjectShortName = t.SubjectVisit.Subject.ShortName, - - -// // PatientId = t.SCPStudy.PatientId, -// // PatientAge = t.SCPStudy.PatientAge, -// // PatientBirthDate = t.SCPStudy.PatientBirthDate, -// // PatientIdStr = t.SCPStudy.PatientIdStr, -// // PatientSex = t.SCPStudy.PatientSex, - -// // //PatientList = t.SubjectVisit.Subject.SubjectPatientList.Select(t => new PatientBasicInfo() -// // //{ -// // // PatientId = t.PatientId, -// // // PatientAge = t.Patient.PatientAge, -// // // PatientBirthDate = t.Patient.PatientBirthDate, -// // // PatientIdStr = t.Patient.PatientIdStr, -// // // PatientSex = t.Patient.PatientSex, -// // //}).ToList(), - -// // SubjectCode = t.SubjectVisit.Subject.Code, -// // SubmitState = t.SubjectVisit.SubmitState, -// // SubmitTime = t.SubjectVisit.SubmitTime, -// // VisitName = t.SubjectVisit.VisitName, -// // SubjectVisitId = t.SubjectVisitId, -// // VisitEarliestStudyTime = t.SubjectVisit.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime), -// // VisitLatestStudyTime = t.SubjectVisit.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime), - -// // StudyId = t.SCPStudyId, -// // StudyTime = t.SCPStudy.StudyTime, -// // CallingAE = t.SCPStudy.CallingAE, -// // CalledAE = t.SCPStudy.CalledAE - -// // }); -// #endregion - -// var query = from scpStudy in _studyRepository.Where(t => !t.SCPStudySubjectVisitList.Any(t => t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted && t.TrialId == inQuery.TrialId)) -// .WhereIf(inQuery.IsBindedVisit == false, t => !t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId)) -// .WhereIf(inQuery.IsBindedVisit == true, t => t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId)) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.VisitName), t => t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId && t.SubjectVisit.VisitName.Contains(inQuery.VisitName))) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Patient.PatientSex.Contains(inQuery.PatientSex)) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) -// .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) -// .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) -// join subjectPatient in _subjectPatientRepository.Where(t => t.Subject.TrialId == inQuery.TrialId) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Subject.Code.Contains(inQuery.SubjectCode)) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.Subject.Sex.Contains(inQuery.SubjectSex)) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.Subject.ShortName.Contains(inQuery.SubjectShortName)) -// on scpStudy.PatientId equals subjectPatient.PatientId -// join scpStudySubjectVisit in _studySubjectVisitRepository.Where(t => t.TrialId == inQuery.TrialId) -// on scpStudy.Id equals scpStudySubjectVisit.SCPStudyId into dd -// from scpStudySV in dd.DefaultIfEmpty() -// select new PatientStudyBeforeConfirmView() -// { -// SubjectId = subjectPatient.Subject.Id, -// SubjectAge = subjectPatient.Subject.Age, -// SubjectSex = subjectPatient.Subject.Sex, -// SubjectShortName = subjectPatient.Subject.ShortName, -// SubjectCode = subjectPatient.Subject.Code, - -// PatientId = scpStudy.PatientId, -// PatientName = scpStudy.PatientName, -// PatientAge = scpStudy.PatientAge, -// PatientBirthDate = scpStudy.PatientBirthDate, -// PatientIdStr = scpStudy.PatientIdStr, -// PatientSex = scpStudy.PatientSex, - -// //PatientList = t.SubjectVisit.Subject.SubjectPatientList.Select(t => new PatientBasicInfo() -// //{ -// // PatientId = t.PatientId, -// // PatientAge = t.Patient.PatientAge, -// // PatientBirthDate = t.Patient.PatientBirthDate, -// // PatientIdStr = t.Patient.PatientIdStr, -// // PatientSex = t.Patient.PatientSex, -// //}).ToList(), - - -// SubmitState = scpStudySV.SubjectVisit.SubmitState, -// SubmitTime = scpStudySV.SubjectVisit.SubmitTime, -// VisitName = scpStudySV.SubjectVisit.VisitName, -// VisitNum = scpStudySV.SubjectVisit.VisitNum, -// SubjectVisitId = scpStudySV.SubjectVisit.Id, -// VisitEarliestStudyTime = scpStudySV.SubjectVisit.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime), -// VisitLatestStudyTime = scpStudySV.SubjectVisit.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime), - -// StudyId = scpStudy.Id, -// StudyTime = scpStudy.StudyTime, -// CallingAE = scpStudy.CallingAE, -// CalledAE = scpStudy.CalledAE, -// Description = scpStudy.Description, -// InstanceCount = scpStudy.InstanceCount, -// Modalities = scpStudy.Modalities, -// ModalityForEdit = scpStudy.ModalityForEdit, -// SeriesCount = scpStudy.SeriesCount - -// }; - -// var defalutSortArray = new string[] { nameof(PatientStudyBeforeConfirmView.SubjectCode), nameof(PatientStudyBeforeConfirmView.VisitNum), nameof(PatientStudyBeforeConfirmView.StudyTime) }; -// var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); - - - -// return ResponseOutput.Ok(pageList); -// } - - -// /// -// ///检查管理-> 检查列表 (同步影像数据之后的 带患者信息 患者信息是数组) -// /// -// /// -// /// -// [HttpPost] -// public async Task>> GetTrialPatientStudyList(TrialPatientStudyQuery inQuery) -// { -// var query = _repository.Where(t => t.SubjectVisit.TrialId == inQuery.TrialId) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.PatientIdStr.Contains(inQuery.PatientIdStr)) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.PatientSex.Contains(inQuery.PatientSex)) -// .WhereIf(inQuery.SubjectAge != null, t => t.SubjectVisit.Subject.Age == inQuery.SubjectAge) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.SubjectVisit.Subject.Sex.Contains(inQuery.SubjectSex)) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.SubjectVisit.Subject.ShortName.Contains(inQuery.SubjectShortName)) -// .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) -// .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) -// .Select(t => new PatientStudyView() -// { -// SubjectId = t.SubjectVisit.SubjectId, -// SubjectAge = t.Subject.Age, -// SubjectSex = t.Subject.Sex, -// SubjectShortName = t.Subject.ShortName, - - -// //PatientId = Guid.Empty, -// PatientAge = t.PatientAge, -// PatientName = t.PatientName, -// PatientBirthDate = t.PatientBirthDate, -// PatientIdStr = t.PatientIdStr, -// PatientSex = t.PatientSex, - -// //PatientList = t.Subject.SubjectPatientList.Select(t => new PatientBasicInfo() -// //{ -// // PatientId = t.PatientId, -// // PatientAge = t.Patient.PatientAge, -// // PatientBirthDate = t.Patient.PatientBirthDate, -// // PatientIdStr = t.Patient.PatientIdStr, -// // PatientSex = t.Patient.PatientSex, -// //}).ToList(), - -// Modalities = t.Modalities, -// ModalityForEdit = t.ModalityForEdit, -// SubjectCode = t.SubjectVisit.Subject.Code, -// SubmitState = t.SubjectVisit.SubmitState, -// SubmitTime = t.SubjectVisit.SubmitTime, -// VisitName = t.SubjectVisit.VisitName, -// VisitNum = t.SubjectVisit.VisitNum, -// SubjectVisitId = t.SubjectVisitId, -// VisitEarliestStudyTime = t.SubjectVisit.StudyList.Min(t => t.StudyTime), -// VisitLatestStudyTime = t.SubjectVisit.StudyList.Max(t => t.StudyTime), - -// StudyId = t.Id, -// StudyTime = t.StudyTime, -// Description = t.Description, -// SeriesCount = t.SeriesCount, -// InstanceCount = t.InstanceCount, -// }); - -// var defalutSortArray = new string[] { nameof(PatientStudyView.SubjectCode), nameof(PatientStudyView.VisitNum), nameof(PatientStudyView.StudyTime) }; - -// var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); - - -// return ResponseOutput.Ok(pageList); -// } - - - -// /// -// /// 获取该项目 患者已绑定subject ,新来了的检查 可能需要新建访视 但是新增的检查未绑定访视的检查列表 -// /// -// /// -// /// -// [HttpPost] -// public async Task>> GetTrialUnbindSubjectVisitStudyList(TrialPatientStudyQuery inQuery) -// { -// //属于该项目的已绑定患者的检查,同时没有绑定任何访视 -// var query = from scpStudy in _studyRepository.Where(t => !t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId)) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Patient.PatientSex.Contains(inQuery.PatientSex)) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) -// .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) -// .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) -// join subjectPatient in _subjectPatientRepository.Where(t => t.Subject.TrialId == inQuery.TrialId) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Subject.Code.Contains(inQuery.SubjectCode)) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.Subject.Sex.Contains(inQuery.SubjectSex)) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.Subject.ShortName.Contains(inQuery.SubjectShortName)) -// on scpStudy.PatientId equals subjectPatient.PatientId -// join scpStudySubjectVisit in _studySubjectVisitRepository.AsQueryable() on scpStudy.Id equals scpStudySubjectVisit.SCPStudyId -// into dd -// from scpStudySV in dd.DefaultIfEmpty() -// select new UnbindStudyView() -// { -// PatientIdStr = scpStudy.PatientIdStr, -// PatientBirthDate = scpStudy.PatientBirthDate, -// PatientAge = scpStudy.PatientAge, -// PatientName = scpStudy.PatientName, -// PatientSex = scpStudy.PatientSex, -// Description = scpStudy.Description, -// CalledAE = scpStudy.CalledAE, -// InstanceCount = scpStudy.InstanceCount, -// Modalities = scpStudy.Modalities, -// PatientId = scpStudy.PatientId, -// SCPStudyId = scpStudy.Id, -// SeriesCount = scpStudy.SeriesCount, -// StudyTime = scpStudy.StudyTime, - -// SubjectVisitId = scpStudySV.SubjectVisitId, -// VisitName = scpStudySV.SubjectVisit.VisitName, - -// SubjectId = subjectPatient.SubjectId, -// SubjectCode = subjectPatient.Subject.Code, -// TrialId = subjectPatient.Subject.TrialId, -// SubjectAge = subjectPatient.Subject.Age, -// SubjectSex = subjectPatient.Subject.Sex, -// SubjectShortName = subjectPatient.Subject.ShortName, -// SubjectBirthDate = subjectPatient.Subject.BirthDate -// }; - -// #region 废弃 -// //var query = from subject in _subjectRepository.Where(t => t.TrialId == inQuery.TrialId) -// // .WhereIf(inQuery.SubjectAge != null, t => t.Age == inQuery.SubjectAge) -// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Code.Contains(inQuery.SubjectCode)) -// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.Sex.Contains(inQuery.SubjectSex)) -// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.ShortName.Contains(inQuery.SubjectShortName)) -// // join subjectPatient in _subjectPatientRepository.AsQueryable() on subject.Id equals subjectPatient.PatientId -// // //没有绑定任何访视 -// // join scpStudy in _studyRepository.AsQueryable() -// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Patient.PatientSex.Contains(inQuery.PatientSex)) -// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) -// // .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) -// // .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) -// // on subjectPatient.PatientId equals scpStudy.PatientId -// // select new SubjectPatientStudyView() -// // { -// // SubjectId = subject.Id, -// // SubjectAge = subject.Age, -// // SubjectSex = subject.Sex, -// // SubjectShortName = subject.ShortName, - -// // PatientList = subject.SubjectPatientList.Select(t => new PatientBasicInfo() -// // { -// // PatientId = t.PatientId, -// // PatientAge = t.Patient.PatientAge, -// // PatientBirthDate = t.Patient.PatientBirthDate, -// // PatientIdStr = t.Patient.PatientIdStr, -// // PatientSex = t.Patient.PatientSex, -// // }).ToList(), - -// // SubjectCode = subject.Code, - -// // SeriesCount = scpStudy.SeriesCount, -// // CalledAE = scpStudy.CalledAE, -// // InstanceCount = scpStudy.InstanceCount, -// // Description = scpStudy.Description, -// // Modalities = scpStudy.Modalities, -// // PatientId = scpStudy.PatientId, - -// // SCPStudyId = scpStudy.Id, -// // StudyTime = scpStudy.StudyTime - -// // }; -// #endregion - - - - - - -// var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(UnbindStudyView.StudyTime) : inQuery.SortField, inQuery.Asc); - - -// return ResponseOutput.Ok(pageList); -// } - -// /// -// /// 删除某个项目 未提交的访视检查绑定, 清理数据,方便测试自动绑定 -// /// -// /// -// /// -// [HttpDelete] -// public async Task DeleteUnSubmittedStudyBind(Guid trialId, Guid? subjectId, -// [FromServices] IRepository _visitTaskRepository, -// [FromServices] IRepository _dicomStudyRepository, -// [FromServices] IRepository _dicomSeriesRepository, -// [FromServices] IRepository _dicomInstanceRepository) -// { -// if (subjectId != null) -// { -// await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.TrialId == trialId && t.SubjectId == subjectId); - -// await _subjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); - -// await _visitTaskRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); - -// await _dicomStudyRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); -// await _dicomSeriesRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); -// await _dicomInstanceRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); - -// } -// else -// { -// await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.TrialId == trialId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted); - -// } - - -// return ResponseOutput.Ok(); -// } - - -// /// -// /// 阅片管理-> 任务列表 -// /// -// /// -// /// -// [HttpPost] -// public async Task>> GetPatientVisitTaskList([FromServices] IRepository _visitTaskRepository, PatientVisitTaskQuery inQuery) -// { -// var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsAnalysisCreate == false) -// .WhereIf(inQuery.ReadingCategory != null, t => t.ReadingCategory == inQuery.ReadingCategory) -// .WhereIf(inQuery.ReadingCategory == null, t => t.ReadingCategory != ReadingCategory.Judge) - -// .WhereIf(inQuery.ReadingTaskState != null, t => t.ReadingTaskState == inQuery.ReadingTaskState) -// .WhereIf(inQuery.TaskState != null, t => t.TaskState == inQuery.TaskState) -// .WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId) -// .WhereIf(inQuery.DoctorUserId != null, t => t.DoctorUserId == inQuery.DoctorUserId) -// .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) - -// .WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.TaskName.Contains(inQuery.TaskName) || t.TaskBlindName.Contains(inQuery.TaskName)) -// .WhereIf(inQuery.BeginSignTime != null, t => t.SignTime >= inQuery.BeginSignTime) -// .WhereIf(inQuery.EndSignTime != null, t => t.SignTime <= inQuery.EndSignTime) - -// .WhereIf(inQuery.BeginTaskCreateTime != null, t => t.CreateTime >= inQuery.BeginTaskCreateTime) -// .WhereIf(inQuery.EndTaskCreateTime != null, t => t.CreateTime <= inQuery.EndTaskCreateTime) - -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientIdStr.Contains(inQuery.PatientIdStr))) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientName.Contains(inQuery.PatientName))) -// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientSex.Contains(inQuery.PatientSex))) -// .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectShortName), t => t.Subject.ShortName.Contains(inQuery.SubjectShortName)) -// .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => (t.Subject.Code.Contains(inQuery.SubjectCode) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(inQuery.SubjectCode) && t.IsAnalysisCreate)) -// .ProjectTo(_mapper.ConfigurationProvider); - -// var defalutSortArray = new string[] { nameof(PatientVisitTaskDTO.SubjectId), nameof(PatientVisitTaskDTO.VisitTaskNum) }; - -// var pageList = await visitTaskQueryable.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); - -// return ResponseOutput.Ok(pageList); -// } - - -// #endregion - - - - - -// /// -// /// scp 影像推送记录表 -// /// -// /// -// /// -// [HttpPost] -// public async Task>> GetSCPImageUploadList(SCPImageUploadQuery inQuery) -// { -// var query = _repository.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.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(SCPImageUploadView.CallingAE) : inQuery.SortField, inQuery.Asc); - - -// return ResponseOutput.Ok(pageList); -// } - - - - -// } - - - -//} +} diff --git a/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs b/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs index 35a01056d..717c22d8e 100644 --- a/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs @@ -112,6 +112,14 @@ namespace IRaCIS.Core.Application.Service .ForMember(d => d.InstanceInfoList, u => u.MapFrom(s => s.InstanceList)); 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)) +; + } } diff --git a/IRaCIS.Core.Domain/Image/DicomStudy.cs b/IRaCIS.Core.Domain/Image/DicomStudy.cs index e5193846b..b0d6526b5 100644 --- a/IRaCIS.Core.Domain/Image/DicomStudy.cs +++ b/IRaCIS.Core.Domain/Image/DicomStudy.cs @@ -99,5 +99,8 @@ namespace IRaCIS.Core.Domain.Models public Guid? DeleteUserId { get; set; } + public bool IsFromPACS { get; set; } + + } } diff --git a/IRaCIS.Core.Domain/Image/SCPImageUpload.cs b/IRaCIS.Core.Domain/Image/SCPImageUpload.cs index a2982fba0..10e6b4ef0 100644 --- a/IRaCIS.Core.Domain/Image/SCPImageUpload.cs +++ b/IRaCIS.Core.Domain/Image/SCPImageUpload.cs @@ -55,6 +55,19 @@ namespace IRaCIS.Core.Domain.Models public int StudyCount { get; set; } + + + + public Guid TrialId { get; set; } + public Guid TrialSiteId { get; set; } + + [JsonIgnore] + + public Trial Trial { get; set; } + + [JsonIgnore] + public TrialSite TrialSite { get; set; } + } diff --git a/IRaCIS.Core.Domain/Image/SCPPatient.cs b/IRaCIS.Core.Domain/Image/SCPPatient.cs index 21eda7f87..0c75de6d4 100644 --- a/IRaCIS.Core.Domain/Image/SCPPatient.cs +++ b/IRaCIS.Core.Domain/Image/SCPPatient.cs @@ -29,9 +29,17 @@ namespace IRaCIS.Core.Domain.Models public DateTime LatestPushTime { get; set; } + public Guid? SubjectId { get; set; } + public Guid TrialId { get; set; } + public Guid TrialSiteId { get; set; } + + [JsonIgnore] public Subject Subject { get; set; } - public Guid? SubjectId { get; set; } + [JsonIgnore] + public Trial Trial { get; set; } + [JsonIgnore] + public TrialSite TrialSite { get; set; } } } diff --git a/IRaCIS.Core.Domain/Image/SCPStudy.cs b/IRaCIS.Core.Domain/Image/SCPStudy.cs index c78222617..4af3686cd 100644 --- a/IRaCIS.Core.Domain/Image/SCPStudy.cs +++ b/IRaCIS.Core.Domain/Image/SCPStudy.cs @@ -84,12 +84,20 @@ namespace IRaCIS.Core.Domain.Models public bool IsUploadFinished { get; set; } - + public Guid TrialId { get; set; } + public Guid TrialSiteId { get; set; } public Guid? SubjectVisitId { get; set; } [JsonIgnore] public SubjectVisit SubjectVisit { get; set; } + [JsonIgnore] + + public Trial Trial { get; set; } + + [JsonIgnore] + public TrialSite TrialSite { get; set; } + } } From 6264d105abd470caddd7d131349d940c41f2d405 Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Thu, 4 Jul 2024 17:22:54 +0800 Subject: [PATCH 8/9] =?UTF-8?q?=E6=B5=8F=E8=A7=88=E5=99=A8=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Service/Common/DTO/ExploreRecommendViewModel.cs | 4 +++- .../Service/Common/ExploreRecommendService.cs | 4 ++-- IRaCIS.Core.Domain/Common/ExploreRecommend.cs | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs index 382271cf1..c61cb31a8 100644 --- a/IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs +++ b/IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs @@ -43,7 +43,9 @@ namespace IRaCIS.Core.Application.ViewModel /// ExploreRecommendAddOrEdit 列表查询参数模型 public class ExploreRecommendAddOrEdit { - public Guid Id { get; set; } + public Guid? Id { get; set; } + public string ExploreType { get; set; } + public string Version { get; set; } public string Title { get; set; } diff --git a/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs b/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs index b67c4f25f..17f314441 100644 --- a/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs +++ b/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs @@ -51,9 +51,9 @@ namespace IRaCIS.Core.Application.Service { var verifyExp2 = new EntityVerifyExp() { - VerifyExp = u => u.IsDeleted == addOrEditExploreRecommend.IsDeleted, + VerifyExp = u => u.IsDeleted == addOrEditExploreRecommend.IsDeleted && u.ExploreType == addOrEditExploreRecommend.ExploreType, - VerifyMsg = "当前启用版本只允许有一个", + VerifyMsg = "当前浏览器启用版本只允许有一个", IsVerify = addOrEditExploreRecommend.IsDeleted == false }; diff --git a/IRaCIS.Core.Domain/Common/ExploreRecommend.cs b/IRaCIS.Core.Domain/Common/ExploreRecommend.cs index db000e174..b58cd06b5 100644 --- a/IRaCIS.Core.Domain/Common/ExploreRecommend.cs +++ b/IRaCIS.Core.Domain/Common/ExploreRecommend.cs @@ -15,8 +15,10 @@ namespace IRaCIS.Core.Domain.Models [Table("ExploreRecommend")] public class ExploreRecommend : Entity, IAuditUpdate, IAuditAdd,ISoftDelete { - - public string Version { get; set; }=string.Empty; + public string ExploreType { get; set; } = string.Empty; + + + public string Version { get; set; }=string.Empty; public string Title { get; set; } = string.Empty; From 933a43f4eaadc8667328052552105e7cb37a9f02 Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Thu, 4 Jul 2024 17:24:29 +0800 Subject: [PATCH 9/9] =?UTF-8?q?=E8=BF=94=E5=9B=9E=E6=8E=A8=E8=8D=90?= =?UTF-8?q?=E7=9A=84=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Service/Common/ExploreRecommendService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs b/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs index 17f314441..163b211a1 100644 --- a/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs +++ b/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs @@ -73,13 +73,13 @@ namespace IRaCIS.Core.Application.Service } [AllowAnonymous] - public async Task GetExploreRecommentInfo() + public async Task > GetExploreRecommentInfo() { - var result = await _exploreRecommendRepository.Where(t => t.IsDeleted == false).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); + var result = await _exploreRecommendRepository.Where(t => t.IsDeleted == false).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); - if (result == null) + if (result .Count==0) { throw new QueryBusinessObjectNotExistException("系统浏览器版本推荐未维护,请联系维护人员"); }