dir 增加字段
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
eafceeb5d1
commit
875a3668c1
|
@ -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)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
19963
IRaCIS.Core.Infra.EFCore/Migrations/20250808062746_addIsEncapsulated.Designer.cs
generated
Normal file
19963
IRaCIS.Core.Infra.EFCore/Migrations/20250808062746_addIsEncapsulated.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue