修改下载
continuous-integration/drone/push Build is passing Details

Test_HIR_Net8
hang 2025-11-21 11:44:43 +08:00
parent a22b7802b7
commit 62352738e8
9 changed files with 19658 additions and 45 deletions

View File

@ -244,8 +244,14 @@ namespace IRaCIS.Core.SCP.Service
_upload.UploadJsonStr = (new SCPImageLog() { UploadList = _ImageUploadList }).ToJsonStr();
//可能是测试echo 导致记录了
await _SCPImageUploadRepository.AddAsync(_upload, _upload.FileCount > 0 ? true : false);
if (_upload.FileCount > 0)
{
//可能是测试echo 导致记录了
await _SCPImageUploadRepository.AddAsync(_upload, true);
}
}
}

View File

@ -333,9 +333,9 @@ namespace IRaCIS.Core.SCP.Service
else
{
findInstance.SOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty);
findInstance.MediaStorageSOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
findInstance.MediaStorageSOPClassUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
findInstance.TransferSyntaxUID = transferSyntaxUID;
findInstance.MediaStorageSOPInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
findInstance.MediaStorageSOPInstanceUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
findInstance.IsEncapsulated = isEncapsulated;
findInstance.UpdateTime = DateTime.Now;
}

View File

@ -18826,6 +18826,17 @@
<param name="inQuery"></param>
<returns></returns>
</member>
<!-- Badly formed XML comment ignored for member "M:IRaCIS.Application.Services.PatientService.TrialImageAddExtralField(System.Guid,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SCPInstance},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomInstance},IRaCIS.Core.Application.Helper.IOSSService)" -->
<member name="M:IRaCIS.Application.Services.PatientService.GetDownloadPatientStudyInfo(IRaCIS.Application.Contracts.PatientImageDownloadCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SCPPatient},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SCPStudy},IRaCIS.Core.Application.Helper.IOSSService)">
<summary>
影像库 和影像下载界面批量下载原始影像 患者列表只传递 患者Id数组 检查列表把患者Id数组 和检查Id数组都传递
</summary>
<param name="inCommand"></param>
<param name="_scpPatientRepository"></param>
<param name="_SCPStudyRepository"></param>
<param name="_oSSService"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Application.Services.PatientService.GetDownloadSubjectVisitStudyInfo(System.Guid,System.Guid,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomStudy},IRaCIS.Core.Application.Helper.IOSSService)">
<summary>
获取下载的访视检查信息

View File

@ -551,7 +551,7 @@ namespace IRaCIS.Application.Contracts
public string? CallingAE { get; set; }
public string? StudyInstanceUid { get; set; }
public string? StudyInstanceUid { get; set; }
}
@ -965,6 +965,13 @@ namespace IRaCIS.Application.Contracts
}
public class PatientImageDownloadCommand
{
public List<Guid> PatientIdList { get; set; }
public List<Guid> SCPStudyIdList { get; set; }
}
public class VisitImageDownloadQuery : PageInput
{
[NotDefault]
@ -1021,6 +1028,8 @@ namespace IRaCIS.Application.Contracts
}
}
public string DownloadJsonStr { get; set; }
}
@ -1203,6 +1212,7 @@ namespace IRaCIS.Application.Contracts
public class DownloadDicomStudyDto
{
public Guid StudyId { get; set; }
public string PatientId { get; set; }
public DateTime? StudyTime { get; set; }
public string StudyCode { get; set; }

View File

@ -1,50 +1,52 @@
using IRaCIS.Application.Interfaces;
using AutoMapper.EntityFrameworkCore;
using Azure;
using DocumentFormat.OpenXml.Office2013.Drawing.ChartStyle;
using FellowOakDicom;
using FellowOakDicom.Imaging;
using FellowOakDicom.Network;
using FellowOakDicom.Network.Client;
using IRaCIS.Application.Contracts;
using IRaCIS.Core.Application.Filter;
using IRaCIS.Core.Domain.Share;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using IRaCIS.Application.Interfaces;
using IRaCIS.Core.Application.Auth;
using MassTransit;
using Panda.DynamicWebApi.Attributes;
using AutoMapper.EntityFrameworkCore;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
using IRaCIS.Core.Application.Filter;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.Helper.OtherTool;
using IRaCIS.Core.Application.Service.Reading.Dto;
using Microsoft.Extensions.Options;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore.Migrations;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.Encryption;
using IRaCIS.Core.Infrastructure.Extention;
using MailKit.Search;
using MassTransit;
using Medallion.Threading;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NPOI.POIFS.Properties;
using Panda.DynamicWebApi.Attributes;
using SharpCompress.Common;
using System.Reactive.Subjects;
using Subject = IRaCIS.Core.Domain.Models.Subject;
using IRaCIS.Core.Application.ViewModel;
using Medallion.Threading;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Application.Contracts;
using MailKit.Search;
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
using IRaCIS.Core.Application.Helper;
using System;
using System.Dynamic;
using System.IO.Compression;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Reactive.Subjects;
using System.Text;
using Azure;
using System.IO.Compression;
using static IRaCIS.Core.Domain.Share.StaticData;
using FellowOakDicom;
using IDistributedLockProvider = Medallion.Threading.IDistributedLockProvider;
using Microsoft.AspNetCore.Hosting;
using IRaCIS.Core.Infrastructure.Encryption;
using FellowOakDicom.Network.Client;
using FellowOakDicom.Network;
using Microsoft.Extensions.Logging;
using System;
using IRaCIS.Core.Infra.EFCore.Migrations;
using System.Dynamic;
using System.Threading.Channels;
using IRaCIS.Core.Application.Helper.OtherTool;
using IRaCIS.Core.Infrastructure.Extention;
using NPOI.POIFS.Properties;
using static IRaCIS.Core.Domain.Share.StaticData;
using IDistributedLockProvider = Medallion.Threading.IDistributedLockProvider;
using Subject = IRaCIS.Core.Domain.Models.Subject;
namespace IRaCIS.Application.Services
@ -3175,6 +3177,248 @@ namespace IRaCIS.Application.Services
#region 影像下载
/// <summary>
/// 维护dir 需求新增的字段
/// </summary>
/// <param name="trialId"></param>
/// <param name="_instanceRepository"></param>
/// <param name="_studyRepository"></param>
/// <param name="_seriesRepository"></param>
/// <returns></returns>
public async Task<IResponseOutput> TrialImageAddExtralField(Guid trialId,
[FromServices] IRepository<SCPInstance> _scpInstanceRepository,
[FromServices] IRepository<DicomInstance> _instanceRepository,
[FromServices] IOSSService _oSSService)
{
// UPDATE DicomStudy
//SET DicomStudyDate = CONVERT(char(8), StudyTime, 112), --yyyyMMdd
// DicomStudyTime = REPLACE(CONVERT(char(8), StudyTime, 108), ':', ''); --HHmmss
// where DicomStudyDate = ''
//instance 找到传输语法为空的,然后分组
var seriesList = _scpInstanceRepository.Where(t => t.TransferSyntaxUID == "")
//按照序列 和 NumberOfFrames 分组
.GroupBy(t => new { t.NumberOfFrames, t.SeriesId })
// 每个分组 取数据最小的一条
.Select(g => new { g.Key.SeriesId, g.Key.NumberOfFrames, g.OrderBy(t => t.FileSize).First().Path }).ToList();
foreach (var item in seriesList)
{
var stream = await _oSSService.GetStreamFromOSSAsync(item.Path);
var dicomFile = DicomFile.Open(stream);
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
//获取像素是否为封装形式
var syntax = dicomFile.Dataset.InternalTransferSyntax;
//读取需要维护的值
var transferSyntaxUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);
var mediaStorageSOPClassUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
var mediaStorageSOPInstanceUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
var sOPClassUID = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty);
//维护序列层级四个字段 后再用sql 维护study series 时间拆分 和 MediaStorageSOPInstanceUID
await _scpInstanceRepository.BatchUpdateNoTrackingAsync(t => t.SeriesId == item.SeriesId, t => new SCPInstance()
{
IsEncapsulated = syntax.IsEncapsulated,
TransferSyntaxUID = transferSyntaxUID,
MediaStorageSOPClassUID = mediaStorageSOPClassUID,
SOPClassUID = sOPClassUID,
});
}
var seriesList2 = _instanceRepository.Where(t => t.TransferSyntaxUID == "")
//按照序列 和 NumberOfFrames 分组
.GroupBy(t => new { t.NumberOfFrames, t.SeriesId })
// 每个分组 取数据最小的一条
.Select(g => new { g.Key.SeriesId, g.Key.NumberOfFrames, g.OrderBy(t => t.FileSize).First().Path }).ToList();
foreach (var item in seriesList2)
{
var stream = await _oSSService.GetStreamFromOSSAsync(item.Path);
var dicomFile = DicomFile.Open(stream);
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
//获取像素是否为封装形式
var syntax = dicomFile.Dataset.InternalTransferSyntax;
//读取需要维护的值
var transferSyntaxUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);
var mediaStorageSOPClassUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
var mediaStorageSOPInstanceUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
var sOPClassUID = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty);
//维护序列层级四个字段 后再用sql 维护study series 时间拆分 和 MediaStorageSOPInstanceUID
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.SeriesId == item.SeriesId, t => new DicomInstance()
{
IsEncapsulated = syntax.IsEncapsulated,
TransferSyntaxUID = transferSyntaxUID,
MediaStorageSOPClassUID = mediaStorageSOPClassUID,
SOPClassUID = sOPClassUID,
});
}
return ResponseOutput.Ok();
}
/// <summary>
/// 影像库 和影像下载界面批量下载原始影像 患者列表只传递 患者Id数组 检查列表把患者Id数组 和检查Id数组都传递
/// </summary>
/// <param name="inCommand"></param>
/// <param name="_scpPatientRepository"></param>
/// <param name="_SCPStudyRepository"></param>
/// <param name="_oSSService"></param>
/// <returns></returns>
public async Task<IResponseOutput> GetDownloadPatientStudyInfo(PatientImageDownloadCommand inCommand,
[FromServices] IRepository<SCPPatient> _scpPatientRepository, IRepository<SCPStudy> _SCPStudyRepository, [FromServices] IOSSService _oSSService)
{
var patientIdList = inCommand.PatientIdList.Distinct().ToList();
var studyIdList = inCommand.SCPStudyIdList.Distinct().ToList();
var dirDic = new Dictionary<string, string>();
#region DIR处理导出文件名并将对应关系上传到OSS里面存储
//有传输语法值的导出 才生成DIR
if (_patientRepository.Where(t => patientIdList.Contains(t.Id)).SelectMany(t => t.SCPStudyList.Where(t => studyIdList.Count > 0 ? studyIdList.Contains(t.Id) : true).SelectMany(t => t.InstanceList)).All(c => c.TransferSyntaxUID != string.Empty))
{
var list = _patientRepository.Where(t => patientIdList.Contains(t.Id))
.SelectMany(t => t.SCPStudyList.Where(t => studyIdList.Count > 0 ? studyIdList.Contains(t.Id) : true))
.SelectMany(t => t.InstanceList)
.Select(t => new StudyDIRInfo()
{
DicomStudyId = t.SCPStudy.Id,
PatientId = t.SCPStudy.PatientIdStr,
PatientName = t.SCPStudy.PatientName,
PatientBirthDate = t.SCPStudy.PatientBirthDate,
PatientSex = t.SCPStudy.PatientSex,
StudyInstanceUid = t.StudyInstanceUid,
StudyId = t.SCPStudy.StudyId,
DicomStudyDate = t.SCPStudy.DicomStudyDate,
DicomStudyTime = t.SCPStudy.DicomStudyTime,
AccessionNumber = t.SCPStudy.AccessionNumber,
StudyDescription = t.SCPStudy.Description,
SeriesInstanceUid = t.SCPSeries.SeriesInstanceUid,
Modality = t.SCPSeries.Modality,
DicomSeriesDate = t.SCPSeries.DicomSeriesDate,
DicomSeriesTime = t.SCPSeries.DicomSeriesTime,
SeriesNumber = t.SCPSeries.SeriesNumber,
SeriesDescription = t.SCPSeries.Description,
InstanceId = t.Id,
SopInstanceUid = t.SopInstanceUid,
SOPClassUID = t.SOPClassUID,
InstanceNumber = t.InstanceNumber,
MediaStorageSOPClassUID = t.MediaStorageSOPClassUID,
MediaStorageSOPInstanceUID = t.MediaStorageSOPInstanceUID,
TransferSyntaxUID = t.TransferSyntaxUID,
}).ToList();
foreach (var item in list.GroupBy(t => new { t.StudyInstanceUid, t.DicomStudyId }))
{
var ossFolder = $"DicomDIR/{item.Key.StudyInstanceUid}";
var isSucess = await SafeBussinessHelper.RunAsync(async () => await DicomDIRHelper.GenerateStudyDIRAndUploadAsync(item.ToList(), dirDic, ossFolder, _oSSService));
if (isSucess)
{
await _SCPStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new SCPStudy() { StudyDIRPath = $"/{ossFolder}/DICOMDIR" });
}
}
}
#endregion
var query = _patientRepository.Where(t => patientIdList.Contains(t.Id)).Select(t => new
{
t.PatientName,
t.PatientIdStr,
StudyList = t.SCPStudyList.Where(t => studyIdList.Count > 0 ? studyIdList.Contains(t.Id) : true).Select(st => new DownloadDicomStudyDto()
{
PatientId = st.PatientIdStr,
StudyTime = st.StudyTime,
StudyCode = "",
StudyId = st.Id,
StudyInstanceUid = st.StudyInstanceUid,
StudyDIRPath = st.StudyDIRPath,
SeriesList = st.SeriesList.Select(z => new DownloadDicomSeriesDto()
{
Modality = z.Modality,
InstanceList = z.SCPInstanceList.Select(k => new DownloadDicomInstanceDto()
{
IsEncapsulated = k.IsEncapsulated,
InstanceId = k.Id,
FileName = string.Empty,
Path = k.Path,
FileSize = k.FileSize
}).ToList()
}).ToList()
})
});
var patientList = await query.ToListAsync();
foreach (var item in patientList.SelectMany(t => t.StudyList).SelectMany(t => t.SeriesList).SelectMany(t => t.InstanceList))
{
var key = item.InstanceId.ToString();
if (dirDic.ContainsKey(key))
{
item.FileName = dirDic[key];
}
}
var downLoadList = new ImageDownloadLog()
{
DownLoadList = patientList.SelectMany(t => t.StudyList)
.Select(t => new ImageDownloadInfo()
{ PatientId = t.PatientId, StudyId = t.StudyId, StudyInstanceUid = t.StudyInstanceUid, ImageCount = t.SeriesList.SelectMany(c => c.InstanceList).Count() }).ToList()
};
var preDownloadInfo = new SubejctVisitDownload()
{
DownLoadListStr = downLoadList.ToJsonStr(),
Id = NewId.NextSequentialGuid(),
IP = _userInfo.IP,
DownloadStartTime = DateTime.Now,
ImageCount = patientList.SelectMany(t => t.StudyList).Sum(s => s.SeriesList.Sum(s => s.InstanceList.Count())),
ImageSize = patientList.SelectMany(t => t.StudyList).Sum(t => t.SeriesList.Sum(s => s.InstanceList.Sum(i => i.FileSize))) ?? 0
};
await _subejctVisitDownloadRepository.AddAsync(preDownloadInfo, true);
return ResponseOutput.Ok(patientList, preDownloadInfo.Id);
}
/// <summary>
@ -3264,6 +3508,7 @@ namespace IRaCIS.Application.Services
.Select(u => new DownloadDicomStudyDto()
{
PatientId = u.PatientIdStr,
StudyId = u.Id,
StudyTime = u.StudyTime,
StudyCode = u.StudyCode,
StudyInstanceUid = u.StudyInstanceUid,
@ -3299,8 +3544,16 @@ namespace IRaCIS.Application.Services
}
}
var downLoadList = new ImageDownloadLog()
{
DownLoadList = result.StudyList
.Select(t => new ImageDownloadInfo()
{ PatientId = t.PatientId, StudyId = t.StudyId, StudyInstanceUid = t.StudyInstanceUid, ImageCount = t.SeriesList.SelectMany(c => c.InstanceList).Count() }).ToList()
};
var preDownloadInfo = new SubejctVisitDownload()
{
DownLoadListStr = downLoadList.ToJsonStr(),
Id = NewId.NextSequentialGuid(),
IP = _userInfo.IP,
SubjectVisitId = subjectVisitId,

View File

@ -7,6 +7,7 @@ using System;
using IRaCIS.Core.Domain.Share;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace IRaCIS.Core.Domain.Models
{
@ -19,7 +20,7 @@ namespace IRaCIS.Core.Domain.Models
public string IP { get; set; }
public Guid SubjectVisitId { get; set; }
public Guid? SubjectVisitId { get; set; }
[ForeignKey("SubjectVisitId")]
[JsonIgnore]
@ -35,6 +36,30 @@ namespace IRaCIS.Core.Domain.Models
public int ImageCount { get; set; }
public long ImageSize { get; set; }
#region 患者层级 和检查下载
[MaxLength]
public string DownLoadListStr { get; set; }
#endregion
}
public class ImageDownloadLog
{
public List<ImageDownloadInfo> DownLoadList { get; set; } = new List<ImageDownloadInfo>();
}
public class ImageDownloadInfo
{
public string PatientId { get; set; }
public Guid StudyId { get; set; }
public string StudyInstanceUid { get; set; }
public int ImageCount { get; set; }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,71 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace IRaCIS.Core.Infra.EFCore.Migrations
{
/// <inheritdoc />
public partial class imageDownloadModify : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
//migrationBuilder.DropForeignKey(
// name: "FK_SubejctVisitDownload_SubjectVisit_SubjectVisitId",
// table: "SubejctVisitDownload");
migrationBuilder.AlterColumn<Guid>(
name: "SubjectVisitId",
table: "SubejctVisitDownload",
type: "uniqueidentifier",
nullable: true,
oldClrType: typeof(Guid),
oldType: "uniqueidentifier");
migrationBuilder.AddColumn<string>(
name: "DownLoadListStr",
table: "SubejctVisitDownload",
type: "nvarchar(max)",
nullable: false,
defaultValue: "");
//migrationBuilder.AddForeignKey(
// name: "FK_SubejctVisitDownload_SubjectVisit_SubjectVisitId",
// table: "SubejctVisitDownload",
// column: "SubjectVisitId",
// principalTable: "SubjectVisit",
// principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_SubejctVisitDownload_SubjectVisit_SubjectVisitId",
table: "SubejctVisitDownload");
migrationBuilder.DropColumn(
name: "DownLoadListStr",
table: "SubejctVisitDownload");
migrationBuilder.AlterColumn<Guid>(
name: "SubjectVisitId",
table: "SubejctVisitDownload",
type: "uniqueidentifier",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
oldClrType: typeof(Guid),
oldType: "uniqueidentifier",
oldNullable: true);
migrationBuilder.AddForeignKey(
name: "FK_SubejctVisitDownload_SubjectVisit_SubjectVisitId",
table: "SubejctVisitDownload",
column: "SubjectVisitId",
principalTable: "SubjectVisit",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
}

View File

@ -8695,6 +8695,10 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
b.Property<Guid>("CreateUserId")
.HasColumnType("uniqueidentifier");
b.Property<string>("DownLoadListStr")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("DownloadEndTime")
.HasColumnType("datetime2");
@ -8715,7 +8719,7 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
b.Property<bool>("IsSuccess")
.HasColumnType("bit");
b.Property<Guid>("SubjectVisitId")
b.Property<Guid?>("SubjectVisitId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
@ -16929,9 +16933,7 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
b.HasOne("IRaCIS.Core.Domain.Models.SubjectVisit", "SubjectVisit")
.WithMany()
.HasForeignKey("SubjectVisitId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
.HasForeignKey("SubjectVisitId");
b.Navigation("CreateUserRole");