diff --git a/IRaCIS.Core.API/IRaCIS.Core.API.xml b/IRaCIS.Core.API/IRaCIS.Core.API.xml index 008e1add6..cc66c1c02 100644 --- a/IRaCIS.Core.API/IRaCIS.Core.API.xml +++ b/IRaCIS.Core.API/IRaCIS.Core.API.xml @@ -365,6 +365,11 @@ IPLimit限流 启动服务 + + + 废弃,没用,不用这种处理方式 + + 创建属性 diff --git a/IRaCIS.Core.API/Middleware/TimeZoneAdjustmentMiddleware.cs b/IRaCIS.Core.API/Middleware/TimeZoneAdjustmentMiddleware.cs new file mode 100644 index 000000000..c3c71da2a --- /dev/null +++ b/IRaCIS.Core.API/Middleware/TimeZoneAdjustmentMiddleware.cs @@ -0,0 +1,118 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +/// +/// 废弃,没用 +/// +public class TimeZoneAdjustmentMiddleware +{ + private readonly RequestDelegate _next; + + public TimeZoneAdjustmentMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task Invoke(HttpContext context) + { + if (string.IsNullOrEmpty(context.Request.ContentType)) + { + // 请求没有内容体,可能是一个没有请求体的请求,比如 GET 请求 + await _next(context); + return; + } + + + var timeZoneId = "Asia/Shanghai"; // 客户端默认时区 + + var timeZoneIdHeaderValue = context.Request.Headers["TimeZoneId"]; + + if (!string.IsNullOrEmpty(timeZoneIdHeaderValue)) + { + timeZoneId = timeZoneIdHeaderValue; + } + + var timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); + + + + // 处理 JSON 请求体中的时间字段 + if (context.Request.ContentType.StartsWith("application/json")) + { + var requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync(); + + // 使用 JSON.NET 或 System.Text.Json 解析 JSON 请求体 + // 假设请求体中有一个名为 "dateTime" 的时间字段 + dynamic jsonData = JsonConvert.DeserializeObject(requestBody); + + if (jsonData.dateTime != null) + { + if (DateTime.TryParse((string)jsonData.dateTime, out DateTime dateTime)) + { + // 将 JSON 请求体中的时间字段转换为服务器时区的时间 + var serverTime = TimeZoneInfo.ConvertTime(dateTime, timeZone); + jsonData.dateTime = serverTime; + } + } + + // 将修改后的 JSON 请求体重新写入请求流中 + var jsonBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(jsonData)); + context.Request.Body = new MemoryStream(jsonBytes); + context.Request.ContentLength = jsonBytes.Length; + } + + + // 处理 URL 表单参数 + var modifiedQuery = new Dictionary(); + + foreach (var key in context.Request.Query.Keys) + { + if (DateTime.TryParse(context.Request.Query[key], out DateTime dateTime)) + { + // 将 URL 表单参数中的时间转换为服务器时区的时间 + var serverTime = TimeZoneInfo.ConvertTime(dateTime, timeZone); + modifiedQuery[key] = new StringValues(serverTime.ToString()); + } + else + { + modifiedQuery[key] = context.Request.Query[key]; + } + } + + context.Request.Query = new QueryCollection(modifiedQuery); + + // 处理Form请求体中的参数 + if (context.Request.HasFormContentType) + { + var modifiedForm = new Dictionary(); + + foreach (var key in context.Request.Form.Keys) + { + if (DateTime.TryParse(context.Request.Form[key], out DateTime dateTime)) + { + // 将请求体中的时间转换为服务器时区的时间 + var serverTime = TimeZoneInfo.ConvertTime(dateTime, timeZone); + modifiedForm[key] = new StringValues(serverTime.ToString()); + } + else + { + modifiedForm[key] = context.Request.Form[key]; + } + } + + var newFormCollection = new FormCollection(modifiedForm); + + // 将新的表单集合设置回请求对象 + context.Request.Form = newFormCollection; + } + + await _next(context); + } + +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/JSONCustomDateConverter.cs b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/JSONCustomDateConverter.cs deleted file mode 100644 index 388f7053a..000000000 --- a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/JSONCustomDateConverter.cs +++ /dev/null @@ -1,70 +0,0 @@ -using IRaCIS.Core.Domain.Share; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using System; - -namespace IRaCIS.Core.API -{ - public class JSONCustomDateConverter : DateTimeConverterBase - { - private TimeZoneInfo _timeZoneInfo; - private string _dateFormat; - - private IUserInfo _userInfo; - public JSONCustomDateConverter(string dateFormat, TimeZoneInfo timeZoneInfo, IUserInfo userInfo) - { - _dateFormat = dateFormat; - _timeZoneInfo = timeZoneInfo; - - _userInfo = userInfo; - } - - private static readonly TimeZoneInfo ChinaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Asia/Shanghai"); - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - - - if (reader.ValueType == typeof(DateTime)) - { - DateTime dateTime = (DateTime)reader.Value; - - var zoneTime = TimeZoneInfo.ConvertTime(dateTime, ChinaTimeZone); - - return zoneTime; - } - else - { - return reader.Value; - } - - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var timeZoneId = _userInfo.TimeZoneId; - - //var needConvertUtcDateTime = Convert.ToDateTime(value); - - - //var tz = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); - - - //var dateTimeOffset = new DateTimeOffset(needConvertUtcDateTime); - - //var time = TimeZoneInfo.ConvertTimeFromUtc(needConvertUtcDateTime, tz).ToString(_dateFormat); - - //writer.WriteValue(time); - //writer.Flush(); - - - if (value is DateTime dateTime) - { - DateTime chinaTime = TimeZoneInfo.ConvertTime(dateTime, ChinaTimeZone); - writer.WriteValue(chinaTime); - } - - } - - } -} diff --git a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/JSONTimeZoneConverter.cs b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/JSONTimeZoneConverter.cs new file mode 100644 index 000000000..af0b905b6 --- /dev/null +++ b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/JSONTimeZoneConverter.cs @@ -0,0 +1,81 @@ +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using StackExchange.Redis; +using System; +using System.Globalization; + +namespace IRaCIS.Core.API +{ + /// + /// 序列化,反序列化的时候,处理时间 时区转换 + /// + public class JSONTimeZoneConverter : DateTimeConverterBase + { + private readonly IHttpContextAccessor _httpContextAccessor; + + private readonly TimeZoneInfo _clientTimeZone; + public JSONTimeZoneConverter(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + + //默认是UTC + //var timeZoneId = "Etc/UTC"; + var timeZoneId = "Asia/Shanghai"; + + var timeZoneIdHeader = _httpContextAccessor?.HttpContext?.Request?.Headers["TimeZoneId"]; + + if (timeZoneIdHeader is not null && !string.IsNullOrEmpty(timeZoneIdHeader.Value)) + { + timeZoneId = timeZoneIdHeader.Value; + } + + _clientTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); + + } + + public override bool CanConvert(Type objectType) + { + // 仅支持 DateTime 类型的转换 + return objectType == typeof(DateTime)|| objectType == typeof(DateTime?); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + DateTime? nullableDateTime = reader.Value as DateTime?; + + if (nullableDateTime != null && nullableDateTime.HasValue) + { + var dateTime = (DateTime)reader.Value; + + // 将客户端时间转换为服务器时区的时间 + var serverZoneTime = TimeZoneInfo.ConvertTime(dateTime, _clientTimeZone, TimeZoneInfo.Local); + + return serverZoneTime; + } + else + { + return null; + } + + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + DateTime? nullableDateTime = value as DateTime?; + + if (nullableDateTime != null && nullableDateTime.HasValue) + { + //第一个参数默认使用系统本地时区 也就是应用服务器的时区 + DateTime clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, _clientTimeZone); + writer.WriteValue(clientZoneTime); + } + else + { + writer.WriteNull(); + } + } + + } +} diff --git a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NewtonsoftJsonSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NewtonsoftJsonSetup.cs index df8aad91f..853afe9d3 100644 --- a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NewtonsoftJsonSetup.cs +++ b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NewtonsoftJsonSetup.cs @@ -10,7 +10,8 @@ namespace IRaCIS.Core.API { public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services) { - services.AddScoped(); + services.AddHttpContextAccessor(); + services.AddScoped(); builder.AddNewtonsoftJson(options => { @@ -24,8 +25,10 @@ namespace IRaCIS.Core.API // 设置时间格式 options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind; + //options.SerializerSettings.Converters.Add(new JSONCustomDateConverter()) ; - //options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService()); + options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService()); //IsoDateTimeConverter //options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; diff --git a/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs b/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs index f24d6bad2..6ccb8d139 100644 --- a/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs +++ b/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs @@ -3,6 +3,7 @@ using DocumentFormat.OpenXml.Presentation; using DocumentFormat.OpenXml.Spreadsheet; using IRaCIS.Application.Contracts; using IRaCIS.Application.Interfaces; +using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson; using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.Contracts.DTO; using IRaCIS.Core.Application.Service.Reading.Dto; @@ -59,7 +60,7 @@ namespace IRaCIS.Core.Application.Service.Common var exportInfo = (await _trialRepository.Where(t => t.Id == param.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - exportInfo.List = await _trialUseRepository.Where(t => t.TrialId == param.TrialId).IgnoreQueryFilters() + var list = await _trialUseRepository.Where(t => t.TrialId == param.TrialId).IgnoreQueryFilters() .WhereIf(param.UserTypeId != null, t => t.User.UserTypeId == param.UserTypeId) .WhereIf(!string.IsNullOrWhiteSpace(param.UserName), t => t.User.UserName.Contains(param.UserName)) @@ -70,8 +71,10 @@ namespace IRaCIS.Core.Application.Service.Common t => (t.User.FullName).Contains(param.UserRealName)) .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); - + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialUserList_Export, exportInfo, exportInfo.TrialCode, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(TrialMaintenanceDTO)); @@ -90,7 +93,7 @@ namespace IRaCIS.Core.Application.Service.Common [HttpPost] [AllowAnonymous] public async Task TrialSiteUserListExport(SiteCRCExportQueryDTO param, - [FromServices] IRepository _commonDocumentRepository, + [FromServices] IRepository _commonDocumentRepository, [FromServices] IDictionaryService _dictionaryService, [FromServices] IRepository _trialRepository, [FromServices] IRepository _trialSiteUserRepository @@ -100,21 +103,26 @@ namespace IRaCIS.Core.Application.Service.Common var exportInfo = (await _trialRepository.Where(t => t.Id == param.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - exportInfo.List = await _trialSiteUserRepository.Where(t => t.TrialId == param.TrialId).IgnoreQueryFilters() - .WhereIf(param.IsDeleted != null, t => t.IsDeleted == param.IsDeleted) - .WhereIf(!string.IsNullOrWhiteSpace(param.SiteName), t => t.Site.SiteName.Contains(param.SiteName)) - .WhereIf(!string.IsNullOrWhiteSpace(param.TrialSiteAliasName), - t => t.TrialSite.TrialSiteAliasName.Contains(param.TrialSiteAliasName)) - .WhereIf(!string.IsNullOrWhiteSpace(param.TrialSiteCode), - t => t.TrialSite.TrialSiteCode.Contains(param.TrialSiteCode)) - .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator, - t => t.UserId == _userInfo.Id) - .WhereIf(!string.IsNullOrWhiteSpace(param.UserKeyInfo), t => (t.User.FullName).Contains(param.UserKeyInfo) - || t.User.UserName.Contains(param.UserKeyInfo) || t.User.EMail.Contains(param.UserKeyInfo)) + var list = await _trialSiteUserRepository.Where(t => t.TrialId == param.TrialId).IgnoreQueryFilters() + .WhereIf(param.IsDeleted != null, t => t.IsDeleted == param.IsDeleted) + .WhereIf(!string.IsNullOrWhiteSpace(param.SiteName), t => t.Site.SiteName.Contains(param.SiteName)) + .WhereIf(!string.IsNullOrWhiteSpace(param.TrialSiteAliasName), + t => t.TrialSite.TrialSiteAliasName.Contains(param.TrialSiteAliasName)) + .WhereIf(!string.IsNullOrWhiteSpace(param.TrialSiteCode), + t => t.TrialSite.TrialSiteCode.Contains(param.TrialSiteCode)) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator, + t => t.UserId == _userInfo.Id) + .WhereIf(!string.IsNullOrWhiteSpace(param.UserKeyInfo), t => (t.User.FullName).Contains(param.UserKeyInfo) + || t.User.UserName.Contains(param.UserKeyInfo) || t.User.EMail.Contains(param.UserKeyInfo)) - .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; + + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSiteUserList_Export, exportInfo, exportInfo.TrialCode, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(SiteUserExportDTO)); } @@ -161,13 +169,14 @@ namespace IRaCIS.Core.Application.Service.Common .WhereIf(!string.IsNullOrEmpty(queryParam.OrganizationName), t => t.OrganizationName.Contains(queryParam.OrganizationName)) .ProjectTo(_mapper.ConfigurationProvider); - data.List = await query.ToListAsync(); - - + var list = await query.ToListAsync(); + data.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); var exportInfo = data; exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSiteUserSummary_Export, exportInfo, exportInfo.TrialCode, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(TrialSiteUserSummaryDto)); } @@ -230,8 +239,9 @@ namespace IRaCIS.Core.Application.Service.Common var exportInfo = (await _trialRepository.Where(t => t.Id == visitSearchDTO.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - exportInfo.List = list; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialCRCUploadImageList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(CRCVisitExportDTO)); @@ -280,8 +290,9 @@ namespace IRaCIS.Core.Application.Service.Common var exportInfo = (await _trialRepository.Where(t => t.Id == challengeQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - exportInfo.List = list; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialQCImageChanllengeList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(QCChanllengeExportDto)); } @@ -317,8 +328,9 @@ namespace IRaCIS.Core.Application.Service.Common var exportInfo = (await _trialRepository.Where(t => t.Id == param.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - exportInfo.List = list; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSubjectList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(SubjectExportDTO)); @@ -425,8 +437,9 @@ namespace IRaCIS.Core.Application.Service.Common exportInfo.CriterionName = await _repository.Where(u => u.TrialId == dto.TrialId && u.IsConfirm && u.Id == dto.TrialReadingCriterionId).Select(t => t.CriterionName).FirstOrDefaultAsync(); - exportInfo.List = list; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; var (memoryStream, fileName) = await ExcelExportHelper.DataExport_NpoiTestAsync(StaticData.Export.TrialSubjectProgressList_Export, exportInfo, /*"", */_commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(SubjectProgressDto)); @@ -491,7 +504,7 @@ namespace IRaCIS.Core.Application.Service.Common } var memoryStream2 = new MemoryStream(); - wb.Write(memoryStream2,true); + wb.Write(memoryStream2, true); memoryStream2.Seek(0, SeekOrigin.Begin); return new FileStreamResult(memoryStream2, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") @@ -565,8 +578,9 @@ namespace IRaCIS.Core.Application.Service.Common var exportInfo = (await _trialRepository.Where(t => t.Id == studyQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - exportInfo.List = list; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialStudyUploadMonitor_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(UnionStudyMonitorExportDto)); @@ -601,8 +615,9 @@ namespace IRaCIS.Core.Application.Service.Common var exportInfo = (await _trialRepository.Where(t => t.Id == param.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - exportInfo.List = list; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSubjectReadingPeriodList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(ReadPeriodExportDto)); @@ -690,8 +705,9 @@ namespace IRaCIS.Core.Application.Service.Common var exportInfo = (await _trialRepository.Where(t => t.Id == studyQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - exportInfo.List = list; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialStudyList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(UnionStudyExportDTO)); } @@ -729,8 +745,9 @@ namespace IRaCIS.Core.Application.Service.Common var exportInfo = (await _trialRepository.Where(t => t.Id == checkQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - exportInfo.List = list; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSubjectVisitCheckList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(PMKCheckEXportDTO)); } @@ -780,8 +797,9 @@ namespace IRaCIS.Core.Application.Service.Common var exportInfo = (await _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - exportInfo.List = list; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialReadingTaskList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(ReadingTaskExportDto)); } @@ -831,8 +849,9 @@ namespace IRaCIS.Core.Application.Service.Common var exportInfo = (await _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - exportInfo.List = list; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialReReadingTaskList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(ReReadingTaskExportDto)); } @@ -873,8 +892,9 @@ namespace IRaCIS.Core.Application.Service.Common var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - exportInfo.List = list; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialMedicalReviewList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(TaskMedicalReviewExportDto)); } @@ -957,8 +977,9 @@ namespace IRaCIS.Core.Application.Service.Common var exportInfo = (await _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); exportInfo.CriterionName = await _repository.Where(u => u.TrialId == queryVisitTask.TrialId && u.IsConfirm && u.Id == queryVisitTask.TrialReadingCriterionId).Select(t => t.CriterionName).FirstOrDefaultAsync(); - exportInfo.List = list; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSelfAnalysisList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}_{exportInfo.CriterionName}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(SelftAnalysisExport)); } @@ -1060,8 +1081,9 @@ namespace IRaCIS.Core.Application.Service.Common var exportInfo = (await _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); exportInfo.CriterionName = await _repository.Where(u => u.TrialId == queryVisitTask.TrialId && u.IsConfirm && u.Id == queryVisitTask.TrialReadingCriterionId).Select(t => t.CriterionName).FirstOrDefaultAsync(); - exportInfo.List = newList; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(newList, _userInfo.TimeZoneId); ; exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialGroupAnalysisList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}_{exportInfo.CriterionName}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(GroupAnalysisExport)); } @@ -1145,7 +1167,7 @@ namespace IRaCIS.Core.Application.Service.Common { foreach (var item in resultList) { - item.IsGenerateJudge = list.Where(t => t.ReadingCategory == ReadingCategory.Judge && t.SubjectCode == item.SubjectCode && t.VisitTaskNum>item.VisitTaskNum + item.IsGenerateJudge = list.Where(t => t.ReadingCategory == ReadingCategory.Judge && t.SubjectCode == item.SubjectCode && t.VisitTaskNum > item.VisitTaskNum ).OrderByDescending(t => t.VisitTaskNum).FirstOrDefault()?.JudgeArmEnum == item.ArmEnum ? true : false; } } @@ -1213,8 +1235,9 @@ namespace IRaCIS.Core.Application.Service.Common //处理裁判标记 list = DealJudgeMark(criterion.ArbitrationRule, list); - exportInfo.List = list; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.OverallTumorEvaluation_Export, exportInfo, $"{exportInfo.ResearchProgramNo}_{exportInfo.CriterionName}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(OverallTumorEvaluationExport), criterion.CriterionType); @@ -1272,8 +1295,9 @@ namespace IRaCIS.Core.Application.Service.Common //处理裁判标记 list = DealJudgeMark(criterion.ArbitrationRule, list); - exportInfo.List = list; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.RECIST1Point1EvaluationOfTumorEfficacy_Export, exportInfo, $"{exportInfo.ResearchProgramNo}_{exportInfo.CriterionName}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(RECIST1Point1EvaluationOfTumorEfficacyExport), criterion.CriterionType); @@ -1305,7 +1329,7 @@ namespace IRaCIS.Core.Application.Service.Common { //每次查询必须是单标准的 - var criterion = await _repository.Where(t => t.Id == queryVisitTask.TrialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionName,t.ArbitrationRule }).FirstOrDefaultAsync(); + var criterion = await _repository.Where(t => t.Id == queryVisitTask.TrialReadingCriterionId).Select(t => new { t.CriterionType, t.CriterionName, t.ArbitrationRule }).FirstOrDefaultAsync(); var query = _repository.Where(t => t.TrialId == queryVisitTask.TrialId && t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false && t.ReadingTaskState == ReadingTaskState.HaveSigned) @@ -1363,8 +1387,9 @@ namespace IRaCIS.Core.Application.Service.Common //处理裁判标记 list = DealJudgeMark(criterion.ArbitrationRule, list); - exportInfo.List = exportList; + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(exportList, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.RECIST1Point1DetailedOfEvaluatedLesion_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(RECIST1Point1DetailedOfEvaluatedLesionExport), criterion.CriterionType); @@ -1396,8 +1421,10 @@ namespace IRaCIS.Core.Application.Service.Common //处理裁判标记 list = DealJudgeMark(criterion.ArbitrationRule, list); - exportInfo.List = exportList; + + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(exportList, _userInfo.TimeZoneId); ; exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; return await ExcelExportHelper.DataExportAsync(StaticData.Export.PCWG3Point1DetailedOfEvaluatedLesion_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(PCWG3DetailedOfEvaluatedLesionExport), criterion.CriterionType); diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs index 69f2ddcde..07dca77ed 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs @@ -6,6 +6,7 @@ using MiniExcelLibs.Attributes; using Newtonsoft.Json; using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.Helper; +using System; namespace IRaCIS.Application.Contracts { @@ -119,10 +120,11 @@ namespace IRaCIS.Application.Contracts public class ExcelExportInfo : TrialSelectDTO { - public string CurrentTime { get; set; } = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + public string CurrentTime => TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.Local, TimeZoneInfo.FindSystemTimeZoneById(ClientZoneId)).ToString("yyyy-MM-dd HH:mm:ss"); public bool IsEn_US { get; set; } + public string ClientZoneId { get; set; } = string.Empty; public object List { get; set; } } diff --git a/IRaCIS.Core.Infra.EFCore/AuthUser/UserInfo.cs b/IRaCIS.Core.Infra.EFCore/AuthUser/UserInfo.cs index 45e0ab3ec..61521c667 100644 --- a/IRaCIS.Core.Infra.EFCore/AuthUser/UserInfo.cs +++ b/IRaCIS.Core.Infra.EFCore/AuthUser/UserInfo.cs @@ -278,8 +278,8 @@ namespace IRaCIS.Core.Domain.Share return timeZoneId.Value; } - return "Etc/UTC"; - //return "Asia/Shanghai"; + //return "Etc/UTC"; + return "Asia/Shanghai"; } diff --git a/IRaCIS.Core.Infrastructure/Extention/ConvertToClientTimeResultFilter.cs b/IRaCIS.Core.Infrastructure/Extention/ConvertToClientTimeResultFilter.cs new file mode 100644 index 000000000..5126ae86f --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Extention/ConvertToClientTimeResultFilter.cs @@ -0,0 +1,60 @@ +//using Microsoft.AspNetCore.Mvc.Filters; +//using Microsoft.AspNetCore.Mvc; +//using Newtonsoft.Json; +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using System.Threading.Tasks; +//using System.Text.RegularExpressions; + +//namespace IRaCIS.Core.Application.BusinessFilter +//{ +// public class ConvertToClientTimeResultFilter : IAsyncResultFilter +// { + + +// public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) +// { + +// //JsonConvert.DeserializeObject(jsonStr, new JsonSerializerSettings +// //{ +// // DateTimeZoneHandling = DateTimeZoneHandling.Local +// //}); + +// if (context.Result is ObjectResult objectResult) +// { +// var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode; + +// if (statusCode == 200) +// { +// var result = objectResult.Value; + +// // 将数据中的 DateTime 值转换为 DateTimeOffset,指定时区为东八区 +// ConvertDateTimeToDateTimeOffset(result); + +// // 使用 Newtonsoft.Json 序列化转换后的数据 +// string json = JsonConvert.SerializeObject(result); + +// // 重新设置 JsonResult 的值为处理后的数据 +// context.Result = new JsonResult(json); + +// } +// } + + +// // 执行动作方法 +// await next(); +// } + +// private void ConvertDateTimeToDateTimeOffset(object data) +// { +// // 假设客户端时区为东八区 +// TimeZoneInfo clientTimeZone = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time"); + +// // 将对象中的 DateTime 值转换为 DateTimeOffset,指定时区为东八区 +// string json = JsonConvert.SerializeObject(data); +// data = json.Replace("\"DateTime\":", "\"DateTimeOffset\":"); +// } +// } +//} diff --git a/IRaCIS.Core.Infrastructure/Extention/ExportExcelDateConverter.cs b/IRaCIS.Core.Infrastructure/Extention/ExportExcelDateConverter.cs new file mode 100644 index 000000000..434152ca4 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Extention/ExportExcelDateConverter.cs @@ -0,0 +1,83 @@ +using Newtonsoft.Json.Converters; +using Newtonsoft.Json; +using System; +using SharpCompress.Writers; + +namespace IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson +{ + + + public class ExportExcelDateConverter : DateTimeConverterBase + { + private readonly TimeZoneInfo _clientTimeZone; + + public ExportExcelDateConverter(TimeZoneInfo clientTimeZone) + { + _clientTimeZone = clientTimeZone; + } + public override bool CanConvert(Type objectType) + { + + return objectType == typeof(DateTime) || objectType == typeof(DateTime?); + } + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + DateTime? nullableDateTime = value as DateTime?; + + if (nullableDateTime != null && nullableDateTime.HasValue) + { + // 将服务器时间转换为客户端时间 + DateTime clientTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, TimeZoneInfo.Local, _clientTimeZone); + writer.WriteValue(clientTime); + } + else + { + writer.WriteNull(); + } + + + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + DateTime? nullableDateTime = reader.Value as DateTime?; + + if (nullableDateTime != null && nullableDateTime.HasValue) + + { // 服务器时区的时间转为客户端时间 + var clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, TimeZoneInfo.Local, _clientTimeZone); + + return clientZoneTime; + } + return reader.Value; + + + } + + + } + public class ExportExcelConverterDate + { + public static T ConvertToClientTimeInObject(T obj, string timeZoneId) + { + var clientTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); + if (obj == null) + return obj; + + // 将对象序列化为 JSON 字符串 + string json = JsonConvert.SerializeObject(obj); + + // 将 JSON 字符串反序列化回对象 + var deserializedObj = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + Converters = { new ExportExcelDateConverter(clientTimeZone) } + }); + + return deserializedObj!; + } + } +} + + + +