From 5d96df5d00c98a352743ecd7c38c4369bfe9b60e Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Sun, 29 May 2022 17:46:14 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=8A=E4=BC=A0=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/CommonController.cs | 94 -- .../Controllers/DownLoadController.cs | 95 -- .../Controllers/ExtraController.cs | 12 - IRaCIS.Core.API/Controllers/FileController.cs | 532 -------- .../Controllers/FinancialChangeController.cs | 6 +- .../Controllers/InspectionController.cs | 10 +- .../Controllers/StudyController.cs | 432 ------- .../Controllers/UploadController.cs | 405 ------- .../Controllers/UploadDownLoadController.cs | 1077 +++++++++++++++++ IRaCIS.Core.API/IRaCIS.Core.API.xml | 248 ++-- .../IRacisHostFileStoreConfig.cs | 47 +- .../Helper/ExcelExportHelper.cs | 21 +- .../Helper/FileStoreHelper.cs | 267 +++- .../Helper/SendEmailHelper.cs | 1 - .../IRaCIS.Core.Application.xml | 47 +- .../ImageAndDoc/Interface/IStudyService.cs | 2 +- .../Service/ImageAndDoc/StudyService.cs | 16 +- .../Service/Inspection/InspectionService.cs | 9 +- .../Service/QC/ClinicalDataService.cs | 65 +- .../QC/Interface/IClinicalDataService.cs | 2 +- .../QC/Interface/INoneDicomStudyService.cs | 2 +- .../QC/Interface/IQCOperationService.cs | 2 +- .../Service/QC/NoneDicomStudyService.cs | 145 +-- .../Service/QC/QCOperationService.cs | 241 +--- .../TrialSiteUser/TrialConfigService.cs | 89 -- .../Repository/IRepository.cs | 61 + 26 files changed, 1567 insertions(+), 2361 deletions(-) delete mode 100644 IRaCIS.Core.API/Controllers/CommonController.cs delete mode 100644 IRaCIS.Core.API/Controllers/DownLoadController.cs delete mode 100644 IRaCIS.Core.API/Controllers/FileController.cs delete mode 100644 IRaCIS.Core.API/Controllers/StudyController.cs delete mode 100644 IRaCIS.Core.API/Controllers/UploadController.cs create mode 100644 IRaCIS.Core.API/Controllers/UploadDownLoadController.cs diff --git a/IRaCIS.Core.API/Controllers/CommonController.cs b/IRaCIS.Core.API/Controllers/CommonController.cs deleted file mode 100644 index 723600c0e..000000000 --- a/IRaCIS.Core.API/Controllers/CommonController.cs +++ /dev/null @@ -1,94 +0,0 @@ -using AutoMapper; -using IRaCIS.Core.Application.Helper; -using IRaCIS.Core.Domain.Share; -using IRaCIS.Core.Infrastructure.Extention; -using MediatR; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using System.IO; -using System.Threading.Tasks; - -namespace IRaCIS.Core.API.Controllers -{ - [ApiExplorerSettings(GroupName = "Trial")] - [ApiController] - public class CommonController : ControllerBase - { - public IMapper _mapper { get; set; } - public IUserInfo _userInfo { get; set; } - - private readonly IWebHostEnvironment _hostEnvironment; - - - - public CommonController(IMapper mapper, IUserInfo userInfo, IMediator mediator, IWebHostEnvironment hostEnvironment) - { - _hostEnvironment = hostEnvironment; - _mapper = mapper; - _userInfo = userInfo; - } - - - [AllowAnonymous] - [HttpGet("Common/LocalFilePreview")] - public async Task LocalFilePreview(string relativePath) - { - var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).IfNullThrowException().FullName; - - var _fileStorePath = Path.Combine(rootPath, relativePath.Replace('/', '\\').Trim('\\')); - - - var storePreviewPath = _fileStorePath + ".preview.jpeg"; - - //if (!System.IO.File.Exists(storePreviewPath)) - //{ - ImageResizeHelper.ResizeSave(_fileStorePath, storePreviewPath); - //} - - return new FileContentResult(await System.IO.File.ReadAllBytesAsync(storePreviewPath), "image/jpeg"); - - //_logger.LogError(rootPath); - - //_logger.LogError(_fileStorePath); - - - //if (!File.Exists(storePreviewPath)) - //{ - - //Bitmap sourceImage = new Bitmap(File.OpenRead(_fileStorePath)); - - //System.Drawing.Image destinationImage = new Bitmap(500, 500); - //Graphics g = Graphics.FromImage(destinationImage); - - //g.DrawImage( - // sourceImage, - // new Rectangle(0, 0, 500, 500), - // new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), - // GraphicsUnit.Pixel - //); - - //destinationImage.Save(storePreviewPath); - - //var image = SKBitmap.Decode(_fileStorePath); - ////设置图片新的size - //var newImg = image.Resize(new SKSizeI(50, 50), SKFilterQuality.Medium); - //using var fs = new FileStream(storePreviewPath, FileMode.Create); - //newImg.Encode(fs, SKEncodedImageFormat.Png, 100); - //fs.Flush(); - - - //var image = NetVips.Image.NewFromFile(_fileStorePath); - //var newImg = image.Resize(0.5); - //newImg.WriteToFile(storePreviewPath); - - - //var image = NetVips.Image.NewFromFile(_fileStorePath); - //var newImg = image.ThumbnailImage(500); - //newImg.WriteToFile(_fileStorePath); - - } - - } -} diff --git a/IRaCIS.Core.API/Controllers/DownLoadController.cs b/IRaCIS.Core.API/Controllers/DownLoadController.cs deleted file mode 100644 index 218d24ea6..000000000 --- a/IRaCIS.Core.API/Controllers/DownLoadController.cs +++ /dev/null @@ -1,95 +0,0 @@ -using AutoMapper; - -using IRaCIS.Core.Domain.Models; - -using IRaCIS.Core.Infra.EFCore; - -using MediatR; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Hosting; - -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.StaticFiles; -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using IRaCIS.Core.Domain.Share; -using Microsoft.EntityFrameworkCore; -using IRaCIS.Core.Application.Helper; - -namespace IRaCIS.Core.API.Controllers -{ - [ApiExplorerSettings(GroupName = "Trial")] - [ApiController] - public class DownLoadController : ControllerBase - { - public IMapper _mapper { get; set; } - public IUserInfo _userInfo { get; set; } - - private readonly IWebHostEnvironment _hostEnvironment; - - - - public DownLoadController(IMapper mapper, IUserInfo userInfo, IMediator mediator, IWebHostEnvironment hostEnvironment) - { - _hostEnvironment = hostEnvironment; - _mapper = mapper; - _userInfo = userInfo; - } - - //[HttpGet("VisitPlan/DownloadInflunceStudyList{trialId:guid}/{createTime:dateTime}")] - //public async Task DownloadInflunceStudyList(Guid trialId, DateTime createTime, [FromServices] IRepository _influnceRepository) - //{ - // var list = _influnceRepository.Where(t => t.TrialId == trialId && t.CreateTime == createTime) - // .ProjectTo(_mapper.ConfigurationProvider).ToList(); - - // if (list.Count == 0) - // { - // list.Add(new VisitPlanInfluenceSubjectVisitDTO() { CreateTime = DateTime.Now, SubjectCode = "test", StudyTime = DateTime.Now, IsDicomStudy = false, HistoryWindow = "test" }); - // } - - // IExporter exporter = new ExcelExporter(); - - // var result = await exporter.ExportAsByteArray(list); - - - // return new XlsxFileResult(bytes: bytes); - - // //return File(result, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"检查导出_{DateTime.Now}.xlsx"); - - //} - - - [AllowAnonymous] - [HttpGet("CommonDocument/DownloadCommonDoc")] - public async Task DownloadCommonFile(string code, [FromServices] IRepository _commonDocumentRepository) - { - var doc = await _commonDocumentRepository.AsQueryable(true).FirstOrDefaultAsync(t => t.Code == code); - - if (doc == null) - { - throw new Exception("当前code 没要找到对应的文件"); - } - - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - - var filePath = Path.Combine(rootPath, doc.Path.Trim('/')); - - if (!System.IO.File.Exists(filePath)) - { - throw new Exception("服务器本地不存在该路径文件"); - } - - new FileExtensionContentTypeProvider().Mappings.TryGetValue(Path.GetExtension(filePath), out var contentType); - - - return File(System.IO.File.OpenRead(filePath), contentType ?? "application/octet-stream", doc.Name); - - - } - - - } -} diff --git a/IRaCIS.Core.API/Controllers/ExtraController.cs b/IRaCIS.Core.API/Controllers/ExtraController.cs index 406959cc2..e6412f877 100644 --- a/IRaCIS.Core.API/Controllers/ExtraController.cs +++ b/IRaCIS.Core.API/Controllers/ExtraController.cs @@ -79,18 +79,6 @@ namespace IRaCIS.Api.Controllers - [HttpPost, Route("enroll/downloadResume/{trialId:guid}/{language}")] - [TypeFilter(typeof(TrialResourceFilter))] - [AllowAnonymous] - public async Task> DownloadResume([FromServices] IFileService _fileService, int language, Guid trialId, Guid[] doctorIdArray) - { - var userId = Guid.Parse(User.FindFirst("id").Value); - var zipPath = await _fileService.CreateOfficialResumeZip(language, doctorIdArray); - - return ResponseOutput.Ok(zipPath); - } - - /// 系统用户登录接口[New] diff --git a/IRaCIS.Core.API/Controllers/FileController.cs b/IRaCIS.Core.API/Controllers/FileController.cs deleted file mode 100644 index 985be4e9a..000000000 --- a/IRaCIS.Core.API/Controllers/FileController.cs +++ /dev/null @@ -1,532 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Threading.Tasks; -using IRaCIS.Application.Interfaces; -using IRaCIS.Application.Contracts; -using IRaCIS.Core.API.Utility; -using IRaCIS.Core.Application.Contracts.Image; -using IRaCIS.Core.Infrastructure.Extention; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.WebUtilities; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Net.Http.Headers; -using IRaCIS.Core.Application.Filter; -using IRaCIS.Core.Application.Contracts; - -namespace IRaCIS.Api.Controllers -{ - /// - /// 文件上传 - /// - [Route("file")] - [ApiController, Authorize, ApiExplorerSettings(GroupName = "Common")] - public class FileController : ControllerBase - { - private readonly IFileService _fileService; - private readonly IWebHostEnvironment _webHostEnvironment; - private readonly IHostEnvironment _hostEnvironment; - private ILogger _logger; - - private string _targetFilePath; - private readonly long _fileSizeLimit; - private readonly string[] _permittedExtensions = { ".pdf", ".doc", ".docx" }; - - public string trustedFileNameForFileStorage = ""; - - // Get the default form options so that we can use them to set the default - // limits for request body data. - private static readonly FormOptions _defaultFormOptions = new FormOptions(); - private string defaultUploadFilePath = string.Empty; - public FileController(IFileService fileService, Microsoft.Extensions.Configuration.IConfiguration config, - IHostEnvironment hostEnvironment, ILogger logger, - IWebHostEnvironment webHostEnvironment) - { - _fileService = fileService; - _hostEnvironment = hostEnvironment; - _webHostEnvironment = webHostEnvironment; - _fileSizeLimit = config.GetValue("FileSizeLimit"); - defaultUploadFilePath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).FullName; - - _logger = logger; - - _logger.LogWarning("File Path:" + defaultUploadFilePath); - - } - - - /// - /// 上传文件[FileUpload] - /// - /// 附件类型 - /// 医生Id - /// 返回文件信息 - [HttpPost, Route("uploadFile/{attachmentType}/{doctorId}")] - [DisableFormValueModelBinding] - public async Task UploadOrdinaryFile(string attachmentType, Guid doctorId) - { - #region 官方文档方式 - - - if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) - { - ModelState.AddModelError("File", - $"The request couldn't be processed (Error 1)."); - // Log error - return BadRequest(ModelState); - } - - var boundary = MultipartRequestHelper.GetBoundary( - MediaTypeHeaderValue.Parse(Request.ContentType), - _defaultFormOptions.MultipartBoundaryLengthLimit); - 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) - { - // This check assumes that there's a file - // present without form data. If form data - // is present, this method immediately fails - // and returns the model error. - if (!MultipartRequestHelper - .HasFileContentDisposition(contentDisposition)) - { - ModelState.AddModelError("File", - $"The request couldn't be processed (Error 2)."); - // Log error - return BadRequest(ModelState); - } - else - { - // Don't trust the file name sent by the client. To display - // the file name, HTML-encode the value. - var trustedFileNameForDisplay = WebUtility.HtmlEncode( - contentDisposition.FileName.Value); - - //var trustedFileNameForFileStorage = Path.GetRandomFileName(); - trustedFileNameForFileStorage = contentDisposition.FileName.Value; - - // **WARNING!** - // In the following example, the file is saved without - // scanning the file's contents. In most production - // scenarios, an anti-virus/anti-malware scanner API - // is used on the file before making the file available - // for download or for use by other systems. - // For more information, see the topic that accompanies - // this sample. - - var streamedFileContent = await FileHelpers.ProcessStreamedFile( - section, contentDisposition, ModelState, - _permittedExtensions, _fileSizeLimit); - - if (!ModelState.IsValid) - { - return BadRequest(ModelState); - } - - - //实际文件处理 - string uploadFolderPath = Path.Combine(defaultUploadFilePath, "UploadFile/" + doctorId + "/"); - - var doctorAttachmentUploadFolder = Path.Combine(uploadFolderPath, attachmentType); - - _targetFilePath = doctorAttachmentUploadFolder; - - if (!Directory.Exists(doctorAttachmentUploadFolder)) Directory.CreateDirectory(doctorAttachmentUploadFolder); - - using (var targetStream = System.IO.File.Create( - Path.Combine(_targetFilePath, trustedFileNameForFileStorage))) - { - await targetStream.WriteAsync(streamedFileContent); - - var attachmentPath = $"/UploadFile/{doctorId}/{attachmentType}/{trustedFileNameForFileStorage}"; - - return new JsonResult(ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = attachmentPath, FullFilePath = attachmentPath + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) })); - - } - - - } - } - - // Drain any remaining section body that hasn't been consumed and - // read the headers for the next section. - section = await reader.ReadNextSectionAsync(); - } - - - return Created(nameof(FileController), null); - - #endregion - } - - - /// - /// 上传文件( 不是医生个人的文件)[FileUpload] - /// 例如:阅片章程等 - /// - /// 文件类型 - /// - - [HttpPost, Route("uploadNonDoctorFile/{type}")] - [DisableFormValueModelBinding] - public async Task UploadNonDoctorFile(string type) - { - #region New Test 实测OK - - //上传根路径 - string uploadFolderPath = Path.Combine(defaultUploadFilePath, "UploadFile"); - - - if (uploadFolderPath != null) - { - //文件类型路径处理 - var uploadTypePath = Path.Combine(uploadFolderPath, type); - if (!Directory.Exists(uploadTypePath)) Directory.CreateDirectory(uploadTypePath); - - - ////实际文件处理 - - _targetFilePath = uploadTypePath; - - //获取boundary - - #region 方式二 - - var boundary = MultipartRequestHelper.GetBoundary( - MediaTypeHeaderValue.Parse(Request.ContentType), - _defaultFormOptions.MultipartBoundaryLengthLimit); - - #endregion - - #region 方式一 - //var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value; - - #endregion - - - //得到reader - var reader = new MultipartReader(boundary, HttpContext.Request.Body); - //{ BodyLengthLimit = 2000 };// - var section = await reader.ReadNextSectionAsync(); - - //读取section - while (section != null) - { - var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); - if (hasContentDispositionHeader) - { - trustedFileNameForFileStorage = contentDisposition.FileName.Value; - - - await WriteFileAsync(section.Body, Path.Combine(_targetFilePath, trustedFileNameForFileStorage)); - - #region 方式二 - - //using (var targetStream = System.IO.File.Create( - // Path.Combine(_targetFilePath, trustedFileNameForFileStorage))) - //{ - - // using (var memoryStream = new MemoryStream()) - // { - // await section.Body.CopyToAsync(memoryStream); - - // await targetStream.WriteAsync(memoryStream.ToArray()); - // } - //} - - - #endregion - - - var attachmentPath = $"/UploadFile/{type}/{trustedFileNameForFileStorage}"; - - return new JsonResult(ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = attachmentPath, FullFilePath = attachmentPath + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) })); - } - - section = await reader.ReadNextSectionAsync(); - } - return Created(nameof(FileController), null); - - - } - return new JsonResult(ResponseOutput.NotOk("服务器端映射路径操作失败")); - #endregion - } - - - - /// - /// 写文件导到磁盘 - /// - /// 流 - /// 文件保存路径 - /// - public static async Task WriteFileAsync(System.IO.Stream stream, string path) - { - const int FILE_WRITE_SIZE = 84975;//写出缓冲区大小 - int writeCount = 0; - using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write, FILE_WRITE_SIZE, true)) - { - byte[] byteArr = new byte[FILE_WRITE_SIZE]; - int readCount = 0; - while ((readCount = await stream.ReadAsync(byteArr, 0, byteArr.Length)) > 0) - { - await fileStream.WriteAsync(byteArr, 0, readCount); - writeCount += readCount; - } - } - return writeCount; - - - } - - - - - #region 上传无影响部分 - - /// - /// 下载多个医生的所有附件 - /// - /// - /// - - [HttpPost, Route("downloadDoctorAttachments")] - public async Task> 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) - - }); - } - - /// - /// 下载医生官方简历 - /// - /// - /// - /// - [HttpPost, Route("downloadOfficialCV/{language}")] - public async Task> DownloadOfficialResume(int language, Guid[] doctorIds) - { - - var path = _fileService.CreateDoctorsAllAttachmentZip(doctorIds); - return ResponseOutput.Ok(new UploadFileInfoDTO - { - FilePath = await _fileService.CreateOfficialResumeZip(language, doctorIds), - FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) - }); - } - - /// - /// 下载指定医生的指定附件 - /// - /// 医生Id - /// 要下载的附件Id - /// - [HttpPost, Route("downloadByAttachmentId/{doctorId}")] - public async Task> 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) - }); - } - - #endregion - - - #region 废弃 - - public class UploadDTFCommand - { - public Guid TrialId { get; set; } - public Guid SiteId { get; set; } - public Guid SubjectId { get; set; } - public Guid SubjectVisitId { get; set; } - - } - /// - /// 流式上传 临时文件 - /// - /// - [HttpPost("UploadDTF/{trialId:guid}/{siteId:guid}/{subjectId:guid}/{subjectVisitId:guid}/{studyId:guid}")] - [DisableFormValueModelBinding] - [DisableRequestSizeLimit] - [Obsolete] - public async Task UploadingStream(Guid trialId, Guid siteId, Guid subjectId, Guid subjectVisitId, Guid studyId, [FromServices] IStudyDTFService studyDTFService) - { - - //上传根路径 - string uploadFolderPath = Path.Combine(defaultUploadFilePath, "Dicom"); - - - var dtfPath = Path.Combine(uploadFolderPath, DateTime.Now.Year.ToString(), trialId.ToString(), - siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString()); - - if (!Directory.Exists(dtfPath)) - { - Directory.CreateDirectory(dtfPath); - } - - - #region 之前DTF 先上传临时文件,再拷贝 - ////上传根路径 - //string uploadFolderPath = Path.Combine(defaultUploadFilePath, "UploadFile"); - ////文件类型路径处理 - //var uploadTempFilePath = Path.Combine(uploadFolderPath, "TempFile"); - //if (!Directory.Exists(uploadTempFilePath)) Directory.CreateDirectory(uploadTempFilePath); - - - //var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX; - - //await WriteFileAsync(section.Body, Path.Combine(uploadTempFilePath, trustedFileNameForFileStorage)); - - //var attachmentPath = $"{uploadTempFilePath}/{trustedFileNameForFileStorage}"; - - ////多个文件上传,在这里返回就不合适,需要在外层,返回所有的文件路径,实际需要时再更改 - //return ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = attachmentPath }); - - #endregion - - - //获取boundary - var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value; - //得到reader - var reader = new MultipartReader(boundary, HttpContext.Request.Body); - - var section = await reader.ReadNextSectionAsync(); - - //读取section - while (section != null) - { - var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); - if (hasContentDispositionHeader) - { - var realName = contentDisposition.FileName.Value; - - var fileNameEX = Path.GetExtension(contentDisposition.FileName.Value); - var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX; - - - var relativePath = $"/Dicom/{DateTime.Now.Year.ToString()}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{trustedFileNameForFileStorage}"; - studyDTFService.AddStudyDTF(new Core.Application.Contracts.Dicom.DTO.StudyDTFAddOrUpdateCommand() - { - StudyId = studyId, - - FileName = realName, - - Path = relativePath - }); - - await WriteFileAsync(section.Body, Path.Combine(dtfPath, trustedFileNameForFileStorage)); - - //仅仅返回一个文件,如果多文件上传 在最后返回多个路径 - return ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = relativePath, FullFilePath = relativePath + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) }); - } - section = await reader.ReadNextSectionAsync(); - } - return ResponseOutput.NotOk("Upload error"); - } - - - /// - /// 流式上传 非Dicom文件 - /// - /// - [HttpPost("UploadNoneDICOM")] - [DisableFormValueModelBinding] - [DisableRequestSizeLimit] - [Obsolete] - public async Task UploadingNoneDicomStream([FromForm] ArchiveStudyCommand archiveStudyCommand, [FromServices] IStudyService studyService) - { - //上传根路径 - string uploadFolderPath = Path.Combine(defaultUploadFilePath, "Dicom"); - - //文件类型路径处理 - //var noneDicomPath = Path.Combine(uploadFolderPath, DateTime.Now.Year.ToString(), archiveStudyCommand.TrialId.ToString(), - // archiveStudyCommand.SiteId.ToString(), archiveStudyCommand.SubjectId.ToString(), archiveStudyCommand.SubjectVisitId.ToString(), "Data"); - - //var dtfPath = Path.Combine(uploadFolderPath, DateTime.Now.Year.ToString(), archiveStudyCommand.TrialId.ToString(), - // archiveStudyCommand.SiteId.ToString(), archiveStudyCommand.SubjectId.ToString(), archiveStudyCommand.SubjectVisitId.ToString()); - - var noneDicomPath = string.Empty; - - var dtfPath = string.Empty; - - var tempDtfPath = Path.Combine(defaultUploadFilePath/*, archiveStudyCommand.DTFPath*/); - - - if (!Directory.Exists(noneDicomPath)) - { - Directory.CreateDirectory(noneDicomPath); - } - if (!System.IO.File.Exists(tempDtfPath)) - { - return ResponseOutput.NotOk("DTF file can not found"); - } - - System.IO.File.Move(tempDtfPath, dtfPath); - - var saveFileDic = new Dictionary(); - - //获取boundary - var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value; - //得到reader - var reader = new MultipartReader(boundary, HttpContext.Request.Body); - - var section = await reader.ReadNextSectionAsync(); - - //读取section - while (section != null) - { - var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); - if (hasContentDispositionHeader) - { - var fileName = contentDisposition.FileName.Value; - var fileNameEX = Path.GetExtension(contentDisposition.FileName.Value); - var trustedFileNameForFileStorage = Guid.NewGuid() + fileNameEX; - - - - await WriteFileAsync(section.Body, Path.Combine(noneDicomPath, trustedFileNameForFileStorage)); - - var attachmentPath = $"{noneDicomPath}/{trustedFileNameForFileStorage}"; - - saveFileDic.Add(attachmentPath, fileName); - - - } - section = await reader.ReadNextSectionAsync(); - } - - //处理数据库操作 - - //studyService.DealNonDicomFile(saveFileDic, archiveStudyCommand); - return ResponseOutput.Ok(saveFileDic); - } - #endregion - - } -} diff --git a/IRaCIS.Core.API/Controllers/FinancialChangeController.cs b/IRaCIS.Core.API/Controllers/FinancialChangeController.cs index d7126efaa..0c5562ad8 100644 --- a/IRaCIS.Core.API/Controllers/FinancialChangeController.cs +++ b/IRaCIS.Core.API/Controllers/FinancialChangeController.cs @@ -10,7 +10,6 @@ using System.Threading.Tasks; using IRaCIS.Application.Services; using IRaCIS.Core.Application.Service.Inspection.DTO; using IRaCIS.Core.Infra.EFCore; -using IRaCIS.Core.Application.Service.Inspection.Interface; using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Application.Auth; @@ -23,15 +22,12 @@ namespace IRaCIS.Core.API.Controllers.Special { private readonly ITrialService _trialService; private readonly ICalculateService _calculateService; - private readonly IInspectionService _inspectionService; - public FinancialChangeController(ITrialService trialService, ICalculateService calculateService, - IInspectionService inspectionService + public FinancialChangeController(ITrialService trialService, ICalculateService calculateService ) { _trialService = trialService; _calculateService = calculateService; - this._inspectionService = inspectionService; } diff --git a/IRaCIS.Core.API/Controllers/InspectionController.cs b/IRaCIS.Core.API/Controllers/InspectionController.cs index 824b8836d..ea0bab87c 100644 --- a/IRaCIS.Core.API/Controllers/InspectionController.cs +++ b/IRaCIS.Core.API/Controllers/InspectionController.cs @@ -1,14 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; + using System.Threading.Tasks; using AutoMapper; -using Castle.Core.Internal; -using IRaCIS.Application.Contracts; + using IRaCIS.Application.Interfaces; using IRaCIS.Core.Application.Contracts; -using IRaCIS.Core.Application.Contracts.DTO; -using IRaCIS.Core.Application.Filter; + using IRaCIS.Core.Application.Image.QA; using IRaCIS.Core.Application.Interfaces; using IRaCIS.Core.Application.Service.Inspection.DTO; diff --git a/IRaCIS.Core.API/Controllers/StudyController.cs b/IRaCIS.Core.API/Controllers/StudyController.cs deleted file mode 100644 index 68212e428..000000000 --- a/IRaCIS.Core.API/Controllers/StudyController.cs +++ /dev/null @@ -1,432 +0,0 @@ -using IRaCIS.Application.Interfaces; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using System; -using System.Collections.Generic; -using IRaCIS.Core.Application.Contracts.Dicom.DTO; -using Microsoft.Net.Http.Headers; -using Microsoft.AspNetCore.WebUtilities; -using System.Threading.Tasks; -using IRaCIS.Core.Application.Contracts.Dicom; -using System.IO; -using System.IO.Compression; -using Microsoft.Extensions.Logging; -using IRaCIS.Core.Application.Filter; -using IRaCIS.Core.Infrastructure.Extention; -using EasyCaching.Core; -using IRaCIS.Core.Application.Contracts; -using IRaCIS.Core.Domain.Models; -using IRaCIS.Core.Application.Service.Inspection.Interface; -using IRaCIS.Core.Domain.Share; - -namespace IRaCIS.Api.Controllers -{ - /// - /// Study - /// - [Route("study")] - [ApiController, Authorize, ApiExplorerSettings(GroupName = "Image")] - public class StudyController : ControllerBase - { - private readonly IStudyService _studyService; - private readonly IDicomArchiveService _dicomArchiveService; - private readonly ILogger _logger; - private readonly IDictionaryService _dictionaryService; - private readonly IInspectionService _inspectionService; - private IEasyCachingProvider _provider; - private IUserInfo _userInfo; - private static object _locker = new object(); - - - public StudyController(IStudyService studyService, - IDicomArchiveService dicomArchiveService, - ILogger logger, - IDictionaryService dictionaryService, - IInspectionService inspectionService, - IEasyCachingProvider provider, IUserInfo userInfo - ) - { - _userInfo = userInfo; - _provider = provider; - _studyService = studyService; - _dicomArchiveService = dicomArchiveService; - _logger = logger; - this._dictionaryService = dictionaryService; - this._inspectionService = inspectionService; - } - - - - - - - /// 归档 - [HttpPost, Route("archiveStudy/{trialId:guid}")] - [DisableFormValueModelBinding] - [DisableRequestSizeLimit] - [TypeFilter(typeof(TrialResourceFilter))] - public async Task ArchiveStudy([FromForm] ArchiveStudyCommand archiveStudyCommand) - { - - string studycode = string.Empty; - var startTime = DateTime.Now; - - if (_provider.Exists("StudyUid_" + archiveStudyCommand.StudyInstanceUid)) - { - return ResponseOutput.NotOk("当前已有人正在上传和归档该检查!"); - } - else - { - _provider.Set("StudyUid_" + archiveStudyCommand.StudyInstanceUid, _userInfo.Id, TimeSpan.FromMinutes(30)); - } - - var archiveResult = new DicomArchiveResult(); - var archivedStudyIds = new List(); - var seriesInstanceUidList = new List(); - var instanceUidList = new List(); - - //重传的时候,找出当前检查已经上传的series instance - if (archiveStudyCommand.AbandonStudyId != null) - { - _studyService.GetHasUploadSeriesAndInstance(archiveStudyCommand.AbandonStudyId.Value, ref seriesInstanceUidList, ref instanceUidList); - } - - var savedInfo = _studyService.GetSaveToDicomInfo(archiveStudyCommand.SubjectVisitId); - - - var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value; - var reader = new MultipartReader(boundary, HttpContext.Request.Body); - var section = await reader.ReadNextSectionAsync(); - while (section != null) - { - //采用post方式 这里多加一个判断 过滤其他参数 - if (string.IsNullOrEmpty(section.ContentType)) - { - section = await reader.ReadNextSectionAsync(); - continue; - } - - var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); - if (hasContentDispositionHeader) - { - string fileName = contentDisposition.FileName.Value; - try - { - - string mediaType = section.ContentType; - - if (mediaType.Contains("zip")) - { - var partStream = section.Body; - using (var zipArchive = new ZipArchive(partStream, ZipArchiveMode.Read)) - { - foreach (var entry in zipArchive.Entries) - { - if (entry.FullName.EndsWith("/")) continue; - try - { - ++archiveResult.ReceivedFileCount; - using (var memoryStream = new MemoryStream()) - { - await section.Body.CopyToAsync(memoryStream); - - memoryStream.Seek(0, SeekOrigin.Begin); - - var archiveStudyId = await _dicomArchiveService.ArchiveDicomStreamAsync(memoryStream, savedInfo, seriesInstanceUidList, instanceUidList); - studycode = archiveStudyId.Item2; - if (!archivedStudyIds.Contains(archiveStudyId.Item1)) - archivedStudyIds.Add(archiveStudyId.Item1); - } - } - catch - { - archiveResult.ErrorFiles.Add($"{fileName}/{entry.FullName}"); - } - } - } - } - - ++archiveResult.ReceivedFileCount; - - - if (mediaType.Contains("octet-stream")) - { - using (var memoryStream = new MemoryStream()) - { - await section.Body.CopyToAsync(memoryStream); - - memoryStream.Seek(0, SeekOrigin.Begin); - - var archiveStudyId = await _dicomArchiveService.ArchiveDicomStreamAsync(memoryStream, savedInfo, seriesInstanceUidList, instanceUidList); - studycode = archiveStudyId.Item2; - - - if (!archivedStudyIds.Contains(archiveStudyId.Item1)) - archivedStudyIds.Add(archiveStudyId.Item1); - } - } - } - catch (Exception e) - { - _logger.LogError(e.Message + e.StackTrace); - - archiveResult.ErrorFiles.Add(fileName); - - _provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid); - - } - } - section = await reader.ReadNextSectionAsync(); - } - - try - { - if (archivedStudyIds.Count > 0) // 上传成功,处理逻辑 - { - - - // 同一个访视 多个线程上传处理 批量保存 可能造成死锁 https://www.cnblogs.com/johnblogs/p/9945767.html - - await _dicomArchiveService.DicomDBDataSaveChange(); - - //sw.Stop(); - _studyService.UploadOrReUploadNeedTodo(archiveStudyCommand, archivedStudyIds, ref archiveResult, new StudyMonitor() - { - TrialId = savedInfo.TrialId, - SiteId = savedInfo.SiteId, - SubjectId = savedInfo.SubjectId, - SubjectVisitId = savedInfo.SubjectVisitId, - StudyId = archivedStudyIds[0], - - StudyCode= studycode, - UploadStartTime = startTime, - UploadFinishedTime = DateTime.Now, - //TotalMillisecondsInterval = (DateTime.Now- startTime).TotalMilliseconds, - FileSize = (decimal)HttpContext.Request.ContentLength, - FileCount = archiveResult.ReceivedFileCount, - IsDicom = true, - IsDicomReUpload = archiveStudyCommand.AbandonStudyId != null, - IP = _userInfo.IP - }); - - _provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid); - - } - else - { - return ResponseOutput.NotOk("未完成该检查的归档"); - } - - - } - catch (Exception e) - { - - _logger.LogError(e.Message + e.StackTrace); - - _provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid); - - return ResponseOutput.NotOk(e.Message, ApiResponseCodeEnum.ProgramException); - - } - - return ResponseOutput.Ok(archiveResult); - } - - - - - - - - - #region 2021.12.14 整理废弃 - - //[Obsolete] - //[HttpGet, Route("forwardStudy/{studyId:guid}/{trialId:guid}")] - //[TrialAudit(AuditType.StudyAudit, AuditOptType.Forwarded)] - //[TypeFilter(typeof(TrialResourceFilter))] - - //public IResponseOutput ForwardStudy(Guid studyId) - //{ - // return _studyService.ForwardStudy(studyId); - //} - - ///// 指定资源Id,获取Dicom检查信息 - ///// Dicom检查的Id - //[HttpGet, Route("item/{studyId:guid}")] - //[Obsolete] - //public IResponseOutput GetStudyItem(Guid studyId) - //{ - // return ResponseOutput.Ok(_studyService.GetStudyItem(studyId)); - //} - - //[Obsolete] - //[HttpDelete, Route("deleteStudy/{id:guid}/{trialId:guid}")] - //public IResponseOutput DeleteStudy(Guid id) - //{ - // return _studyService.DeleteStudy(id); - //} - - //[Obsolete] - //[HttpPost, Route("getStudyList")] - //public IResponseOutput> GetStudyList(StudyQueryDTO queryDto) - //{ - // return ResponseOutput.Ok(_studyService.GetStudyList(queryDto)); - //} - - - /////// 指定资源Id,渲染Dicom检查的Jpeg预览图像 - /////// Dicom检查的Id - ////[HttpGet, Route("preview/{studyId:guid}")] - ////[AllowAnonymous] - ////public FileContentResult GetStudyPreview(Guid studyId) - ////{ - //// string path = _studyService.GetStudyPreview(studyId); - //// using (var sw = DicomRenderingHelper.RenderPreviewJpeg(path)) - //// { - //// var bytes = new byte[sw.Length]; - //// sw.Read(bytes, 0, bytes.Length); - //// sw.Close(); - //// return new FileContentResult(bytes, "image/jpeg"); - //// } - ////} - - ///// - ///// Dicom匿名化 - ///// - ///// 需要匿名化的检查Id - ///// - //[HttpPost, Route("dicomAnonymize/{studyId:guid}/{trialId:guid}")] - //[TrialAudit(AuditType.StudyAudit, AuditOptType.Anonymized)] - //[Obsolete] - //[TypeFilter(typeof(TrialResourceFilter))] - //public async Task DicomAnonymize(Guid studyId) - //{ - // string userName = User.FindFirst("realName").Value; ; - // return await _studyService.DicomAnonymize(studyId, userName); - //} - - - ///// - ///// 获取受试者 这次访视 对应的study modality 列表 - ///// - ///// - ///// - ///// - ///// - ///// - //[HttpPost, Route("getSubjectVisitStudyList/{trialId:guid}/{siteId:guid}/{subjectId:guid}/{subjectVisitId:guid}")] - //[Obsolete] - //[AllowAnonymous] - //public IResponseOutput> GetSubjectVisitStudyList(Guid trialId, Guid siteId, Guid subjectId, - // Guid subjectVisitId) - //{ - // return ResponseOutput.Ok(_studyService.GetSubjectVisitStudyList(trialId, siteId, subjectId, subjectVisitId)); - //} - - //[HttpPost, Route("getDistributeStudyList")] - //[Obsolete] - //public IResponseOutput> GetDistributeStudyList(StudyStatusQueryDTO studyStatusQueryDto) - //{ - // return ResponseOutput.Ok(_studyService.GetDistributeStudyList(studyStatusQueryDto)); - //} - - /////// 删除检查 - ////[HttpDelete, Route("deleteStudy/{id:guid}/{trialId:guid}")] - //// - ////[TypeFilter(typeof(TrialResourceFilter))] - ////public IResponseOutput DeleteStudy(Guid id) - ////{ - //// return _studyService.DeleteStudy(id); - ////} - - ///// 更新Study状态,并保存状态变更信息 - ///// - - //[HttpPost, Route("updateStudyStatus/{trialId:guid}")] - - //[TrialAudit(AuditType.StudyAudit, AuditOptType.ChangeStudyStatus)] - //[Obsolete] - //[TypeFilter(typeof(TrialResourceFilter))] - //public IResponseOutput UpdateStudyStatus(StudyStatusDetailCommand studyStatusDetailCommand) - //{ - // return _studyService.UpdateStudyStatus(studyStatusDetailCommand); - //} - - ///// - ///// 根据项目Id 获取可选医生列表 - ///// - ///// - ///// - //[HttpGet, Route("GetReviewerList/{trialId:guid}")] - //[Obsolete] - //public IResponseOutput> GetReviewerListByTrialId(Guid trialId) - //{ - // var result = _studyService.GetReviewerListByTrialId(trialId); - // return ResponseOutput.Ok(result); - //} - - ///// - ///// 根据StudyId获取该Study的操作记录,时间倒序 - ///// - ///// - ///// - //[HttpGet, Route("getStudyStatusDetailList/{studyId:guid}")] - //[Obsolete] - //public IResponseOutput> GetStudyStatusDetailList(Guid studyId) - //{ - // var result = _studyService.GetStudyStatusDetailList(studyId); - // return ResponseOutput.Ok(result); - //} - - ///// - ///// 获取某个访视的关联访视 - ///// 用于获取关联影像(调用之前的接口:/series/list/,根据StudyId,获取访视的序列列表) - ///// - ///// - ///// - ///// - //[HttpGet, Route("getRelationVisitList/{visitNum}/{tpCode}")] - //[Obsolete] - //[AllowAnonymous] - //public IResponseOutput> GetRelationVisitList(decimal visitNum, string tpCode) - //{ - // return ResponseOutput.Ok(_studyService.GetRelationVisitList(visitNum, tpCode)); - //} - - ///// - ///// 保存标记(跟删除合并,每次保存最新的标记),会删除替换之前的标记 - ///// 外层的TPcode 必须传,里面的标记数组可为空数组,表示删除该Study的所有标记 - ///// - ///// - ///// - //[HttpPost, Route("saveImageLabelList")] - //[AllowAnonymous] - //[Obsolete] - - //public IResponseOutput SaveImageLabelList(ImageLabelCommand imageLabelCommand) - //{ - // return ResponseOutput.Result(_studyService.SaveImageLabelList(imageLabelCommand)); - //} - - ///// - ///// 根据TPCode 获取所有的标记 - ///// - ///// - ///// - //[HttpGet, Route("getImageLabelList/{tpCode}")] - //[Obsolete] - //[AllowAnonymous] - //public IResponseOutput> GetImageLabelList(string tpCode) - //{ - // return ResponseOutput.Ok(_studyService.GetImageLabel(tpCode)); - //} - - #endregion - - - - - } -} diff --git a/IRaCIS.Core.API/Controllers/UploadController.cs b/IRaCIS.Core.API/Controllers/UploadController.cs deleted file mode 100644 index e9559c04e..000000000 --- a/IRaCIS.Core.API/Controllers/UploadController.cs +++ /dev/null @@ -1,405 +0,0 @@ -using AutoMapper; -using IRaCIS.Core.Application.Contracts; -using IRaCIS.Core.Application.Filter; -using IRaCIS.Core.Application.Helper; -using IRaCIS.Core.Domain.Models; -using IRaCIS.Core.Domain.Share; -using IRaCIS.Core.Infra.EFCore; -using IRaCIS.Core.Infrastructure.Extention; -using MediatR; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.WebUtilities; -using Microsoft.Net.Http.Headers; -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - -namespace IRaCIS.Core.API.Controllers -{ - [ApiExplorerSettings(GroupName = "Trial")] - [ApiController] - public class UploadController : ControllerBase - { - public IMapper _mapper { get; set; } - public IUserInfo _userInfo { get; set; } - private readonly IMediator _mediator; - - private readonly IWebHostEnvironment _hostEnvironment; - - private readonly IServiceProvider _serviceProvider; - - - public UploadController(IMapper mapper, IUserInfo userInfo, IMediator mediator, IWebHostEnvironment hostEnvironment, IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - _hostEnvironment = hostEnvironment; - _mediator = mediator; - _mapper = mapper; - _userInfo = userInfo; - } - - [HttpPost("TrialDocument/UploadTrialDoc/{trialId:guid}")] - [DisableRequestSizeLimit] - [DisableFormValueModelBinding] - public async Task UploadTrialDoc(Guid trialId) - { - 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) - { - - DealTrialStorePath(trialId, contentDisposition.FileName.Value, out string serverFilePath, out string relativePath ); - - await WriteFileAsync(section.Body, serverFilePath); - - //仅仅返回一个文件,如果多文件上传 在最后返回多个路径 - return ResponseOutput.Ok(new - { - FilePath = relativePath, - FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken - }); - - } - section = await reader.ReadNextSectionAsync(); - } - return ResponseOutput.Ok(); - } - - - [HttpPost("TrialDocument/UploadSystemDoc")] - [DisableRequestSizeLimit] - [DisableFormValueModelBinding] - public async Task UploadSysTemDoc( ) - { - 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) - { - - DealSysTemStorePath( contentDisposition.FileName.Value, out string serverFilePath, out string relativePath); - - await WriteFileAsync(section.Body, serverFilePath); - - //仅仅返回一个文件,如果多文件上传 在最后返回多个路径 - return ResponseOutput.Ok(new - { - FilePath = relativePath, - FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken - }); - - } - section = await reader.ReadNextSectionAsync(); - } - return ResponseOutput.Ok(); - } - - - /// - /// 上传通用文档 比如一致性核查的 比如导出的excel 模板 - /// - /// - [HttpPost("CommonDocument/UploadCommonDoc")] - [DisableRequestSizeLimit] - [DisableFormValueModelBinding] - public async Task UploadCommonDoc(/*string fileType, string moduleType*/) - { - 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) - { - - DealCommonStorePath(/*fileType, moduleType,*/ contentDisposition.FileName.Value, out string serverFilePath, out string relativePath); - - await WriteFileAsync(section.Body, serverFilePath); - - //仅仅返回一个文件,如果多文件上传 在最后返回多个路径 - return ResponseOutput.Ok(new - { - FilePath = relativePath, - FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken - }); - - } - section = await reader.ReadNextSectionAsync(); - } - return ResponseOutput.Ok(); - } - - - - - /// - /// 上传系统通知文档 - /// - /// - [HttpPost("SystemNotice/UploadSystemNoticeDoc")] - [DisableRequestSizeLimit] - [DisableFormValueModelBinding] - public async Task UploadSystemNoticeDoc() - { - 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) - { - - DealSystemNoticePath( contentDisposition.FileName.Value, out string serverFilePath, out string relativePath); - - await WriteFileAsync(section.Body, serverFilePath); - - //仅仅返回一个文件,如果多文件上传 在最后返回多个路径 - return ResponseOutput.Ok(new - { - FilePath = relativePath, - FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken - }); - - } - section = await reader.ReadNextSectionAsync(); - } - return ResponseOutput.Ok(); - } - - - private void DealCommonStorePath(/*string fileType, string moduleType,*/ string fileRealName, out string serverFilePath, out string relativePath) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment) ; - //上传根路径 - var _fileStorePath = Path.Combine(rootPath, StaticData.SystemDataFolder); - - //文件类型路径处理 - var uploadFolderPath = Path.Combine(_fileStorePath, StaticData.DataTemplate); - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - - var (trustedFileNameForFileStorage, fileName) = FileStoreHelper.GetStoreFileName(fileRealName); - - - relativePath = $"/{StaticData.IRaCISDataFolder}/{StaticData.SystemDataFolder}/{StaticData.DataTemplate}/{trustedFileNameForFileStorage}"; - - serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - } - - - private void DealSystemNoticePath(string fileRealName, out string serverFilePath, out string relativePath) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - //上传根路径 - var _fileStorePath = Path.Combine(rootPath, StaticData.SystemDataFolder); - - //文件类型路径处理 - var uploadFolderPath = Path.Combine(_fileStorePath, StaticData.NoticeAttachment); - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - - var (trustedFileNameForFileStorage, fileName) = FileStoreHelper.GetStoreFileName(fileRealName); - - - - relativePath = $"/{StaticData.IRaCISDataFolder}/{StaticData.SystemDataFolder}/{StaticData.NoticeAttachment}/{trustedFileNameForFileStorage}"; - - serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - } - - - private void DealSysTemStorePath( string fileRealName, out string serverFilePath, out string relativePath) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - //上传根路径 - var _fileStorePath = Path.Combine(rootPath, StaticData.SystemDataFolder); - - //文件类型路径处理 - var uploadFolderPath = Path.Combine(_fileStorePath, StaticData.SignDocumentFolder); - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - - //var fileNameEX = Path.GetExtension(fileRealName); - //var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX; - - var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileRealName; - - relativePath = $"/{StaticData.IRaCISDataFolder}/{StaticData.SystemDataFolder}/{ StaticData.SignDocumentFolder}/{trustedFileNameForFileStorage}"; - - serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - } - - - private void DealTrialStorePath(Guid trialId,string fileRealName, out string serverFilePath, out string relativePath) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - //上传根路径 - var _fileStorePath = Path.Combine(rootPath, StaticData.TrialDataFolder); - - //文件类型路径处理 - var uploadFolderPath = Path.Combine(_fileStorePath, trialId.ToString(), StaticData.SignDocumentFolder); - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - var (trustedFileNameForFileStorage, fileName) = FileStoreHelper.GetStoreFileName(fileRealName); - - relativePath = $"/{StaticData.IRaCISDataFolder}/{StaticData.TrialDataFolder}/{trialId}/{StaticData.SignDocumentFolder}/{trustedFileNameForFileStorage}"; - - serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - } - - - - /// - /// 上传临床数据 - /// - /// - /// 1:DICOM DTF 2:非DIOM DTF 3: 受试者临床数据 - /// - /// - [HttpPost("ClinicalData/UploadVisitClinicalData/{trialId:guid}/{subjectVisitId:guid}/{type}")] - [DisableRequestSizeLimit] - [DisableFormValueModelBinding] - [Obsolete] - public async Task UploadVisitData(Guid subjectVisitId, [FromRoute] UploadFileTypeEnum uploadType, [FromServices] IRepository _subjectVisitRepository) - { - - 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) - { - - DealStorePath(subjectVisitId, contentDisposition.FileName.Value, uploadType, out string serverFilePath, out string relativePath, _subjectVisitRepository); - - await WriteFileAsync(section.Body, serverFilePath); - - //仅仅返回一个文件,如果多文件上传 在最后返回多个路径 - return ResponseOutput.Ok(new - { - FilePath = relativePath, - FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken - }); - - } - section = await reader.ReadNextSectionAsync(); - } - return ResponseOutput.Ok(); - - } - - public enum UploadFileTypeEnum - { - DICOM_DTF = 1, - - NonDICOM_DTF = 2, - - SubjectTreatement = 3 - } - - private void DealStorePath(Guid subjectVisitId, string realName, UploadFileTypeEnum typeEnum, out string serverFilePath, out string relativePath, IRepository _subjectVisitRepository) - { - - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - //上传根路径 - var _fileStorePath = Path.Combine(rootPath, StaticData.TrialDataFolder); - - var sv = _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SiteId, t.SubjectId }).FirstOrDefault(); - - //处理存储的文件夹 - var typeFolder = typeEnum == UploadFileTypeEnum.SubjectTreatement ? StaticData.TreatmenthistoryFolder - : typeEnum == UploadFileTypeEnum.NonDICOM_DTF ? StaticData.NoneDicomFolder - : /*typeEnum == UploadFileTypeEnum.DICOM_DTF ?*/ StaticData.DicomFolder; - - string uploadFolderPath = Path.Combine(_fileStorePath, sv.TrialId.ToString(), sv.SiteId.ToString(), sv.SubjectId.ToString(), subjectVisitId.ToString(), typeFolder); - - if (!Directory.Exists(uploadFolderPath)) - { - Directory.CreateDirectory(uploadFolderPath); - } - - var fileNameEX = Path.GetExtension(realName); - - var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX; - - relativePath = $"/{StaticData.TrialDataFolder}/{sv.TrialId}/{sv.SiteId}/{sv.SubjectId}/{subjectVisitId}/{typeFolder}/{trustedFileNameForFileStorage}"; - - serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - //处理 是上传文件返回路径 还是上传文件后,需要保存数据库 - - - if (typeEnum == UploadFileTypeEnum.SubjectTreatement) - { - var repository = _serviceProvider.GetService(typeof(IRepository)) as IRepository; - - _= repository.InsertOrUpdateAsync(new PreviousPDFAddOrEdit() { FileName = realName, Path = relativePath, SubjectVisitId = subjectVisitId }, true).Result; - } - else if (typeEnum == UploadFileTypeEnum.NonDICOM_DTF) - { - - } - else if (typeEnum == UploadFileTypeEnum.NonDICOM_DTF) - { - - } - } - - /// - /// 写文件导到磁盘 - /// - /// 流 - /// 文件保存路径 - /// - private static async Task WriteFileAsync(System.IO.Stream stream, string path) - { - const int FILE_WRITE_SIZE = 84975;//写出缓冲区大小 - int writeCount = 0; - using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write, FILE_WRITE_SIZE, true)) - { - byte[] byteArr = new byte[FILE_WRITE_SIZE]; - int readCount = 0; - while ((readCount = await stream.ReadAsync(byteArr, 0, byteArr.Length)) > 0) - { - await fileStream.WriteAsync(byteArr, 0, readCount); - writeCount += readCount; - } - } - return writeCount; - } - } -} diff --git a/IRaCIS.Core.API/Controllers/UploadDownLoadController.cs b/IRaCIS.Core.API/Controllers/UploadDownLoadController.cs new file mode 100644 index 000000000..a0c08da75 --- /dev/null +++ b/IRaCIS.Core.API/Controllers/UploadDownLoadController.cs @@ -0,0 +1,1077 @@ +using AutoMapper; +using EasyCaching.Core; +using ExcelDataReader; +using IRaCIS.Application.Contracts; +using IRaCIS.Application.Interfaces; +using IRaCIS.Core.Application.Auth; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Contracts.Dicom; +using IRaCIS.Core.Application.Contracts.Dicom.DTO; +using IRaCIS.Core.Application.Filter; +using IRaCIS.Core.Application.Helper; +using IRaCIS.Core.Application.MediatR.CommandAndQueries; +using IRaCIS.Core.Application.Service.Verify; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Infrastructure.Extention; +using Magicodes.ExporterAndImporter.Excel; +using MediatR; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.StaticFiles; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; +using MiniExcelLibs; +using Newtonsoft.Json; +using SharpCompress.Archives; +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.API.Controllers +{ + + #region 上传基类封装 + [DisableFormValueModelBinding] + public abstract class UploadBaseController : ControllerBase + { + /// 流式上传 直接返回 + [Route("base")] + public virtual async Task SingleFileUploadAsync(Func 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(); + } + + + /// 流式上传 通用封装 不返回任何数据,后续还有事情处理 + [Route("base")] + public virtual async Task FileUploadAsync(Func> 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(); + } + } + + + + /// 流式上传 Dicom上传 需要返回文件数量 + [Route("base")] + public virtual async Task DicomFileUploadAsync(Func filePathFunc) + { + + var fileCount = 0; + var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value; + + var reader = new MultipartReader(boundary, HttpContext.Request.Body); + + var section = await reader.ReadNextSectionAsync(); + + while (section != null) + { + var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); + + if (hasContentDispositionHeader) + { + + var fileName = contentDisposition.FileName.Value??String.Empty; + + string mediaType = section.ContentType ?? String.Empty; + + + //处理压缩文件 + if (fileName.Contains(".Zip", StringComparison.OrdinalIgnoreCase) || fileName.Contains(".rar", StringComparison.OrdinalIgnoreCase)) + { + var archive = ArchiveFactory.Open(section.Body); + + foreach (var entry in archive.Entries) + { + if (!entry.IsDirectory) + { + ++fileCount; + + await filePathFunc(entry.Key, entry.OpenEntryStream()); + + + } + } + } + //普通单个文件 + else + { + if (mediaType.Contains("octet-stream")) + { + ++fileCount; + + await filePathFunc(fileName, section.Body); + } + + } + + + + } + section = await reader.ReadNextSectionAsync(); + } + + return fileCount; + } + + + } + + #endregion + + #region Dicom 影像上传 临床数据 非diocm + + [ApiExplorerSettings(GroupName = "Image")] + [ApiController] + public class StudyController : UploadBaseController + { + public IMapper _mapper { get; set; } + public IUserInfo _userInfo { get; set; } + + private readonly IWebHostEnvironment _hostEnvironment; + + + + public StudyController(IMapper mapper, IUserInfo userInfo, IWebHostEnvironment hostEnvironment) + { + _hostEnvironment = hostEnvironment; + _mapper = mapper; + _userInfo = userInfo; + } + + /// Dicom 归档 + [HttpPost, Route("Study/ArchiveStudy1/{trialId:guid}")] + [DisableFormValueModelBinding] + [DisableRequestSizeLimit] + [TypeFilter(typeof(TrialResourceFilter))] + public async Task ArchiveStudy([FromForm] ArchiveStudyCommand archiveStudyCommand, + [FromServices] ILogger _logger, + [FromServices] IEasyCachingProvider _provider, + [FromServices] IStudyService _studyService, + [FromServices] IDicomArchiveService _dicomArchiveService + ) + { + + string studycode = string.Empty; + var startTime = DateTime.Now; + + if (_provider.Exists("StudyUid_" + archiveStudyCommand.StudyInstanceUid)) + { + return ResponseOutput.NotOk("当前已有人正在上传和归档该检查!"); + } + else + { + _provider.Set("StudyUid_" + archiveStudyCommand.StudyInstanceUid, _userInfo.Id, TimeSpan.FromMinutes(30)); + } + + var archiveResult = new DicomArchiveResult(); + var archivedStudyIds = new List(); + + var (seriesInstanceUidList, sopInstanceUidList) = (new List(), new List()); + + //重传的时候,找出当前检查已经上传的series instance + if (archiveStudyCommand.AbandonStudyId != null) + { + (seriesInstanceUidList, sopInstanceUidList) = _studyService.GetHasUploadSeriesAndInstance(archiveStudyCommand.AbandonStudyId.Value); + } + + var savedInfo = _studyService.GetSaveToDicomInfo(archiveStudyCommand.SubjectVisitId); + + + var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value; + var reader = new MultipartReader(boundary, HttpContext.Request.Body); + var section = await reader.ReadNextSectionAsync(); + while (section != null) + { + //采用post方式 这里多加一个判断 过滤其他参数 + if (string.IsNullOrEmpty(section.ContentType)) + { + section = await reader.ReadNextSectionAsync(); + continue; + } + + var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); + if (hasContentDispositionHeader) + { + string fileName = contentDisposition.FileName.Value; + try + { + + string mediaType = section.ContentType; + + if (mediaType.Contains("zip")) + { + var partStream = section.Body; + using (var zipArchive = new ZipArchive(partStream, ZipArchiveMode.Read)) + { + foreach (var entry in zipArchive.Entries) + { + if (entry.FullName.EndsWith("/")) continue; + try + { + ++archiveResult.ReceivedFileCount; + using (var memoryStream = new MemoryStream()) + { + await section.Body.CopyToAsync(memoryStream); + + memoryStream.Seek(0, SeekOrigin.Begin); + + var archiveStudyId = await _dicomArchiveService.ArchiveDicomStreamAsync(memoryStream, savedInfo, seriesInstanceUidList, sopInstanceUidList); + studycode = archiveStudyId.Item2; + if (!archivedStudyIds.Contains(archiveStudyId.Item1)) + archivedStudyIds.Add(archiveStudyId.Item1); + } + } + catch + { + archiveResult.ErrorFiles.Add($"{fileName}/{entry.FullName}"); + } + } + } + } + + ++archiveResult.ReceivedFileCount; + + + if (mediaType.Contains("octet-stream")) + { + using (var memoryStream = new MemoryStream()) + { + await section.Body.CopyToAsync(memoryStream); + + memoryStream.Seek(0, SeekOrigin.Begin); + + var archiveStudyId = await _dicomArchiveService.ArchiveDicomStreamAsync(memoryStream, savedInfo, seriesInstanceUidList, sopInstanceUidList); + studycode = archiveStudyId.Item2; + + + if (!archivedStudyIds.Contains(archiveStudyId.Item1)) + archivedStudyIds.Add(archiveStudyId.Item1); + } + } + } + catch (Exception e) + { + _logger.LogError(e.Message + e.StackTrace); + + archiveResult.ErrorFiles.Add(fileName); + + _provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid); + + } + } + section = await reader.ReadNextSectionAsync(); + } + + try + { + if (archivedStudyIds.Count > 0) // 上传成功,处理逻辑 + { + + + // 同一个访视 多个线程上传处理 批量保存 可能造成死锁 https://www.cnblogs.com/johnblogs/p/9945767.html + + await _dicomArchiveService.DicomDBDataSaveChange(); + + //sw.Stop(); + _studyService.UploadOrReUploadNeedTodo(archiveStudyCommand, archivedStudyIds, ref archiveResult, new StudyMonitor() + { + TrialId = savedInfo.TrialId, + SiteId = savedInfo.SiteId, + SubjectId = savedInfo.SubjectId, + SubjectVisitId = savedInfo.SubjectVisitId, + StudyId = archivedStudyIds[0], + + StudyCode = studycode, + UploadStartTime = startTime, + UploadFinishedTime = DateTime.Now, + //TotalMillisecondsInterval = (DateTime.Now- startTime).TotalMilliseconds, + FileSize = (decimal)HttpContext.Request.ContentLength, + FileCount = archiveResult.ReceivedFileCount, + IsDicom = true, + IsDicomReUpload = archiveStudyCommand.AbandonStudyId != null, + IP = _userInfo.IP + }); + + _provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid); + + } + else + { + return ResponseOutput.NotOk("未完成该检查的归档"); + } + + + } + catch (Exception e) + { + + _logger.LogError(e.Message + e.StackTrace); + + _provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid); + + return ResponseOutput.NotOk(e.Message, ApiResponseCodeEnum.ProgramException); + + } + + return ResponseOutput.Ok(archiveResult); + } + + + + + /// Dicom 归档 + [HttpPost, Route("Study/ArchiveStudy/{trialId:guid}")] + [DisableFormValueModelBinding] + [DisableRequestSizeLimit] + [TypeFilter(typeof(TrialResourceFilter))] + public async Task ArchiveStudyNew([FromForm] ArchiveStudyCommand archiveStudyCommand, + [FromServices] ILogger _logger, + [FromServices] IEasyCachingProvider _provider, + [FromServices] IStudyService _studyService, + [FromServices] IDicomArchiveService _dicomArchiveService + ) + { + + string studycode = string.Empty; + + var startTime = DateTime.Now; + + if (_provider.Exists("StudyUid_" + archiveStudyCommand.StudyInstanceUid)) + { + return ResponseOutput.NotOk("当前已有人正在上传和归档该检查!"); + } + else + { + _provider.Set("StudyUid_" + archiveStudyCommand.StudyInstanceUid, _userInfo.Id, TimeSpan.FromMinutes(30)); + } + + var (archiveResult, archivedStudyIds) = (new DicomArchiveResult(), new List()); + + + var (seriesInstanceUidList, sopInstanceUidList) = (new List(), new List()); + + //重传的时候,找出当前检查已经上传的series instance + if (archiveStudyCommand.AbandonStudyId != null) + { + (seriesInstanceUidList, sopInstanceUidList) =_studyService.GetHasUploadSeriesAndInstance(archiveStudyCommand.AbandonStudyId.Value); + } + + var savedInfo = _studyService.GetSaveToDicomInfo(archiveStudyCommand.SubjectVisitId); + + archiveResult.ReceivedFileCount= await DicomFileUploadAsync(async (fileName, fileStream) => + { + + try + { + using (var memoryStream = new MemoryStream()) + { + await fileStream.CopyToAsync(memoryStream); + + memoryStream.Seek(0, SeekOrigin.Begin); + + var archiveStudyId = await _dicomArchiveService.ArchiveDicomStreamAsync(memoryStream, savedInfo, seriesInstanceUidList, sopInstanceUidList); + studycode = archiveStudyId.Item2; + + + if (!archivedStudyIds.Contains(archiveStudyId.Item1)) + archivedStudyIds.Add(archiveStudyId.Item1); + } + } + catch (Exception e) + { + _logger.LogError(e.Message + e.StackTrace); + + archiveResult.ErrorFiles.Add(fileName); + + _provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid); + + } + + + }); + + + try + { + if (archivedStudyIds.Count > 0) // 上传成功,处理逻辑 + { + + // 同一个访视 多个线程上传处理 批量保存 可能造成死锁 https://www.cnblogs.com/johnblogs/p/9945767.html + + await _dicomArchiveService.DicomDBDataSaveChange(); + + _studyService.UploadOrReUploadNeedTodo(archiveStudyCommand, archivedStudyIds, ref archiveResult, new StudyMonitor() + { + TrialId = savedInfo.TrialId, + SiteId = savedInfo.SiteId, + SubjectId = savedInfo.SubjectId, + SubjectVisitId = savedInfo.SubjectVisitId, + StudyId = archivedStudyIds[0], + + StudyCode = studycode, + UploadStartTime = startTime, + UploadFinishedTime = DateTime.Now, + //TotalMillisecondsInterval = (DateTime.Now- startTime).TotalMilliseconds, + FileSize = (decimal)HttpContext.Request.ContentLength, + FileCount = archiveResult.ReceivedFileCount, + IsDicom = true, + IsDicomReUpload = archiveStudyCommand.AbandonStudyId != null, + IP = _userInfo.IP + }); + + _provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid); + + } + else + { + return ResponseOutput.NotOk("未完成该检查的归档"); + } + + + } + catch (Exception e) + { + + _logger.LogError(e.Message + e.StackTrace); + + _provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid); + + return ResponseOutput.NotOk(e.Message, ApiResponseCodeEnum.ProgramException); + + } + + return ResponseOutput.Ok(archiveResult); + + + + } + + + + + + /// + /// 上传临床数据 多文件 + /// + /// + /// + [HttpPost("ClinicalData/UploadVisitClinicalData/{trialId:guid}/{subjectVisitId:guid}")] + [DisableRequestSizeLimit] + [Authorize(Policy = IRaCISPolicy.CRC)] + public async Task UploadVisitClinicalData( Guid subjectVisitId, [FromServices] IRepository _repository) + { + await QCCommonVerify.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId); + + var sv = _repository.Where(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SiteId, t.SubjectId }).FirstOrDefault().IfNullThrowException(); + + + await FileUploadAsync(async (fileName) => + { + var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetClinicalDataPath(_hostEnvironment, fileName, sv.TrialId, sv.SiteId, sv.SubjectId, subjectVisitId); + + //插入临床pdf 路径 + await _repository.AddAsync(new PreviousPDF() { SubjectVisitId = subjectVisitId, Path = relativePath, FileName = fileRealName }); + + return serverFilePath; + }); + + await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + + } + + + + /// + /// 上传非Dicom 文件 支持压缩包 多文件上传 + /// + /// + /// + /// + /// + //[DisableRequestSizeLimit] + [RequestSizeLimit(1_073_741_824)] + [HttpPost("NoneDicomStudy/UploadNoneDicomFile/{trialId:guid}/{subjectVisitId:guid}/{noneDicomStudyId:guid}")] + [TypeFilter(typeof(TrialResourceFilter))] + [Authorize(Policy = IRaCISPolicy.CRC)] + public async Task UploadNoneDicomFile(IFormCollection formCollection, Guid subjectVisitId, Guid noneDicomStudyId, [FromServices] IRepository _repository) + { + var startTime = DateTime.Now; + + await QCCommonVerify.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId); + + var sv = (await _repository.Where(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SiteId, t.SubjectId }).FirstOrDefaultAsync()).IfNullThrowConvertException(); + + + await FileUploadAsync(async (fileName) => + { + + var (serverFilePath, relativePath, fileRealName) = FileStoreHelper.GetNoneDicomFilePath(_hostEnvironment, fileName, sv.TrialId, sv.SiteId, sv.SubjectId, subjectVisitId); + + await _repository.AddAsync(new NoneDicomStudyFile() { FileName = fileRealName, Path = relativePath, NoneDicomStudyId = noneDicomStudyId }); + + return serverFilePath; + }); + + + + // 上传非Dicom 后 将状态改为待提交 分为普通上传 和QC后重传 普通上传时才改为待提交 + await _repository.UpdatePartialFromQueryAsync(t => t.Id == subjectVisitId && t.SubmitState == SubmitStateEnum.None, u => new SubjectVisit() { SubmitState = SubmitStateEnum.ToSubmit }); + + var studyCode = await _repository.Where(t => t.Id == noneDicomStudyId).Select(t => t.StudyCode).FirstOrDefaultAsync(); + + await _repository.AddAsync(new StudyMonitor() + { + FileCount = formCollection.Files.Count, + FileSize = formCollection.Files.Sum(t => t.Length), + IsDicom = false, + IsDicomReUpload = false, + StudyId = noneDicomStudyId, + StudyCode = studyCode, + UploadStartTime = startTime, + UploadFinishedTime = DateTime.Now, + IP = _userInfo.IP, + TrialId = sv.TrialId, + SiteId = sv.SiteId, + SubjectId = sv.SubjectId, + SubjectVisitId = subjectVisitId, + }); + + await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + + + + /// + /// 一致性核查 excel上传 支持三种格式 + /// + /// + /// + [HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")] + [TypeFilter(typeof(TrialResourceFilter))] + [Authorize(Policy = IRaCISPolicy.PM_APM)] + public async Task UploadVisitCheckExcel(Guid trialId, + [FromServices] IMediator _mediator, + [FromServices] IRepository _repository + ) + { + + + var (serverFilePath, relativePath, fileName) = (string.Empty, string.Empty, string.Empty); + await FileUploadAsync(async (realFileName) => + { + fileName = realFileName; + + if (!fileName.EndsWith(".xlsx") && !fileName.EndsWith(".csv") && !fileName.EndsWith(".xls")) + { + throw new BusinessValidationFailedException("支持.xlsx、.xls、.csv格式的文件上传。"); + } + + (serverFilePath, relativePath) = FileStoreHelper.GetTrialCheckFilePath(_hostEnvironment, fileName, trialId); + + await _repository.AddAsync(new ConsistencyCheckFile() + { + TrialId = trialId, + CreateTime = DateTime.Now, + FileName = fileName, + FilePath = relativePath, + RelativePaths = relativePath, + CreateUserId = _userInfo.Id + }); + + return serverFilePath; + }); + + + + + var etcCheckList = new List(); + + + + #region MiniExcel 需要自己验证数据格式规范 + + //if (fileName.EndsWith(".csv")) + //{ + // //因为csv 需要加配置文件 不然都是null + // etcCheckList = MiniExcel.Query(filePath, null, configuration: config).ToList(); + //} + //else if (fileName.EndsWith(".xlsx")) + //{ + // + + + // etcCheckList = MiniExcel.Query(filePath).ToList(); + //} + + #endregion + + //Magicodes 支持自定义特性验证 + if (fileName.EndsWith(".xlsx")) + { + var Importer = new ExcelImporter(); + + var import = await Importer.Import(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(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(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(File.OpenRead(filePath)); + + //if (import.Exception != null) return ResponseOutput.NotOk(import.Exception.ToString()); + + //if (import.RowErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.RowErrors)); + + //if (import.TemplateErrors.Count > 0) return ResponseOutput.NotOk(JsonConvert.SerializeObject(import.TemplateErrors)); + + //etcCheckList = import.Data.ToList(); + #endregion + + } + //ExcelReaderFactory 需要自己验证数据 并且从固定列取数据 + else + { + //为了支持 xls 引入新的组件库 + using (var stream = System.IO.File.Open(serverFilePath, FileMode.Open, FileAccess.Read)) + { + // Auto-detect format, supports: + // - Binary Excel files (2.0-2003 format; *.xls) + // - OpenXml Excel files (2007 format; *.xlsx, *.xlsb) + using (var reader = ExcelReaderFactory.CreateReader(stream)) + { + + // 2. Use the AsDataSet extension method + var dateset = reader.AsDataSet(); + + foreach (DataRow col in dateset.Tables[0].Rows) + { + + etcCheckList.Add(new CheckViewModel() + { + SiteCode = col[0].ToString(), + SubjectCode = col[1].ToString(), + VisitName = col[2].ToString(), + StudyDate = col[3].ToString(), + Modality = col[4].ToString(), + }); + } + + etcCheckList.Remove(etcCheckList[0]); + + // The result of each spreadsheet is in result.Tables + } + } + } + + if (etcCheckList == null || etcCheckList.Count == 0) + { + return ResponseOutput.NotOk("请保证上传数据符合模板文件中的样式,且存在有效数据。"); + } + else + { + //处理Excel 有时只是清除某些行的数据 读取也会读到数据,只是数据是null 后面处理的时候转为字符串为报错 + etcCheckList.ForEach(t => + { + t.Modality = t.Modality ?? string.Empty; + t.SiteCode = t.SiteCode ?? string.Empty; + t.SubjectCode = t.SubjectCode ?? string.Empty; + t.VisitName = t.VisitName ?? string.Empty; + t.StudyDate = t.StudyDate ?? string.Empty; + }); + + var dt = DateTime.Now; + + etcCheckList = etcCheckList.Where(t => !(t.Modality == string.Empty && t.SiteCode == string.Empty && t.SubjectCode == string.Empty && t.VisitName == string.Empty && t.StudyDate == string.Empty && DateTime.TryParse(t.StudyDate, out dt))).ToList(); + + if (etcCheckList.Count == 0) + { + return ResponseOutput.NotOk("请保证上传数据符合模板文件中的样式,且存在有效数据。"); + } + + } + + + await _mediator.Send(new ConsistencyVerificationRequest() { ETCList = etcCheckList, TrialId = trialId }); + + return ResponseOutput.Ok(); + + } + } + + #endregion + + #region 医生文件上传下载 + + /// 医生文件上传下载 + [Route("file")] + [ApiExplorerSettings(GroupName = "Common")] + [ApiController] + public class FileController : UploadBaseController + { + public IMapper _mapper { get; set; } + public IUserInfo _userInfo { get; set; } + private readonly IMediator _mediator; + + private readonly IWebHostEnvironment _hostEnvironment; + + + + public FileController(IMapper mapper, IUserInfo userInfo, IMediator mediator, IWebHostEnvironment hostEnvironment) + { + _hostEnvironment = hostEnvironment; + _mediator = mediator; + _mapper = mapper; + _userInfo = userInfo; + } + + /// + /// 上传文件[FileUpload] + /// + /// 附件类型 + /// 医生Id + /// 返回文件信息 + [HttpPost, Route("UploadFile/{attachmentType}/{doctorId}")] + [DisableFormValueModelBinding] + [DisableRequestSizeLimit] + public async Task UploadOrdinaryFile(string attachmentType, Guid doctorId) + { + + return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetDoctorOrdinaryFilePath(_hostEnvironment, fileName, doctorId, attachmentType)); + + } + + + /// + /// 上传文件( 不是医生个人的文件)[FileUpload] + /// 例如:阅片章程等 + /// + /// 文件类型 + /// + + [HttpPost, Route("file/UploadNonDoctorFile/{type}")] + [DisableFormValueModelBinding] + [DisableRequestSizeLimit] + public async Task UploadNonDoctorFile(string type) + { + return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetNonDoctorFilePath(_hostEnvironment, fileName, type)); + } + + + /// + /// 下载多个医生的所有附件 + /// + /// + /// + + [HttpPost, Route("downloadDoctorAttachments")] + public async Task> DownloadAttachment(Guid[] doctorIds, [FromServices] IFileService _fileService) + { + + var path = await _fileService.CreateDoctorsAllAttachmentZip(doctorIds); + + return ResponseOutput.Ok(new UploadFileInfoDTO + { + FilePath = path, + FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) + + }); + } + + /// + /// 下载医生官方简历 + /// + /// + /// + /// + [HttpPost, Route("downloadOfficialCV/{language}")] + public async Task> DownloadOfficialResume(int language, Guid[] doctorIds, [FromServices] IFileService _fileService) + { + + var path = _fileService.CreateDoctorsAllAttachmentZip(doctorIds); + return ResponseOutput.Ok(new UploadFileInfoDTO + { + FilePath = await _fileService.CreateOfficialResumeZip(language, doctorIds), + FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) + }); + } + + /// + /// 下载指定医生的指定附件 + /// + /// 医生Id + /// 要下载的附件Id + /// + [HttpPost, Route("downloadByAttachmentId/{doctorId}")] + public async Task> DownloadAttachmentById(Guid doctorId, Guid[] attachmentIds, [FromServices] IFileService _fileService) + { + var path = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds); + return ResponseOutput.Ok(new UploadFileInfoDTO + { + FilePath = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds), + FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) + }); + } + + + + + [HttpPost, Route("enroll/downloadResume/{trialId:guid}/{language}")] + [TypeFilter(typeof(TrialResourceFilter))] + [AllowAnonymous] + public async Task> DownloadResume([FromServices] IFileService _fileService, int language, Guid trialId, Guid[] doctorIdArray) + { + var userId = Guid.Parse(User.FindFirst("id").Value); + var zipPath = await _fileService.CreateOfficialResumeZip(language, doctorIdArray); + + return ResponseOutput.Ok(zipPath); + } + } + #endregion + + + #region 项目 系统 基本文件 上传 下载 预览 + + [ApiExplorerSettings(GroupName = "Common")] + [ApiController] + public class UploadDownLoadController : UploadBaseController + { + public IMapper _mapper { get; set; } + public IUserInfo _userInfo { get; set; } + private readonly IMediator _mediator; + + private readonly IWebHostEnvironment _hostEnvironment; + + + + public UploadDownLoadController(IMapper mapper, IUserInfo userInfo, IMediator mediator, IWebHostEnvironment hostEnvironment) + { + _hostEnvironment = hostEnvironment; + _mediator = mediator; + _mapper = mapper; + _userInfo = userInfo; + } + + /// 缩略图 + [AllowAnonymous] + [HttpGet("Common/LocalFilePreview")] + public async Task LocalFilePreview(string relativePath) + { + + var _fileStorePath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, relativePath); + + var storePreviewPath = _fileStorePath + ".preview.jpeg"; + + if (!System.IO.File.Exists(storePreviewPath)) + { + ImageResizeHelper.ResizeSave(_fileStorePath, storePreviewPath); + } + + return new FileContentResult(await System.IO.File.ReadAllBytesAsync(storePreviewPath), "image/jpeg"); + + } + + + /// 通用文件下载 + [AllowAnonymous] + [HttpGet("CommonDocument/DownloadCommonDoc")] + public async Task DownloadCommonFile(string code, [FromServices] IRepository _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); + + } + + + /// + ///上传项目签名文档 + /// + /// + /// + [HttpPost("TrialDocument/UploadTrialDoc/{trialId:guid}")] + [DisableRequestSizeLimit] + [DisableFormValueModelBinding] + public async Task UploadTrialDoc(Guid trialId) + { + + return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetTrialSignDocPath(_hostEnvironment, trialId, fileName)); + + } + + /// + /// 上传系统签名文档 + /// + /// + [HttpPost("TrialDocument/UploadSystemDoc")] + [DisableRequestSizeLimit] + [DisableFormValueModelBinding] + public async Task UploadSysTemDoc() + { + + return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemSignDocPath(_hostEnvironment, fileName)); + + } + + + /// + /// 上传通用文档 比如一致性核查的 比如导出的excel 模板 + /// + /// + [HttpPost("CommonDocument/UploadCommonDoc")] + [DisableRequestSizeLimit] + [DisableFormValueModelBinding] + public async Task UploadCommonDoc() + { + + return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetCommonDocPath(_hostEnvironment, fileName)); + } + + + /// + /// 上传系统通知文档 + /// + /// + [HttpPost("SystemNotice/UploadSystemNoticeDoc")] + [DisableRequestSizeLimit] + [DisableFormValueModelBinding] + public async Task UploadSystemNoticeDoc() + { + + return await SingleFileUploadAsync((fileName) => FileStoreHelper.GetSystemNoticePath(_hostEnvironment, fileName)); + + } + + } + #endregion + +} diff --git a/IRaCIS.Core.API/IRaCIS.Core.API.xml b/IRaCIS.Core.API/IRaCIS.Core.API.xml index a4200f649..ab1c3a3f7 100644 --- a/IRaCIS.Core.API/IRaCIS.Core.API.xml +++ b/IRaCIS.Core.API/IRaCIS.Core.API.xml @@ -4,6 +4,34 @@ IRaCIS.Core.API + + + 主要处理 前端404等错误 全局业务异常已统一处理了,非业务错误会来到这里 + + + + + + + 医生基本信息 、工作信息 专业信息、审核状态 + + + + + 获取医生详情 + + + + + + + + + + + + 系统用户登录接口[New] + 添加实验项目-返回新增Id[AUTH] 新记录Id @@ -124,35 +152,117 @@ - + + 流式上传 直接返回 + + + 流式上传 通用封装 不返回任何数据,后续还有事情处理 + + + 流式上传 Dicom上传 需要返回文件数量 + + + Dicom 归档 + + + Dicom 归档 + + + + 上传临床数据 多文件 + + + + + + + 上传非Dicom 文件 支持压缩包 多文件上传 + + + + + + + + + 一致性核查 excel上传 支持三种格式 + + + + + + 医生文件上传下载 + + + + 上传文件[FileUpload] + + 附件类型 + 医生Id + 返回文件信息 + + + + 上传文件( 不是医生个人的文件)[FileUpload] + 例如:阅片章程等 + + 文件类型 + + + + + 下载多个医生的所有附件 + + + + + + + 下载医生官方简历 + + + + + + + + 下载指定医生的指定附件 + + 医生Id + 要下载的附件Id + + + + 缩略图 + + + 通用文件下载 + + + + 上传项目签名文档 + + + + + + + 上传系统签名文档 + + + + 上传通用文档 比如一致性核查的 比如导出的excel 模板 - + 上传系统通知文档 - - - 上传临床数据 - - - 1:DICOM DTF 2:非DIOM DTF 3: 受试者临床数据 - - - - - - 写文件导到磁盘 - - 流 - 文件保存路径 - - IPLimit限流 启动服务 @@ -166,99 +276,6 @@ 序列化成员 - - - 医生基本信息 、工作信息 专业信息、审核状态 - - - - - 获取医生详情 - - - - - - - - - - - - 系统用户登录接口[New] - - - - 文件上传 - - - - - 上传文件[FileUpload] - - 附件类型 - 医生Id - 返回文件信息 - - - - 上传文件( 不是医生个人的文件)[FileUpload] - 例如:阅片章程等 - - 文件类型 - - - - - 写文件导到磁盘 - - 流 - 文件保存路径 - - - - - 下载多个医生的所有附件 - - - - - - - 下载医生官方简历 - - - - - - - - 下载指定医生的指定附件 - - 医生Id - 要下载的附件Id - - - - - 流式上传 临时文件 - - - - - - 流式上传 非Dicom文件 - - - - - - Study - - - - 归档 - 为了前端 一段时间无操作,需要重新登陆 @@ -266,13 +283,6 @@ - - - 主要处理 前端404等错误 全局业务异常已统一处理了,非业务错误会来到这里 - - - - 对称可逆加密 diff --git a/IRaCIS.Core.API/_PipelineExtensions/IRacisHostFileStoreConfig.cs b/IRaCIS.Core.API/_PipelineExtensions/IRacisHostFileStoreConfig.cs index e5611c713..2c2cd11c4 100644 --- a/IRaCIS.Core.API/_PipelineExtensions/IRacisHostFileStoreConfig.cs +++ b/IRaCIS.Core.API/_PipelineExtensions/IRacisHostFileStoreConfig.cs @@ -32,10 +32,7 @@ namespace IRaCIS.Core.API var uploadPath = Path.Combine(Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).FullName, StaticData.UploadFileFolder); - //var dicomPath = Path.Combine(Directory.GetParent(env.ContentRootPath.TrimEnd('\\')).FullName, StaticData.TrialDataFolder); - - //var systemDataFolder = Path.Combine(Directory.GetParent(env.ContentRootPath.TrimEnd('\\')).FullName, StaticData.SystemDataFolder); - + if (!Directory.Exists(uploadPath)) { @@ -48,49 +45,9 @@ namespace IRaCIS.Core.API RequestPath = $"/{StaticData.UploadFileFolder}" }); - //if (!Directory.Exists(dicomPath)) - //{ - // Directory.CreateDirectory(dicomPath); - //} - - //if (!Directory.Exists(systemDataFolder)) - //{ - // Directory.CreateDirectory(systemDataFolder); - //} + - - - //app.UseStaticFiles(new StaticFileOptions - //{ - // FileProvider = new PhysicalFileProvider(dicomPath), - // RequestPath = $"/{StaticData.TrialDataFolder}" - //}); - - //app.UseStaticFiles(new StaticFileOptions - //{ - // FileProvider = new PhysicalFileProvider(systemDataFolder), - // RequestPath = $"/{StaticData.SystemDataFolder}" - //}); - - - #region 兼容之前的文档能查看 - - //var comonPathPath = Path.Combine(Directory.GetParent(env.ContentRootPath.TrimEnd('\\')).FullName, StaticData.CommonFileFolder); - - //var systemNoticePath = Path.Combine(Directory.GetParent(env.ContentRootPath.TrimEnd('\\')).FullName, StaticData.SystemNoticeFolder); - - //if (!Directory.Exists(comonPathPath)) - //{ - // Directory.CreateDirectory(comonPathPath); - //} - //app.UseStaticFiles(new StaticFileOptions - //{ - // FileProvider = new PhysicalFileProvider(comonPathPath), - // RequestPath = $"/{StaticData.CommonFileFolder}" - //}); - - #endregion #endregion diff --git a/IRaCIS.Core.Application/Helper/ExcelExportHelper.cs b/IRaCIS.Core.Application/Helper/ExcelExportHelper.cs index fff4c84d0..bb01342af 100644 --- a/IRaCIS.Core.Application/Helper/ExcelExportHelper.cs +++ b/IRaCIS.Core.Application/Helper/ExcelExportHelper.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Hosting; +using IRaCIS.Core.Application.Helper; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using MiniExcelLibs; @@ -9,24 +10,12 @@ public static class ExcelExportHelper //MiniExcel_Export public static async Task DataExportAsync(string code, object data, string exportFileNamePrefix, IRepository _commonDocumentRepository, IWebHostEnvironment _hostEnvironment) { - var doc = _commonDocumentRepository.AsQueryable(true).FirstOrDefault(t => t.Code == code); - if (doc == null) - { - throw new Exception("程序没有找到对应的数据模板文件,请联系系统运维人员。"); - } + var (physicalPath, fileNmae) = await FileStoreHelper.GetCommonDocPhysicalFilePathAsync(_hostEnvironment, _commonDocumentRepository, code); - var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\'))?.FullName; - - var filePath = Path.Combine(rootPath, doc.Path.Trim('/')); - - if (!System.IO.File.Exists(filePath)) - { - throw new Exception("数据模板文件存储路径上未找对应文件,请联系系统运维人员。"); - } //模板路径 - var tplPath = filePath; + var tplPath = physicalPath; #region MiniExcel @@ -38,7 +27,7 @@ public static class ExcelExportHelper return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { - FileDownloadName = $"{exportFileNamePrefix}_{doc.Name.Substring(0,doc.Name.LastIndexOf('.'))}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx" + FileDownloadName = $"{exportFileNamePrefix}_{fileNmae.Substring(0, fileNmae.LastIndexOf('.'))}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx" }; #endregion diff --git a/IRaCIS.Core.Application/Helper/FileStoreHelper.cs b/IRaCIS.Core.Application/Helper/FileStoreHelper.cs index 50bb7d075..f6a275a30 100644 --- a/IRaCIS.Core.Application/Helper/FileStoreHelper.cs +++ b/IRaCIS.Core.Application/Helper/FileStoreHelper.cs @@ -1,6 +1,7 @@  using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; using Microsoft.AspNetCore.Hosting; namespace IRaCIS.Core.Application.Helper; @@ -9,7 +10,7 @@ public static class FileStoreHelper { - + //处理文件名 压缩包,或者目录类的 会带上相对路径 public static (string TrustedFileNameForFileStorage, string RealName) GetStoreFileName(string fileName) { @@ -29,17 +30,273 @@ public static class FileStoreHelper return (trustedFileNameForFileStorage, fileName); } - - public static string GetIRaCISRootDataFolder(IWebHostEnvironment _hostEnvironment) + //API vue 部署目录 + public static string GetIRaCISRootPath(IWebHostEnvironment _hostEnvironment) { - var root = (Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\'))).IfNullThrowException().FullName; + var rootPath = (Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\'))).IfNullThrowException().FullName; + return rootPath; + + } - var rootFolder =Path.Combine(root, StaticData.IRaCISDataFolder) ; + //获取环境存储根目录 + public static string GetIRaCISRootDataFolder(IWebHostEnvironment _hostEnvironment) + { + var rootPath = GetIRaCISRootPath(_hostEnvironment); + var rootFolder = Path.Combine(rootPath, StaticData.IRaCISDataFolder); return rootFolder; } + //根据相对路径 获取具体文件物理地址 + public static string GetPhysicalFilePath(IWebHostEnvironment _hostEnvironment, string relativePath) + { + var rootPath = GetIRaCISRootPath(_hostEnvironment); + + var physicalFilePath = Path.Combine(rootPath, relativePath.Trim('/')); + + return physicalFilePath; + } + + + + //通过编码获取通用文档具体物理路径 + + public static async Task<(string PhysicalPath, string FileName)> GetCommonDocPhysicalFilePathAsync(IWebHostEnvironment _hostEnvironment, IRepository _commonDocumentRepository, string code) + { + var doc = await _commonDocumentRepository.FirstOrDefaultAsync(t => t.Code == code); + + if (doc == null) + { + throw new BusinessValidationFailedException("数据库没有找到对应的数据模板文件,请联系系统运维人员。"); + } + + var filePath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, doc.Path); + + if (!System.IO.File.Exists(filePath)) + { + throw new BusinessValidationFailedException("数据模板文件存储路径上未找对应文件,请联系系统运维人员。"); + } + + return (filePath, doc.Name.Trim('/')); + } + + + /// + /// 写文件导到磁盘 + /// + /// 流 + /// 文件保存路径 + /// + public static async Task WriteFileAsync(System.IO.Stream stream, string path) + { + const int FILE_WRITE_SIZE = 84975;//写出缓冲区大小 + int writeCount = 0; + using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write, FILE_WRITE_SIZE, true)) + { + byte[] byteArr = new byte[FILE_WRITE_SIZE]; + int readCount = 0; + while ((readCount = await stream.ReadAsync(byteArr, 0, byteArr.Length)) > 0) + { + await fileStream.WriteAsync(byteArr, 0, readCount); + writeCount += readCount; + } + } + return writeCount; + } + + // 获取项目签名文档存放路径 + + public static (string PhysicalPath, string RelativePath) GetTrialSignDocPath(IWebHostEnvironment _hostEnvironment, Guid trialId, string fileName) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + //文件类型路径处理 + var uploadFolderPath = Path.Combine(rootPath, StaticData.TrialDataFolder, trialId.ToString(), StaticData.SignDocumentFolder); + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + var relativePath = $"/{StaticData.IRaCISDataFolder}/{StaticData.TrialDataFolder}/{trialId}/{StaticData.SignDocumentFolder}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath); + } + + // 获取系统签名文档存放路径 + public static (string PhysicalPath, string RelativePath) GetSystemSignDocPath(IWebHostEnvironment _hostEnvironment, string fileName) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + + //文件类型路径处理 + var uploadFolderPath = Path.Combine(rootPath, StaticData.SystemDataFolder, StaticData.SignDocumentFolder); + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + var relativePath = $"/{StaticData.IRaCISDataFolder}/{StaticData.SystemDataFolder}/{ StaticData.SignDocumentFolder}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath); + } + + // 获取通用文档存放路径(excel模板 ) + + public static (string PhysicalPath, string RelativePath) GetCommonDocPath(IWebHostEnvironment _hostEnvironment, string fileName) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + //文件类型路径处理 + var uploadFolderPath = Path.Combine(rootPath, StaticData.SystemDataFolder, StaticData.DataTemplate); + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + var relativePath = $"/{StaticData.IRaCISDataFolder}/{StaticData.SystemDataFolder}/{StaticData.DataTemplate}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath); + } + + //获取系统通知文档存放路径 + + public static (string PhysicalPath, string RelativePath) GetSystemNoticePath(IWebHostEnvironment _hostEnvironment, string fileName) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + //文件类型路径处理 + var uploadFolderPath = Path.Combine(rootPath, StaticData.SystemDataFolder, StaticData.NoticeAttachment); + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + + var relativePath = $"/{StaticData.IRaCISDataFolder}/{StaticData.SystemDataFolder}/{StaticData.NoticeAttachment}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath); + } + + // 获取一致性核查路径 + public static (string PhysicalPath, string RelativePath) GetTrialCheckFilePath(IWebHostEnvironment _hostEnvironment, string fileName,Guid trialId) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + //上传根路径 + string uploadFolderPath = Path.Combine(rootPath, StaticData.TrialDataFolder, trialId.ToString(), "CheckExcel"); + + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + + //存放核对表 + var (trustedFileNameForFileStorage, realFileName) = FileStoreHelper.GetStoreFileName(fileName); + + + var relativePath = $"/{StaticData.IRaCISDataFolder}/{StaticData.TrialDataFolder}/{trialId}/CheckExcel/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath); + } + + //获取临床数据存放路径 + public static (string PhysicalPath, string RelativePath,string FileRealName) GetClinicalDataPath(IWebHostEnvironment _hostEnvironment, string fileName,Guid trialId,Guid siteId,Guid subjectId,Guid subjectVisitId) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + string uploadFolderPath = Path.Combine(rootPath, StaticData.TrialDataFolder, trialId.ToString(),siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString(), StaticData.TreatmenthistoryFolder); + + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + var relativePath = $"/{StaticData.IRaCISDataFolder}/{StaticData.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{StaticData.TreatmenthistoryFolder}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath, fileRealName); + + } + + //获取非dicom文件存放路径 + public static (string PhysicalPath, string RelativePath, string FileRealName) GetNoneDicomFilePath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid siteId, Guid subjectId, Guid subjectVisitId) + { + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + string uploadFolderPath = Path.Combine(rootPath, StaticData.TrialDataFolder, trialId.ToString(), siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString(), StaticData.NoneDicomFolder); + + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + var relativePath = $"/{StaticData.IRaCISDataFolder}/{StaticData.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{StaticData.NoneDicomFolder}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath, fileRealName); + } + + + + + // 获取医生通用文件存放路径 + + public static (string PhysicalPath, string RelativePath) GetDoctorOrdinaryFilePath(IWebHostEnvironment _hostEnvironment, string fileName,Guid doctorId,string attachmentType) + { + + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + //文件类型路径处理 + var uploadFolderPath = Path.Combine(rootPath, "UploadFile", doctorId.ToString(), attachmentType); + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + + var relativePath = $"/{StaticData.IRaCISDataFolder}/UploadFile/{doctorId}/{attachmentType}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath); + } + + public static (string PhysicalPath, string RelativePath) GetNonDoctorFilePath(IWebHostEnvironment _hostEnvironment, string fileName, string attachmentType) + { + + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + //文件类型路径处理 + var uploadFolderPath = Path.Combine(rootPath, "UploadFile", attachmentType); + if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); + + + var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); + + + + var relativePath = $"/{StaticData.IRaCISDataFolder}/UploadFile/{attachmentType}/{trustedFileNameForFileStorage}"; + + var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); + + return (serverFilePath, relativePath); + } + + + } diff --git a/IRaCIS.Core.Application/Helper/SendEmailHelper.cs b/IRaCIS.Core.Application/Helper/SendEmailHelper.cs index 1fc71d559..a1d1a1964 100644 --- a/IRaCIS.Core.Application/Helper/SendEmailHelper.cs +++ b/IRaCIS.Core.Application/Helper/SendEmailHelper.cs @@ -8,7 +8,6 @@ namespace IRaCIS.Core.Application.Helper; public static class SendEmailHelper { - public static async Task SendEmailAsync(MimeMessage messageToSend, EventHandler? messageSentSuccess = null) { using (var smtp = new MailKit.Net.Smtp.SmtpClient()) diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml index 401826e13..be2734983 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml @@ -422,6 +422,14 @@ + + + 写文件导到磁盘 + + 流 + 文件保存路径 + + CommonDocumentView 列表视图模型 @@ -678,15 +686,6 @@ 受试者临床信息 - - - 上传临床数据 - - - - - - 获取访视+受试者级别的数据 @@ -830,15 +829,6 @@ - - - 上传非Dicom 文件 支持压缩包 - - - - - - 系统QC 问题管理 @@ -1556,14 +1546,6 @@ - - - 一致性核查 excel上传 支持三种格式 - - - - - 添加或者更新 QC核对问题列表 两个人不能同时操作,就算意外进去了,提交数据,也不会覆盖前一个人数据, 后台已经做好判断 @@ -1751,19 +1733,6 @@ - - - 签名认证 + - - - - - - 业务接口操作成功后, 让签署数据生效 - - - - 添加访视计划 要给改项目下的所有Subject 添加该访视 diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IStudyService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IStudyService.cs index 688d155ed..ffc4dbeae 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IStudyService.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/Interface/IStudyService.cs @@ -17,7 +17,7 @@ namespace IRaCIS.Application.Interfaces DicomTrialSiteSubjectInfo GetSaveToDicomInfo(Guid subjctVisitId); - void GetHasUploadSeriesAndInstance(Guid studyId, ref List seriesInstanceUidList, ref List instanceUidList); + (List SeriesInstanceUid, List SopInstanceUid) GetHasUploadSeriesAndInstance(Guid studyId); void UploadOrReUploadNeedTodo(ArchiveStudyCommand archiveStudyCommand, List archiveStudyIds, ref DicomArchiveResult reusult, StudyMonitor monitor); diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs index a3acff7d3..2712a99dc 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs @@ -26,7 +26,7 @@ namespace IRaCIS.Application.Services private static string _fileStorePath = string.Empty; private readonly IRepository _dicomStudyMonitorRepository; - + @@ -44,7 +44,7 @@ namespace IRaCIS.Application.Services { _dicomStudyMonitorRepository = dicomStudyMonitorRepository; - + _userInfo = userInfo; _studyRepository = studyRepository; @@ -72,14 +72,16 @@ namespace IRaCIS.Application.Services return info; } - public void GetHasUploadSeriesAndInstance(Guid studyId, ref List seriesInstanceUidList, ref List sopInstanceUidList) + public (List SeriesInstanceUid, List SopInstanceUid) GetHasUploadSeriesAndInstance(Guid studyId) { - seriesInstanceUidList = _dicomSeriesRepository.Where(t => t.StudyId == studyId).Select(t => t.SeriesInstanceUid).ToList(); + var seriesInstanceUidList = _dicomSeriesRepository.Where(t => t.StudyId == studyId).Select(t => t.SeriesInstanceUid).ToList(); - sopInstanceUidList = _dicomInstanceRepository.Where(t => t.StudyId == studyId).Select(t => t.SopInstanceUid).ToList(); + var sopInstanceUidList = _dicomInstanceRepository.Where(t => t.StudyId == studyId).Select(t => t.SopInstanceUid).ToList(); + + return (seriesInstanceUidList, sopInstanceUidList); } - public void UploadOrReUploadNeedTodo(ArchiveStudyCommand archiveStudyCommand, List archiveStudyIds, ref DicomArchiveResult result,StudyMonitor monitor) + public void UploadOrReUploadNeedTodo(ArchiveStudyCommand archiveStudyCommand, List archiveStudyIds, ref DicomArchiveResult result, StudyMonitor monitor) { @@ -150,7 +152,7 @@ namespace IRaCIS.Application.Services _ = _dicomStudyMonitorRepository.AddAsync(monitor).Result; _ = _studyRepository.SaveChangesAsync().Result; - + diff --git a/IRaCIS.Core.Application/Service/Inspection/InspectionService.cs b/IRaCIS.Core.Application/Service/Inspection/InspectionService.cs index 5da81713e..94c7c8eaa 100644 --- a/IRaCIS.Core.Application/Service/Inspection/InspectionService.cs +++ b/IRaCIS.Core.Application/Service/Inspection/InspectionService.cs @@ -4,15 +4,8 @@ using IRaCIS.Core.Application.Service.Inspection.DTO; using IRaCIS.Core.Application.Service.Inspection.Interface; using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Infrastructure; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Linq; using Panda.DynamicWebApi.Attributes; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; + namespace IRaCIS.Core.Application.Service.Inspection { diff --git a/IRaCIS.Core.Application/Service/QC/ClinicalDataService.cs b/IRaCIS.Core.Application/Service/QC/ClinicalDataService.cs index 1fd1f1c07..4d0b274d1 100644 --- a/IRaCIS.Core.Application/Service/QC/ClinicalDataService.cs +++ b/IRaCIS.Core.Application/Service/QC/ClinicalDataService.cs @@ -4,14 +4,8 @@ // 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 //-------------------------------------------------------------------- using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Hosting; -using IRaCIS.Core.Domain.Share; -using IRaCIS.Core.Application.Helper; -using IRaCIS.Core.Infrastructure; using IRaCIS.Core.Application.Service.Verify; -using Microsoft.AspNetCore.Authorization; -using IRaCIS.Core.Application.Auth; + namespace IRaCIS.Core.Application.Contracts { @@ -44,63 +38,6 @@ namespace IRaCIS.Core.Application.Contracts - /// - /// 上传临床数据 - /// - /// - /// - /// - /// - [HttpPost("{trialId:guid}/{subjectVisitId:guid}")] - [DisableRequestSizeLimit] - [Authorize(Policy = IRaCISPolicy.CRC)] - public async Task UploadVisitClinicalData(IFormCollection formCollection, Guid subjectVisitId, [FromServices] IWebHostEnvironment _hostEnvironment) - { - await QCCommonVerify.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId); - - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - //上传根路径 - var _fileStorePath = Path.Combine(rootPath, StaticData.TrialDataFolder); - - var sv = _repository.Where(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SiteId, t.SubjectId }).FirstOrDefault().IfNullThrowException(); - - string uploadFolderPath = Path.Combine(_fileStorePath, sv.TrialId.ToString(), - sv.SiteId.ToString(), sv.SubjectId.ToString(), subjectVisitId.ToString(), StaticData.TreatmenthistoryFolder); - - if (!Directory.Exists(uploadFolderPath)) - { - Directory.CreateDirectory(uploadFolderPath); - } - - foreach (IFormFile file in formCollection.Files) - { - - var (trustedFileNameForFileStorage, fileName) = FileStoreHelper.GetStoreFileName(file.FileName); - - var relativePath = $"/{StaticData.IRaCISDataFolder}/{StaticData.TrialDataFolder}/{sv.TrialId}/{sv.SiteId}/{sv.SubjectId}/{subjectVisitId}/{StaticData.TreatmenthistoryFolder}/{trustedFileNameForFileStorage}"; - - var filePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - using (FileStream fs = System.IO.File.Create(filePath)) - { - await file.CopyToAsync(fs); - await fs.FlushAsync(); - } - - //插入临床pdf 路径 - await _previousPdfRepository.AddAsync(new PreviousPDF() { SubjectVisitId = subjectVisitId, Path = relativePath, FileName = fileName }); - } - - - await _repository.SaveChangesAsync(); - - return ResponseOutput.Ok(); - - - } - - /// /// 获取访视+受试者级别的数据 diff --git a/IRaCIS.Core.Application/Service/QC/Interface/IClinicalDataService.cs b/IRaCIS.Core.Application/Service/QC/Interface/IClinicalDataService.cs index ac02d5d77..808940ca5 100644 --- a/IRaCIS.Core.Application/Service/QC/Interface/IClinicalDataService.cs +++ b/IRaCIS.Core.Application/Service/QC/Interface/IClinicalDataService.cs @@ -25,6 +25,6 @@ namespace IRaCIS.Core.Application.Contracts Task> GetPreviousPDFList(Guid subjectVisitId); Task> GetPreviousSurgeryList(PreviousSurgeryQuery queryPreviousSurgery); Task GetSubjectVisitClinicalData(Guid subjectVisitId); - Task UploadVisitClinicalData(IFormCollection formCollection, Guid subjectVisitId, [FromServices] IWebHostEnvironment _hostEnvironment); + //Task UploadVisitClinicalData(IFormCollection formCollection, Guid subjectVisitId, [FromServices] IWebHostEnvironment _hostEnvironment); } } \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/QC/Interface/INoneDicomStudyService.cs b/IRaCIS.Core.Application/Service/QC/Interface/INoneDicomStudyService.cs index 459b6c5e9..a90a92c94 100644 --- a/IRaCIS.Core.Application/Service/QC/Interface/INoneDicomStudyService.cs +++ b/IRaCIS.Core.Application/Service/QC/Interface/INoneDicomStudyService.cs @@ -18,7 +18,7 @@ namespace IRaCIS.Core.Application.Contracts Task> GetNoneDicomStudyFileList(Guid noneDicomStudyId); Task> GetNoneDicomStudyList(Guid subjectVisitId, Guid? sujectVisitId = null); Task> GetVisitNoneDicomStudyFileList(Guid subjectVisitId); - Task UploadNoneDicomFile(IFormCollection formCollection, Guid subjectVisitId, Guid noneDicomStudyId); + //Task UploadNoneDicomFile(IFormCollection formCollection, Guid subjectVisitId, Guid noneDicomStudyId); diff --git a/IRaCIS.Core.Application/Service/QC/Interface/IQCOperationService.cs b/IRaCIS.Core.Application/Service/QC/Interface/IQCOperationService.cs index eb90d2e04..ad9244d10 100644 --- a/IRaCIS.Core.Application/Service/QC/Interface/IQCOperationService.cs +++ b/IRaCIS.Core.Application/Service/QC/Interface/IQCOperationService.cs @@ -36,7 +36,7 @@ namespace IRaCIS.Core.Application.Image.QA Task SetVisitUrgent(Guid trialId, Guid subjectVisitId, bool setOrCancel); Task UpdateModality(UpdateModalityCommand updateModalityCommand); Task UpdateSubjectAndSVInfo(UploadSubjectAndVisitCommand command); - Task UploadVisitCheckExcel(IFormFile file, Guid trialId); + //Task UploadVisitCheckExcel(IFormFile file, Guid trialId); Task VerifyCanQCPassedOrFailed(Guid subjectVisitId); diff --git a/IRaCIS.Core.Application/Service/QC/NoneDicomStudyService.cs b/IRaCIS.Core.Application/Service/QC/NoneDicomStudyService.cs index d9b1360da..69e811038 100644 --- a/IRaCIS.Core.Application/Service/QC/NoneDicomStudyService.cs +++ b/IRaCIS.Core.Application/Service/QC/NoneDicomStudyService.cs @@ -4,20 +4,10 @@ // 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 //-------------------------------------------------------------------- using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Http; -using IRaCIS.Core.Domain.Share; -using SharpCompress.Archives; using IRaCIS.Core.Application.Filter; -using IRaCIS.Core.Application.Service.Inspection.Interface; using Nito.AsyncEx; -using IRaCIS.Application.Interfaces; -using IRaCIS.Core.Infrastructure; -using Microsoft.Extensions.Logging; using System.ComponentModel.DataAnnotations; -using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.Service.Verify; -using Microsoft.AspNetCore.Authorization; -using IRaCIS.Core.Application.Auth; namespace IRaCIS.Core.Application.Contracts { @@ -28,39 +18,21 @@ namespace IRaCIS.Core.Application.Contracts public class NoneDicomStudyService : BaseService, INoneDicomStudyService { private readonly IRepository _noneDicomStudyRepository; - private readonly IHttpContextAccessor _httpContext; - private readonly IDictionaryService _dictionaryService; - private readonly IInspectionService _inspectionService; - private readonly IRepository _studyMonitorRepository; private readonly IRepository _noneDicomStudyFileRepository; - private readonly IRepository _subjectVisitRepository; private readonly AsyncLock _mutex = new AsyncLock(); - private readonly ILogger _logger; - public NoneDicomStudyService(IRepository noneDicomStudyRepository, - IHttpContextAccessor httpContext, - IDictionaryService dictionaryService, - IInspectionService inspectionService, - IRepository studyMonitorRepository, - IRepository noneDicomStudyFileRepository, IRepository subjectVisitRepository, ILogger logger) + + IRepository noneDicomStudyFileRepository) { _noneDicomStudyRepository = noneDicomStudyRepository; - - this._httpContext = httpContext; - this._dictionaryService = dictionaryService; - this._inspectionService = inspectionService; - this._studyMonitorRepository = studyMonitorRepository; + _noneDicomStudyFileRepository = noneDicomStudyFileRepository; - this._subjectVisitRepository = subjectVisitRepository; - _logger = logger; } - - [HttpGet] public async Task> GetNoneDicomStudyList( [FromQuery,NotDefault] Guid subjectVisitId,Guid? nonedicomStudyId) { @@ -171,116 +143,5 @@ namespace IRaCIS.Core.Application.Contracts - /// - /// 上传非Dicom 文件 支持压缩包 - /// - /// - /// - /// - /// - //[DisableRequestSizeLimit] - [RequestSizeLimit(1_073_741_824)] - [HttpPost("{trialId:guid}/{subjectVisitId:guid}/{noneDicomStudyId:guid}")] - [TypeFilter(typeof(TrialResourceFilter))] - [Authorize(Policy = IRaCISPolicy.CRC)] - public async Task UploadNoneDicomFile(IFormCollection formCollection, Guid subjectVisitId, Guid noneDicomStudyId) - { - await QCCommonVerify.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId); - - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - //上传根路径 - var _fileStorePath = Path.Combine(rootPath, StaticData.TrialDataFolder); - - var sv = (await _repository.Where(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SiteId, t.SubjectId }).FirstOrDefaultAsync()).IfNullThrowConvertException(); - - string uploadFolderPath = Path.Combine(_fileStorePath, sv.TrialId.ToString(), sv.SiteId.ToString(), sv.SubjectId.ToString(), subjectVisitId.ToString(), StaticData.NoneDicomFolder); - - if (!Directory.Exists(uploadFolderPath)) - { - Directory.CreateDirectory(uploadFolderPath); - } - - var startTime = DateTime.Now; - - foreach (IFormFile file in formCollection.Files) - { - if (file.FileName.Contains(".Zip", StringComparison.OrdinalIgnoreCase) || file.FileName.Contains(".rar", StringComparison.OrdinalIgnoreCase)) - { - var archive = ArchiveFactory.Open(file.OpenReadStream()); - - foreach (var entry in archive.Entries) - { - if (!entry.IsDirectory) - { - DealCompressFile(entry, sv, subjectVisitId, noneDicomStudyId, uploadFolderPath); - } - } - } - else - { - - - - - var (trustedFileNameForFileStorage, fileName) = FileStoreHelper.GetStoreFileName(file.FileName); - - var relativePath = $"/{StaticData.TrialDataFolder}/{sv.TrialId}/{sv.SiteId}/{sv.SubjectId}/{subjectVisitId}/{StaticData.NoneDicomFolder}/{trustedFileNameForFileStorage}"; - - var filePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - using (FileStream fs = System.IO.File.Create(filePath)) - { - await file.CopyToAsync(fs); - await fs.FlushAsync(); - } - - await _noneDicomStudyFileRepository.AddAsync(new NoneDicomStudyFile() { FileName = fileName, Path = relativePath, NoneDicomStudyId = noneDicomStudyId }); - - } - } - - // 上传非Dicom 后 将状态改为待提交 分为普通上传 和QC后重传 普通上传时才改为待提交 - await _subjectVisitRepository.UpdatePartialFromQueryAsync(t => t.Id == subjectVisitId && t.SubmitState == SubmitStateEnum.None, u => new SubjectVisit() { SubmitState = SubmitStateEnum.ToSubmit }); - - var studyCode = await _noneDicomStudyRepository.Where(t => t.Id == noneDicomStudyId).Select(t => t.StudyCode).FirstOrDefaultAsync(); - - await _studyMonitorRepository.AddAsync(new StudyMonitor() - { - FileCount = formCollection.Files.Count, - FileSize = formCollection.Files.Sum(t => t.Length), - IsDicom = false, - IsDicomReUpload = false, - StudyId = noneDicomStudyId, - StudyCode = studyCode, - UploadStartTime = startTime, - UploadFinishedTime = DateTime.Now, - IP = _userInfo.IP, - TrialId = sv.TrialId, - SiteId = sv.SiteId, - SubjectId = sv.SubjectId, - SubjectVisitId = subjectVisitId, - }); - - await _noneDicomStudyFileRepository.SaveChangesAsync(); - - return ResponseOutput.Ok(/*list*/); - } - - - - private async void DealCompressFile(IArchiveEntry entry, dynamic sv, Guid subjectVisitId, Guid noneDicomStudyId, string uploadFolderPath) - { - var (trustedFileNameForFileStorage, fileName) = FileStoreHelper.GetStoreFileName(entry.Key); - - var relativePath = $"/{StaticData.IRaCISDataFolder}/{StaticData.TrialDataFolder}/{sv.TrialId}/{sv.SiteId}/{sv.SubjectId}/{subjectVisitId}/{StaticData.NoneDicomFolder}/{trustedFileNameForFileStorage}"; - - var filePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - entry.WriteToFile(filePath); - - await _noneDicomStudyFileRepository.AddAsync(new NoneDicomStudyFile() { FileName = fileName, Path = relativePath, NoneDicomStudyId = noneDicomStudyId }); - } - } } diff --git a/IRaCIS.Core.Application/Service/QC/QCOperationService.cs b/IRaCIS.Core.Application/Service/QC/QCOperationService.cs index efa337b55..93f7ce8b1 100644 --- a/IRaCIS.Core.Application/Service/QC/QCOperationService.cs +++ b/IRaCIS.Core.Application/Service/QC/QCOperationService.cs @@ -1,28 +1,18 @@ -using Hangfire; -using IRaCIS.Core.Application.MediatR.CommandAndQueries; -using IRaCIS.Core.Application.BackGroundJob; -using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.Contracts.DTO; using IRaCIS.Core.Application.Filter; using IRaCIS.Core.Domain.Share; using MediatR; using Microsoft.AspNetCore.Mvc; -using System.Net.Http.Headers; -using Microsoft.AspNetCore.Http; -using MiniExcelLibs; -using ExcelDataReader; -using System.Text; using System.Data; using Microsoft.AspNetCore.Authorization; using WinSCP; -using Magicodes.ExporterAndImporter.Excel; using Newtonsoft.Json; using IRaCIS.Core.Infrastructure; using IRaCIS.Core.Application.Service.Inspection.DTO; using Nito.AsyncEx; using System.ComponentModel.DataAnnotations; using IRaCIS.Core.Application.Auth; -using IRaCIS.Core.Application.Helper; namespace IRaCIS.Core.Application.Image.QA { @@ -39,16 +29,12 @@ namespace IRaCIS.Core.Application.Image.QA private readonly IRepository _qCChallengeDialogrepository; private readonly IRepository _checkChallengeDialogrepository; private readonly IRepository _trialRepository; - private readonly IRepository _consistencyCheckFileRepository; - private readonly IMediator _mediator; private readonly AsyncLock _mutex = new AsyncLock(); public QCOperationService(DicomFileStoreHelper dicomFileStoreHelper, IRepository subjectVisitRepository, IRepository qcChallengeRepository, IRepository trialRepository, - IRepository consistencyCheckFileRepository, - IMediator mediator, IRepository dicomStudyRepository, IRepository dicomSeriesrepository, IRepository subjectRepository, @@ -64,9 +50,7 @@ namespace IRaCIS.Core.Application.Image.QA this._subjectRepository = subjectRepository; this._qCChallengeDialogrepository = qCChallengeDialogrepository; this._checkChallengeDialogrepository = checkChallengeDialogrepository; - _mediator = mediator; _trialRepository = trialRepository; - this._consistencyCheckFileRepository = consistencyCheckFileRepository; } #region QC质疑 以及回复 关闭 @@ -519,229 +503,6 @@ namespace IRaCIS.Core.Application.Image.QA } - - /// - /// 一致性核查 excel上传 支持三种格式 - /// - /// - /// - /// - [HttpPost("{trialId:guid}")] - [TypeFilter(typeof(TrialResourceFilter))] - [Authorize(Policy = IRaCISPolicy.PM_APM)] - public async Task UploadVisitCheckExcel(IFormFile file, Guid trialId) - { - //if (_userInfo.UserTypeEnumInt != (int)UserTypeEnum.ProjectManager && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.APM) - //{ - // return ResponseOutput.NotOk("只有PM/APM具有操作权限!"); - //} - - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.ToString().Trim('"').ToLower(); - if (!fileName.EndsWith(".xlsx") && !fileName.EndsWith(".csv") && !fileName.EndsWith(".xls")) - { - return ResponseOutput.NotOk("支持.xlsx、.xls、.csv格式的文件上传。"); - } - - //上传根路径 - string uploadFolderPath = Path.Combine(rootPath,StaticData.TrialDataFolder, trialId.ToString(), "CheckExcel"); - - if (!Directory.Exists(uploadFolderPath)) - { - Directory.CreateDirectory(uploadFolderPath); - } - - //存放核对表 - var (trustedFileNameForFileStorage, realFileName) = FileStoreHelper.GetStoreFileName(fileName); - - var filePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - - - using (FileStream fs = System.IO.File.Create(filePath)) - { - await file.CopyToAsync(fs); - await fs.FlushAsync(); - } - - //获取Excel表内容 - var config = new MiniExcelLibs.Csv.CsvConfiguration() - { - StreamReaderFunc = (stream) => new StreamReader(stream, Encoding.GetEncoding("gb2312")) - }; - - var etcCheckList = new List(); - - - - #region MiniExcel 需要自己验证数据格式规范 - - //if (fileName.EndsWith(".csv")) - //{ - // //因为csv 需要加配置文件 不然都是null - // etcCheckList = MiniExcel.Query(filePath, null, configuration: config).ToList(); - //} - //else if (fileName.EndsWith(".xlsx")) - //{ - // - - - // etcCheckList = MiniExcel.Query(filePath).ToList(); - //} - - #endregion - - //Magicodes 支持自定义特性验证 - if (fileName.EndsWith(".xlsx")) - { - var Importer = new ExcelImporter(); - - var import = await Importer.Import(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(); - } - else if (fileName.EndsWith(".csv")) - { - #region 临时方案 MiniExcel读取 然后保存为xlsx 再用 Magicodes验证数据 - - //因为csv 需要加配置文件 不然都是null - etcCheckList = MiniExcel.Query(filePath, null, configuration: config).ToList(); - - var csVToXlsxPath = Path.Combine(uploadFolderPath, DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss") + Path.GetFileNameWithoutExtension(fileName) + ".xlsx"); - - await MiniExcel.SaveAsAsync(csVToXlsxPath, etcCheckList, excelType: ExcelType.XLSX); - - - var Importer = new ExcelImporter(); - - var import = await Importer.Import(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(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(filePath, 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 - } - } - } - - - //etcCheckList = etcCheckList.Where(t=>) - - if (etcCheckList == null || etcCheckList.Count == 0) - { - return ResponseOutput.NotOk("请保证上传数据符合模板文件中的样式,且存在有效数据。"); - } - else - { - //处理Excel 有时只是清除某些行的数据 读取也会读到数据,只是数据是null 后面处理的时候转为字符串为报错 - etcCheckList.ForEach(t => - { - t.Modality = t.Modality ?? string.Empty; - t.SiteCode = t.SiteCode ?? string.Empty; - t.SubjectCode = t.SubjectCode ?? string.Empty; - t.VisitName = t.VisitName ?? string.Empty; - t.StudyDate = t.StudyDate ?? string.Empty; - }); - - var dt = DateTime.Now; - - etcCheckList = etcCheckList.Where(t => !(t.Modality == string.Empty && t.SiteCode == string.Empty && t.SubjectCode == string.Empty && t.VisitName == string.Empty && t.StudyDate == string.Empty && DateTime.TryParse(t.StudyDate, out dt))).ToList(); - - if (etcCheckList.Count == 0) - { - return ResponseOutput.NotOk("请保证上传数据符合模板文件中的样式,且存在有效数据。"); - } - - } - - await _consistencyCheckFileRepository.AddAsync(new ConsistencyCheckFile() - { - TrialId = trialId, - CreateTime = DateTime.Now, - FileName = fileName, - FilePath = filePath, - RelativePaths = filePath.Replace(rootPath, ""), - CreateUserId = _userInfo.Id - }); - - //new() - //{ - // TrialId = trialId, - // CreateTime = DateTime.Now, - // FileName = fileName, - // FilePath = filePath, - // CreateUser = - // RelativePaths = filePath.Replace(rootPath, ""); - //}; - - - - await _mediator.Send(new ConsistencyVerificationRequest() { ETCList = etcCheckList, TrialId = trialId }); - - return ResponseOutput.Ok(); - - } - #endregion diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs index 6c8d3a393..f0c596aa1 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs @@ -349,94 +349,5 @@ namespace IRaCIS.Core.Application - #region 调整废弃 - [Obsolete] - [HttpPost("{trialId:guid}/{type}")] - public async Task UploadTrialFile(IFormFile file, string type, Guid trialId, [FromServices] IWebHostEnvironment _hostEnvironment) - { - - var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).IfNullThrowException().FullName; - //上传根路径 - var _fileStorePath = Path.Combine(rootPath, StaticData.TrialDataFolder); - - //文件类型路径处理 - var uploadFolderPath = Path.Combine(_fileStorePath, trialId.ToString(), type); - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - - var realName = file.FileName; - var fileNameEX = Path.GetExtension(realName); - var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX; - - var relativePath = $"/{StaticData.TrialDataFolder}/{trialId}/{type}/{trustedFileNameForFileStorage}"; - - var filePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - using (FileStream fs = System.IO.File.Create(filePath)) - { - await file.CopyToAsync(fs); - fs.Flush(); - } - - return ResponseOutput.Ok(new - { - FilePath = relativePath, - FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken - }); - - } - - /// - /// 签名认证 + - /// - /// - [HttpPost] - [Obsolete] - public async Task VerifySignature(SignDTO signDTO) - { - - var user = await _repository.FirstOrDefaultAsync(u => u.UserName == signDTO.UserName && u.Password == signDTO.PassWord); - if (user == null) - { - return ResponseOutput.NotOk("密码错误。"); - } - else if (user.Status == UserStateEnum.Disable) - { - return ResponseOutput.NotOk("该用户已被禁止使用。"); - } - - //if (signDTO.IsAddSignData) - //{ - // 记录签名信息 - var add = await _repository.AddAsync(_mapper.Map(signDTO)); - - await _repository.SaveChangesAsync(); - - return ResponseOutput.Ok(add.Id); - //} - - //return ResponseOutput.Ok(); - - } - - - - - /// - /// 业务接口操作成功后, 让签署数据生效 - /// - /// - /// - [HttpPut("{signId:guid}")] - [Obsolete] - public async Task MakeSignEffective(Guid signId) - { - var success = await _repository.BatchUpdateAsync(t => t.Id == signId, u => new TrialSign() { IsCompleted = true }); - - return ResponseOutput.Result(success); - } - #endregion - - - } } diff --git a/IRaCIS.Core.Infra.EFCore/Repository/IRepository.cs b/IRaCIS.Core.Infra.EFCore/Repository/IRepository.cs index ce580b926..c2fa439e4 100644 --- a/IRaCIS.Core.Infra.EFCore/Repository/IRepository.cs +++ b/IRaCIS.Core.Infra.EFCore/Repository/IRepository.cs @@ -14,6 +14,7 @@ using System.Threading.Tasks; using EFCore.BulkExtensions; using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Infra.EFCore.Common.Dto; +using System.Reflection; namespace IRaCIS.Core.Infra.EFCore { @@ -88,6 +89,10 @@ namespace IRaCIS.Core.Infra.EFCore Task BatchDeleteAsync(Expression> deleteFilter) where T : Entity; Task BatchUpdateAsync(Expression> where, Expression> updateFactory) where T : Entity; + + Task UpdatePartialFromQueryAsync(Expression> updateFilter, + Expression> updateFactory, + bool autoSave = false, bool ignoreQueryFilter = false) where T : Entity; } public class Repository : IRepository @@ -493,6 +498,62 @@ namespace IRaCIS.Core.Infra.EFCore return await _dbContext.Set().AsNoTracking().IgnoreQueryFilters().Where(whereFilter).BatchUpdateAsync(updateFactory).ConfigureAwait(false) > 0; } + + + public async Task UpdatePartialFromQueryAsync(Expression> updateFilter, + Expression> updateFactory, + bool autoSave = false, bool ignoreQueryFilter = false) where T : Entity + { + if (updateFilter == null) + { + throw new ArgumentException("更新过滤条件不允许为空", nameof(updateFilter)); + } + var query = ignoreQueryFilter ? _dbContext.Set().AsNoTracking().IgnoreQueryFilters() : _dbContext.Set().AsNoTracking(); + + var searchEntityList = await query.Where(updateFilter).ToListAsync(); + + foreach (var needUpdateEntity in searchEntityList) + { + await UpdateAsync(needUpdateEntity, updateFactory, autoSave); + } + + } + + public async Task UpdateAsync(T waitModifyEntity, Expression> updateFactory, bool autoSave = false) where T : Entity + { + var entityEntry = _dbContext.Entry(waitModifyEntity); + entityEntry.State = EntityState.Detached; + + + ModifyPartialFiled(waitModifyEntity, updateFactory); + + return await SaveChangesAsync(autoSave); + + } + + /// 更新后拥有完整的实体信息,便于稽查 + private void ModifyPartialFiled(T waitModifyEntity, Expression> updateFactory) + { + List list = ((MemberInitExpression)updateFactory.Body).Bindings.Select(mb => mb.Member.Name) + .Select(propName => typeof(T).GetProperty(propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)).ToList(); + + Func func = updateFactory.Compile(); + + T applyObj = func(waitModifyEntity); + + //深拷贝更新之前实体信息 + //var copyObj = waitModifyEntity.Clone(); + + foreach (PropertyInfo prop in list) + { + object value = prop.GetValue(applyObj); + prop.SetValue(waitModifyEntity, value); + + _dbContext.Entry(waitModifyEntity).Property(prop.Name).IsModified = true; + } + + + } } #endregion