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

1173 lines
46 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.Cors.Infrastructure;
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 SkiaSharp;
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();
}
}
[Route("base")]
public virtual async Task FileUploadToOSSAsync(Func<string, Stream, Task<string>> toMemoryStreamFunc)
{
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;
await toMemoryStreamFunc(fileName, section.Body);
}
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 )
{
_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);
}
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="incommand"></param>
/// <param name="_noneDicomStudyRepository"></param>
/// <param name="_studyMonitorRepository"></param>
/// <returns></returns>
[HttpPost("NoneDicomStudy/UploadNoneDicomFile")]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
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>
/// <param name="oSSService"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService)
{
var fileName = string.Empty;
var templateFileStream = new MemoryStream();
await FileUploadToOSSAsync(async (realFileName, fileStream) =>
{
fileName = realFileName;
if (!fileName.EndsWith(".xlsx") && !fileName.EndsWith(".csv") && !fileName.EndsWith(".xls"))
{
//---支持.xlsx、.xls、.csv格式的文件上传。
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_SupportedFormats"));
}
fileStream.CopyTo(templateFileStream);
templateFileStream.Seek(0, SeekOrigin.Begin);
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, "InspectionUpload/Check", realFileName);
await _repository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId });
return ossRelativePath;
});
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>(templateFileStream);
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>(templateFileStream, 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(templateFileStream, 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(templateFileStream))
{
// 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 医生文件上传下载
#region DTO
public class DoctorDownloadInfo
{
public Guid Id { get; set; }
public string Name { get; set; }
public string ReviewerCode { get; set; }
public List<DownloadFileInfo> FileList { get; set; }
}
public class DownloadFileInfo
{
public string FileName { get; set; }
public string Path { get; set; }
}
public class GetDoctorPathCommand
{
public int Language { get; set; }
public List<Guid> DoctorIdList { get; set; }
}
public class GetDoctoreAttachPathCommand
{
public Guid DoctorId { get; set; }
public List<Guid> AttachmentIdList { get; set; }
}
#endregion
/// <summary>医生文件上传下载</summary>
[ApiExplorerSettings(GroupName = "Common")]
[ApiController]
public class FileController : UploadBaseController
{
public IMapper _mapper { get; set; }
public IUserInfo _userInfo { get; set; }
private readonly IWebHostEnvironment _hostEnvironment;
private readonly IFileService _fileService;
public FileController(IMapper mapper, IUserInfo userInfo, IWebHostEnvironment hostEnvironment, IFileService fileService)
{
_fileService = fileService;
_hostEnvironment = hostEnvironment;
_mapper = mapper;
_userInfo = userInfo;
}
/// <summary>
/// New 医生首页 多选 获取多个医生信息+文件路径列表 医生压缩包名称 doctorCode + "_" + doctorName _(时间戳或者随机的Guid)
/// </summary>
/// <returns></returns>
[HttpPost, Route("file/GetOfficialResume")]
public async Task<IResponseOutput<List<DoctorDownloadInfo>>> GetOfficialResume(GetDoctorPathCommand command,
[FromServices] IRepository<Attachment> _attachmentrepository)
{
var list = await _attachmentrepository.Where(t => command.DoctorIdList.Contains(t.DoctorId) && command.Language == t.Language).GroupBy(t => new { Name = t.Doctor.FirstName + "_" + t.Doctor.LastName, ReviewerCode = t.Doctor.ReviewerCode, t.DoctorId })
.Select(g => new DoctorDownloadInfo()
{
Id = g.Key.DoctorId,
Name = g.Key.Name,
ReviewerCode = g.Key.ReviewerCode,
FileList = g.Select(t => new DownloadFileInfo() { FileName = t.FileName, Path = t.Path }).ToList()
}).ToListAsync();
return ResponseOutput.Ok(list);
}
/// <summary>
/// New 项目入组 勾选获取简历路径
/// </summary>
/// <param name="command"></param>
/// <param name="_doctorService"></param>
/// <param name="_attachmentrepository"></param>
/// <returns></returns>
[HttpPost, Route("file/GetTrialDoctorOfficialResume")]
public async Task<IResponseOutput<List<DoctorDownloadInfo>>> GetTrialDoctorOfficialResume(GetDoctorPathCommand command,
[FromServices] IDoctorService _doctorService,
[FromServices] IRepository<Attachment> _attachmentrepository)
{
var list = await _attachmentrepository.Where(t => command.DoctorIdList.Contains(t.DoctorId) && command.Language == t.Language && t.IsOfficial && t.Type.Equals("Resume")).GroupBy(t => new { Name = t.Doctor.FirstName + "_" + t.Doctor.LastName, ReviewerCode = t.Doctor.ReviewerCode, t.DoctorId })
.Select(g => new DoctorDownloadInfo()
{
Id = g.Key.DoctorId,
Name = g.Key.Name,
ReviewerCode = g.Key.ReviewerCode,
FileList = g.Select(t => new DownloadFileInfo() { FileName = t.FileName, Path = t.Path }).ToList()
}).ToListAsync();
return ResponseOutput.Ok(list);
}
/// <summary>
/// new 医生详情 勾选或者下载文件路径
/// </summary>
/// <param name="command"></param>
/// <param name="_doctorService"></param>
/// <param name="_attachmentrepository"></param>
/// <returns></returns>
[HttpPost, Route("file/GetDoctorAttachment")]
public async Task<IResponseOutput<DoctorDownloadInfo>> GetDoctorAttachment(GetDoctoreAttachPathCommand command,
[FromServices] IDoctorService _doctorService,
[FromServices] IRepository<Attachment> _attachmentrepository)
{
var find = await _attachmentrepository.Where(t => command.DoctorId == t.DoctorId && command.AttachmentIdList.Contains(t.Id)).GroupBy(t => new { Name = t.Doctor.FirstName + "_" + t.Doctor.LastName, ReviewerCode = t.Doctor.ReviewerCode, t.DoctorId })
.Select(g => new DoctorDownloadInfo()
{
Id = g.Key.DoctorId,
Name = g.Key.Name,
ReviewerCode = g.Key.ReviewerCode,
FileList = g.Select(t => new DownloadFileInfo() { FileName = t.FileName, Path = t.Path }).ToList()
}).FirstOrDefaultAsync();
return ResponseOutput.Ok(find);
}
#region 废弃
/// <summary>
/// 下载多个医生的所有附件
/// </summary>
/// <param name="doctorIds"></param>
/// <returns></returns>
[Obsolete]
[HttpPost, Route("file/downloadDoctorAttachments")]
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadAttachment(Guid[] doctorIds)
{
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="doctorId">医生Id</param>
/// <param name="attachmentIds">要下载的附件Id</param>
/// <returns></returns>
[HttpPost, Route("file/downloadByAttachmentId/{doctorId}")]
[Obsolete]
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadAttachmentById(Guid doctorId, Guid[] attachmentIds)
{
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)
});
}
/// <summary>
/// 下载医生官方简历 首页 区分 中文和英文
/// </summary>
/// <param name="language"></param>
/// <param name="doctorIds"></param>
/// <returns></returns>
[HttpPost, Route("file/downloadOfficialCV/{language}")]
[Obsolete]
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadOfficialResume(int language, Guid[] doctorIds)
{
var path = await _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="language"></param>
/// <param name="trialId"></param>
/// <param name="doctorIdArray"></param>
/// <returns></returns>
[HttpPost, Route("enroll/downloadResume/{trialId:guid}/{language}")]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
[AllowAnonymous]
[Obsolete]
public async Task<IResponseOutput<string>> DownloadResume(int language, Guid trialId, Guid[] doctorIdArray)
{
var zipPath = await _fileService.CreateOfficialResumeZip(language, doctorIdArray);
return ResponseOutput.Ok(zipPath);
}
#endregion
}
#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/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,
[FromServices] IOSSService oSSService,
[FromServices] IRepository<InspectionFile> _inspectionFileRepository)
{
var templateFileStream = new MemoryStream();
await FileUploadToOSSAsync(async (realFileName, fileStream) =>
{
await fileStream.CopyToAsync(templateFileStream);
templateFileStream.Seek(0, SeekOrigin.Begin);
if (!realFileName.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase))
{
// 请用提供格式的模板excel上传需要处理的数据
throw new BusinessValidationFailedException(StaticData.International("UploadDownLoad_TemplateUploadData"));
}
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, "InspectionUpload/SiteSurvey", realFileName);
await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId });
return ossRelativePath;
});
//去掉空白行
var excelList = MiniExcel.Query<SiteSurveyUserImportDto>(templateFileStream, excelType: ExcelType.XLSX).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", "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).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;
}
item.SiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.ToUpper() == item.TrialSiteCode.ToUpper()).SiteId;
}
await _trialSiteSurveyService.ImportGenerateAccountAndJoinTrialAsync(trialId, baseUrl, routeUrl, excelList.ToList());
return ResponseOutput.Ok();
}
/// <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>
/// 上传通用文档 比如一致性核查的 比如导出的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
}