diff --git a/IRaCIS.Core.API/Progranm.cs b/IRaCIS.Core.API/Progranm.cs index 394625ba5..a3b23aa44 100644 --- a/IRaCIS.Core.API/Progranm.cs +++ b/IRaCIS.Core.API/Progranm.cs @@ -96,8 +96,11 @@ builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resourc // 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim() builder.Services.AddControllers(options => { + // 关键配置:禁用不可空引用类型的自动 Required 验证 + options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true; + // 插到最前,抢在默认绑定器之前 - //options.ModelBinderProviders.Insert(0, new NullableStructModelBinderProvider()); + //options.ModelBinderProviders.Insert(0, new SpecificNullableBinderProvider()); options.Filters.Add(); options.Filters.Add(); diff --git a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NullToEmptyStringResolver.cs b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NullToEmptyStringResolver.cs index 15814a6cc..146bdf25d 100644 --- a/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NullToEmptyStringResolver.cs +++ b/IRaCIS.Core.API/_ServiceExtensions/NewtonsoftJson/NullToEmptyStringResolver.cs @@ -101,22 +101,24 @@ namespace IRaCIS.Core.API public void SetValue(object target, object value) { + _memberInfo.SetValue(target, value); #region 前端针对 string 类型的变量,如果传递null 会报错必传 - if (_memberInfo.PropertyType == typeof(string)) - { - _memberInfo.SetValue(target, value == null ? string.Empty : value); - } - else - { - _memberInfo.SetValue(target, value); - } + //if (_memberInfo.PropertyType == typeof(string)) + //{ + // _memberInfo.SetValue(target, value == null ? string.Empty : value); + //} + //else + //{ + // _memberInfo.SetValue(target, value); + //} #endregion #region 处理模型验证区分 string string? + //////接收模型的时候 定义的明明是string 但是上面也有该属性,判断不准的 比如阅片跟踪列表查询 //var isNullable1 = _memberInfo.CustomAttributes.Any(a => a.AttributeType.Name == "NullableAttribute"); ////不影响 string? 传递null 变为"" diff --git a/IRaCIS.Core.Application/BusinessFilter/LegacyController/ModelActionFilter .cs b/IRaCIS.Core.Application/BusinessFilter/LegacyController/ModelActionFilter .cs index 8095e55b8..7d7c885a3 100644 --- a/IRaCIS.Core.Application/BusinessFilter/LegacyController/ModelActionFilter .cs +++ b/IRaCIS.Core.Application/BusinessFilter/LegacyController/ModelActionFilter .cs @@ -1,8 +1,11 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; +using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.Extensions.Localization; using Newtonsoft.Json; +using System.Reflection; namespace IRaCIS.Core.Application.Filter; @@ -31,97 +34,40 @@ public class ModelActionFilter(IStringLocalizer _localizer) : ActionFilterAttrib } -public class NullableStructModelBinderProvider : IModelBinderProvider + + + +public class SpecificNullableBinderProvider : IModelBinderProvider { public IModelBinder? GetBinder(ModelBinderProviderContext context) { - // 获取要绑定的模型类型,比如 Guid?, int?, DateTime? 等 var type = context.Metadata.ModelType; - //创建默认的模型绑定器(系统的原逻辑) - var fallback = context.CreateBinder(context.Metadata); - - - // 1. 处理 string 类型 - if (type == typeof(string)) + // 只处理 Guid? 和 int? + if (type == typeof(Guid?) || type == typeof(int?)) { - return new StringNotNullableModelBinder(fallback); + return new SpecificNullableBinder(); } - // 检查是否是 Nullable 类型 - // 1. 必须是泛型类型 - // 2. 泛型定义必须是 Nullable<> - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - // 获取可空类型内部的实际类型,如 Guid?, int? 中的 Guid, int - var innerType = Nullable.GetUnderlyingType(type)!; - - - var binderType = typeof(NullableEmptyStringToNullBinder<>).MakeGenericType(innerType); - - // 实例化绑定器,传入默认绑定器作为备用 - return (IModelBinder)Activator.CreateInstance(binderType, fallback)!; - } - - - return null; // 返回 null 表示"我不处理这个类型" - + return null; } } -// 泛型约束:T 必须是值类型(struct),这确保了只处理可空值类型 比如处理guid? 传递"" 为null -public class NullableEmptyStringToNullBinder : IModelBinder where T : struct +public class SpecificNullableBinder : IModelBinder { - private readonly IModelBinder _fallbackBinder; - - // 构造函数接收一个备用的绑定器(系统的默认绑定器) - public NullableEmptyStringToNullBinder(IModelBinder fallbackBinder) + public Task BindModelAsync(ModelBindingContext context) { - _fallbackBinder = fallbackBinder; - } - - public async Task BindModelAsync(ModelBindingContext context) - { - // 获取前端传递的值 var value = context.ValueProvider.GetValue(context.ModelName).FirstValue; - // 关键逻辑:如果值是空字符串或空白,直接返回 null if (string.IsNullOrWhiteSpace(value)) { - // 设置绑定结果为 null(表示空值) context.Result = ModelBindingResult.Success(null); - return; } - - // 非空 → 完全走系统原逻辑 - - // 如果不是空字符串,则使用系统的默认绑定逻辑 - // 比如将 "123" 转换为 int? 123,或将 GUID 字符串转换为 Guid? - await _fallbackBinder.BindModelAsync(context); - } -} - -// 处理 string 的绑定器 如果前端不传递 或者null 时 处理为 "" -public class StringNotNullableModelBinder : IModelBinder -{ - private readonly IModelBinder _fallbackBinder; - - public StringNotNullableModelBinder(IModelBinder fallbackBinder) - { - _fallbackBinder = fallbackBinder; - } - - public async Task BindModelAsync(ModelBindingContext context) - { - var value = context.ValueProvider.GetValue(context.ModelName).FirstValue; - - // 前端不传或传空字符串,都设为 string.Empty - if (string.IsNullOrWhiteSpace(value)) + else { - context.Result = ModelBindingResult.Success(string.Empty); - return; + context.Result = ModelBindingResult.Success(value); } - await _fallbackBinder.BindModelAsync(context); + return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/IRaCIS.Core.Application/Helper/DicomDIRHelper.cs b/IRaCIS.Core.Application/Helper/DicomDIRHelper.cs index 44f71203b..12c387440 100644 --- a/IRaCIS.Core.Application/Helper/DicomDIRHelper.cs +++ b/IRaCIS.Core.Application/Helper/DicomDIRHelper.cs @@ -1,4 +1,5 @@ -using FellowOakDicom; +using DocumentFormat.OpenXml.Office.CustomUI; +using FellowOakDicom; using FellowOakDicom.Media; using System; using System.Collections.Generic; @@ -57,6 +58,7 @@ namespace IRaCIS.Core.Application.Helper var mappings = new List(); int index = 1; + var studyUid=list.FirstOrDefault()?.StudyInstanceUid; var dicomDir = new DicomDirectory(); @@ -130,7 +132,7 @@ namespace IRaCIS.Core.Application.Helper var relativePath= await _oSSService.UploadToOSSAsync(memoryStream, ossFolder, "DICOMDIR", true); - dic.Add("DICOMDIR" , relativePath.Split('/').Last()); + dic.Add($"{studyUid}_DICOMDIR" , relativePath.Split('/').Last()); } //清理临时文件 diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs index 2dad6b397..4491fb6fe 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs @@ -1185,6 +1185,9 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc foreach (var item in list.GroupBy(t => new { t.StudyInstanceUid, t.DicomStudyId })) { + + var studyUid = item.Key.StudyInstanceUid; + var ossFolder = $"{pathInfo.TrialId}/Image/{pathInfo.SubjectId}/{pathInfo.VisitId}/{item.Key.StudyInstanceUid}"; var isSucess = await SafeBussinessHelper.RunAsync(async () => await DicomDIRHelper.GenerateStudyDIRAndUploadAsync(item.ToList(), dirDic, ossFolder, _oSSService)); @@ -1192,7 +1195,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc if (isSucess) { - await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/{dirDic["DICOMDIR"]}" }); + await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/{dirDic[$"{studyUid}_DICOMDIR"]}" }); } } @@ -1625,6 +1628,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc var ossFolder = $"{pathInfo.TrialId}/Image/{pathInfo.SubjectId}/{visitId}/{item.Key.StudyInstanceUid}"; + var studyUid = item.Key.StudyInstanceUid; + var isSucess = await SafeBussinessHelper.RunAsync(async () => await DicomDIRHelper.GenerateStudyDIRAndUploadAsync(item.ToList(), dirDic, ossFolder, _oSSService)); @@ -1632,11 +1637,11 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc { if (isTaskStudy) { - await _taskStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new TaskStudy() { StudyDIRPath = $"/{ossFolder}/{dirDic["DICOMDIR"]}" }); + await _taskStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new TaskStudy() { StudyDIRPath = $"/{ossFolder}/{dirDic[$"{studyUid}_DICOMDIR"]}" }); } else { - await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/{dirDic["DICOMDIR"]}" }); + await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/{dirDic[$"{studyUid}_DICOMDIR"]}" }); } } @@ -2303,6 +2308,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc var subjectId = item.First().SubjectId; + var studyUid = item.Key.StudyInstanceUid; var ossFolder = $"{inCommand.TrialId}/Image/{subjectId}/{visitId}/{item.Key.StudyInstanceUid}"; @@ -2310,7 +2316,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc if (isSucess) { - await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/{dirDic["DICOMDIR"]}" }); + await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/{dirDic[$"{studyUid}_DICOMDIR"]}" }); } } } diff --git a/IRaCIS.Core.Application/TestService.cs b/IRaCIS.Core.Application/TestService.cs index f7c4723e2..ecb3badd6 100644 --- a/IRaCIS.Core.Application/TestService.cs +++ b/IRaCIS.Core.Application/TestService.cs @@ -87,6 +87,8 @@ namespace IRaCIS.Core.Application.Service public string? StringNUllValue { get; set; } + public string StringBackDefaultValue { get; set; } = string.Empty; + public Guid GuidValue { get; set; } = NewId.NextSequentialGuid(); public Guid? GuidNUllValue { get; set; } @@ -104,12 +106,15 @@ namespace IRaCIS.Core.Application.Service public DateTime? DateTimeNUllValue { get; set; } } + //创建一个模型验证的方法 [AllowAnonymous] [HttpPost] public async Task PostModelVerify(ModelVerifyCommand modelVerify) { + return ResponseOutput.Ok(modelVerify); + }