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

997 lines
39 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 ExcelDataReader;
using IRaCIS.Application.Interfaces;
using IRaCIS.Core.Application.BusinessFilter;
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.MassTransit.Command;
using IRaCIS.Core.Application.Service;
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 MassTransit.Mediator;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization;
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.ComponentModel.DataAnnotations;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Path = System.IO.Path;
namespace IRaCIS.Core.API.Controllers
{
#region 上传基类封装
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
var factories = context.ValueProviderFactories;
//factories.RemoveType<FormValueProviderFactory>();
factories.RemoveType<FormFileValueProviderFactory>();
factories.RemoveType<JQueryFormValueProviderFactory>();
context.HttpContext.Request.EnableBuffering();
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}
[DisableFormValueModelBinding]
public abstract class UploadBaseController : ControllerBase
{
/// <summary> 流式上传 直接返回</summary>
[Route("SingleFileUpload")]
[ApiExplorerSettings(IgnoreApi = true)]
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("FileUpload")]
[ApiExplorerSettings(IgnoreApi = true)]
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("FileUploadToOSS")]
[ApiExplorerSettings(IgnoreApi = true)]
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("DicomFileUpload")]
[ApiExplorerSettings(IgnoreApi = true)]
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
public class UploadNoneDicomFileCommand
{
[NotDefault]
public Guid SubjectVisitId { get; set; }
[NotDefault]
public Guid StudyMonitorId { get; set; }
public Guid? NoneDicomStudyId { get; set; }
//IR 上传的时候跟任务绑定
public Guid? VisitTaskId { get; set; }
public string RecordPath { get; set; }
public int FailedFileCount { get; set; }
public long FileSize { 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; }
public string FileType { get; set; }
}
}
[ApiController, ApiExplorerSettings(GroupName = "Image")]
public class StudyController(
IMediator _mediator,
QCCommon _qCCommon,
IUserInfo _userInfo,
IRepository<SubjectVisit> _subjectVisitRepository,
IStringLocalizer _localizer) : UploadBaseController
{
/// <summary>Dicom 归档</summary>
[HttpPost, Route("Study/ArchiveStudy")]
[DisableFormValueModelBinding]
[DisableRequestSizeLimit]
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
public async Task<IResponseOutput> ArchiveStudyNew(Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId, Guid studyMonitorId,
[FromServices] ILogger<UploadDownLoadController> _logger,
[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(_localizer["UploadDownLoad_UnsupportedMedia"]);
}
var archiveStudyCommand = new ArchiveStudyCommand() { AbandonStudyId = abandonStudyId, StudyInstanceUid = studyInstanceUid, SubjectVisitId = subjectVisitId };
string studycode = string.Empty;
var startTime = DateTime.Now;
//到了接口,代表上传结束了
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.UserRoleId.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)
{
//---请求异常,请重试!
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_RequestError"]);
}
studyMonitor.FileSize = (long)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
{
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);
}
/// <summary>
/// 非dicom 上传预上传接口
/// </summary>
/// <param name="preArchiveStudyCommand"></param>
/// <param name="_studyService"></param>
/// <param name="_studyMonitorRepository"></param>
/// <returns></returns>
[HttpPost, Route("Study/PreArchiveStudy")]
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
public async Task<IResponseOutput> PreArchiveStudy(PreArchiveStudyCommand preArchiveStudyCommand,
[FromServices] IStudyService _studyService,
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
{
var savedInfo = _studyService.GetSaveToDicomInfo(preArchiveStudyCommand.SubjectVisitId);
var studyMonitor = new StudyMonitor()
{
TrialId = savedInfo.TrialId,
SubjectId = savedInfo.SubjectId,
SubjectVisitId = savedInfo.SubjectVisitId,
IsSuccess = false,
UploadStartTime = DateTime.Now,
FileCount = preArchiveStudyCommand.FileCount,
IsDicom = preArchiveStudyCommand.IsDicom,
IP = _userInfo.IP
};
var addEntity = await _studyMonitorRepository.AddAsync(studyMonitor, true);
return ResponseOutput.Ok(addEntity.Id);
}
/// <summary>
/// 上传非Dicom 文件 支持压缩包 多文件上传
/// </summary>
/// <param name="incommand"></param>
/// <param name="_noneDicomStudyRepository"></param>
/// <param name="_studyMonitorRepository"></param>
/// <param name="_noneDicomStudyFileRepository"></param>
/// <returns></returns>
[HttpPost("NoneDicomStudy/UploadNoneDicomFile")]
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
public async Task<IResponseOutput> UploadNoneDicomFile(UploadNoneDicomFileCommand incommand,
[FromServices] IRepository<NoneDicomStudy> _noneDicomStudyRepository,
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository,
[FromServices] IRepository<NoneDicomStudyFile> _noneDicomStudyFileRepository)
{
var subjectVisitId = incommand.SubjectVisitId;
var studyMonitorId = incommand.StudyMonitorId;
var noneDicomStudyId = incommand.NoneDicomStudyId;
await _qCCommon.VerifyIsCRCSubmmitAsync(_subjectVisitRepository, _userInfo, subjectVisitId);
var sv = (await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.TrialSiteId, t.SubjectId }).FirstOrDefaultAsync()).IfNullThrowConvertException();
var studyMonitor = await _studyMonitorRepository.FirstOrDefaultAsync(t => t.Id == studyMonitorId);
studyMonitor.UploadFinishedTime = DateTime.Now;
foreach (var item in incommand.UploadedFileList)
{
//如果是跟任务绑那么NoneDicomStudyId 设置为空,不影响之前的检查,同时设置 OriginNoneDicomStudyId 保证关系
if (incommand.VisitTaskId != null && incommand.VisitTaskId != Guid.Empty)
{
await _noneDicomStudyFileRepository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, OriginNoneDicomStudyId = noneDicomStudyId.Value, VisitTaskId = incommand.VisitTaskId, FileType = item.FileType, FileSize = item.FileFize });
}
else
{
await _noneDicomStudyFileRepository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, NoneDicomStudyId = noneDicomStudyId.Value, FileType = item.FileType, FileSize = item.FileFize });
}
}
var uploadFinishedTime = DateTime.Now;
var noneDicomStudy = await _noneDicomStudyRepository.FirstOrDefaultAsync((t => t.Id == noneDicomStudyId));
noneDicomStudy.FileCount = noneDicomStudy.FileCount + (incommand.VisitTaskId != null ? 0 : incommand.UploadedFileList.Count);
studyMonitor.RecordPath = incommand.RecordPath;
studyMonitor.FailedFileCount = incommand.FailedFileCount;
studyMonitor.IsSuccess = incommand.FailedFileCount == 0;
studyMonitor.FileSize = incommand.UploadedFileList.Sum(t => t.FileFize);
studyMonitor.IsDicom = false;
studyMonitor.IsDicomReUpload = false;
studyMonitor.StudyId = noneDicomStudyId.Value;
studyMonitor.StudyCode = noneDicomStudy.StudyCode;
studyMonitor.ArchiveFinishedTime = DateTime.Now;
studyMonitor.IP = _userInfo.IP;
await _noneDicomStudyRepository.SaveChangesAsync();
return ResponseOutput.Ok();
}
/// <summary>
/// 一致性核查 excel上传 支持三种格式
/// </summary>
/// <param name="trialId"></param>
/// <param name="oSSService"></param>
/// <param name="_inspectionFileRepository"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
{
var fileName = string.Empty;
var templateFileStream = new MemoryStream();
await FileUploadToOSSAsync(async (realFileName, fileStream) =>
{
fileName = realFileName;
if (!fileName.EndsWith(".xlsx") && !fileName.EndsWith(".xls"))
{
//---支持.xlsx、.xls格式的文件上传。
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_SupportedFormats"]);
}
fileStream.CopyTo(templateFileStream);
templateFileStream.Seek(0, SeekOrigin.Begin);
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, "InspectionUpload/Check", realFileName);
await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId });
return ossRelativePath;
});
var etcCheckList = new List<CheckViewModel>();
#region MiniExcel 需要自己验证数据格式规范
if (fileName.EndsWith(".xlsx"))
{
etcCheckList = MiniExcel.Query<CheckViewModel>(templateFileStream, excelType: ExcelType.XLSX).ToList();
}
else if (fileName.EndsWith(".csv"))
{
//因为csv 需要加配置文件 不然都是null
etcCheckList = MiniExcel.Query<CheckViewModel>(templateFileStream, null, configuration: new MiniExcelLibs.Csv.CsvConfiguration()
{
StreamReaderFunc = (stream) => new StreamReader(stream, Encoding.GetEncoding("gb2312"))
}).ToList();
}
//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
#region 升级net8 导入有问题
//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(_localizer["UploadDownLoad_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
//}
#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(_localizer["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(_localizer["UploadDownLoad_InvalidData"]);
}
}
// 适合获取结果的
//var client = _mediator.CreateRequestClient<ConsistenCheckCommand>();
//await client.GetResponse<ConsistenCheckResult>(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
//不获取结果,不用定义返回类型
await _mediator.Send(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
return ResponseOutput.Ok();
}
}
#endregion
#region 项目 系统 基本文件 上传 下载 预览
[ApiController, ApiExplorerSettings(GroupName = "Common")]
public class UploadDownLoadController : UploadBaseController
{
public IMapper _mapper { get; set; }
public IUserInfo _userInfo { get; set; }
private readonly IMediator _mediator;
public IStringLocalizer _localizer { get; set; }
private readonly IWebHostEnvironment _hostEnvironment;
public UploadDownLoadController(IMapper mapper, IUserInfo userInfo, IStringLocalizer localizer, IMediator mediator, IWebHostEnvironment hostEnvironment)
{
_hostEnvironment = hostEnvironment;
_localizer = localizer;
_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(_localizer["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(_localizer["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(_localizer["UploadDownLoad_InvalidCenters"]);
}
if (excelList.GroupBy(t => new { t.TrialSiteCode, t.UserTypeStr, t.Email }).Any(g => g.Count() > 1))
{
// 同一邮箱,同一用户类型,只能生成一个账户,请核查Excel数据
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_CheckDuplicateAccounts"]);
}
if (excelList.Any(t => !t.Email.Contains("@")))
{
//有邮箱不符合邮箱格式,请核查Excel数据
throw new BusinessValidationFailedException(_localizer["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(_localizer["UploadDownLoad_InvalidUserType"]);
//}
if (excelList.Count == 0)
{
throw new BusinessValidationFailedException(_localizer["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, TrialSiteId = t.Id }).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.TrialSiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.ToUpper() == item.TrialSiteCode.ToUpper()).TrialSiteId;
}
var list = excelList.Where(t => t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator || t.UserTypeEnum == UserTypeEnum.CRA).ToList();
await _trialSiteSurveyService.ImportGenerateAccountAndJoinTrialAsync(trialId, baseUrl, routeUrl, list);
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
}