1078 lines
41 KiB
C#
1078 lines
41 KiB
C#
using AutoMapper;
|
||
using EasyCaching.Core;
|
||
using ExcelDataReader;
|
||
using IRaCIS.Application.Contracts;
|
||
using IRaCIS.Application.Interfaces;
|
||
using IRaCIS.Core.Application.Auth;
|
||
using IRaCIS.Core.Application.Contracts;
|
||
using IRaCIS.Core.Application.Contracts.Dicom;
|
||
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
||
using IRaCIS.Core.Application.Filter;
|
||
using IRaCIS.Core.Application.Helper;
|
||
using IRaCIS.Core.Application.MediatR.CommandAndQueries;
|
||
using IRaCIS.Core.Application.Service.Verify;
|
||
using IRaCIS.Core.Domain.Models;
|
||
using IRaCIS.Core.Domain.Share;
|
||
using IRaCIS.Core.Infra.EFCore;
|
||
using IRaCIS.Core.Infrastructure;
|
||
using IRaCIS.Core.Infrastructure.Extention;
|
||
using Magicodes.ExporterAndImporter.Excel;
|
||
using MediatR;
|
||
using Microsoft.AspNetCore.Authorization;
|
||
using Microsoft.AspNetCore.Hosting;
|
||
using Microsoft.AspNetCore.Http;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Microsoft.AspNetCore.StaticFiles;
|
||
using Microsoft.AspNetCore.WebUtilities;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Logging;
|
||
using Microsoft.Net.Http.Headers;
|
||
using MiniExcelLibs;
|
||
using Newtonsoft.Json;
|
||
using SharpCompress.Archives;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Data;
|
||
using System.IO;
|
||
using System.IO.Compression;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace IRaCIS.Core.API.Controllers
|
||
{
|
||
|
||
#region 上传基类封装
|
||
[DisableFormValueModelBinding]
|
||
public abstract class UploadBaseController : ControllerBase
|
||
{
|
||
/// <summary> 流式上传 直接返回</summary>
|
||
[Route("base")]
|
||
public virtual async Task<IResponseOutput> SingleFileUploadAsync(Func<string, (string, string)> filePathFunc)
|
||
{
|
||
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)
|
||
{
|
||
|
||
var (serverFilePath, relativePath) = filePathFunc(contentDisposition.FileName.Value);
|
||
|
||
await FileStoreHelper.WriteFileAsync(section.Body, serverFilePath);
|
||
|
||
//仅仅返回一个文件,如果多文件上传 在最后返回多个路径
|
||
return ResponseOutput.Ok(new
|
||
{
|
||
FilePath = relativePath,
|
||
FullFilePath = relativePath /*+ "?access_token=" + _userInfo.UserToken*/
|
||
});
|
||
|
||
}
|
||
section = await reader.ReadNextSectionAsync();
|
||
}
|
||
return ResponseOutput.Ok();
|
||
}
|
||
|
||
|
||
/// <summary> 流式上传 通用封装 不返回任何数据,后续还有事情处理 </summary>
|
||
[Route("base")]
|
||
public virtual async Task FileUploadAsync(Func<string, Task<string>> filePathFunc)
|
||
{
|
||
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)
|
||
{
|
||
|
||
var fileName = contentDisposition.FileName.Value;
|
||
|
||
|
||
//处理压缩文件
|
||
if (fileName.Contains(".Zip", StringComparison.OrdinalIgnoreCase) || fileName.Contains(".rar", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
var archive = ArchiveFactory.Open(section.Body);
|
||
|
||
foreach (var entry in archive.Entries)
|
||
{
|
||
if (!entry.IsDirectory)
|
||
{
|
||
|
||
var serverFilePath = await filePathFunc(entry.Key);
|
||
|
||
entry.WriteToFile(serverFilePath);
|
||
|
||
}
|
||
}
|
||
}
|
||
//普通单个文件
|
||
else
|
||
{
|
||
var serverFilePath = await filePathFunc(fileName);
|
||
|
||
await FileStoreHelper.WriteFileAsync(section.Body, serverFilePath);
|
||
|
||
}
|
||
|
||
|
||
|
||
}
|
||
section = await reader.ReadNextSectionAsync();
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/// <summary> 流式上传 Dicom上传 需要返回文件数量 </summary>
|
||
[Route("base")]
|
||
public virtual async Task<int> DicomFileUploadAsync(Func<string ,Stream, Task> filePathFunc)
|
||
{
|
||
|
||
var fileCount = 0;
|
||
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)
|
||
{
|
||
|
||
var fileName = contentDisposition.FileName.Value??String.Empty;
|
||
|
||
string mediaType = section.ContentType ?? String.Empty;
|
||
|
||
|
||
//处理压缩文件
|
||
if (fileName.Contains(".Zip", StringComparison.OrdinalIgnoreCase) || fileName.Contains(".rar", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
var archive = ArchiveFactory.Open(section.Body);
|
||
|
||
foreach (var entry in archive.Entries)
|
||
{
|
||
if (!entry.IsDirectory)
|
||
{
|
||
++fileCount;
|
||
|
||
await filePathFunc(entry.Key, entry.OpenEntryStream());
|
||
|
||
|
||
}
|
||
}
|
||
}
|
||
//普通单个文件
|
||
else
|
||
{
|
||
if (mediaType.Contains("octet-stream"))
|
||
{
|
||
++fileCount;
|
||
|
||
await filePathFunc(fileName, section.Body);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
}
|
||
section = await reader.ReadNextSectionAsync();
|
||
}
|
||
|
||
return fileCount;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Dicom 影像上传 临床数据 非diocm
|
||
|
||
[ApiExplorerSettings(GroupName = "Image")]
|
||
[ApiController]
|
||
public class StudyController : UploadBaseController
|
||
{
|
||
public IMapper _mapper { get; set; }
|
||
public IUserInfo _userInfo { get; set; }
|
||
|
||
private readonly IWebHostEnvironment _hostEnvironment;
|
||
|
||
|
||
|
||
public StudyController(IMapper mapper, IUserInfo userInfo, IWebHostEnvironment hostEnvironment)
|
||
{
|
||
_hostEnvironment = hostEnvironment;
|
||
_mapper = mapper;
|
||
_userInfo = userInfo;
|
||
}
|
||
|
||
/// <summary>Dicom 归档</summary>
|
||
[HttpPost, Route("Study/ArchiveStudy1/{trialId:guid}")]
|
||
[DisableFormValueModelBinding]
|
||
[DisableRequestSizeLimit]
|
||
[TypeFilter(typeof(TrialResourceFilter))]
|
||
public async Task<IResponseOutput> ArchiveStudy([FromForm] ArchiveStudyCommand archiveStudyCommand,
|
||
[FromServices] ILogger<UploadDownLoadController> _logger,
|
||
[FromServices] IEasyCachingProvider _provider,
|
||
[FromServices] IStudyService _studyService,
|
||
[FromServices] IDicomArchiveService _dicomArchiveService
|
||
)
|
||
{
|
||
|
||
string studycode = string.Empty;
|
||
var startTime = DateTime.Now;
|
||
|
||
if (_provider.Exists("StudyUid_" + archiveStudyCommand.StudyInstanceUid))
|
||
{
|
||
return ResponseOutput.NotOk("当前已有人正在上传和归档该检查!");
|
||
}
|
||
else
|
||
{
|
||
_provider.Set("StudyUid_" + archiveStudyCommand.StudyInstanceUid, _userInfo.Id, TimeSpan.FromMinutes(30));
|
||
}
|
||
|
||
var archiveResult = new DicomArchiveResult();
|
||
var archivedStudyIds = new List<Guid>();
|
||
|
||
var (seriesInstanceUidList, sopInstanceUidList) = (new List<string>(), new List<string>());
|
||
|
||
//重传的时候,找出当前检查已经上传的series instance
|
||
if (archiveStudyCommand.AbandonStudyId != null)
|
||
{
|
||
(seriesInstanceUidList, sopInstanceUidList) = _studyService.GetHasUploadSeriesAndInstance(archiveStudyCommand.AbandonStudyId.Value);
|
||
}
|
||
|
||
var savedInfo = _studyService.GetSaveToDicomInfo(archiveStudyCommand.SubjectVisitId);
|
||
|
||
|
||
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)
|
||
{
|
||
//采用post方式 这里多加一个判断 过滤其他参数
|
||
if (string.IsNullOrEmpty(section.ContentType))
|
||
{
|
||
section = await reader.ReadNextSectionAsync();
|
||
continue;
|
||
}
|
||
|
||
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
|
||
if (hasContentDispositionHeader)
|
||
{
|
||
string fileName = contentDisposition.FileName.Value;
|
||
try
|
||
{
|
||
|
||
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);
|
||
|
||
var archiveStudyId = await _dicomArchiveService.ArchiveDicomStreamAsync(memoryStream, savedInfo, seriesInstanceUidList, sopInstanceUidList);
|
||
studycode = archiveStudyId.Item2;
|
||
if (!archivedStudyIds.Contains(archiveStudyId.Item1))
|
||
archivedStudyIds.Add(archiveStudyId.Item1);
|
||
}
|
||
}
|
||
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);
|
||
|
||
var archiveStudyId = await _dicomArchiveService.ArchiveDicomStreamAsync(memoryStream, savedInfo, seriesInstanceUidList, sopInstanceUidList);
|
||
studycode = archiveStudyId.Item2;
|
||
|
||
|
||
if (!archivedStudyIds.Contains(archiveStudyId.Item1))
|
||
archivedStudyIds.Add(archiveStudyId.Item1);
|
||
}
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
_logger.LogError(e.Message + e.StackTrace);
|
||
|
||
archiveResult.ErrorFiles.Add(fileName);
|
||
|
||
_provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid);
|
||
|
||
}
|
||
}
|
||
section = await reader.ReadNextSectionAsync();
|
||
}
|
||
|
||
try
|
||
{
|
||
if (archivedStudyIds.Count > 0) // 上传成功,处理逻辑
|
||
{
|
||
|
||
|
||
// 同一个访视 多个线程上传处理 批量保存 可能造成死锁 https://www.cnblogs.com/johnblogs/p/9945767.html
|
||
|
||
await _dicomArchiveService.DicomDBDataSaveChange();
|
||
|
||
//sw.Stop();
|
||
_studyService.UploadOrReUploadNeedTodo(archiveStudyCommand, archivedStudyIds, ref archiveResult, new StudyMonitor()
|
||
{
|
||
TrialId = savedInfo.TrialId,
|
||
SiteId = savedInfo.SiteId,
|
||
SubjectId = savedInfo.SubjectId,
|
||
SubjectVisitId = savedInfo.SubjectVisitId,
|
||
StudyId = archivedStudyIds[0],
|
||
|
||
StudyCode = studycode,
|
||
UploadStartTime = startTime,
|
||
UploadFinishedTime = DateTime.Now,
|
||
//TotalMillisecondsInterval = (DateTime.Now- startTime).TotalMilliseconds,
|
||
FileSize = (decimal)HttpContext.Request.ContentLength,
|
||
FileCount = archiveResult.ReceivedFileCount,
|
||
IsDicom = true,
|
||
IsDicomReUpload = archiveStudyCommand.AbandonStudyId != null,
|
||
IP = _userInfo.IP
|
||
});
|
||
|
||
_provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid);
|
||
|
||
}
|
||
else
|
||
{
|
||
return ResponseOutput.NotOk("未完成该检查的归档");
|
||
}
|
||
|
||
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
|
||
_logger.LogError(e.Message + e.StackTrace);
|
||
|
||
_provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid);
|
||
|
||
return ResponseOutput.NotOk(e.Message, ApiResponseCodeEnum.ProgramException);
|
||
|
||
}
|
||
|
||
return ResponseOutput.Ok(archiveResult);
|
||
}
|
||
|
||
|
||
|
||
|
||
/// <summary>Dicom 归档</summary>
|
||
[HttpPost, Route("Study/ArchiveStudy/{trialId:guid}")]
|
||
[DisableFormValueModelBinding]
|
||
[DisableRequestSizeLimit]
|
||
[TypeFilter(typeof(TrialResourceFilter))]
|
||
public async Task<IResponseOutput> ArchiveStudyNew([FromForm] ArchiveStudyCommand archiveStudyCommand,
|
||
[FromServices] ILogger<UploadDownLoadController> _logger,
|
||
[FromServices] IEasyCachingProvider _provider,
|
||
[FromServices] IStudyService _studyService,
|
||
[FromServices] IDicomArchiveService _dicomArchiveService
|
||
)
|
||
{
|
||
|
||
string studycode = string.Empty;
|
||
|
||
var startTime = DateTime.Now;
|
||
|
||
if (_provider.Exists("StudyUid_" + archiveStudyCommand.StudyInstanceUid))
|
||
{
|
||
return ResponseOutput.NotOk("当前已有人正在上传和归档该检查!");
|
||
}
|
||
else
|
||
{
|
||
_provider.Set("StudyUid_" + archiveStudyCommand.StudyInstanceUid, _userInfo.Id, TimeSpan.FromMinutes(30));
|
||
}
|
||
|
||
var (archiveResult, archivedStudyIds) = (new DicomArchiveResult(), new List<Guid>());
|
||
|
||
|
||
var (seriesInstanceUidList, sopInstanceUidList) = (new List<string>(), new List<string>());
|
||
|
||
//重传的时候,找出当前检查已经上传的series instance
|
||
if (archiveStudyCommand.AbandonStudyId != null)
|
||
{
|
||
(seriesInstanceUidList, sopInstanceUidList) =_studyService.GetHasUploadSeriesAndInstance(archiveStudyCommand.AbandonStudyId.Value);
|
||
}
|
||
|
||
var savedInfo = _studyService.GetSaveToDicomInfo(archiveStudyCommand.SubjectVisitId);
|
||
|
||
archiveResult.ReceivedFileCount= await DicomFileUploadAsync(async (fileName, fileStream) =>
|
||
{
|
||
|
||
try
|
||
{
|
||
using (var memoryStream = new MemoryStream())
|
||
{
|
||
await fileStream.CopyToAsync(memoryStream);
|
||
|
||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||
|
||
var archiveStudyId = await _dicomArchiveService.ArchiveDicomStreamAsync(memoryStream, savedInfo, seriesInstanceUidList, sopInstanceUidList);
|
||
studycode = archiveStudyId.Item2;
|
||
|
||
|
||
if (!archivedStudyIds.Contains(archiveStudyId.Item1))
|
||
archivedStudyIds.Add(archiveStudyId.Item1);
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
_logger.LogError(e.Message + e.StackTrace);
|
||
|
||
archiveResult.ErrorFiles.Add(fileName);
|
||
|
||
_provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid);
|
||
|
||
}
|
||
|
||
|
||
});
|
||
|
||
|
||
try
|
||
{
|
||
if (archivedStudyIds.Count > 0) // 上传成功,处理逻辑
|
||
{
|
||
|
||
// 同一个访视 多个线程上传处理 批量保存 可能造成死锁 https://www.cnblogs.com/johnblogs/p/9945767.html
|
||
|
||
await _dicomArchiveService.DicomDBDataSaveChange();
|
||
|
||
_studyService.UploadOrReUploadNeedTodo(archiveStudyCommand, archivedStudyIds, ref archiveResult, new StudyMonitor()
|
||
{
|
||
TrialId = savedInfo.TrialId,
|
||
SiteId = savedInfo.SiteId,
|
||
SubjectId = savedInfo.SubjectId,
|
||
SubjectVisitId = savedInfo.SubjectVisitId,
|
||
StudyId = archivedStudyIds[0],
|
||
|
||
StudyCode = studycode,
|
||
UploadStartTime = startTime,
|
||
UploadFinishedTime = DateTime.Now,
|
||
//TotalMillisecondsInterval = (DateTime.Now- startTime).TotalMilliseconds,
|
||
FileSize = (decimal)HttpContext.Request.ContentLength,
|
||
FileCount = archiveResult.ReceivedFileCount,
|
||
IsDicom = true,
|
||
IsDicomReUpload = archiveStudyCommand.AbandonStudyId != null,
|
||
IP = _userInfo.IP
|
||
});
|
||
|
||
_provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid);
|
||
|
||
}
|
||
else
|
||
{
|
||
return ResponseOutput.NotOk("未完成该检查的归档");
|
||
}
|
||
|
||
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
|
||
_logger.LogError(e.Message + e.StackTrace);
|
||
|
||
_provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid);
|
||
|
||
return ResponseOutput.NotOk(e.Message, ApiResponseCodeEnum.ProgramException);
|
||
|
||
}
|
||
|
||
return ResponseOutput.Ok(archiveResult);
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 上传临床数据 多文件
|
||
/// </summary>
|
||
/// <param name="subjectVisitId"></param>
|
||
/// <returns></returns>
|
||
[HttpPost("ClinicalData/UploadVisitClinicalData/{trialId:guid}/{subjectVisitId:guid}")]
|
||
[DisableRequestSizeLimit]
|
||
[Authorize(Policy = IRaCISPolicy.CRC)]
|
||
public async Task<IResponseOutput> UploadVisitClinicalData( Guid subjectVisitId, [FromServices] IRepository _repository)
|
||
{
|
||
await QCCommonVerify.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId);
|
||
|
||
var sv = _repository.Where<SubjectVisit>(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SiteId, t.SubjectId }).FirstOrDefault().IfNullThrowException();
|
||
|
||
|
||
await FileUploadAsync(async (fileName) =>
|
||
{
|
||
var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetClinicalDataPath(_hostEnvironment, fileName, sv.TrialId, sv.SiteId, sv.SubjectId, subjectVisitId);
|
||
|
||
//插入临床pdf 路径
|
||
await _repository.AddAsync(new PreviousPDF() { SubjectVisitId = subjectVisitId, Path = relativePath, FileName = fileRealName });
|
||
|
||
return serverFilePath;
|
||
});
|
||
|
||
await _repository.SaveChangesAsync();
|
||
|
||
return ResponseOutput.Ok();
|
||
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 上传非Dicom 文件 支持压缩包 多文件上传
|
||
/// </summary>
|
||
/// <param name="formCollection"></param>
|
||
/// <param name="subjectVisitId"></param>
|
||
/// <param name="noneDicomStudyId"></param>
|
||
/// <returns></returns>
|
||
//[DisableRequestSizeLimit]
|
||
[RequestSizeLimit(1_073_741_824)]
|
||
[HttpPost("NoneDicomStudy/UploadNoneDicomFile/{trialId:guid}/{subjectVisitId:guid}/{noneDicomStudyId:guid}")]
|
||
[TypeFilter(typeof(TrialResourceFilter))]
|
||
[Authorize(Policy = IRaCISPolicy.CRC)]
|
||
public async Task<IResponseOutput> UploadNoneDicomFile(IFormCollection formCollection, Guid subjectVisitId, Guid noneDicomStudyId, [FromServices] IRepository _repository)
|
||
{
|
||
var startTime = DateTime.Now;
|
||
|
||
await QCCommonVerify.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId);
|
||
|
||
var sv = (await _repository.Where<SubjectVisit>(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SiteId, t.SubjectId }).FirstOrDefaultAsync()).IfNullThrowConvertException();
|
||
|
||
|
||
await FileUploadAsync(async (fileName) =>
|
||
{
|
||
|
||
var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetNoneDicomFilePath(_hostEnvironment, fileName, sv.TrialId, sv.SiteId, sv.SubjectId, subjectVisitId);
|
||
|
||
await _repository.AddAsync(new NoneDicomStudyFile() { FileName = fileRealName, Path = relativePath, NoneDicomStudyId = noneDicomStudyId });
|
||
|
||
return serverFilePath;
|
||
});
|
||
|
||
|
||
|
||
// 上传非Dicom 后 将状态改为待提交 分为普通上传 和QC后重传 普通上传时才改为待提交
|
||
await _repository.UpdatePartialFromQueryAsync<SubjectVisit>(t => t.Id == subjectVisitId && t.SubmitState == SubmitStateEnum.None, u => new SubjectVisit() { SubmitState = SubmitStateEnum.ToSubmit });
|
||
|
||
var studyCode = await _repository.Where<NoneDicomStudy>(t => t.Id == noneDicomStudyId).Select(t => t.StudyCode).FirstOrDefaultAsync();
|
||
|
||
await _repository.AddAsync(new StudyMonitor()
|
||
{
|
||
FileCount = formCollection.Files.Count,
|
||
FileSize = formCollection.Files.Sum(t => t.Length),
|
||
IsDicom = false,
|
||
IsDicomReUpload = false,
|
||
StudyId = noneDicomStudyId,
|
||
StudyCode = studyCode,
|
||
UploadStartTime = startTime,
|
||
UploadFinishedTime = DateTime.Now,
|
||
IP = _userInfo.IP,
|
||
TrialId = sv.TrialId,
|
||
SiteId = sv.SiteId,
|
||
SubjectId = sv.SubjectId,
|
||
SubjectVisitId = subjectVisitId,
|
||
});
|
||
|
||
await _repository.SaveChangesAsync();
|
||
|
||
return ResponseOutput.Ok();
|
||
}
|
||
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 一致性核查 excel上传 支持三种格式
|
||
/// </summary>
|
||
/// <param name="trialId"></param>
|
||
/// <returns></returns>
|
||
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
|
||
[TypeFilter(typeof(TrialResourceFilter))]
|
||
[Authorize(Policy = IRaCISPolicy.PM_APM)]
|
||
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId,
|
||
[FromServices] IMediator _mediator,
|
||
[FromServices] IRepository _repository
|
||
)
|
||
{
|
||
|
||
|
||
var (serverFilePath, relativePath, fileName) = (string.Empty, string.Empty, string.Empty);
|
||
await FileUploadAsync(async (realFileName) =>
|
||
{
|
||
fileName = realFileName;
|
||
|
||
if (!fileName.EndsWith(".xlsx") && !fileName.EndsWith(".csv") && !fileName.EndsWith(".xls"))
|
||
{
|
||
throw new BusinessValidationFailedException("支持.xlsx、.xls、.csv格式的文件上传。");
|
||
}
|
||
|
||
(serverFilePath, relativePath) = FileStoreHelper.GetTrialCheckFilePath(_hostEnvironment, fileName, trialId);
|
||
|
||
await _repository.AddAsync(new ConsistencyCheckFile()
|
||
{
|
||
TrialId = trialId,
|
||
CreateTime = DateTime.Now,
|
||
FileName = fileName,
|
||
FilePath = relativePath,
|
||
RelativePaths = relativePath,
|
||
CreateUserId = _userInfo.Id
|
||
});
|
||
|
||
return serverFilePath;
|
||
});
|
||
|
||
|
||
|
||
|
||
var etcCheckList = new List<CheckViewModel>();
|
||
|
||
|
||
|
||
#region MiniExcel 需要自己验证数据格式规范
|
||
|
||
//if (fileName.EndsWith(".csv"))
|
||
//{
|
||
// //因为csv 需要加配置文件 不然都是null
|
||
// etcCheckList = MiniExcel.Query<CheckViewModel>(filePath, null, configuration: config).ToList();
|
||
//}
|
||
//else if (fileName.EndsWith(".xlsx"))
|
||
//{
|
||
//
|
||
|
||
|
||
// etcCheckList = MiniExcel.Query<CheckViewModel>(filePath).ToList();
|
||
//}
|
||
|
||
#endregion
|
||
|
||
//Magicodes 支持自定义特性验证
|
||
if (fileName.EndsWith(".xlsx"))
|
||
{
|
||
var Importer = new ExcelImporter();
|
||
|
||
var import = await Importer.Import<CheckViewModel>(System.IO.File.OpenRead(serverFilePath));
|
||
|
||
if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||
|
||
if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||
|
||
if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
|
||
|
||
etcCheckList = import.Data.ToList();
|
||
}
|
||
else if (fileName.EndsWith(".csv"))
|
||
{
|
||
#region 临时方案 MiniExcel读取 然后保存为xlsx 再用 Magicodes验证数据
|
||
|
||
//因为csv 需要加配置文件 不然都是null
|
||
etcCheckList = MiniExcel.Query<CheckViewModel>(serverFilePath, null, configuration: new MiniExcelLibs.Csv.CsvConfiguration()
|
||
{
|
||
StreamReaderFunc = (stream) => new StreamReader(stream, Encoding.GetEncoding("gb2312"))
|
||
}).ToList();
|
||
|
||
var (csVToXlsxPath, csVToXlsxRelativePath) = FileStoreHelper.GetTrialCheckFilePath(_hostEnvironment, Path.GetFileNameWithoutExtension(fileName) + ".xlsx", trialId);
|
||
|
||
|
||
await MiniExcel.SaveAsAsync(csVToXlsxPath, etcCheckList, excelType: ExcelType.XLSX);
|
||
|
||
|
||
var Importer = new ExcelImporter();
|
||
|
||
var import = await Importer.Import<CheckViewModel>(System.IO.File.OpenRead(csVToXlsxPath));
|
||
|
||
if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||
|
||
if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||
|
||
if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
|
||
|
||
etcCheckList = import.Data.ToList();
|
||
|
||
#endregion
|
||
|
||
|
||
#region 导入组件有问题 excel编码格式
|
||
//var Importer = new CsvImporter();
|
||
|
||
//var import = await Importer.Import<CheckViewModel>(File.OpenRead(filePath));
|
||
|
||
//if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString());
|
||
|
||
//if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors));
|
||
|
||
//if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors));
|
||
|
||
//etcCheckList = import.Data.ToList();
|
||
#endregion
|
||
|
||
}
|
||
//ExcelReaderFactory 需要自己验证数据 并且从固定列取数据
|
||
else
|
||
{
|
||
//为了支持 xls 引入新的组件库
|
||
using (var stream = System.IO.File.Open(serverFilePath, FileMode.Open, FileAccess.Read))
|
||
{
|
||
// Auto-detect format, supports:
|
||
// - Binary Excel files (2.0-2003 format; *.xls)
|
||
// - OpenXml Excel files (2007 format; *.xlsx, *.xlsb)
|
||
using (var reader = ExcelReaderFactory.CreateReader(stream))
|
||
{
|
||
|
||
// 2. Use the AsDataSet extension method
|
||
var dateset = reader.AsDataSet();
|
||
|
||
foreach (DataRow col in dateset.Tables[0].Rows)
|
||
{
|
||
|
||
etcCheckList.Add(new CheckViewModel()
|
||
{
|
||
SiteCode = col[0].ToString(),
|
||
SubjectCode = col[1].ToString(),
|
||
VisitName = col[2].ToString(),
|
||
StudyDate = col[3].ToString(),
|
||
Modality = col[4].ToString(),
|
||
});
|
||
}
|
||
|
||
etcCheckList.Remove(etcCheckList[0]);
|
||
|
||
// The result of each spreadsheet is in result.Tables
|
||
}
|
||
}
|
||
}
|
||
|
||
if (etcCheckList == null || etcCheckList.Count == 0)
|
||
{
|
||
return ResponseOutput.NotOk("请保证上传数据符合模板文件中的样式,且存在有效数据。");
|
||
}
|
||
else
|
||
{
|
||
//处理Excel 有时只是清除某些行的数据 读取也会读到数据,只是数据是null 后面处理的时候转为字符串为报错
|
||
etcCheckList.ForEach(t =>
|
||
{
|
||
t.Modality = t.Modality ?? string.Empty;
|
||
t.SiteCode = t.SiteCode ?? string.Empty;
|
||
t.SubjectCode = t.SubjectCode ?? string.Empty;
|
||
t.VisitName = t.VisitName ?? string.Empty;
|
||
t.StudyDate = t.StudyDate ?? string.Empty;
|
||
});
|
||
|
||
var dt = DateTime.Now;
|
||
|
||
etcCheckList = etcCheckList.Where(t => !(t.Modality == string.Empty && t.SiteCode == string.Empty && t.SubjectCode == string.Empty && t.VisitName == string.Empty && t.StudyDate == string.Empty && DateTime.TryParse(t.StudyDate, out dt))).ToList();
|
||
|
||
if (etcCheckList.Count == 0)
|
||
{
|
||
return ResponseOutput.NotOk("请保证上传数据符合模板文件中的样式,且存在有效数据。");
|
||
}
|
||
|
||
}
|
||
|
||
|
||
await _mediator.Send(new ConsistencyVerificationRequest() { ETCList = etcCheckList, TrialId = trialId });
|
||
|
||
return ResponseOutput.Ok();
|
||
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 医生文件上传下载
|
||
|
||
/// <summary>医生文件上传下载</summary>
|
||
[Route("file")]
|
||
[ApiExplorerSettings(GroupName = "Common")]
|
||
[ApiController]
|
||
public class FileController : UploadBaseController
|
||
{
|
||
public IMapper _mapper { get; set; }
|
||
public IUserInfo _userInfo { get; set; }
|
||
private readonly IMediator _mediator;
|
||
|
||
private readonly IWebHostEnvironment _hostEnvironment;
|
||
|
||
|
||
|
||
public FileController(IMapper mapper, IUserInfo userInfo, IMediator mediator, IWebHostEnvironment hostEnvironment)
|
||
{
|
||
_hostEnvironment = hostEnvironment;
|
||
_mediator = mediator;
|
||
_mapper = mapper;
|
||
_userInfo = userInfo;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 上传文件[FileUpload]
|
||
/// </summary>
|
||
/// <param name="attachmentType">附件类型</param>
|
||
/// <param name="doctorId">医生Id</param>
|
||
/// <returns>返回文件信息</returns>
|
||
[HttpPost, Route("UploadFile/{attachmentType}/{doctorId}")]
|
||
[DisableFormValueModelBinding]
|
||
[DisableRequestSizeLimit]
|
||
public async Task<IResponseOutput> UploadOrdinaryFile(string attachmentType, Guid doctorId)
|
||
{
|
||
|
||
return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetDoctorOrdinaryFilePath(_hostEnvironment, fileName, doctorId, attachmentType));
|
||
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 上传文件( 不是医生个人的文件)[FileUpload]
|
||
/// 例如:阅片章程等
|
||
/// </summary>
|
||
/// <param name="type">文件类型</param>
|
||
/// <returns></returns>
|
||
|
||
[HttpPost, Route("file/UploadNonDoctorFile/{type}")]
|
||
[DisableFormValueModelBinding]
|
||
[DisableRequestSizeLimit]
|
||
public async Task<IResponseOutput> UploadNonDoctorFile(string type)
|
||
{
|
||
return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetNonDoctorFilePath(_hostEnvironment, fileName, type));
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 下载多个医生的所有附件
|
||
/// </summary>
|
||
/// <param name="doctorIds"></param>
|
||
/// <returns></returns>
|
||
|
||
[HttpPost, Route("downloadDoctorAttachments")]
|
||
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadAttachment(Guid[] doctorIds, [FromServices] IFileService _fileService)
|
||
{
|
||
|
||
var path = await _fileService.CreateDoctorsAllAttachmentZip(doctorIds);
|
||
|
||
return ResponseOutput.Ok(new UploadFileInfoDTO
|
||
{
|
||
FilePath = path,
|
||
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
||
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 下载医生官方简历
|
||
/// </summary>
|
||
/// <param name="language"></param>
|
||
/// <param name="doctorIds"></param>
|
||
/// <returns></returns>
|
||
[HttpPost, Route("downloadOfficialCV/{language}")]
|
||
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadOfficialResume(int language, Guid[] doctorIds, [FromServices] IFileService _fileService)
|
||
{
|
||
|
||
var path = _fileService.CreateDoctorsAllAttachmentZip(doctorIds);
|
||
return ResponseOutput.Ok(new UploadFileInfoDTO
|
||
{
|
||
FilePath = await _fileService.CreateOfficialResumeZip(language, doctorIds),
|
||
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 下载指定医生的指定附件
|
||
/// </summary>
|
||
/// <param name="doctorId">医生Id</param>
|
||
/// <param name="attachmentIds">要下载的附件Id</param>
|
||
/// <returns></returns>
|
||
[HttpPost, Route("downloadByAttachmentId/{doctorId}")]
|
||
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadAttachmentById(Guid doctorId, Guid[] attachmentIds, [FromServices] IFileService _fileService)
|
||
{
|
||
var path = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds);
|
||
return ResponseOutput.Ok(new UploadFileInfoDTO
|
||
{
|
||
FilePath = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds),
|
||
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
||
});
|
||
}
|
||
|
||
|
||
|
||
|
||
[HttpPost, Route("enroll/downloadResume/{trialId:guid}/{language}")]
|
||
[TypeFilter(typeof(TrialResourceFilter))]
|
||
[AllowAnonymous]
|
||
public async Task<IResponseOutput<string>> DownloadResume([FromServices] IFileService _fileService, int language, Guid trialId, Guid[] doctorIdArray)
|
||
{
|
||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||
var zipPath = await _fileService.CreateOfficialResumeZip(language, doctorIdArray);
|
||
|
||
return ResponseOutput.Ok(zipPath);
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
|
||
#region 项目 系统 基本文件 上传 下载 预览
|
||
|
||
[ApiExplorerSettings(GroupName = "Common")]
|
||
[ApiController]
|
||
public class UploadDownLoadController : UploadBaseController
|
||
{
|
||
public IMapper _mapper { get; set; }
|
||
public IUserInfo _userInfo { get; set; }
|
||
private readonly IMediator _mediator;
|
||
|
||
private readonly IWebHostEnvironment _hostEnvironment;
|
||
|
||
|
||
|
||
public UploadDownLoadController(IMapper mapper, IUserInfo userInfo, IMediator mediator, IWebHostEnvironment hostEnvironment)
|
||
{
|
||
_hostEnvironment = hostEnvironment;
|
||
_mediator = mediator;
|
||
_mapper = mapper;
|
||
_userInfo = userInfo;
|
||
}
|
||
|
||
/// <summary> 缩略图 </summary>
|
||
[AllowAnonymous]
|
||
[HttpGet("Common/LocalFilePreview")]
|
||
public async Task<FileContentResult> LocalFilePreview(string relativePath)
|
||
{
|
||
|
||
var _fileStorePath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, relativePath);
|
||
|
||
var storePreviewPath = _fileStorePath + ".preview.jpeg";
|
||
|
||
if (!System.IO.File.Exists(storePreviewPath))
|
||
{
|
||
ImageResizeHelper.ResizeSave(_fileStorePath, storePreviewPath);
|
||
}
|
||
|
||
return new FileContentResult(await System.IO.File.ReadAllBytesAsync(storePreviewPath), "image/jpeg");
|
||
|
||
}
|
||
|
||
|
||
/// <summary> 通用文件下载 </summary>
|
||
[AllowAnonymous]
|
||
[HttpGet("CommonDocument/DownloadCommonDoc")]
|
||
public async Task<IActionResult> DownloadCommonFile(string code, [FromServices] IRepository<CommonDocument> _commonDocumentRepository)
|
||
{
|
||
var (filePath, fileName) = await FileStoreHelper.GetCommonDocPhysicalFilePathAsync(_hostEnvironment, _commonDocumentRepository, code);
|
||
|
||
new FileExtensionContentTypeProvider().Mappings.TryGetValue(Path.GetExtension(filePath), out var contentType);
|
||
|
||
return File(System.IO.File.OpenRead(filePath), contentType ?? "application/octet-stream", fileName);
|
||
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
///上传项目签名文档
|
||
/// </summary>
|
||
/// <param name="trialId"></param>
|
||
/// <returns></returns>
|
||
[HttpPost("TrialDocument/UploadTrialDoc/{trialId:guid}")]
|
||
[DisableRequestSizeLimit]
|
||
[DisableFormValueModelBinding]
|
||
public async Task<IResponseOutput> UploadTrialDoc(Guid trialId)
|
||
{
|
||
|
||
return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetTrialSignDocPath(_hostEnvironment, trialId, fileName));
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 上传系统签名文档
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpPost("TrialDocument/UploadSystemDoc")]
|
||
[DisableRequestSizeLimit]
|
||
[DisableFormValueModelBinding]
|
||
public async Task<IResponseOutput> UploadSysTemDoc()
|
||
{
|
||
|
||
return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemSignDocPath(_hostEnvironment, fileName));
|
||
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 上传通用文档 比如一致性核查的 比如导出的excel 模板
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpPost("CommonDocument/UploadCommonDoc")]
|
||
[DisableRequestSizeLimit]
|
||
[DisableFormValueModelBinding]
|
||
public async Task<IResponseOutput> UploadCommonDoc()
|
||
{
|
||
|
||
return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetCommonDocPath(_hostEnvironment, fileName));
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 上传系统通知文档
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpPost("SystemNotice/UploadSystemNoticeDoc")]
|
||
[DisableRequestSizeLimit]
|
||
[DisableFormValueModelBinding]
|
||
public async Task<IResponseOutput> UploadSystemNoticeDoc()
|
||
{
|
||
|
||
return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemNoticePath(_hostEnvironment, fileName));
|
||
|
||
}
|
||
|
||
}
|
||
#endregion
|
||
|
||
}
|