dir 增加字段
continuous-integration/drone/push Build is passing Details

Test_IRC_Net8
hang 2025-08-08 14:29:29 +08:00
parent eafceeb5d1
commit 875a3668c1
10 changed files with 20167 additions and 76 deletions

View File

@ -61,6 +61,9 @@ namespace IRaCIS.Core.Application.Helper
foreach (var item in list.OrderBy(t => t.SeriesNumber).ThenBy(t => t.InstanceNumber)) foreach (var item in list.OrderBy(t => t.SeriesNumber).ThenBy(t => t.InstanceNumber))
{ {
var dicomUid = DicomUID.Enumerate().FirstOrDefault(uid => uid.UID == item.TransferSytaxUID); var dicomUid = DicomUID.Enumerate().FirstOrDefault(uid => uid.UID == item.TransferSytaxUID);
if (dicomUid != null)
{
var ts = DicomTransferSyntax.Query(dicomUid); var ts = DicomTransferSyntax.Query(dicomUid);
var dataset = new DicomDataset(ts) var dataset = new DicomDataset(ts)
@ -104,6 +107,12 @@ namespace IRaCIS.Core.Application.Helper
dicomDir.AddFile(dicomFile, filename); dicomDir.AddFile(dicomFile, filename);
} }
}
//有实际的文件
if (mappings.Count > 0)
{
#region 写入临时路径 #region 写入临时路径
var tempFilePath = Path.GetTempFileName(); var tempFilePath = Path.GetTempFileName();
@ -132,6 +141,9 @@ namespace IRaCIS.Core.Application.Helper
await _oSSService.UploadToOSSAsync(mappingStream, ossFolder, $"Download_{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", false); await _oSSService.UploadToOSSAsync(mappingStream, ossFolder, $"Download_{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", false);
#endregion #endregion
}
return dic; return dic;
} }
@ -142,6 +154,7 @@ namespace IRaCIS.Core.Application.Helper
{ {
var dataset = dicomFile.Dataset; var dataset = dicomFile.Dataset;
var info = new StudyDIRInfo var info = new StudyDIRInfo
{ {
PatientId = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty), PatientId = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
@ -166,8 +179,10 @@ namespace IRaCIS.Core.Application.Helper
SopInstanceUid = dataset.GetSingleValueOrDefault(DicomTag.SOPInstanceUID, string.Empty), SopInstanceUid = dataset.GetSingleValueOrDefault(DicomTag.SOPInstanceUID, string.Empty),
SOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty), SOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty),
InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1), InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
MediaStorageSOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty),
MediaStorageSOPInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty), MediaStorageSOPClassUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty),
MediaStorageSOPInstanceUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty),
TransferSytaxUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty) TransferSytaxUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty)
}; };

View File

@ -1,5 +1,8 @@
using DocumentFormat.OpenXml.EMMA; using DocumentFormat.OpenXml.EMMA;
using FellowOakDicom; using FellowOakDicom;
using FellowOakDicom.Imaging;
using FellowOakDicom.Imaging.Render;
using FellowOakDicom.IO.Buffer;
using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.Helper;
using MassTransit; using MassTransit;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@ -244,17 +247,86 @@ namespace IRaCIS.Core.Application.Service
[FromServices] IRepository<DicomStudy> _studyRepository, [FromServices] IRepository<DicomStudy> _studyRepository,
[FromServices] IRepository<DicomSeries> _seriesRepository) [FromServices] IRepository<DicomSeries> _seriesRepository)
{ {
var list = await _instanceRepository.Where(t => t.TrialId == trialId) var list = await _instanceRepository.Where(t => t.TrialId == trialId && t.SubjectVisitId == Guid.Parse("01000000-0a00-0242-bd20-08dcce543ded"))
.Select(t => new { t.SeriesId, t.StudyId, t.Id, t.Path }).ToListAsync(); .Select(t => new { t.SeriesId, t.StudyId, t.Id, t.Path }).ToListAsync();
int totalCount = list.Count;
int dealCount = 0;
foreach (var item in list) foreach (var item in list)
{ {
dealCount++;
var stream = await _oSSService.GetStreamFromOSSAsync(item.Path); var stream = await _oSSService.GetStreamFromOSSAsync(item.Path);
var dicomFile = DicomFile.Open(stream); var dicomFile = DicomFile.Open(stream);
// 获取 Pixel Data 标签
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
//获取像素是否为封装形式
var syntax = dicomFile.Dataset.InternalTransferSyntax;
//对于封装像素的文件做转换
if (syntax.IsEncapsulated)
{
// 创建一个新的片段序列
var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
// 获取每帧数据并封装为单独的片段
for (int n = 0; n < pixelData.NumberOfFrames; n++)
{
var frameData = pixelData.GetFrame(n);
newFragments.Fragments.Add(new MemoryByteBuffer(frameData.Data));
}
// 替换原有的片段序列
dicomFile.Dataset.AddOrUpdate(newFragments);
}
#region 获取dir信息 维护数据库三个表数据
var dirInfo = DicomDIRHelper.ReadDicomDIRInfo(dicomFile); var dirInfo = DicomDIRHelper.ReadDicomDIRInfo(dicomFile);
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Id,
u => new DicomInstance()
{
TransferSytaxUID = dirInfo.TransferSytaxUID,
SOPClassUID = dirInfo.SOPClassUID,
MediaStorageSOPClassUID = dirInfo.MediaStorageSOPClassUID,
MediaStorageSOPInstanceUID = dirInfo.MediaStorageSOPInstanceUID
}, false);
await _seriesRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.SeriesId,
u => new DicomSeries()
{
DicomSeriesDate = dirInfo.DicomSeriesDate,
DicomSeriesTime = dirInfo.DicomSeriesTime,
}, false);
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.StudyId,
u => new DicomStudy()
{
DicomStudyDate = dirInfo.DicomStudyDate,
DicomStudyTime = dirInfo.DicomStudyTime
}, false);
#endregion
//保存到内存流
using var memoryStream = new MemoryStream();
dicomFile.Save(memoryStream);
memoryStream.Position = 0;
//获取原始目录 和文件名
var folder = item.Path.Substring(0, item.Path.LastIndexOf('/')).TrimStart('/');
var fileName = Path.GetFileName(item.Path);
await _oSSService.UploadToOSSAsync(memoryStream, folder, fileName, false);
dealCount++;
Console.WriteLine($"{DateTime.Now}已下载 {dealCount} / {totalCount} 个文件,完成 {(dealCount * 100.0 / totalCount):F2}%");
} }
return ResponseOutput.Ok(); return ResponseOutput.Ok();

View File

@ -1,4 +1,5 @@
using IRaCIS.Core.Application.Service.ImageAndDoc.DTO; using FellowOakDicom;
using IRaCIS.Core.Application.Service.ImageAndDoc.DTO;
using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Domain.Share;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
@ -330,6 +331,7 @@ namespace IRaCIS.Core.Application.Contracts
public List<AddOrUpdateSeriesDto> SeriesList { get; set; } public List<AddOrUpdateSeriesDto> SeriesList { get; set; }
} }
public class AddOrUpdateSeriesDto public class AddOrUpdateSeriesDto
@ -402,7 +404,10 @@ namespace IRaCIS.Core.Application.Contracts
public string TransferSytaxUID { get; set; } public string TransferSytaxUID { get; set; }
public string mediaStorageSOPInstanceUID { get; set; } public string MediaStorageSOPInstanceUID { get; set; }
public bool IsEncapsulated => DicomTransferSyntax.Lookup(DicomUID.Parse(TransferSytaxUID)).IsEncapsulated;
} }
public class CRCUploadTaskQuery public class CRCUploadTaskQuery

View File

@ -90,5 +90,7 @@ public class DicomInstance : BaseFullDeleteAuditEntity, IEntitySeqId
public string MediaStorageSOPInstanceUID { get; set; } public string MediaStorageSOPInstanceUID { get; set; }
public bool IsEncapsulated { get; set; }
#endregion #endregion
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace IRaCIS.Core.Infra.EFCore.Migrations
{
/// <inheritdoc />
public partial class addIsEncapsulated : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsEncapsulated",
table: "DicomInstance",
type: "bit",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsEncapsulated",
table: "DicomInstance");
}
}
}

View File

@ -930,6 +930,9 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
b.Property<bool>("IsDeleted") b.Property<bool>("IsDeleted")
.HasColumnType("bit"); .HasColumnType("bit");
b.Property<bool>("IsEncapsulated")
.HasColumnType("bit");
b.Property<bool>("IsReading") b.Property<bool>("IsReading")
.HasColumnType("bit"); .HasColumnType("bit");

View File

