CostCalculationItem/IRaCIS.Core.API/Controllers/Image/StudyController.cs

537 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using IRaCIS.Application.Interfaces;
using IRaCIS.Application.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using IRaCIS.Core.Application.Contracts.RequestAndResponse;
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
using Microsoft.Net.Http.Headers;
using Microsoft.AspNetCore.WebUtilities;
using System.Threading.Tasks;
using IRaCIS.Core.Application.Contracts.Dicom;
using System.IO;
using System.IO.Compression;
using IRaCIS.Core.API.Filter;
using Microsoft.AspNetCore.Http.Features;
using IRaCIS.Core.Application.Dicom;
using IRaCIS.Core.Domain.Share;
using System.Linq;
using System.Threading;
using Microsoft.AspNetCore.Http;
using IRaCIS.Api.Filter;
using NLog;
using Microsoft.Extensions.Logging;
using System.Text;
namespace IRaCIS.Api.Controllers
{
/// <summary>
/// Study
/// </summary>
[Route("study")]
[ApiController, Authorize, ApiExplorerSettings(GroupName = "Image")]
public class StudyController : ControllerBase
{
private readonly IStudyService _studyService;
private readonly IDicomArchiveService _dicomArchiveService;
private readonly IDoctorService _doctorService;
private static readonly FormOptions _defaultFormOptions = new FormOptions();
private readonly ILogger<StudyController> _logger;
public StudyController(IStudyService studyService,
IDicomArchiveService dicomArchiveService, IDoctorService doctorService,
ILogger<StudyController> logger
)
{
_studyService = studyService;
_dicomArchiveService = dicomArchiveService;
_doctorService = doctorService;
_logger = logger;
}
///// <summary> 根据项目Id受试者Id获取受试者检查统计(不分页)</summary>
//[HttpGet, Route("getStudyStatList/{trialId:guid}/{subjectId:guid}/{siteId:guid}")]
//public IResponseOutput<IEnumerable<StudyStatDTO>> GetStudy(Guid trialId, Guid subjectId, Guid siteId)
//{
// return ResponseOutput.Ok(_studyService.GetStudyStatList(trialId, subjectId, siteId));
//}
[HttpPost, Route("getStudyList")]
public IResponseOutput<PageOutput<StudyDTO>> GetStudyList(StudyQueryDTO queryDto)
{
return ResponseOutput.Ok(_studyService.GetStudyList(queryDto));
}
[HttpPost, Route("getDistributeStudyList")]
public IResponseOutput<PageOutput<DistributeReviewerStudyStatusDTO>> GetDistributeStudyList(
StudyStatusQueryDTO studyStatusQueryDto)
{
return ResponseOutput.Ok(_studyService.GetDistributeStudyList(studyStatusQueryDto));
}
[HttpPost, Route("updateStudyBinding")]
//[LogFilter]
public IResponseOutput UpdateStudyBinding(StudyEditCommand studyEditCommand)
{
if (studyEditCommand.SubjectVisitId == Guid.Empty)
{
studyEditCommand.SubjectVisitId = _studyService.AddSubjectVisit(studyEditCommand.SubjectId, studyEditCommand.SiteId,
studyEditCommand.VisitStageId, studyEditCommand.SVSTDTC, studyEditCommand.SVENDTC);
}
else
{
_studyService.UpdateSubjectVisit(studyEditCommand.SubjectVisitId, studyEditCommand.SVSTDTC,
studyEditCommand.SVENDTC);
}
return ResponseOutput.Ok( _studyService.UpdateStudyBinding(studyEditCommand).Result);
}
/// <summary>
/// 获取受试者 这次访视 对应的study modality 列表
/// </summary>
/// <param name="trialId"></param>
/// <param name="siteId"></param>
/// <param name="subjectId"></param>
/// <param name="subjectVisitId"></param>
/// <returns></returns>
[HttpPost, Route("getSubjectVisitStudyList/{trialId:guid}/{siteId:guid}/{subjectId:guid}/{subjectVisitId:guid}")]
[AllowAnonymous]
public IResponseOutput<List<SubjectVisitStudyDTO>> GetSubjectVisitStudyList(Guid trialId, Guid siteId, Guid subjectId,
Guid subjectVisitId)
{
return ResponseOutput.Ok(
_studyService.GetSubjectVisitStudyList(trialId, siteId, subjectId, subjectVisitId));
}
/// <summary>
/// 提示 该项目是否允许上传该study如果不允许 提示 已经上传到哪个受试者 哪次方式 studyCode是多少
/// </summary>
/// <param name="studyInstanceUid"></param>
/// <param name="trialId"></param>
/// <param name="abandonStudyId"></param>
/// <returns></returns>
[HttpGet, Route("verifyStudyAllowUpload/{trialId:guid}/{studyInstanceUid}/{abandonStudyId?}")]
[AllowAnonymous]
public IResponseOutput<VerifyStudyUploadResult> VerifyStudyAllowUpload(string studyInstanceUid, Guid trialId, Guid? abandonStudyId)
{
return ResponseOutput.Ok(_studyService.VerifyStudyAllowUpload(studyInstanceUid, trialId, abandonStudyId));
}
/// <summary> 归档</summary>
//[HttpPost, Route("archiveStudy/{trialId:guid}/{siteId:guid}/{subjectId:guid}/{visitStageId:guid}/{subjectVisitId:guid}/{SVSTDTC?}/{SVENDTC?}/{comment}")]
[HttpPost, Route("archiveStudy")]
[DisableFormValueModelBinding]
[DisableRequestSizeLimit]
[LogFilter]
//public async Task<IResponseOutput> ArchiveStudy(Guid trialId, Guid siteId, Guid subjectId, Guid visitStageId, Guid subjectVisitId, DateTime? SVSTDTC, DateTime? SVENDTC, string comment)
public async Task<IResponseOutput> ArchiveStudy([FromForm] ArchiveStudyCommand archiveStudyCommand)
{
//处理 受试者访视没有记录 系统自动添加
#region url方式
//if (subjectVisitId == Guid.Empty)
//{
// subjectVisitId = _studyService.AddSubjectVisit(subjectId, visitStageId);
//}
//string userName = User.FindFirst("realName").Value;
//StudyCommand addtionalInfo = new StudyCommand
//{
// SiteId = siteId,
// SubjectId = subjectId,
// TrialId = trialId,
// SubjectVisitId = subjectVisitId,
//};
//var savedInfo = _studyService.GetSaveToDicomInfo(addtionalInfo);
////加入comment
//savedInfo.Comment = comment;
#endregion
try
{
#region post body 方式 subjectVisitId
////var subjectVisitStudyOldList = new List<SubjectVisitStudyDTO>();
// 受试者方式计划一次性添加,因为没有了上传时添加逻辑
//if (archiveStudyCommand.SubjectVisitId == Guid.Empty)
//{
// archiveStudyCommand.SubjectVisitId = _studyService.AddSubjectVisit(archiveStudyCommand.SubjectId, archiveStudyCommand.SiteId,
// archiveStudyCommand.VisitStageId.Value, archiveStudyCommand.SVSTDTC, archiveStudyCommand.SVENDTC);
//}
//else
//{
//_studyService.UpdateSubjectVisit(archiveStudyCommand.SubjectVisitId, archiveStudyCommand.SVSTDTC,
// archiveStudyCommand.SVENDTC);
//#region 重传和上传放在一起 暂不用
////处理 重传 在归档前 事先查出 这次访视已经有的study列表
////subjectVisitStudyOldList = _studyService.GetSubjectVisitStudyList(archiveStudyCommand.TrialId,
//// archiveStudyCommand.SiteId, archiveStudyCommand.SubjectId, archiveStudyCommand.SubjectVisitId);
//#endregion
//}
var userName = User.FindFirst("realName").Value;
StudyCommand addtionalInfo = new StudyCommand
{
SiteId = archiveStudyCommand.SiteId,
SubjectId = archiveStudyCommand.SubjectId,
TrialId = archiveStudyCommand.TrialId,
SubjectVisitId = archiveStudyCommand.SubjectVisitId,
};
var savedInfo = _studyService.GetSaveToDicomInfo(addtionalInfo);
#endregion
var archiveResult = new DicomArchiveResult();
var archivedStudyIds = new List<Guid>();
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
var section = await reader.ReadNextSectionAsync();
while (section != null)
{
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
if (hasContentDispositionHeader)
{
string fileName = contentDisposition.FileName.Value;
try
{
//采用post方式 这里多加一个判断 过滤其他参数
if (string.IsNullOrEmpty(section.ContentType))
{
break;
}
string mediaType = section.ContentType;
if (mediaType.Contains("zip"))
{
var partStream = section.Body;
using (var zipArchive = new ZipArchive(partStream, ZipArchiveMode.Read))
{
foreach (var entry in zipArchive.Entries)
{
if (entry.FullName.EndsWith("/")) continue;
try
{
++archiveResult.ReceivedFileCount;
using (var memoryStream = new MemoryStream())
{
await section.Body.CopyToAsync(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
DicomInstanceDTO instance = await _dicomArchiveService.ArchiveDicomStreamAsync(memoryStream, savedInfo);
if (!archivedStudyIds.Contains(instance.StudyId))
archivedStudyIds.Add(instance.StudyId);
}
}
catch
{
archiveResult.ErrorFiles.Add($"{fileName}/{entry.FullName}");
}
}
}
}
++archiveResult.ReceivedFileCount;
if (mediaType.Contains("octet-stream"))
{
using (var memoryStream = new MemoryStream())
{
await section.Body.CopyToAsync(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
DicomInstanceDTO instance = await _dicomArchiveService.ArchiveDicomStreamAsync(memoryStream, savedInfo);
if (!archivedStudyIds.Contains(instance.StudyId))
archivedStudyIds.Add(instance.StudyId);
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
archiveResult.ErrorFiles.Add(fileName);
}
}
section = await reader.ReadNextSectionAsync();
}
if (archivedStudyIds.Count > 0)
{
//更新受试者访视信息 及上传状态
_studyService.UpdateSubjectVisit(archiveStudyCommand.SubjectVisitId, archiveStudyCommand.SVSTDTC,
archiveStudyCommand.SVENDTC);
//更新受试者最新的访视状态,访视号最大的
_studyService.UpdateSubjectLatestInfo(archiveStudyCommand.SubjectId, archiveStudyCommand.TrialId);
archiveResult.ArchivedDicomStudies = _dicomArchiveService.GetArchivedStudyList(archivedStudyIds);
//归档完成,插入记录表
foreach (var archivedDicomStudy in archiveResult.ArchivedDicomStudies)
{
var seriesModalityList = _dicomArchiveService.GetSeriesList(archivedDicomStudy.Id).Select(u => u.Modality).Distinct();
StringBuilder sb = new StringBuilder();
foreach (var item in seriesModalityList)
{
sb.Append(item).Append(",");
}
string ModaliyStr = sb.ToString();
if (ModaliyStr.Length > 0)
{
ModaliyStr = ModaliyStr.Substring(0, ModaliyStr.Length - 1);
}
string comment = string.Empty;// 上传或重传完成之后更新Comment信息
if (!string.IsNullOrWhiteSpace(archiveStudyCommand.Comment))
{
comment = archiveStudyCommand.Comment;
}
_studyService.UpdateStudyModaliyAndComment(archivedDicomStudy.Id, ModaliyStr,comment);
//重传 并且不是同一份文件
if (archiveStudyCommand.AbandonStudyId != null &&
archiveStudyCommand.AbandonStudyId != Guid.Empty && archiveStudyCommand.AbandonStudyId != archivedDicomStudy.Id)
{
//更新状态为废弃
_studyService.UpdateStudyStatus(new StudyStatusDetailCommand
{
StudyId = archiveStudyCommand.AbandonStudyId.Value,
Status = (int)StudyStatus.Abandon,
Note = string.Empty
}, userName);
//此时归档的新的Study 初始状态设置为 已重传完成
_studyService.UpdateStudyStatus(new StudyStatusDetailCommand
{
StudyId = archivedDicomStudy.Id,
Status = (int)StudyStatus.QAReuploaded,
Note = string.Empty
}, userName);
//更新新得study code 为废弃的
_studyService.UpdateReUploadNewStudyCode(archiveStudyCommand.AbandonStudyId.Value, archivedDicomStudy.Id);
}//重传 是同一份文件
else if (archiveStudyCommand.AbandonStudyId != null &&
archiveStudyCommand.AbandonStudyId != Guid.Empty && archiveStudyCommand.AbandonStudyId == archivedDicomStudy.Id)
{
//此时归档的新的Study 初始状态设置为 已重传完成
_studyService.UpdateStudyStatus(new StudyStatusDetailCommand
{
StudyId = archivedDicomStudy.Id,
Status = (int)StudyStatus.QAReuploaded,
Note = string.Empty
}, userName);
}//上传
else
{
_studyService.UpdateStudyStatus(new StudyStatusDetailCommand
{
StudyId = archivedDicomStudy.Id,
Status = (int)StudyStatus.Uploaded,
Note = string.Empty
}, userName);
}
}
}
return ResponseOutput.Ok(archiveResult);
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
throw;
}
}
/// <summary> 删除检查</summary>
[HttpDelete, Route("deleteStudy/{id:guid}")]
[LogFilter]
public IResponseOutput DeleteStudy(Guid id)
{
return _studyService.DeleteStudy(id);
}
/// <summary> 指定资源Id渲染Dicom检查的Jpeg预览图像 </summary>
/// <param name="studyId"> Dicom检查的Id </param>
[HttpGet, Route("preview/{studyId:guid}")]
[AllowAnonymous]
public FileContentResult GetStudyPreview(Guid studyId)
{
string path = _dicomArchiveService.GetStudyPreview(studyId);
using (var sw = DicomRenderingHelper.RenderPreviewJpeg(path))
{
var bytes = new byte[sw.Length];
sw.Read(bytes, 0, bytes.Length);
sw.Close();
return new FileContentResult(bytes, "image/jpeg");
}
}
/// <summary> 指定资源Id获取Dicom检查信息 </summary>
/// <param name="studyId"> Dicom检查的Id </param>
[HttpGet, Route("item/{studyId:guid}")]
public IResponseOutput<DicomStudyDTO> GetStudyItem(Guid studyId)
{
return ResponseOutput.Ok(_dicomArchiveService.GetStudyItem(studyId));
}
/// <summary> 更新Study状态并保存状态变更信息 </summary>
/// <param name="studyStatusDetailCommand"></param>
[HttpPost, Route("updateStudyStatus")]
[LogFilter]
public IResponseOutput UpdateStudyStatus(StudyStatusDetailCommand studyStatusDetailCommand)
{
string userName = User.FindFirst("realName").Value; ;
return ResponseOutput.Result(_studyService.UpdateStudyStatus(studyStatusDetailCommand, userName));
}
/// <summary>
/// Dicom匿名化
/// </summary>
/// <param name="studyId">需要匿名化的检查Id</param>
/// <returns></returns>
[HttpPost, Route("dicomAnonymize/{studyId:guid}")]
[LogFilter]
public async Task<IResponseOutput> DicomAnonymize(Guid studyId)
{
string userName = User.FindFirst("realName").Value; ;
return ResponseOutput.Result(await _studyService.DicomAnonymize(studyId, userName));
}
/// <summary> Study分配 </summary>
[HttpPost, Route("studyDistribution")]
[Obsolete]
public IResponseOutput StudyDistribution(StudyReviewerCommand studyReviewerCommand)
{
string userName = User.FindFirst("realName").Value;
var doctorInfo = _doctorService.GetDoctorBasicInfo(studyReviewerCommand.ReviewerId);
List<StudyStatusDetailCommand> studyStatusCommandList = new List<StudyStatusDetailCommand>();
foreach (var study in studyReviewerCommand.StudyList)
{
var studyStatus = new StudyStatusDetailCommand
{
StudyId = study.StudyId,
Status = (int)StudyStatus.Distributed,
Note = doctorInfo.LastName + " " + doctorInfo.FirstName
};
studyStatusCommandList.Add(studyStatus);
}
var result = _studyService.DistributeStudy(studyReviewerCommand);
_studyService.UpdateStudyStatus(studyStatusCommandList, userName);
return ResponseOutput.Result(result);
}
[Obsolete]
[HttpPost, Route("editStudyReviewer")]
public IResponseOutput EditStudyReviewer(StudyReviewerEditCommand studyReviewerEditCommand)
{
return _studyService.EditStudyReviewer(studyReviewerEditCommand);
}
/// <summary>
/// 根据项目Id 获取可选医生列表
/// </summary>
/// <param name="trialId"></param>
/// <returns></returns>
[HttpGet, Route("GetReviewerList/{trialId:guid}")]
public IResponseOutput<List<ReviewerDistributionDTO>> GetReviewerListByTrialId(Guid trialId)
{
var result = _studyService.GetReviewerListByTrialId(trialId);
return ResponseOutput.Ok(result);
}
/// <summary>
/// 根据StudyId获取该Study的操作记录时间倒序
/// </summary>
/// <param name="studyId"></param>
/// <returns></returns>
[HttpGet, Route("getStudyStatusDetailList/{studyId:guid}")]
public IResponseOutput<List<StudyStatusDetailDTO>> GetStudyStatusDetailList(Guid studyId)
{
var result = _studyService.GetStudyStatusDetailList(studyId);
return ResponseOutput.Ok(result);
}
/// <summary>
/// 根据TPCode 获取所有的标记
/// </summary>
/// <param name="tpCode"></param>
/// <returns></returns>
[HttpGet, Route("getImageLabelList/{tpCode}")]
[AllowAnonymous]
public IResponseOutput<IEnumerable<ImageLabelDTO>> GetImageLabelList(string tpCode)
{
return ResponseOutput.Ok(_dicomArchiveService.GetImageLabel(tpCode));
}
/// <summary>
/// 保存标记(跟删除合并,每次保存最新的标记),会删除替换之前的标记
/// 外层的TPcode 必须传里面的标记数组可为空数组表示删除该Study的所有标记
/// </summary>
/// <param name="imageLabelCommand"></param>
/// <returns></returns>
[HttpPost, Route("saveImageLabelList")]
[AllowAnonymous]
[LogFilter]
public IResponseOutput SaveImageLabelList(ImageLabelCommand imageLabelCommand)
{
return ResponseOutput.Result(_dicomArchiveService.SaveImageLabelList(imageLabelCommand));
}
/// <summary>
/// 获取某个访视的关联访视
/// 用于获取关联影像(调用之前的接口:/series/list/根据StudyId获取访视的序列列表
/// </summary>
/// <param name="visitNum"></param>
/// <param name="tpCode"></param>
/// <returns></returns>
[HttpGet, Route("getRelationVisitList/{visitNum}/{tpCode}")]
[AllowAnonymous]
public IResponseOutput<IEnumerable<RelationVisitDTO>> GetRelationVisitList(decimal visitNum, string tpCode)
{
return ResponseOutput.Ok(_studyService.GetRelationVisitList(visitNum, tpCode));
}
}
}