208 lines
8.3 KiB
C#
208 lines
8.3 KiB
C#
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
|
using IRaCIS.Core.Application.Helper;
|
|
using IRaCIS.Core.Infrastructure.Extention;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace IRaCIS.Core.Application.Services
|
|
{
|
|
[ApiExplorerSettings(GroupName = "Image")]
|
|
[AllowAnonymous]
|
|
public class SeriesService(IRepository<DicomSeries> _seriesRepository,
|
|
IRepository<DicomInstance> _instanceRepository,
|
|
IRepository<SCPSeries> _scpSeriesRepository,
|
|
IRepository<SCPInstance> _scpInstanceRepository,
|
|
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IWebHostEnvironment _hostEnvironment) : BaseService
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
/// 更新缩略图路径
|
|
/// </summary>
|
|
/// <param name="dto"></param>
|
|
/// <returns></returns>
|
|
[HttpPost]
|
|
public async Task<IResponseOutput> UpdateImageResizePath(UpdateImageResizeDTO dto)
|
|
{
|
|
await _seriesRepository.UpdatePartialFromQueryAsync(t => t.Id == dto.SeriesId, u => new DicomSeries() { ImageResizePath = dto.ImageResizePath }, true);
|
|
|
|
return ResponseOutput.Ok();
|
|
}
|
|
|
|
//医生读片那一块有耦合,关键序列 这里暂时留存
|
|
/// <summary> 指定资源Id,获取Dicom检查所属序列信息列表 </summary>
|
|
/// <param name="studyId"> Dicom检查的Id </param>
|
|
/// <param name="isReading"></param>
|
|
[HttpGet, Route("{studyId:guid}")]
|
|
public async Task<IResponseOutput<List<DicomSeriesDTO>>> List(Guid studyId, bool? isReading, bool? isPacs)
|
|
{
|
|
if (isPacs == true)
|
|
{
|
|
var seriesList = await _scpSeriesRepository.Where(s => s.StudyId == studyId).IgnoreQueryFilters()
|
|
.OrderBy(s => s.SeriesNumber).ThenBy(s => s.SeriesTime).ThenBy(s => s.CreateTime)
|
|
.ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).ToListAsync();
|
|
|
|
var instanceList = await _scpInstanceRepository.Where(s => s.StudyId == studyId).IgnoreQueryFilters()
|
|
.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, t.FileSize, t.ImagePositionPatient, t.ImageOrientationPatient }).ToListAsync();//.GroupBy(u => u.SeriesId);
|
|
|
|
|
|
foreach (var series in seriesList)
|
|
{
|
|
|
|
var instances = instanceList.Where(x => x.SeriesId == series.Id);
|
|
|
|
// ⭐ DICOM 空间排序(带兜底)
|
|
var sorted = DicomSortHelper.SortSlices(
|
|
instances,
|
|
x => x.ImagePositionPatient,
|
|
x => x.ImageOrientationPatient,
|
|
x => x.InstanceNumber
|
|
);
|
|
|
|
series.InstanceInfoList = sorted.Select(k =>
|
|
new InstanceBasicInfo()
|
|
{
|
|
Id = k.Id,
|
|
NumberOfFrames = k.NumberOfFrames,
|
|
HtmlPath = string.Empty,
|
|
Path = k.Path,
|
|
InstanceNumber = k.InstanceNumber,
|
|
IsReading = true,
|
|
IsDeleted = false,
|
|
FileSize = k.FileSize,
|
|
|
|
|
|
}).ToList();
|
|
|
|
series.InstanceCount = series.InstanceInfoList.Count;
|
|
}
|
|
|
|
return ResponseOutput.Ok(seriesList);
|
|
|
|
}
|
|
else
|
|
{
|
|
//质控的时候,要标记序列,和instance 删除 ,所以要返回全部,但是 质控通过后,pm 进去看的时候要看过滤后的
|
|
|
|
var qcAuditState = await _seriesRepository.Where(s => s.StudyId == studyId).Select(t => t.DicomStudy.SubjectVisit.AuditState).FirstOrDefaultAsync();
|
|
|
|
//质控通过以后,预览过滤删除的 质控之前的不过滤
|
|
var isQCFinished = qcAuditState == AuditStateEnum.QCPassed;
|
|
|
|
//断点
|
|
var seriesList = await _seriesRepository.Where(s => s.StudyId == studyId).IgnoreQueryFilters()
|
|
.WhereIf(isQCFinished, t => t.IsDeleted == false)
|
|
.WhereIf(isReading == true, t => t.IsReading == true)
|
|
.OrderBy(s => s.SeriesNumber).ThenBy(s => s.SeriesTime).ThenBy(s => s.CreateTime)
|
|
.ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).ToListAsync();
|
|
|
|
var instanceList = await _instanceRepository.Where(s => s.StudyId == studyId).IgnoreQueryFilters()
|
|
.WhereIf(isQCFinished, t => t.IsDeleted == false)
|
|
.WhereIf(isReading == true, t => t.IsReading == true)
|
|
.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, t.HtmlPath, t.IsReading, t.IsDeleted, t.FileSize ,t.IsMasked}).ToListAsync();//.GroupBy(u => u.SeriesId);
|
|
|
|
|
|
foreach (var series in seriesList)
|
|
{
|
|
|
|
series.InstanceInfoList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k =>
|
|
new InstanceBasicInfo()
|
|
{
|
|
Id = k.Id,
|
|
NumberOfFrames = k.NumberOfFrames,
|
|
HtmlPath = k.HtmlPath,
|
|
Path = k.Path,
|
|
InstanceNumber = k.InstanceNumber,
|
|
IsReading = k.IsReading,
|
|
IsDeleted = k.IsDeleted,
|
|
FileSize = k.FileSize,
|
|
IsMasked = k.IsMasked
|
|
|
|
}).ToList();
|
|
|
|
series.InstanceCount = series.InstanceInfoList.Count;
|
|
}
|
|
|
|
return ResponseOutput.Ok(seriesList);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary> 指定资源Id,渲染Dicom序列的Jpeg预览图像 </summary>
|
|
/// <param name="seriesId"> Dicom序列的Id </param>
|
|
[HttpGet, Route("{seriesId:guid}")]
|
|
[AllowAnonymous]
|
|
public async Task<FileContentResult> Preview(Guid seriesId)
|
|
{
|
|
string path = string.Empty;
|
|
|
|
path = (await _instanceRepository.Where(s => s.SeriesId == seriesId).Select(t => t.Path).FirstOrDefaultAsync()).IfNullThrowException();
|
|
|
|
#region 切换磁盘存储之前
|
|
//var physicalPath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, path);
|
|
|
|
#endregion
|
|
|
|
#region 切换磁盘存储后
|
|
|
|
var physicalPath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, path);
|
|
|
|
//不存在,去其他路径找
|
|
if (!File.Exists(physicalPath))
|
|
{
|
|
//找到所有磁盘
|
|
var drives = DriveInfo.GetDrives()
|
|
.Where(d => d.DriveType == DriveType.Fixed && d.IsReady)
|
|
//剩余空间最多的
|
|
.OrderByDescending(d => d.AvailableFreeSpace)
|
|
|
|
//存储空间相同,则按照按照总空间从大到小排序
|
|
.ThenByDescending(d => d.TotalSize - d.TotalFreeSpace)
|
|
.Select(d => d.RootDirectory.FullName)
|
|
.ToList();
|
|
|
|
|
|
foreach (var drive in drives)
|
|
{
|
|
physicalPath = Path.Combine(drive, _hostEnvironment.EnvironmentName, path.Trim('/'));
|
|
|
|
if (File.Exists(physicalPath))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
using (var sw = ImageHelper.RenderPreviewJpeg(physicalPath))
|
|
{
|
|
var bytes = new byte[sw.Length];
|
|
sw.Read(bytes, 0, bytes.Length);
|
|
sw.Close();
|
|
return new FileContentResult(bytes, "image/jpeg");
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
} |