HIR 流式写入zip 返回前端
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
93362ea4c9
commit
d78a42dead
|
|
@ -1,6 +1,8 @@
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using ExcelDataReader;
|
using ExcelDataReader;
|
||||||
|
using IRaCIS.Application.Contracts;
|
||||||
using IRaCIS.Application.Interfaces;
|
using IRaCIS.Application.Interfaces;
|
||||||
|
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
|
||||||
using IRaCIS.Core.Application.BusinessFilter;
|
using IRaCIS.Core.Application.BusinessFilter;
|
||||||
using IRaCIS.Core.Application.Contracts;
|
using IRaCIS.Core.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Contracts.Dicom;
|
using IRaCIS.Core.Application.Contracts.Dicom;
|
||||||
|
|
@ -30,12 +32,16 @@ using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
using MiniExcelLibs;
|
using MiniExcelLibs;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using NPOI.HPSF;
|
||||||
|
using Serilog;
|
||||||
using SharpCompress.Archives;
|
using SharpCompress.Archives;
|
||||||
|
using SharpCompress.Common;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -297,7 +303,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
[HttpPost, Route("Study/ArchiveStudy")]
|
[HttpPost, Route("Study/ArchiveStudy")]
|
||||||
[DisableFormValueModelBinding]
|
[DisableFormValueModelBinding]
|
||||||
[DisableRequestSizeLimit]
|
[DisableRequestSizeLimit]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
public async Task<IResponseOutput> ArchiveStudyNew(Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId, Guid studyMonitorId,
|
public async Task<IResponseOutput> ArchiveStudyNew(Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId, Guid studyMonitorId,
|
||||||
[FromServices] ILogger<UploadDownLoadController> _logger,
|
[FromServices] ILogger<UploadDownLoadController> _logger,
|
||||||
[FromServices] IStudyService _studyService,
|
[FromServices] IStudyService _studyService,
|
||||||
|
|
@ -452,7 +458,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="_studyMonitorRepository"></param>
|
/// <param name="_studyMonitorRepository"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Study/PreArchiveStudy")]
|
[HttpPost, Route("Study/PreArchiveStudy")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
public async Task<IResponseOutput> PreArchiveStudy(PreArchiveStudyCommand preArchiveStudyCommand,
|
public async Task<IResponseOutput> PreArchiveStudy(PreArchiveStudyCommand preArchiveStudyCommand,
|
||||||
[FromServices] IStudyService _studyService,
|
[FromServices] IStudyService _studyService,
|
||||||
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
|
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
|
||||||
|
|
@ -488,7 +494,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="_noneDicomStudyFileRepository"></param>
|
/// <param name="_noneDicomStudyFileRepository"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("NoneDicomStudy/UploadNoneDicomFile")]
|
[HttpPost("NoneDicomStudy/UploadNoneDicomFile")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
public async Task<IResponseOutput> UploadNoneDicomFile(UploadNoneDicomFileCommand incommand,
|
public async Task<IResponseOutput> UploadNoneDicomFile(UploadNoneDicomFileCommand incommand,
|
||||||
[FromServices] IRepository<NoneDicomStudy> _noneDicomStudyRepository,
|
[FromServices] IRepository<NoneDicomStudy> _noneDicomStudyRepository,
|
||||||
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository,
|
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository,
|
||||||
|
|
@ -560,7 +566,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||||
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
|
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
||||||
|
|
||||||
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
|
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
|
||||||
{
|
{
|
||||||
|
|
@ -880,7 +886,102 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
[HttpPost("download/PatientStudyBatchDownload")]
|
||||||
|
public async Task<IActionResult> DownloadPatientStudyBatch([FromServices] IPatientService _patientService, [FromServices] IOSSService _oSSService,
|
||||||
|
[FromServices] IHubContext<DownloadHub, IDownloadClient> _downLoadHub,
|
||||||
|
PatientImageDownloadCommand inCommand)
|
||||||
|
{
|
||||||
|
var rusult = await _patientService.GetDownloadPatientStudyInfo(inCommand);
|
||||||
|
|
||||||
|
var patientList = rusult.Data;
|
||||||
|
var downloadInfo = (SubejctVisitDownload)rusult.OtherData;
|
||||||
|
|
||||||
|
|
||||||
|
long totalSize = downloadInfo.ImageSize;
|
||||||
|
var abortToken = HttpContext.RequestAborted;
|
||||||
|
var lastNotify = DateTime.UtcNow;
|
||||||
|
|
||||||
|
Response.ContentType = "application/zip";
|
||||||
|
Response.Headers["Content-Disposition"] = $"attachment; filename=Image_{ExportExcelConverterDate.DateTimeInternationalToString(DateTime.Now, _userInfo.TimeZoneId)}.zip";
|
||||||
|
Response.Headers["Cache-Control"] = "no-store";
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await using var responseStream = Response.BodyWriter.AsStream();
|
||||||
|
using var zip = new ZipArchive(responseStream, ZipArchiveMode.Create, leaveOpen: true);
|
||||||
|
|
||||||
|
foreach (var patient in patientList)
|
||||||
|
{
|
||||||
|
foreach (var study in patient.StudyList)
|
||||||
|
{
|
||||||
|
abortToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
var studyTime = study.StudyTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "UnknownTime";
|
||||||
|
var modalitysStr = string.Join('_', study.SeriesList.Select(t => t.Modality).Distinct());
|
||||||
|
|
||||||
|
// ---------- DICOMDIR ----------
|
||||||
|
var dicomDirPath = $"{patient.PatientIdStr}/{studyTime}_{modalitysStr}/DICOMDIR";
|
||||||
|
var dicomDirEntry = zip.CreateEntry(dicomDirPath, CompressionLevel.Fastest);
|
||||||
|
|
||||||
|
await using (var entryStream = dicomDirEntry.Open())
|
||||||
|
await using (var dirStream = await _oSSService.GetStreamFromOSSAsync(study.StudyDIRPath))
|
||||||
|
{
|
||||||
|
await dirStream.CopyToAsync(entryStream, 32 * 1024, abortToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- IMAGE FILES ----------
|
||||||
|
foreach (var series in study.SeriesList)
|
||||||
|
{
|
||||||
|
foreach (var instance in series.InstanceList)
|
||||||
|
{
|
||||||
|
abortToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
var entryPath =
|
||||||
|
$"{patient.PatientIdStr}/{studyTime}_{modalitysStr}/IMAGE/{instance.FileName}";
|
||||||
|
|
||||||
|
var entry = zip.CreateEntry(entryPath, CompressionLevel.Fastest);
|
||||||
|
|
||||||
|
await using var entryStream = entry.Open();
|
||||||
|
await using var source = await _oSSService.GetStreamFromOSSAsync(instance.Path);
|
||||||
|
|
||||||
|
await source.CopyToAsync(entryStream, 32 * 1024, abortToken);
|
||||||
|
|
||||||
|
|
||||||
|
//await _downLoadHub.Clients.User(_userInfo.IdentityUserId.ToString()).ReceivProgressAsync(archiveStudyCommand.StudyInstanceUid, receivedCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正常完成
|
||||||
|
await _patientService.DownloadImageSuccess(downloadInfo.Id);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// ✅ 客户端取消 / 断开 —— 正常情况
|
||||||
|
Log.Logger.Warning("Download canceled by client");
|
||||||
|
}
|
||||||
|
catch (IOException ex) when (abortToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
// ✅ HttpClient 流在中断时的常见异常
|
||||||
|
Log.Logger.Warning($"Client disconnected: {ex.Message}");
|
||||||
|
}
|
||||||
|
catch (NullReferenceException ex) when (abortToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
// ✅ HttpConnection.ContentLengthReadStream 已知问题
|
||||||
|
Log.Logger.Warning($"Stream aborted: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new EmptyResult();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@ app.MapMasaMinimalAPIs();
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
app.MapHub<UploadHub>("/UploadHub");
|
app.MapHub<UploadHub>("/UploadHub");
|
||||||
|
app.MapHub<DownloadHub>("/DownloadHub");
|
||||||
app.MapHealthChecks("/health");
|
app.MapHealthChecks("/health");
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,12 @@ using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
public interface IUploadClient
|
|
||||||
{
|
|
||||||
Task ReceivProgressAsync(string studyInstanceUid, int haveReceivedCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class IRaCISUserIdProvider : IUserIdProvider
|
public class IRaCISUserIdProvider : IUserIdProvider
|
||||||
|
|
@ -21,6 +19,11 @@ namespace IRaCIS.Core.API
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IUploadClient
|
||||||
|
{
|
||||||
|
Task ReceivProgressAsync(string studyInstanceUid, int haveReceivedCount);
|
||||||
|
}
|
||||||
|
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[DisableCors]
|
[DisableCors]
|
||||||
public class UploadHub : Hub<IUploadClient>
|
public class UploadHub : Hub<IUploadClient>
|
||||||
|
|
@ -53,5 +56,34 @@ namespace IRaCIS.Core.API
|
||||||
// await Clients.All.ReceivProgressAsync(studyInstanceUid, haveReceivedCount);
|
// await Clients.All.ReceivProgressAsync(studyInstanceUid, haveReceivedCount);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface IDownloadClient
|
||||||
|
{
|
||||||
|
Task ReceivProgressAsync(Guid downloadId, string percent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[DisableCors]
|
||||||
|
public class DownloadHub : Hub<IDownloadClient>
|
||||||
|
{
|
||||||
|
|
||||||
|
public ILogger<UploadHub> _logger { get; set; }
|
||||||
|
public DownloadHub(ILogger<UploadHub> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task OnConnectedAsync()
|
||||||
|
{
|
||||||
|
_logger.LogError("连接: " + Context.ConnectionId);
|
||||||
|
|
||||||
|
return base.OnConnectedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -541,16 +541,46 @@ public class OSSService : IOSSService
|
||||||
.WithHttpClient(new HttpClient(httpClientHandler))
|
.WithHttpClient(new HttpClient(httpClientHandler))
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var memoryStream = new MemoryStream();
|
var pipe = new System.IO.Pipelines.Pipe();
|
||||||
|
|
||||||
var getObjectArgs = new GetObjectArgs()
|
_ = Task.Run(async () =>
|
||||||
.WithBucket(minIOConfig.BucketName)
|
{
|
||||||
.WithObject(ossRelativePath)
|
try
|
||||||
.WithCallbackStream(stream => stream.CopyToAsync(memoryStream));
|
{
|
||||||
|
var args = new GetObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.BucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithCallbackStream( stream =>
|
||||||
|
{
|
||||||
|
stream.CopyTo(pipe.Writer.AsStream());
|
||||||
|
});
|
||||||
|
|
||||||
|
await minioClient.GetObjectAsync(args);
|
||||||
|
await pipe.Writer.CompleteAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await pipe.Writer.CompleteAsync(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return pipe.Reader.AsStream();
|
||||||
|
|
||||||
|
#region 废弃
|
||||||
|
|
||||||
|
//var memoryStream = new MemoryStream();
|
||||||
|
|
||||||
|
//var getObjectArgs = new GetObjectArgs()
|
||||||
|
// .WithBucket(minIOConfig.BucketName)
|
||||||
|
// .WithObject(ossRelativePath)
|
||||||
|
// .WithCallbackStream(stream => stream.CopyToAsync(memoryStream));
|
||||||
|
|
||||||
|
//await minioClient.GetObjectAsync(getObjectArgs);
|
||||||
|
//memoryStream.Position = 0;
|
||||||
|
//return memoryStream;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
await minioClient.GetObjectAsync(getObjectArgs);
|
|
||||||
memoryStream.Position = 0;
|
|
||||||
return memoryStream;
|
|
||||||
}
|
}
|
||||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1208,7 +1208,14 @@ namespace IRaCIS.Application.Contracts
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DownloadPatientDto
|
||||||
|
{
|
||||||
|
public string PatientName { get; set; }
|
||||||
|
|
||||||
|
public string PatientIdStr { get; set; }
|
||||||
|
|
||||||
|
public List<DownloadDicomStudyDto> StudyList { get; set; }
|
||||||
|
}
|
||||||
public class DownloadDicomStudyDto
|
public class DownloadDicomStudyDto
|
||||||
{
|
{
|
||||||
public Guid StudyId { get; set; }
|
public Guid StudyId { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
using IRaCIS.Application.Contracts;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace IRaCIS.Application.Interfaces
|
||||||
|
{
|
||||||
|
public interface IPatientService
|
||||||
|
{
|
||||||
|
|
||||||
|
public Task<IResponseOutput<List<DownloadPatientDto>>> GetDownloadPatientStudyInfo(PatientImageDownloadCommand inCommand);
|
||||||
|
|
||||||
|
public Task<IResponseOutput> DownloadImageSuccess(Guid trialImageDownloadId);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -75,7 +75,7 @@ namespace IRaCIS.Application.Services
|
||||||
IDistributedLockProvider _distributedLockProvider, IMapper _mapper, IUserInfo _userInfo, IWebHostEnvironment _hostEnvironment, IStringLocalizer _localizer, IFusionCache _fusionCache
|
IDistributedLockProvider _distributedLockProvider, IMapper _mapper, IUserInfo _userInfo, IWebHostEnvironment _hostEnvironment, IStringLocalizer _localizer, IFusionCache _fusionCache
|
||||||
|
|
||||||
|
|
||||||
) : BaseService
|
) : BaseService, IPatientService
|
||||||
{
|
{
|
||||||
#region 访视提交生成任务了,但是需要退回
|
#region 访视提交生成任务了,但是需要退回
|
||||||
|
|
||||||
|
|
@ -3301,7 +3301,7 @@ namespace IRaCIS.Application.Services
|
||||||
/// <param name="inCommand"></param>
|
/// <param name="inCommand"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IResponseOutput> GetDownloadPatientStudyInfo(PatientImageDownloadCommand inCommand)
|
public async Task<IResponseOutput<List<DownloadPatientDto>>> GetDownloadPatientStudyInfo(PatientImageDownloadCommand inCommand)
|
||||||
{
|
{
|
||||||
var isAdminOrOA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.Admin || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.OA || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin;
|
var isAdminOrOA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.Admin || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.OA || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin;
|
||||||
|
|
||||||
|
|
@ -3384,10 +3384,10 @@ namespace IRaCIS.Application.Services
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
var query = _patientRepository.Where(t => patientIdList.Contains(t.Id)).Select(t => new
|
var query = _patientRepository.Where(t => patientIdList.Contains(t.Id)).Select(t => new DownloadPatientDto()
|
||||||
{
|
{
|
||||||
t.PatientName,
|
PatientName= t.PatientName,
|
||||||
t.PatientIdStr,
|
PatientIdStr= t.PatientIdStr,
|
||||||
|
|
||||||
StudyList = t.SCPStudyList.Where(t => studyIdList.Count > 0 ? studyIdList.Contains(t.Id) : true)
|
StudyList = t.SCPStudyList.Where(t => studyIdList.Count > 0 ? studyIdList.Contains(t.Id) : true)
|
||||||
.Where(t => isAdminOrOA ? true : t.HospitalGroupList.Any(c => currentUserHospitalGroupIdList.Contains(c.HospitalGroupId)))
|
.Where(t => isAdminOrOA ? true : t.HospitalGroupList.Any(c => currentUserHospitalGroupIdList.Contains(c.HospitalGroupId)))
|
||||||
|
|
@ -3414,7 +3414,7 @@ namespace IRaCIS.Application.Services
|
||||||
FileSize = k.FileSize
|
FileSize = k.FileSize
|
||||||
}).ToList()
|
}).ToList()
|
||||||
}).ToList()
|
}).ToList()
|
||||||
})
|
}).ToList()
|
||||||
});
|
});
|
||||||
|
|
||||||
var patientList = await query.ToListAsync();
|
var patientList = await query.ToListAsync();
|
||||||
|
|
@ -3610,7 +3610,7 @@ namespace IRaCIS.Application.Services
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IResponseOutput> DownloadImageSuccess(Guid trialImageDownloadId)
|
public async Task<IResponseOutput> DownloadImageSuccess(Guid trialImageDownloadId)
|
||||||
{
|
{
|
||||||
await _subejctVisitDownloadRepository.UpdatePartialFromQueryAsync(t => t.Id == trialImageDownloadId, u => new SubejctVisitDownload()
|
await _subejctVisitDownloadRepository.UpdatePartialFromQueryAsync( trialImageDownloadId, u => new SubejctVisitDownload()
|
||||||
{ DownloadEndTime = DateTime.Now, IsSuccess = true }, true);
|
{ DownloadEndTime = DateTime.Now, IsSuccess = true }, true);
|
||||||
return ResponseOutput.Ok();
|
return ResponseOutput.Ok();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@
|
||||||
/// 返回数据
|
/// 返回数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
T Data { get; set; }
|
T Data { get; set; }
|
||||||
|
|
||||||
|
object OtherData { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue