irc-netcore-api/IRaCIS.Core.API/Controllers/UploadDownLoadController.cs

1277 lines
50 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 AutoMapper;
using DocumentFormat.OpenXml.Drawing;
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.MediatR.Handlers;
using IRaCIS.Core.Application.Service;
using IRaCIS.Core.Application.Service.ImageAndDoc;
using IRaCIS.Core.Application.Service.Reading.Dto;
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 MassTransit;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.SignalR;
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 SharpCompress.Common;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Path = System.IO.Path;
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 DicomFileUploadAsync(Func<string, Stream, int, Task> filePathFunc, string boundary)
{
var fileCount = 0;
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(), fileCount);
}
}
}
//普通单个文件
else
{
if (mediaType.Contains("octet-stream") || mediaType.Contains("dicom"))
{
++fileCount;
await filePathFunc(fileName, section.Body, fileCount);
}
}
}
section = await reader.ReadNextSectionAsync();
}
}
}
#endregion
#region Dicom 影像上传 临床数据 非diocm
[ApiExplorerSettings(GroupName = "Image")]
[ApiController]
public class StudyController : UploadBaseController
{
public IMapper _mapper { get; set; }
public IUserInfo _userInfo { get; set; }
private readonly IMediator _mediator;
private readonly IWebHostEnvironment _hostEnvironment;
private readonly IRepository _repository;
private readonly IEasyCachingProvider _provider;
private readonly QCCommon _qCCommon;
public StudyController(IMapper mapper, IUserInfo userInfo, IWebHostEnvironment hostEnvironment, IMediator mediator, IEasyCachingProvider provider,
QCCommon qCCommon,
IRepository repository)
{
_qCCommon = qCCommon;
_provider = provider;
_hostEnvironment = hostEnvironment;
_mediator = mediator;
_mapper = mapper;
_userInfo = userInfo;
_repository = repository;
}
[HttpPost, Route("Study/PreArchiveStudy")]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
public async Task<IResponseOutput> PreArchiveStudy(PreArchiveStudyCommand preArchiveStudyCommand,
[FromServices] IStudyService _studyService,
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
{
if (_provider.Get<List<SystemAnonymization>>(StaticData.Anonymize.Anonymize_AddFixedFiled).Value == null)
{
await _mediator.Send(new AnonymizeCacheRequest());
}
var savedInfo = _studyService.GetSaveToDicomInfo(preArchiveStudyCommand.SubjectVisitId);
var studyMonitor = new StudyMonitor()
{
TrialId = savedInfo.TrialId,
SiteId = savedInfo.SiteId,
SubjectId = savedInfo.SubjectId,
SubjectVisitId = savedInfo.SubjectVisitId,
IsSuccess = false,
UploadStartTime = DateTime.Now,
IsDicom = preArchiveStudyCommand.IsDicom,
IP = _userInfo.IP
};
var addEntity = await _studyMonitorRepository.AddAsync(studyMonitor, true);
return ResponseOutput.Ok(addEntity.Id);
}
/// <summary>Dicom 归档</summary>
[HttpPost, Route("Study/ArchiveStudy")]
[DisableFormValueModelBinding]
[DisableRequestSizeLimit]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
public async Task<IResponseOutput> ArchiveStudyNew(/*[FromForm] ArchiveStudyCommand archiveStudyCommand,*/ Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId, Guid studyMonitorId,
[FromServices] ILogger<UploadDownLoadController> _logger,
[FromServices] IEasyCachingProvider _provider,
[FromServices] IStudyService _studyService,
[FromServices] IHubContext<UploadHub, IUploadClient> _uploadHub,
[FromServices] IDicomArchiveService _dicomArchiveService,
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository
)
{
if (!HttpContext.Request.HasFormContentType ||
!MediaTypeHeaderValue.TryParse(HttpContext.Request.ContentType, out var mediaTypeHeader) ||
string.IsNullOrEmpty(mediaTypeHeader.Boundary.Value))
{
//---不支持的MediaType
return ResponseOutput.NotOk(StaticData.International("UploadDownLoad_UnsupportedMedia"));
}
var archiveStudyCommand = new ArchiveStudyCommand() { AbandonStudyId = abandonStudyId, StudyInstanceUid = studyInstanceUid, SubjectVisitId = subjectVisitId };
string studycode = string.Empty;
var startTime = DateTime.Now;
if (_provider.Exists($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}") && _provider.Get<Guid>($"StudyUid_{trialId}_{studyInstanceUid}").Value == _userInfo.Id)
{
_provider.Set($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}", _userInfo.Id, TimeSpan.FromMinutes(30));
}
//if (_provider.Exists($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}"))
//{
// //---当前已有人正在上传和归档该检查!
// return ResponseOutput.NotOk(StaticData.International("UploadDownLoad_ArchiveInProgress"));
//}
//else
//{
// _provider.Set($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}", _userInfo.Id, TimeSpan.FromMinutes(30));
//}
//到了接口,代表上传结束了
var studyMonitor = await _studyMonitorRepository.FirstOrDefaultAsync(t => t.Id == studyMonitorId);
studyMonitor.UploadFinishedTime = DateTime.Now;
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);
try
{
await DicomFileUploadAsync(async (fileName, fileStream, receivedCount) =>
{
try
{
using (var memoryStream = new MemoryStream())
{
await fileStream.CopyToAsync(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
var (studyId, studyCode) = await _dicomArchiveService.ArchiveDicomStreamAsync(memoryStream, savedInfo, seriesInstanceUidList, sopInstanceUidList);
if (!archivedStudyIds.Contains(studyId))
{
archivedStudyIds.Add(studyId);
archiveResult.ArchivedDicomStudies.Add(new DicomStudyBasicDTO() { StudyCode = studyCode, Id = studyId });
}
}
//await _uploadHub.Clients.All.ReceivProgressAsync(archiveStudyCommand.StudyInstanceUid, receivedCount);
await _uploadHub.Clients.User(_userInfo.Id.ToString()).ReceivProgressAsync(archiveStudyCommand.StudyInstanceUid, receivedCount);
archiveResult.ReceivedFileCount = receivedCount;
}
catch (Exception e)
{
_logger.LogError(e.Message + e.StackTrace);
archiveResult.ErrorFiles.Add(fileName);
}
}, mediaTypeHeader.Boundary.Value);
}
catch (Exception ex)
{
_provider.Remove($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}");
//---请求异常,请重试!
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_RequestError"));
}
studyMonitor.FileSize = (decimal)HttpContext.Request.ContentLength;
studyMonitor.FileCount = archiveResult.ReceivedFileCount;
studyMonitor.FailedFileCount = archiveResult.ErrorFiles.Count;
studyMonitor.IsDicomReUpload = archiveStudyCommand.AbandonStudyId != null;
studyMonitor.Note = JsonConvert.SerializeObject(archiveResult);
try
{
if (archivedStudyIds.Count > 0) // 上传成功,处理逻辑
{
// 同一个访视 多个线程上传处理 批量保存 可能造成死锁 https://www.cnblogs.com/johnblogs/p/9945767.html
await _dicomArchiveService.DicomDBDataSaveChange();
archiveResult.ReuploadNewStudyId = archivedStudyIds[0] == archiveStudyCommand.AbandonStudyId ? archivedStudyIds[0] : Guid.Empty;
studyMonitor.IsSuccess = true;
}
}
catch (Exception e)
{
studyMonitor.Note = JsonConvert.SerializeObject(new { Message = e.Message, Result = archiveResult });
_logger.LogError(e.Message + e.StackTrace);
}
finally
{
_provider.Remove($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}");
studyMonitor.StudyId = archiveResult.ArchivedDicomStudies.FirstOrDefault()?.Id ?? Guid.Empty;
studyMonitor.StudyCode = archiveResult.ArchivedDicomStudies.FirstOrDefault()?.StudyCode;
studyMonitor.ArchiveFinishedTime = DateTime.Now;
await _studyMonitorRepository.SaveChangesAsync();
}
return ResponseOutput.Result(studyMonitor.IsSuccess, archiveResult);
}
#region 废弃
///// <summary>
///// 上传临床数据 多文件
///// </summary>
///// <param name="subjectVisitId"></param>
///// <returns></returns>
//[HttpPost("ClinicalData/UploadVisitClinicalData/{trialId:guid}/{subjectVisitId:guid}")]
//[DisableRequestSizeLimit]
////[Authorize(Policy = IRaCISPolicy.CRC)]
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
//public async Task<IResponseOutput> UploadVisitClinicalData(Guid subjectVisitId)
//{
// await _qCCommon.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,
// IsVisist = true,
// DataType = ClinicalDataType.MedicalHistory,
// UploadType = ClinicalUploadType.PDF,
// SubjectId = sv.SubjectId,
// TrialId = sv.TrialId,
// ClinicalLevel = ClinicalLevel.Subject,
// Path = relativePath,
// FileName = fileRealName
// });
// return serverFilePath;
// });
// await _repository.SaveChangesAsync();
// return ResponseOutput.Ok();
//}
///// <summary>
///// 上传临床数据模板
///// </summary>
///// <param name="trialId"></param>
///// <returns></returns>
//[HttpPost("ClinicalData/UploadClinicalTemplate")]
//[DisableRequestSizeLimit]
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })]
//public async Task<IResponseOutput<List<FileDto>>> UploadClinicalTemplate(Guid? trialId)
//{
// if (trialId == null)
// trialId = default(Guid);
// var filerelativePath = string.Empty;
// List<FileDto> fileDtos = new List<FileDto>();
// await FileUploadAsync(async (fileName) =>
// {
// var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetClinicalTemplatePath(_hostEnvironment, fileName, trialId.Value);
// //插入临床pdf 路径
// filerelativePath = relativePath;
// fileDtos.Add(new FileDto()
// {
// FileName = fileName,
// Path = relativePath
// });
// await Task.CompletedTask;
// return serverFilePath;
// });
// return ResponseOutput.Ok(fileDtos);
//}
///// <summary>
///// 上传阅片临床数据
///// </summary>
///// <param name="trialId"></param>
///// <param name="subjectId"></param>
///// <param name="readingId"></param>
///// <returns></returns>
//[HttpPost("ClinicalData/UploadClinicalData/{trialId:guid}/{subjectId:guid}/{readingId:guid}")]
//[DisableRequestSizeLimit]
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
//public async Task<IResponseOutput<List<FileDto>>> UploadReadClinicalData(Guid trialId, Guid subjectId, Guid readingId)
//{
// var filerelativePath = string.Empty;
// List<FileDto> fileDtos = new List<FileDto>();
// var siteid = await _repository.Where<Subject>(x => x.Id == subjectId).Select(x => x.SiteId).FirstOrDefaultAsync();
// await FileUploadAsync(async (fileName) =>
// {
// var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetReadClinicalDataPath(_hostEnvironment, fileName, trialId, siteid, subjectId, readingId);
// //插入临床pdf 路径
// filerelativePath = relativePath;
// fileDtos.Add(new FileDto()
// {
// FileName = fileName,
// Path = relativePath
// });
// await Task.CompletedTask;
// return serverFilePath;
// });
// return ResponseOutput.Ok(fileDtos);
//}
///// <summary>
///// 上传截图
///// </summary>
///// <param name="subjectId"></param>
///// <returns></returns>
//[HttpPost("Printscreen/UploadPrintscreen/{subjectId:guid}")]
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
//public async Task<IResponseOutput<FileDto>> UploadPrintscreen(Guid subjectId)
//{
// var subjectInfo = await this._repository.Where<Subject>(x => x.Id == subjectId).FirstNotNullAsync();
// FileDto fileDto = new FileDto();
// await FileUploadAsync(async (fileName) =>
// {
// var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetUploadPrintscreenFilePath(_hostEnvironment, fileName, subjectInfo.TrialId, subjectInfo.SiteId, subjectInfo.Id);
// fileDto.Path = relativePath;
// fileDto.FileName = fileName;
// await Task.CompletedTask;
// return serverFilePath;
// });
// return ResponseOutput.Ok(fileDto);
//}
///// <summary>
///// 上传Reading问题的图像
///// </summary>
///// <param name="trialId"></param>
///// <param name="visitTaskId"></param>
///// <returns></returns>
//[HttpPost("VisitTask/UploadReadingAnswerImage/{trialId:guid}/{visitTaskId:guid}")]
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
//public async Task<IResponseOutput<FileDto>> UploadReadingAnswerImage(Guid trialId, Guid visitTaskId)
//{
// FileDto fileDto = new FileDto();
// await FileUploadAsync(async (fileName) =>
// {
// var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetFilePath(_hostEnvironment, fileName, trialId, visitTaskId, StaticData.Folder.JudgeTask);
// fileDto.Path = relativePath;
// fileDto.FileName = fileName;
// await Task.CompletedTask;
// return serverFilePath;
// });
// return ResponseOutput.Ok(fileDto);
//}
///// <summary>
///// 上传裁判任务的图像
///// </summary>
///// <param name="trialId"></param>
///// <param name="visitTaskId"></param>
///// <returns></returns>
//[HttpPost("VisitTask/UploadJudgeTaskImage/{trialId:guid}/{visitTaskId:guid}")]
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
//public async Task<IResponseOutput<FileDto>> UploadJudgeTaskImage(Guid trialId, Guid visitTaskId)
//{
// FileDto fileDto = new FileDto();
// await FileUploadAsync(async (fileName) =>
// {
// var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetFilePath(_hostEnvironment, fileName, trialId, visitTaskId, StaticData.Folder.JudgeTask);
// fileDto.Path = relativePath;
// fileDto.FileName = fileName;
// await Task.CompletedTask;
// return serverFilePath;
// });
// return ResponseOutput.Ok(fileDto);
//}
///// <summary>
///// 上传医学审核图片
///// </summary>
///// <param name="trialId"></param>
///// <param name="taskMedicalReviewId"></param>
///// <returns></returns>
//[HttpPost("TaskMedicalReview/UploadMedicalReviewImage/{trialId:guid}/{taskMedicalReviewId:guid}")]
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
//public async Task<IResponseOutput<FileDto>> UploadMedicalReviewImage(Guid trialId, Guid taskMedicalReviewId)
//{
// string path = string.Empty;
// FileDto fileDto = new FileDto();
// await FileUploadAsync(async (fileName) =>
// {
// var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetMedicalReviewImage(_hostEnvironment, fileName, trialId, taskMedicalReviewId);
// //await _repository.UpdatePartialFromQueryAsync<TaskMedicalReview>(x => x.Id == taskMedicalReviewId, x => new TaskMedicalReview()
// //{
// // ImagePath = relativePath,
// // FileName = fileName,
// //});
// path = relativePath;
// fileDto.Path = relativePath;
// fileDto.FileName = fileName;
// return serverFilePath;
// });
// await _repository.SaveChangesAsync();
// return ResponseOutput.Ok(fileDto);
//}
#endregion
public class UploadNoneDicomFileCommand
{
public Guid TrialId { get; set; }
public Guid SubjectVisitId { get; set; }
public Guid NoneDicomStudyId { get; set; }
public Guid StudyMonitorId { get; set; }
public List<OSSFileDTO> UploadedFileList { get; set; } = new List<OSSFileDTO>();
public class OSSFileDTO
{
public string FilePath { get; set; }
public string FileName { get; set; }
public int FileFize { get; set; }
}
}
/// <summary>
/// 上传非Dicom 文件 支持压缩包 多文件上传
/// </summary>
/// <param name="_noneDicomStudyRepository"></param>
/// <returns></returns>
//[DisableRequestSizeLimit]
[HttpPost("NoneDicomStudy/UploadNoneDicomFile")]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
//[Authorize(Policy = IRaCISPolicy.CRC)]
public async Task<IResponseOutput> UploadNoneDicomFile(UploadNoneDicomFileCommand incommand,
[FromServices] IRepository<NoneDicomStudy> _noneDicomStudyRepository, [FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
{
var subjectVisitId = incommand.SubjectVisitId;
var studyMonitorId = incommand.StudyMonitorId;
var noneDicomStudyId = incommand.NoneDicomStudyId;
await _qCCommon.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();
var studyMonitor = await _studyMonitorRepository.FirstOrDefaultAsync(t => t.Id == studyMonitorId);
studyMonitor.UploadFinishedTime = DateTime.Now;
foreach (var item in incommand.UploadedFileList)
{
await _repository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, NoneDicomStudyId = noneDicomStudyId });
}
var uploadFinishedTime = DateTime.Now;
var noneDicomStudy = await _noneDicomStudyRepository.FirstOrDefaultAsync((t => t.Id == noneDicomStudyId));
noneDicomStudy.FileCount = noneDicomStudy.FileCount + incommand.UploadedFileList.Count;
studyMonitor.FileCount = incommand.UploadedFileList.Count;
studyMonitor.FileSize = incommand.UploadedFileList.Sum(t => t.FileFize);
studyMonitor.IsDicom = false;
studyMonitor.IsDicomReUpload = false;
studyMonitor.StudyId = noneDicomStudyId;
studyMonitor.StudyCode = noneDicomStudy.StudyCode;
studyMonitor.ArchiveFinishedTime = DateTime.Now;
studyMonitor.IP = _userInfo.IP;
await _repository.SaveChangesAsync();
return ResponseOutput.Ok();
}
/// <summary>
/// 一致性核查 excel上传 支持三种格式
/// </summary>
/// <param name="trialId"></param>
/// <returns></returns>
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
//[Authorize(Policy = IRaCISPolicy.PM_APM)]
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId)
{
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"))
{
//---支持.xlsx、.xls、.csv格式的文件上传。
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_SupportedFormats"));
}
(serverFilePath, relativePath) = FileStoreHelper.GetTrialCheckFilePath(_hostEnvironment, fileName, trialId);
await _repository.AddAsync(new UserUploadFile()
{
TrialId = trialId,
CreateTime = DateTime.Now,
FileName = fileName,
FilePath = relativePath,
RelativePath = 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(StaticData.International("UploadDownLoad_InvalidData"));
}
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(StaticData.International("UploadDownLoad_InvalidData"));
}
}
await _mediator.Send(new ConsistencyVerificationRequest() { ETCList = etcCheckList, TrialId = trialId });
return ResponseOutput.Ok();
}
}
#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;
}
[HttpPost, Route("TrialSiteSurvey/TestOOS")]
public string TestUploadFileToOOS(string path)
{
return FileStoreHelper.UploadOOS(path, "testc/test", true);
}
[HttpPost, Route("TrialSiteSurvey/UploadTrialSiteSurveyUser")]
[DisableFormValueModelBinding]
[UnitOfWork]
public async Task<IResponseOutput> UploadTrialSiteSurveyUser(Guid trialId, string baseUrl, string routeUrl,
[FromServices] IRepository<TrialSite> _trialSiteRepository,
[FromServices] IRepository<UserType> _usertypeRepository,
[FromServices] ITrialSiteSurveyService _trialSiteSurveyService)
{
var (serverFilePath, relativePath, fileName) = (string.Empty, string.Empty, string.Empty);
await FileUploadAsync(async (realFileName) =>
{
fileName = realFileName;
if (!fileName.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase))
{
// 请用提供格式的模板excel上传需要处理的数据
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_TemplateUploadData"));
}
(serverFilePath, relativePath) = FileStoreHelper.GetOtherFileUploadPath(_hostEnvironment, StaticData.Folder.TempFile, fileName);
//FileStoreHelper.UploadOOS(serverFilePath, "testc/test", true);
return serverFilePath;
});
//去掉空白行
var excelList = MiniExcel.Query<SiteSurveyUserImportDto>(serverFilePath).ToList()
.Where(t => !(string.IsNullOrWhiteSpace(t.TrialSiteCode) && string.IsNullOrWhiteSpace(t.FirstName) && string.IsNullOrWhiteSpace(t.LastName) && string.IsNullOrWhiteSpace(t.Email)
&& string.IsNullOrWhiteSpace(t.Phone) && string.IsNullOrWhiteSpace(t.UserTypeStr) && string.IsNullOrWhiteSpace(t.OrganizationName))).ToList();
if (excelList.Any(t => string.IsNullOrWhiteSpace(t.TrialSiteCode) || string.IsNullOrWhiteSpace(t.FirstName) || string.IsNullOrWhiteSpace(t.LastName) || string.IsNullOrWhiteSpace(t.Email) || string.IsNullOrWhiteSpace(t.UserTypeStr)))
{
//请确保Excel中 每一行的 中心编号,姓名,邮箱,用户类型数据记录完整再进行上传
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_EnsureCompleteData"));
}
var siteCodeList = excelList.Select(t => t.TrialSiteCode.Trim().ToUpper()).Distinct().ToList();
if (_trialSiteRepository.Where(t => t.TrialId == trialId && siteCodeList.Contains(t.TrialSiteCode.ToUpper())).Count() != siteCodeList.Count)
{
//在项目中未找到该Excel中部分或全部中心
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_InvalidCenters"));
}
if (excelList.GroupBy(t => new {t.TrialSiteCode, t.UserTypeStr, t.Email }).Any(g => g.Count() > 1))
{
// 同一邮箱,同一用户类型,只能生成一个账户,请核查Excel数据
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_CheckDuplicateAccounts"));
}
if (excelList.Any(t => !t.Email.Contains("@")))
{
//有邮箱不符合邮箱格式,请核查Excel数据
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_InvalidEmail"));
}
var generateUserTypeList = new List<string>() { "CRC", "SR", "CRA" };
if (excelList.Any(t => !generateUserTypeList.Contains(t.UserTypeStr.ToUpper())))
{
//用户类型仅能为 CRC,SR,CRA 请核查Excel数据
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_InvalidUserType"));
}
if (excelList.Count == 0)
{
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_NoValiddata"));
}
//处理好 用户类型 和用户类型枚举
var sysUserTypeList = _usertypeRepository.Where(t => t.UserTypeEnum == UserTypeEnum.CRA || t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator || t.UserTypeEnum == UserTypeEnum.SR).Select(t => new { UserTypeId = t.Id, t.UserTypeEnum }).ToList();
var siteList = _trialSiteRepository.Where(t => t.TrialId == trialId && siteCodeList.Contains(t.TrialSiteCode)).Select(t => new { t.TrialSiteCode, t.SiteId }).ToList();
foreach (var item in excelList)
{
switch (item.UserTypeStr.ToUpper())
{
case "CRC":
item.UserTypeId = sysUserTypeList.FirstOrDefault(t => t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).UserTypeId;
item.UserTypeEnum = UserTypeEnum.ClinicalResearchCoordinator;
break;
case "CRA":
item.UserTypeId = sysUserTypeList.FirstOrDefault(t => t.UserTypeEnum == UserTypeEnum.CRA).UserTypeId;
item.UserTypeEnum = UserTypeEnum.CRA;
break;
case "SR":
item.UserTypeId = sysUserTypeList.FirstOrDefault(t => t.UserTypeEnum == UserTypeEnum.SR).UserTypeId;
item.UserTypeEnum = UserTypeEnum.SR;
break;
}
item.SiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.ToUpper() == item.TrialSiteCode.ToUpper()).SiteId;
}
await _trialSiteSurveyService.ImportGenerateAccountAndJoinTrialAsync(trialId, baseUrl, routeUrl, excelList.ToList());
return ResponseOutput.Ok();
}
#region 废弃
///// <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))
// {
// ImageHelper.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="clinicalDataTrialSetId"></param>
///// <param name="_clinicalDataTrialSetRepository"></param>
///// <returns></returns>
//[AllowAnonymous]
//[HttpGet("CommonDocument/DownloadTrialClinicalFile")]
//public async Task<IActionResult> DownloadTrialClinicalFile(Guid clinicalDataTrialSetId, [FromServices] IRepository<ClinicalDataTrialSet> _clinicalDataTrialSetRepository)
//{
// var (filePath, fileName) = await FileStoreHelper.GetTrialClinicalPathAsync(_hostEnvironment, _clinicalDataTrialSetRepository, clinicalDataTrialSetId);
// 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="clinicalDataSystemSetId"></param>
///// <param name="_clinicalDataSystemSetRepository"></param>
///// <returns></returns>
//[AllowAnonymous]
//[HttpGet("CommonDocument/DownloadSystemClinicalFile")]
//public async Task<IActionResult> DownloadSystemClinicalFile(Guid clinicalDataSystemSetId, [FromServices] IRepository<ClinicalDataSystemSet> _clinicalDataSystemSetRepository)
//{
// var (filePath, fileName) = await FileStoreHelper.GetSystemClinicalPathAsync(_hostEnvironment, _clinicalDataSystemSetRepository, clinicalDataSystemSetId);
// 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>
///// 上传系统通知文档
///// </summary>
///// <returns></returns>
//[HttpPost("SystemNotice/UploadSystemNoticeDoc")]
//[DisableRequestSizeLimit]
//[DisableFormValueModelBinding]
//public async Task<IResponseOutput> UploadSystemNoticeDoc()
//{
// return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemNoticePath(_hostEnvironment, fileName));
//}
#endregion
/// <summary>
/// 上传通用文档 比如一致性核查的 比如导出的excel 模板
/// </summary>
/// <returns></returns>
[HttpPost("CommonDocument/UploadCommonDoc")]
[DisableRequestSizeLimit]
[DisableFormValueModelBinding]
public async Task<IResponseOutput> UploadCommonDoc()
{
return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetCommonDocPath(_hostEnvironment, fileName));
}
public enum UploadFileType
{
DataUpload = 1,
DataDownload = 2,
EmailAttachment = 3,
EmailBodyHtml = 4,
Other = 5
}
/// <summary>
/// 1数据上传 2导出、 3邮件附件 4邮件Html 通过 ----new
/// </summary>
/// <returns></returns>
[HttpPost("SystemFile/Upload")]
[DisableRequestSizeLimit]
[DisableFormValueModelBinding]
public async Task<IResponseOutput> Upload(UploadFileType fileType)
{
IResponseOutput result = null;
switch (fileType)
{
case UploadFileType.DataUpload:
result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemFileUploadPath(_hostEnvironment, StaticData.Folder.DataTemplate, fileName));
break;
case UploadFileType.DataDownload:
result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemFileUploadPath(_hostEnvironment, StaticData.Folder.DataTemplate, fileName));
break;
case UploadFileType.EmailAttachment:
result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemFileUploadPath(_hostEnvironment, StaticData.Folder.EmailTemplate, fileName));
break;
case UploadFileType.EmailBodyHtml:
result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemFileUploadPath(_hostEnvironment, StaticData.Folder.EmailTemplate, fileName));
break;
default:
result = await SingleFileUploadAsync((fileName) => FileStoreHelper.GetOtherFileUploadPath(_hostEnvironment, StaticData.Folder.TempFile, fileName));
break;
}
return result;
}
}
#endregion
}