@ -58,7 +58,7 @@ namespace IRaCIS.Core.Infra.EFCore
Task<bool> BatchDeleteNoTrackingAsync(Expression<Func<TEntity, bool>> deleteFilter); Task<bool> BatchDeleteNoTrackingAsync(Expression<Func<TEntity, bool>> deleteFilter);
/// <summary>批量更新相当于原生sql 没用EF跟踪方式所有查询出来再更新 浪费性能)</summary> /// <summary>批量更新相当于原生sql 没用EF跟踪方式所有查询出来再更新 浪费性能)</summary>
Task<bool> BatchUpdateNoTrackingAsync(Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, TEntity>> updateFactory); Task<bool> BatchUpdateNoTrackingAsync(Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, TEntity>> updateFactory, bool isAutoIncludeTimeAndUser = true);
Task<bool> ExecuteUpdateAsync(Expression<Func<TEntity, bool>> where, Expression<Func<SetPropertyCalls<TEntity>, SetPropertyCalls<TEntity>>> setPropertyCalls); Task<bool> ExecuteUpdateAsync(Expression<Func<TEntity, bool>> where, Expression<Func<SetPropertyCalls<TEntity>, SetPropertyCalls<TEntity>>> setPropertyCalls);

View File

@ -157,7 +157,7 @@ namespace IRaCIS.Core.Infra.EFCore
/// <summary>批量更新相当于原生sql 没用EF跟踪方式所有查询出来再更新 浪费性能)</summary> /// <summary>批量更新相当于原生sql 没用EF跟踪方式所有查询出来再更新 浪费性能)</summary>
public static async Task<bool> BatchUpdateNoTrackingAsync<T>(this IRaCISDBContext _dbContext, Expression<Func<T, bool>> where, Expression<Func<T, T>> updateFactory, Guid updateUserId) where T : Entity public static async Task<bool> BatchUpdateNoTrackingAsync<T>(this IRaCISDBContext _dbContext, Expression<Func<T, bool>> where, Expression<Func<T, T>> updateFactory, Guid updateUserId, bool isAutoIncludeTimeAndUser = true) where T : Entity
{ {
if (where == null) throw new ArgumentNullException(nameof(where)); if (where == null) throw new ArgumentNullException(nameof(where));
@ -202,7 +202,8 @@ namespace IRaCIS.Core.Infra.EFCore
if (typeof(IAuditUpdate).IsAssignableFrom(typeof(T))) if (typeof(IAuditUpdate).IsAssignableFrom(typeof(T)))
{ {
if (isAutoIncludeTimeAndUser)
{
if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateTime))) if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateTime)))
{ {
fieldValues.Add(nameof(IAuditUpdate.UpdateTime), DateTime.Now); fieldValues.Add(nameof(IAuditUpdate.UpdateTime), DateTime.Now);
@ -213,6 +214,7 @@ namespace IRaCIS.Core.Infra.EFCore
fieldValues.Add(nameof(IAuditUpdate.UpdateUserId), updateUserId); fieldValues.Add(nameof(IAuditUpdate.UpdateUserId), updateUserId);
} }
} }
}
return await _dbContext.Set<T>().IgnoreQueryFilters().Where(where).ExecuteUpdateAsync(fieldValues).ConfigureAwait(false) > 0; return await _dbContext.Set<T>().IgnoreQueryFilters().Where(where).ExecuteUpdateAsync(fieldValues).ConfigureAwait(false) > 0;
} }

View File

@ -297,10 +297,10 @@ namespace IRaCIS.Core.Infra.EFCore
/// <summary>批量更新相当于原生sql 没用EF跟踪方式所有查询出来再更新 浪费性能)</summary> /// <summary>批量更新相当于原生sql 没用EF跟踪方式所有查询出来再更新 浪费性能)</summary>
public async Task<bool> BatchUpdateNoTrackingAsync(Expression<Func<TEntity, bool>> where, public async Task<bool> BatchUpdateNoTrackingAsync(Expression<Func<TEntity, bool>> where,
Expression<Func<TEntity, TEntity>> updateFactory) Expression<Func<TEntity, TEntity>> updateFactory ,bool isAutoIncludeTimeAndUser = true)
{ {
return await _dbContext.BatchUpdateNoTrackingAsync(where, updateFactory, _userInfo.UserRoleId); return await _dbContext.BatchUpdateNoTrackingAsync(where, updateFactory, _userInfo.UserRoleId, isAutoIncludeTimeAndUser);
} }