From 7a64959ecadb5df18706e042a4a2527b4139db8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E9=B9=8F?= Date: Thu, 15 May 2025 16:49:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=A5=E5=8E=82=E6=96=B9=E6=B3=95=20?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ReZero.sln | 10 +- .../ParameterProvider/BindHttpParameters.cs | 131 ++++- .../AttibuteInterfaceInitializerService.cs | 29 +- .../MethodGeneratorAPI/MethodGeneratorAPI.cs | 336 ++++++++++++- SuperAPI/Attributes/SkipAuthAttribute.cs | 31 ++ SuperAPI/Controllers/GatewayController.cs | 46 ++ SuperAPI/Controllers/MedCommonController.cs | 48 ++ .../Controllers/MedicalInsuranceController.cs | 240 +++++++++ SuperAPI/Controllers/MyApiController.cs | 43 -- .../Controllers/MyApiWithIocController.cs | 29 -- .../MyApiWithUnitOfWorkController.cs | 26 - SuperAPI/NoAuthPaths.cs | 110 +++++ SuperAPI/Program.cs | 31 +- SuperAPI/medical.insu.transfomer.csproj | 1 + .../STD/STD_TRANSACTION_LOG.cs | 98 ++++ .../IInsuranceBusinessService.cs | 1 - .../MedicalInsuranceService.cs | 139 +++++- .../StartupExtensions.cs | 66 +++ .../TransformerFactory.cs | 455 ++++++++++++++++++ .../TransformerService.cs | 451 ----------------- .../medical.transfomer.business.csproj | 11 +- medical.transfomer.service/Class1.cs | 7 - .../MedicalInsuranceTransactionService.cs | 238 +++++++++ .../medical.transfomer.service.csproj | 13 + 24 files changed, 1986 insertions(+), 604 deletions(-) create mode 100644 SuperAPI/Attributes/SkipAuthAttribute.cs create mode 100644 SuperAPI/Controllers/GatewayController.cs create mode 100644 SuperAPI/Controllers/MedCommonController.cs create mode 100644 SuperAPI/Controllers/MedicalInsuranceController.cs delete mode 100644 SuperAPI/Controllers/MyApiController.cs delete mode 100644 SuperAPI/Controllers/MyApiWithIocController.cs delete mode 100644 SuperAPI/Controllers/MyApiWithUnitOfWorkController.cs create mode 100644 SuperAPI/NoAuthPaths.cs create mode 100644 medical.jzyb.entity/STD/STD_TRANSACTION_LOG.cs create mode 100644 medical.transfomer.business/StartupExtensions.cs create mode 100644 medical.transfomer.business/TransformerFactory.cs delete mode 100644 medical.transfomer.business/TransformerService.cs delete mode 100644 medical.transfomer.service/Class1.cs create mode 100644 medical.transfomer.service/MedicalInsuranceTransactionService.cs diff --git a/ReZero.sln b/ReZero.sln index 6c5b36e..ffc96c8 100644 --- a/ReZero.sln +++ b/ReZero.sln @@ -1,5 +1,4 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.7.34031.279 MinimumVisualStudioVersion = 10.0.40219.1 @@ -25,6 +24,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "medical.transfomer.dto", "m EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "medical.transfomer.dal", "medical.transfomer.dal\medical.transfomer.dal.csproj", "{066B2678-5991-4B7A-B5DE-636529E988FE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "medical.transfomer.service", "medical.transfomer.service\medical.transfomer.service.csproj", "{B4C3D1C9-1A8F-4A87-BF0D-60AFFFAC3C5A}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "medical.transfomer", "medical.transfomer", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" EndProject Global @@ -69,6 +70,10 @@ Global {066B2678-5991-4B7A-B5DE-636529E988FE}.Debug|Any CPU.Build.0 = Debug|Any CPU {066B2678-5991-4B7A-B5DE-636529E988FE}.Release|Any CPU.ActiveCfg = Release|Any CPU {066B2678-5991-4B7A-B5DE-636529E988FE}.Release|Any CPU.Build.0 = Release|Any CPU + {B4C3D1C9-1A8F-4A87-BF0D-60AFFFAC3C5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B4C3D1C9-1A8F-4A87-BF0D-60AFFFAC3C5A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B4C3D1C9-1A8F-4A87-BF0D-60AFFFAC3C5A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B4C3D1C9-1A8F-4A87-BF0D-60AFFFAC3C5A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -81,6 +86,7 @@ Global {9518ADBB-507D-4530-891B-7C2423006624} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {2906FADC-1A72-45AB-BD65-215AA1288EC0} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {066B2678-5991-4B7A-B5DE-636529E988FE} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {B4C3D1C9-1A8F-4A87-BF0D-60AFFFAC3C5A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3ACCBAF-6C08-4037-A398-1D5A9188B7E9} diff --git a/ReZero/SuperAPI/ApiProvider/ParameterProvider/BindHttpParameters.cs b/ReZero/SuperAPI/ApiProvider/ParameterProvider/BindHttpParameters.cs index 3bfa547..418b8f9 100644 --- a/ReZero/SuperAPI/ApiProvider/ParameterProvider/BindHttpParameters.cs +++ b/ReZero/SuperAPI/ApiProvider/ParameterProvider/BindHttpParameters.cs @@ -88,7 +88,53 @@ namespace ReZero.SuperAPI var data = dataModel?.DefaultParameters?.FirstOrDefault(); if (data != null) { - data.Value = App.Db.Utilities.SerializeObject(formDatas); + System.Diagnostics.Debug.WriteLine("检测到JObject请求参数,开始处理"); + try + { + // 先检查是否有_rawJson + if (formDatas.ContainsKey("_rawJson")) + { + string rawJson = formDatas["_rawJson"].ToString(); + System.Diagnostics.Debug.WriteLine($"使用_rawJson作为JObject参数: {rawJson}"); + data.Value = rawJson; + return; // 已找到JSON数据,提前返回 + } + + // 读取请求body + context.Request.EnableBuffering(); + context.Request.Body.Position = 0; + using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8, true, 1024, true)) + { + var body = reader.ReadToEndAsync().Result; + if (!string.IsNullOrEmpty(body)) + { + data.Value = body; + System.Diagnostics.Debug.WriteLine($"JObject参数值设置为: {body}"); + } + else if (formDatas.ContainsKey("methodName") && formDatas.ContainsKey("data")) + { + // 尝试构建符合接口要求的JSON对象 + var jsonObj = new + { + methodName = formDatas["methodName"], + data = formDatas["data"] + }; + data.Value = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj); + System.Diagnostics.Debug.WriteLine($"从formDatas构建JObject参数: {data.Value}"); + } + else + { + data.Value = Newtonsoft.Json.JsonConvert.SerializeObject(formDatas); + System.Diagnostics.Debug.WriteLine($"使用formDatas序列化作为JObject参数: {data.Value}"); + } + } + context.Request.Body.Position = 0; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"处理JObject参数异常: {ex.Message}"); + data.Value = Newtonsoft.Json.JsonConvert.SerializeObject(formDatas); + } } } else if (dataModel!.DefaultParameters != null) @@ -110,7 +156,28 @@ namespace ReZero.SuperAPI private static bool IsJObjct(DataModel? dataModel, Dictionary formDatas) { - var isJObject = dataModel?.DefaultParameters?.Count == 1 && dataModel!.DefaultParameters!.First().ValueType == nameof(JObject) && dataModel!.DefaultParameters!.First().IsSingleParameter == true; + if (dataModel?.DefaultParameters == null || !dataModel.DefaultParameters.Any()) + return false; + + var firstParam = dataModel.DefaultParameters.First(); + + // 检查是否是JToken或其子类型 + bool isJTokenType = firstParam.ValueType == nameof(JObject) || + firstParam.ValueType == nameof(JToken) || + firstParam.ValueType == "JTokenType" || + firstParam.ValueType == "Json" || + (firstParam.ValueType?.Contains("JToken") == true) || + (firstParam.ValueType?.Contains("JObject") == true); + + bool isSingleParam = dataModel.DefaultParameters.Count == 1; + + // 检查bodyJson + bool hasJsonBody = formDatas.ContainsKey("_rawJson"); + + var isJObject = (isSingleParam && isJTokenType) || hasJsonBody; + + System.Diagnostics.Debug.WriteLine($"IsJObjct检查: {isJObject}, 参数类型: {firstParam.ValueType}, 有原始JSON: {hasJsonBody}, MethodArgs: {dataModel?.MyMethodInfo?.MethodArgsCount}"); + return isJObject; } @@ -207,24 +274,58 @@ namespace ReZero.SuperAPI } } - private static StreamReader AddRawParameters(HttpContext context, Dictionary formDatas) + private static void AddRawParameters(HttpContext context, Dictionary formDatas) { - using StreamReader reader = new System.IO.StreamReader(context.Request.Body); - var body = reader.ReadToEndAsync().Result; - - if (!string.IsNullOrEmpty(body)) + try { - - var bodyParams = Newtonsoft.Json.JsonConvert.DeserializeObject>(body) ?? new Dictionary(); - - var items = formDatas.Union(bodyParams).ToDictionary(pair => pair.Key, pair => pair.Value); - foreach (var item in items) + context.Request.EnableBuffering(); + context.Request.Body.Position = 0; + + string body; + using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8, true, 1024, true)) + { + body = reader.ReadToEndAsync().Result; + } + + context.Request.Body.Position = 0; + + if (!string.IsNullOrEmpty(body)) { - formDatas.Add(item.Key,item.Value); + System.Diagnostics.Debug.WriteLine($"原始请求体: {body}"); + + // 先保存原始JSON,无论是否能解析都确保有原始数据 + if ((body.StartsWith("{") && body.EndsWith("}")) || + (body.StartsWith("[") && body.EndsWith("]"))) + { + formDatas["_rawJson"] = body; + } + + try + { + var bodyParams = Newtonsoft.Json.JsonConvert.DeserializeObject>(body); + + if (bodyParams != null) + { + foreach (var item in bodyParams) + { + if (!formDatas.ContainsKey(item.Key)) + { + formDatas[item.Key] = item.Value; + } + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"解析请求体异常: {ex.Message}"); + // 如果解析为字典失败,保持原始JSON已在上面保存 + } } } - - return reader; + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"处理请求体异常: {ex.Message}"); + } } private static bool IsFormData(HttpContext context) diff --git a/ReZero/SuperAPI/DataInitializerService/AttibuteInterfaceInitializerService.cs b/ReZero/SuperAPI/DataInitializerService/AttibuteInterfaceInitializerService.cs index 05a9c33..82cde6f 100644 --- a/ReZero/SuperAPI/DataInitializerService/AttibuteInterfaceInitializerService.cs +++ b/ReZero/SuperAPI/DataInitializerService/AttibuteInterfaceInitializerService.cs @@ -117,11 +117,36 @@ namespace ReZero.SuperAPI { addItem.ValueType = "Byte[]"; } + else if (typeof(Newtonsoft.Json.Linq.JToken).IsAssignableFrom(p.PropertyType)) + { + // 特殊处理JToken类型(包括JObject, JArray等) + addItem.ValueType = "Json"; + if (p.PropertyType == typeof(Newtonsoft.Json.Linq.JObject)) + { + addItem.Value = "{}"; + } + else if (p.PropertyType == typeof(Newtonsoft.Json.Linq.JArray)) + { + addItem.Value = "[]"; + } + else + { + addItem.Value = "null"; + } + } else { addItem.ValueType = "Json"; - object obj = Activator.CreateInstance(p.PropertyType); - addItem.Value = new SerializeService().SerializeObject(obj); + try + { + object obj = Activator.CreateInstance(p.PropertyType); + addItem.Value = new SerializeService().SerializeObject(obj); + } + catch (Exception) + { + // 如果无法创建实例,设置为默认值 + addItem.Value = "{}"; + } } it!.DataModel!.DefaultParameters!.Add(addItem); } diff --git a/ReZero/SuperAPI/MethodGeneratorAPI/MethodGeneratorAPI.cs b/ReZero/SuperAPI/MethodGeneratorAPI/MethodGeneratorAPI.cs index 0442ab4..b032391 100644 --- a/ReZero/SuperAPI/MethodGeneratorAPI/MethodGeneratorAPI.cs +++ b/ReZero/SuperAPI/MethodGeneratorAPI/MethodGeneratorAPI.cs @@ -57,8 +57,20 @@ namespace ReZero.SuperAPI private object[] GetParameters(DataModel dataModel, MethodInfo methodInfo, object[] parameters, Type[]? argsTypes) { - if (IsJObject(dataModel, parameters)) + // 先检查method的参数类型 + var methodParams = methodInfo.GetParameters(); + + // 先尝试判断是否是JToken/JObject参数 + if (methodParams.Length == 1 && + (typeof(Newtonsoft.Json.Linq.JToken).IsAssignableFrom(methodParams[0].ParameterType))) + { + System.Diagnostics.Debug.WriteLine($"检测到JToken类型参数: {methodParams[0].ParameterType.Name}"); + // 直接通过参数类型判断 + FillJObjectDirectly(dataModel, methodParams[0].ParameterType, parameters); + } + else if (IsJObject(dataModel, parameters)) { + // 通过参数定义判断 FillJObjectParameters(dataModel, methodInfo, parameters, argsTypes); } else if (IsSingleModel(dataModel)) @@ -95,15 +107,99 @@ namespace ReZero.SuperAPI var parameterOjb = Activator.CreateInstance(type, nonPublic: true); foreach (var item in type!.GetProperties()) { - var p = dataModel.DefaultParameters.First(it => it.Name == item.Name); - p.Value = ConvetEmptyValue(item.PropertyType, p.Value); - if (IsJson(item, p)) + // 确保属性有set方法 + if (!item.CanWrite) + continue; + + // 获取对应的参数数据 + var propertyParam = dataModel.DefaultParameters.FirstOrDefault(it => it.Name == item.Name); + if (propertyParam == null) + continue; + + try { - item.SetValue(parameterOjb,JsonConvert.DeserializeObject(p.Value + "",item.PropertyType)); + propertyParam.Value = ConvetEmptyValue(item.PropertyType, propertyParam.Value); + + // 特殊处理JToken及其子类型 + if (typeof(Newtonsoft.Json.Linq.JToken).IsAssignableFrom(item.PropertyType)) + { + if (item.PropertyType == typeof(Newtonsoft.Json.Linq.JObject)) + { + string jsonStr = propertyParam.Value?.ToString() ?? "{}"; + if (jsonStr.Trim() == "null") jsonStr = "{}"; + try + { + item.SetValue(parameterOjb, Newtonsoft.Json.Linq.JObject.Parse(jsonStr)); + } + catch + { + item.SetValue(parameterOjb, new Newtonsoft.Json.Linq.JObject()); + } + } + else if (item.PropertyType == typeof(Newtonsoft.Json.Linq.JArray)) + { + string jsonStr = propertyParam.Value?.ToString() ?? "[]"; + if (jsonStr.Trim() == "null") jsonStr = "[]"; + try + { + item.SetValue(parameterOjb, Newtonsoft.Json.Linq.JArray.Parse(jsonStr)); + } + catch + { + item.SetValue(parameterOjb, new Newtonsoft.Json.Linq.JArray()); + } + } + else if (item.PropertyType == typeof(Newtonsoft.Json.Linq.JToken)) + { + string jsonStr = propertyParam.Value?.ToString() ?? "null"; + try + { + item.SetValue(parameterOjb, Newtonsoft.Json.Linq.JToken.Parse(jsonStr)); + } + catch + { + item.SetValue(parameterOjb, Newtonsoft.Json.Linq.JValue.CreateNull()); + } + } + } + else if (IsJson(item, propertyParam)) + { + try + { + var value = JsonConvert.DeserializeObject(propertyParam.Value + "", item.PropertyType); + item.SetValue(parameterOjb, value); + } + catch + { + // 如果JSON反序列化失败,尝试使用默认值 + if (item.PropertyType.IsValueType) + item.SetValue(parameterOjb, Activator.CreateInstance(item.PropertyType)); + else + item.SetValue(parameterOjb, null); + } + } + else + { + // 尝试使用类型转换设置值 + try + { + var convertedValue = UtilMethods.ChangeType2(propertyParam.Value, item.PropertyType); + item.SetValue(parameterOjb, convertedValue); + } + catch + { + // 如果类型转换失败,使用默认值 + if (item.PropertyType.IsValueType) + item.SetValue(parameterOjb, Activator.CreateInstance(item.PropertyType)); + else + item.SetValue(parameterOjb, null); + } + } } - else + catch (Exception) { - item.SetValue(parameterOjb, UtilMethods.ChangeType2(p.Value, item.PropertyType)); + // 如果设置属性值失败,继续处理下一个属性 + continue; } } parameters = new object[] { parameterOjb }; @@ -145,21 +241,146 @@ namespace ReZero.SuperAPI private void FillJObjectParameters(DataModel dataModel, MethodInfo methodInfo, object[] parameters, Type[]? argsTypes) { - var value = dataModel?.DefaultParameters?.FirstOrDefault()?.Value! + ""; - var type = methodInfo.GetParameters().First().ParameterType; - if (!string.IsNullOrEmpty(value)) + try { - parameters[0] = JsonConvert.DeserializeObject(value, type)!; - if (parameters[0] is SaveInterfaceListModel saveInterfaceListModel) + // 检查DefaultParameters是否有效 + if (dataModel?.DefaultParameters == null || !dataModel.DefaultParameters.Any()) + { + System.Diagnostics.Debug.WriteLine("DefaultParameters为空或null"); + return; // 无法继续 + } + + // 获取第一个参数的值 + var param = dataModel.DefaultParameters.First(); + var value = param.Value?.ToString() ?? ""; + System.Diagnostics.Debug.WriteLine($"参数值: {value}"); + + var type = methodInfo.GetParameters().First().ParameterType; + System.Diagnostics.Debug.WriteLine($"参数类型: {type.FullName}"); + + // 检查值是否为JSON格式 + if (!string.IsNullOrWhiteSpace(value)) { - saveInterfaceListModel.InterfaceCategoryId =Convert.ToInt64( Convert.ToDouble(saveInterfaceListModel.InterfaceCategoryId)) + ""; + value = value.Trim(); + if (!((value.StartsWith("{") && value.EndsWith("}")) || + (value.StartsWith("[") && value.EndsWith("]")))) + { + System.Diagnostics.Debug.WriteLine("值不是有效的JSON格式"); + // 尝试将值包装为JSON格式 + value = $"{{\"_value\": {value}}}"; + System.Diagnostics.Debug.WriteLine($"转换为: {value}"); + } } + + // JToken及其派生类的特殊处理 + if (typeof(Newtonsoft.Json.Linq.JToken).IsAssignableFrom(type)) + { + System.Diagnostics.Debug.WriteLine("处理JToken类型"); + try + { + if (type == typeof(Newtonsoft.Json.Linq.JObject)) + { + if (string.IsNullOrEmpty(value) || value == "null") + { + value = "{}"; + } + + parameters[0] = Newtonsoft.Json.Linq.JObject.Parse(value); + System.Diagnostics.Debug.WriteLine("JObject解析成功"); + } + else if (type == typeof(Newtonsoft.Json.Linq.JArray)) + { + if (string.IsNullOrEmpty(value) || value == "null") + { + value = "[]"; + } + + parameters[0] = Newtonsoft.Json.Linq.JArray.Parse(value); + } + else // JToken或其他子类 + { + if (string.IsNullOrEmpty(value)) + { + value = "null"; + } + + parameters[0] = Newtonsoft.Json.Linq.JToken.Parse(value); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"JSON解析错误: {ex.Message}"); + // 如果解析失败,创建一个空对象 + if (type == typeof(Newtonsoft.Json.Linq.JObject)) + { + parameters[0] = new Newtonsoft.Json.Linq.JObject(); + } + else if (type == typeof(Newtonsoft.Json.Linq.JArray)) + { + parameters[0] = new Newtonsoft.Json.Linq.JArray(); + } + else + { + parameters[0] = Newtonsoft.Json.Linq.JValue.CreateNull(); + } + } + } + else if (!string.IsNullOrEmpty(value)) + { + System.Diagnostics.Debug.WriteLine("处理非JToken类型"); + // 非JToken类型的常规处理 + try + { + parameters[0] = JsonConvert.DeserializeObject(value, type)!; + if (parameters[0] is SaveInterfaceListModel saveInterfaceListModel) + { + saveInterfaceListModel.InterfaceCategoryId = Convert.ToInt64(Convert.ToDouble(saveInterfaceListModel.InterfaceCategoryId)) + ""; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"非JToken反序列化错误: {ex.Message}"); + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"FillJObjectParameters总体异常: {ex.Message}"); } } private static bool IsJObject(DataModel dataModel, object[] parameters) { - return parameters.Count() == 1 && dataModel.DefaultParameters.First().ValueType == nameof(JObject)&& dataModel.DefaultParameters.First().IsSingleParameter==true; + if (parameters.Length != 1 || dataModel.DefaultParameters.Count == 0) + return false; + + var firstParam = dataModel.DefaultParameters.First(); + + // 检查是否已标记为JObject类型 + bool isJObjectType = firstParam.ValueType == nameof(JObject) || + firstParam.ValueType == nameof(JToken) || + firstParam.ValueType == "JTokenType" || + firstParam.ValueType == "Json" || + firstParam.ValueType?.Contains("JObject") == true || + firstParam.ValueType?.Contains("JToken") == true; + + // 检查是否为单个参数 + bool isSingleParam = dataModel.DefaultParameters.Count == 1; + + // 检查JSON字符串 + bool isJsonString = firstParam.Value != null && IsJsonString(firstParam.Value.ToString()); + + return isSingleParam && (isJObjectType || isJsonString); + } + + private static bool IsJsonString(string value) + { + if (string.IsNullOrEmpty(value)) + return false; + + value = value.Trim(); + return (value.StartsWith("{") && value.EndsWith("}")) || + (value.StartsWith("[") && value.EndsWith("]")); } private static int FillDefaultParameters(DataModel dataModel, MethodInfo methodInfo, object[] parameters, Type[]? argsTypes) @@ -211,6 +432,23 @@ namespace ReZero.SuperAPI value = null; } + // 处理JToken及其子类型 + if (typeof(Newtonsoft.Json.Linq.JToken).IsAssignableFrom(type)) + { + var jTokenValue = value?.ToString()?.Trim(); + if (string.IsNullOrEmpty(jTokenValue)) + { + // 为各种JToken类型提供默认值 + if (type == typeof(Newtonsoft.Json.Linq.JObject)) + return "{}"; + else if (type == typeof(Newtonsoft.Json.Linq.JArray)) + return "[]"; + else + return "null"; + } + return jTokenValue; + } + // 处理JSON数组或对象 var strValue = value?.ToString()?.Trim(); if (strValue != null) @@ -266,5 +504,75 @@ namespace ReZero.SuperAPI return false; } + + // 直接填充JObject参数,简化处理 + private void FillJObjectDirectly(DataModel dataModel, Type parameterType, object[] parameters) + { + try + { + System.Diagnostics.Debug.WriteLine("直接填充JObject参数"); + if (dataModel?.DefaultParameters == null || !dataModel.DefaultParameters.Any()) + { + System.Diagnostics.Debug.WriteLine("直接填充JObject - 参数列表为空"); + parameters[0] = CreateDefaultJToken(parameterType); + return; + } + + var jsonData = dataModel.DefaultParameters.First().Value?.ToString(); + if (string.IsNullOrEmpty(jsonData)) + { + System.Diagnostics.Debug.WriteLine("直接填充JObject - 参数值为空"); + parameters[0] = CreateDefaultJToken(parameterType); + return; + } + + System.Diagnostics.Debug.WriteLine($"直接填充JObject - JSON数据: {jsonData}"); + + try + { + // 检查是否需要处理嵌套的数据结构 + if (jsonData.Contains("\"methodName\"") && jsonData.Contains("\"data\"") && + parameterType == typeof(Newtonsoft.Json.Linq.JObject)) + { + var jobj = Newtonsoft.Json.Linq.JObject.Parse(jsonData); + // ExecuteTransaction方法直接需要整个请求对象,无需处理 + parameters[0] = jobj; + } + else if (parameterType == typeof(Newtonsoft.Json.Linq.JObject)) + { + parameters[0] = Newtonsoft.Json.Linq.JObject.Parse(jsonData); + } + else if (parameterType == typeof(Newtonsoft.Json.Linq.JArray)) + { + parameters[0] = Newtonsoft.Json.Linq.JArray.Parse(jsonData); + } + else + { + parameters[0] = Newtonsoft.Json.Linq.JToken.Parse(jsonData); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"直接填充JObject - 解析异常: {ex.Message}"); + parameters[0] = CreateDefaultJToken(parameterType); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"直接填充JObject - 总异常: {ex.Message}"); + parameters[0] = CreateDefaultJToken(parameterType); + } + } + + // 创建默认的JToken对象 + private static Newtonsoft.Json.Linq.JToken CreateDefaultJToken(Type parameterType) + { + if (parameterType == typeof(Newtonsoft.Json.Linq.JObject)) + return new Newtonsoft.Json.Linq.JObject(); + else if (parameterType == typeof(Newtonsoft.Json.Linq.JArray)) + return new Newtonsoft.Json.Linq.JArray(); + else + return Newtonsoft.Json.Linq.JValue.CreateNull(); + } } } diff --git a/SuperAPI/Attributes/SkipAuthAttribute.cs b/SuperAPI/Attributes/SkipAuthAttribute.cs new file mode 100644 index 0000000..44df1d8 --- /dev/null +++ b/SuperAPI/Attributes/SkipAuthAttribute.cs @@ -0,0 +1,31 @@ +using System; + +namespace medical.insu.transfomer.Attributes +{ + /// + /// 标记不需要认证的API方法 + /// 可以应用于类(控制器)或方法 + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] + public class SkipAuthAttribute : Attribute + { + /// + /// 说明 + /// + public string Description { get; set; } + + /// + /// 构造函数 + /// + public SkipAuthAttribute() { } + + /// + /// 带说明的构造函数 + /// + /// 免验证的说明 + public SkipAuthAttribute(string description) + { + Description = description; + } + } +} \ No newline at end of file diff --git a/SuperAPI/Controllers/GatewayController.cs b/SuperAPI/Controllers/GatewayController.cs new file mode 100644 index 0000000..f4347bf --- /dev/null +++ b/SuperAPI/Controllers/GatewayController.cs @@ -0,0 +1,46 @@ +using Microsoft.AspNetCore.Mvc; +using medical.insu.transfomer.Attributes; +using ReZero.SuperAPI; +using System; +using System.Threading.Tasks; + +namespace medical.insu.transfomer.Controllers +{ + /// + /// 公共网关控制器,所有方法都不需要身份验证 + /// + [ApiController] + [Api(200200, GroupName = "公共接口")] + [SkipAuth("整个控制器都不需要验证")] + public class GatewayController : ControllerBase + { + /// + /// 健康检查接口 + /// + /// 服务健康状态 + [ApiMethod("健康检查")] + [HttpGet("health")] + public IActionResult HealthCheck() + { + return Ok(new { status = "healthy", timestamp = DateTime.Now }); + } + + /// + /// 获取服务版本信息 + /// + /// 版本信息 + [ApiMethod("获取版本")] + [HttpGet("version")] + public IActionResult GetVersion() + { + var version = new + { + version = "1.0.0", + buildDate = "2023-08-01", + apiVersion = "v1" + }; + + return Ok(version); + } + } +} \ No newline at end of file diff --git a/SuperAPI/Controllers/MedCommonController.cs b/SuperAPI/Controllers/MedCommonController.cs new file mode 100644 index 0000000..816a65d --- /dev/null +++ b/SuperAPI/Controllers/MedCommonController.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json.Linq; +using ReZero.DependencyInjection; +using ReZero.SuperAPI; +using System; +using System.Threading.Tasks; +using medical.transfomer.business; + +namespace medical.insu.transfomer.Controllers +{ + [Api(200100, GroupName = "分组0")] + public class MedCommonController + { + [DI] + public TransformerFactory? transformerFactory { get; set; } + + //执行国家医保接口 + [HttpPost] + public async Task execPublic(JObject value) + { + try + { + if (transformerFactory == null) + { + return new { code = -1, msg = "医保转换服务未初始化" }; + } + + string action = value["action"]?.ToString(); + if (string.IsNullOrEmpty(action)) + { + return new { code = -1, msg = "缺少action参数" }; + } + + // 获取数据部分 + JObject data = value["data"] as JObject ?? new JObject(); + + // 使用转换工厂处理医保交易 + return await transformerFactory.ExecuteMethod(action, data); + } + catch (Exception ex) + { + return new { code = -1, msg = $"处理请求发生异常: {ex.Message}" }; + } + } + } +} + diff --git a/SuperAPI/Controllers/MedicalInsuranceController.cs b/SuperAPI/Controllers/MedicalInsuranceController.cs new file mode 100644 index 0000000..3dce999 --- /dev/null +++ b/SuperAPI/Controllers/MedicalInsuranceController.cs @@ -0,0 +1,240 @@ +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json.Linq; +using ReZero.DependencyInjection; +using ReZero.SuperAPI; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using medical.transfomer.service; +using medical.transfomer.business; +using medical.transfomer.entity; +using SqlSugar; +using medical.insu.transfomer.Attributes; + +namespace medical.insu.transfomer.Controllers +{ + [Api(200100, GroupName = "分组0")] + + public class MedicalInsuranceController:ControllerBase + { + [DI] + public MedicalInsuranceTransactionService? MedicalInsuranceService { get; set; } + + [DI] + public TransformerFactory? TransformerFactory { get; set; } + + [DI] + public ISqlSugarClient? Db { get; set; } + + /// + /// 执行医保交易 + /// + /// 包含交易参数的JSON对象 + /// 交易处理结果 + [ApiMethod("执行医保交易")] + [HttpPost] + [SkipAuth("医保交易接口,无需身份验证")] + public async Task ExecuteTransaction([FromBody] JObject value) + { + try + { + if (MedicalInsuranceService == null) + { + return new { code = -1, msg = "医保交易服务未初始化" }; + } + + // 确保value不为null + if (value == null) + { + return new { code = -1, msg = "请求数据为空" }; + } + + string methodName = value["methodName"]?.ToString(); + if (string.IsNullOrEmpty(methodName)) + { + return new { code = -1, msg = "缺少methodName参数" }; + } + + // 获取数据部分 + JObject data = value["data"] as JObject ?? new JObject(); + + // 使用医保交易服务处理 + return await MedicalInsuranceService.ExecuteTransaction(methodName, data); + } + catch (Exception ex) + { + return new { code = -1, msg = $"处理请求发生异常: {ex.Message}" }; + } + } + + /// + /// 获取可用的医保交易方法列表 + /// + /// 医保交易方法列表 + [HttpGet] + public async Task GetTransactionMethods() + { + try + { + if (Db == null) + { + return new { code = -1, msg = "数据库服务未初始化" }; + } + + // 获取方法配置列表 + var methods = await Db.Queryable() + .Select(m => new + { + id = m.METHOD_ID, + name = m.METHOD_NAME, + path = m.METHOD_VALUE, + saveInput = m.SAVE_INPUT, + saveOutput = m.SAVE_OUTPUT + }) + .ToListAsync(); + + return new { code = 0, msg = "获取成功", data = methods }; + } + catch (Exception ex) + { + return new { code = -1, msg = $"获取医保交易方法列表异常: {ex.Message}" }; + } + } + + /// + /// 获取交易日志列表 + /// + /// 方法名称(可选) + /// 状态(可选, 1:成功, 0:失败) + /// 开始时间(可选) + /// 结束时间(可选) + /// 页索引(从1开始) + /// 页大小 + /// 交易日志列表 + [HttpGet] + public async Task GetTransactionLogs( + string? methodName, + int? status, + DateTime? startTime, + DateTime? endTime, + int pageIndex = 1, + int pageSize = 20) + { + try + { + if (Db == null) + { + return new { code = -1, msg = "数据库服务未初始化" }; + } + + // 构建查询条件 + var query = Db.Queryable(); + + if (!string.IsNullOrEmpty(methodName)) + { + query = query.Where(l => l.METHOD_NAME == methodName); + } + + if (status.HasValue) + { + query = query.Where(l => l.STATUS == status.Value); + } + + if (startTime.HasValue) + { + query = query.Where(l => l.REQUEST_TIME >= startTime.Value); + } + + if (endTime.HasValue) + { + query = query.Where(l => l.REQUEST_TIME <= endTime.Value); + } + + // 执行分页查询 + var result = await query + .OrderByDescending(l => l.REQUEST_TIME) + .Select(l => new + { + id = l.LOG_ID, + methodId = l.METHOD_ID, + methodName = l.METHOD_NAME, + requestTime = l.REQUEST_TIME, + responseTime = l.RESPONSE_TIME, + status = l.STATUS, + errorMessage = l.ERROR_MESSAGE, + elapsedTime = l.ELAPSED_TIME + }) + .ToPageListAsync(pageIndex, pageSize); + + // 获取总记录数 + var total = await query.CountAsync(); + + return new + { + code = 0, + msg = "获取成功", + data = result, + total = total, + pageIndex = pageIndex, + pageSize = pageSize, + pageCount = (total + pageSize - 1) / pageSize + }; + } + catch (Exception ex) + { + return new { code = -1, msg = $"获取交易日志列表异常: {ex.Message}" }; + } + } + + /// + /// 获取交易日志详情 + /// + /// 日志ID + /// 交易日志详情 + [HttpGet] + public async Task GetTransactionLogDetail(decimal logId) + { + try + { + if (Db == null) + { + return new { code = -1, msg = "数据库服务未初始化" }; + } + + // 获取日志详情 + var log = await Db.Queryable() + .FirstAsync(l => l.LOG_ID == logId); + + if (log == null) + { + return new { code = -1, msg = $"未找到ID为{logId}的交易日志" }; + } + + return new + { + code = 0, + msg = "获取成功", + data = new + { + id = log.LOG_ID, + methodId = log.METHOD_ID, + methodName = log.METHOD_NAME, + requestTime = log.REQUEST_TIME, + requestData = log.REQUEST_DATA, + responseTime = log.RESPONSE_TIME, + responseData = log.RESPONSE_DATA, + status = log.STATUS, + errorMessage = log.ERROR_MESSAGE, + elapsedTime = log.ELAPSED_TIME, + clientIp = log.CLIENT_IP, + userId = log.USER_ID + } + }; + } + catch (Exception ex) + { + return new { code = -1, msg = $"获取交易日志详情异常: {ex.Message}" }; + } + } + } +} \ No newline at end of file diff --git a/SuperAPI/Controllers/MyApiController.cs b/SuperAPI/Controllers/MyApiController.cs deleted file mode 100644 index 9822697..0000000 --- a/SuperAPI/Controllers/MyApiController.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Microsoft.AspNetCore.Mvc.RazorPages; -using ReZero.SuperAPI; -using System.Security.Policy; -namespace medical.insu.transfomer -{ - /// - /// 动态接口 - /// - [Api(200100, GroupName = "分组1",Url= "/api/MyApiController")] - public class MyApiController - { - [ApiMethod("我是A方法")] - public int A(int num,int num2) - { - return num+num2; - } - - [ApiMethod("我是B方法")] - public string B(byte[] file) - { - return "文件长度"+ file.Length; - } - - [ApiMethod("我是C方法", HttpMethod = HttpType.Get)] - public Object C(SqlSugar.PageModel classA) - { - return classA; - } - - [ApiMethod("我是D方法")] - [UrlParameters] - public int D(int num, int num2) - { - return num + num2; - } - } - - public class ClassA - { - public int Id { get; set; } - public string? Name { get; set; } - } -} diff --git a/SuperAPI/Controllers/MyApiWithIocController.cs b/SuperAPI/Controllers/MyApiWithIocController.cs deleted file mode 100644 index def0a22..0000000 --- a/SuperAPI/Controllers/MyApiWithIocController.cs +++ /dev/null @@ -1,29 +0,0 @@ -using ReZero.DependencyInjection; -using ReZero.SuperAPI; -namespace medical.insu.transfomer -{ - /// - /// 动态接口+IOC - /// - [Api(200100,GroupName = "分组2")] - public class MyApiWithIocController - { - //属性注入 - [DI] - public MyService? MyService { get; set; } - - [ApiMethod("我是A方法")] - public int A(int num, int num2) - { - return this.MyService!.CalculateSum(num, num2); - } - } - //继承IScopeContract 、ISingletonContract或者ITransientContract就可以自动注入 - public class MyService : IScopeContract - { - public int CalculateSum(int num, int num2) - { - return num2 + num; - } - } -} diff --git a/SuperAPI/Controllers/MyApiWithUnitOfWorkController.cs b/SuperAPI/Controllers/MyApiWithUnitOfWorkController.cs deleted file mode 100644 index 063181a..0000000 --- a/SuperAPI/Controllers/MyApiWithUnitOfWorkController.cs +++ /dev/null @@ -1,26 +0,0 @@ -using ReZero.DependencyInjection; -using ReZero.SuperAPI; -using SqlSugar; - -namespace medical.insu.transfomer -{ - /// - /// 动态接口+工作单元 - /// - [Api(200100, GroupName = "分组3")] - public class MyApiWithUnitOfWorkController - { - //属性注入 - [DI] - public ISqlSugarClient? db { get; set; } - - //工作单元,可以用自带的也可以重新写 - [UnitOfWork] - [ApiMethod("我是QueryTest方法")] - public bool QueryTest() - { - db!.Ado.ExecuteCommand("select 1 as id"); - return true; - } - } -} diff --git a/SuperAPI/NoAuthPaths.cs b/SuperAPI/NoAuthPaths.cs new file mode 100644 index 0000000..b9e4740 --- /dev/null +++ b/SuperAPI/NoAuthPaths.cs @@ -0,0 +1,110 @@ +using medical.insu.transfomer.Attributes; +using ReZero.SuperAPI; +using System; +using System.Linq; +using System.Reflection; + +namespace medical.insu.transfomer +{ + /// + /// 定义不需要登录验证的API路径 + /// + public static class NoAuthPaths + { + /// + /// 医保交易接口 + /// + public const string MedicalInsuranceExecuteTransaction = "/api/200100/medicalinsurancecontroller/executetransaction"; + + /// + /// 检查路径是否在免验证列表中 + /// + /// 请求路径 + /// true表示不需要验证 + public static bool IsNoAuthPath(string path) + { + path = path.ToLower(); + + // 检查是否匹配医保交易接口 + if (path.Contains(MedicalInsuranceExecuteTransaction)) + { + return true; + } + + // 如果需要添加更多免验证路径,可以在这里扩展 + + return false; + } + + /// + /// 通过特性检查是否需要验证 + /// + /// 接口上下文 + /// true表示不需要验证 + public static bool IsSkipAuthByAttribute(InterfaceContext context) + { + // 先检查路径 + if (IsNoAuthPath(context.HttpContext.Request.Path)) + { + return true; + } + + try + { + // 检查具体接口信息 + if (context.InterfaceInfo?.DataModel?.MyMethodInfo != null) + { + var methodInfo = context.InterfaceInfo.DataModel.MyMethodInfo; + var classFullName = methodInfo.MethodClassFullName; + var methodName = methodInfo.MethodName; + + if (!string.IsNullOrEmpty(classFullName) && !string.IsNullOrEmpty(methodName)) + { + // 获取所有程序集 + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + + foreach (var assembly in assemblies) + { + try + { + // 查找类型 + var type = assembly.GetType(classFullName); + if (type != null) + { + // 检查类是否有SkipAuthAttribute + if (type.GetCustomAttributes(typeof(SkipAuthAttribute), true).Any()) + { + return true; + } + + // 查找方法 + var method = type.GetMethod(methodName); + if (method != null) + { + // 检查方法是否有SkipAuthAttribute + if (method.GetCustomAttributes(typeof(SkipAuthAttribute), true).Any()) + { + return true; + } + } + } + } + catch + { + // 忽略查找过程中的异常 + continue; + } + } + } + } + } + catch (Exception ex) + { + // 捕获任何异常,防止验证过程崩溃 + Console.WriteLine($"检查免验证特性时发生异常: {ex.Message}"); + } + + return false; + } + } +} \ No newline at end of file diff --git a/SuperAPI/Program.cs b/SuperAPI/Program.cs index f0a8158..aa084b8 100644 --- a/SuperAPI/Program.cs +++ b/SuperAPI/Program.cs @@ -12,12 +12,25 @@ using System.Text; using Microsoft.AspNetCore.Cors; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using medical.transfomer.business; +using medical.transfomer.service; +using medical.insu.transfomer; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); - + +// 注册HttpClient工厂 +builder.Services.AddHttpClient(); + +// 添加医保服务配置 +// 假设医保服务的基础URL在配置文件中或者使用默认值 +string medicalInsuranceBaseUrl = builder.Configuration["MedicalInsurance:BaseUrl"] ?? "http://localhost:8080"; +builder.Services.AddMedicalInsuranceServices(medicalInsuranceBaseUrl); + +// 注册医保交易服务 +builder.Services.AddScoped(); //注册db: 这个不写代码可以不注册 builder.Services.AddScoped(it => @@ -42,10 +55,24 @@ builder.Services.AddReZeroServices(api => //IOC业务等所有需要的所有集程集 var assemblyList = Assembly.GetExecutingAssembly() - .GetAllDependentAssemblies(it => it.Contains("medical.insu.transfomer") || it.Contains("medical.transfomer")) + .GetAllDependentAssemblies(it => it.Contains("medical.insu.transfomer") || + it.Contains("medical.transfomer")) .ToArray(); apiObj!.DependencyInjectionOptions = new DependencyInjectionOptions(assemblyList); + + // 配置不需要验证的接口 + apiObj!.InterfaceOptions!.NoAuthorizationFunc = (context) => { + // 使用特性判断方法检查是否需要验证 + var isNoAuth = NoAuthPaths.IsSkipAuthByAttribute(context); + + if (isNoAuth) + { + Console.WriteLine($"跳过验证: {context.HttpContext.Request.Path}"); + } + + return isNoAuth; + }; //启用超级API api.EnableSuperApi(apiObj); diff --git a/SuperAPI/medical.insu.transfomer.csproj b/SuperAPI/medical.insu.transfomer.csproj index 3f1c3f9..11d78f8 100644 --- a/SuperAPI/medical.insu.transfomer.csproj +++ b/SuperAPI/medical.insu.transfomer.csproj @@ -22,6 +22,7 @@ + diff --git a/medical.jzyb.entity/STD/STD_TRANSACTION_LOG.cs b/medical.jzyb.entity/STD/STD_TRANSACTION_LOG.cs new file mode 100644 index 0000000..8ab81f2 --- /dev/null +++ b/medical.jzyb.entity/STD/STD_TRANSACTION_LOG.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SqlSugar; +namespace medical.transfomer.entity + +{ + /// + /// 医保交易日志表 + /// + [SugarTable("STD_TRANSACTION_LOG")] + public class STD_TRANSACTION_LOG + { + /// + /// 备 注:日志ID + /// 默认值: + /// + [SugarColumn(ColumnName="LOG_ID", IsPrimaryKey = true, IsIdentity = true)] + public decimal? LOG_ID { get; set; } + + /// + /// 备 注:方法ID + /// 默认值: + /// + [SugarColumn(ColumnName="METHOD_ID")] + public string METHOD_ID { get; set; } = null!; + + /// + /// 备 注:方法名称 + /// 默认值: + /// + [SugarColumn(ColumnName="METHOD_NAME")] + public string METHOD_NAME { get; set; } = null!; + + /// + /// 备 注:请求时间 + /// 默认值: + /// + [SugarColumn(ColumnName="REQUEST_TIME")] + public DateTime REQUEST_TIME { get; set; } + + /// + /// 备 注:请求数据 + /// 默认值: + /// + [SugarColumn(ColumnName="REQUEST_DATA", ColumnDataType = "CLOB", IsNullable = true)] + public string? REQUEST_DATA { get; set; } + + /// + /// 备 注:响应时间 + /// 默认值: + /// + [SugarColumn(ColumnName="RESPONSE_TIME")] + public DateTime RESPONSE_TIME { get; set; } + + /// + /// 备 注:响应数据 + /// 默认值: + /// + [SugarColumn(ColumnName="RESPONSE_DATA", ColumnDataType = "CLOB", IsNullable = true)] + public string? RESPONSE_DATA { get; set; } + + /// + /// 备 注:状态(1:成功, 0:失败) + /// 默认值: + /// + [SugarColumn(ColumnName="STATUS")] + public int STATUS { get; set; } + + /// + /// 备 注:错误信息 + /// 默认值: + /// + [SugarColumn(ColumnName="ERROR_MESSAGE", IsNullable = true)] + public string? ERROR_MESSAGE { get; set; } + + /// + /// 备 注:交易耗时(毫秒) + /// 默认值: + /// + [SugarColumn(ColumnName="ELAPSED_TIME", IsNullable = true)] + public long? ELAPSED_TIME { get; set; } + + /// + /// 备 注:IP地址 + /// 默认值: + /// + [SugarColumn(ColumnName="CLIENT_IP", IsNullable = true)] + public string? CLIENT_IP { get; set; } + + /// + /// 备 注:用户ID + /// 默认值: + /// + [SugarColumn(ColumnName="USER_ID", IsNullable = true)] + public string? USER_ID { get; set; } + } +} \ No newline at end of file diff --git a/medical.transfomer.business/InsuranceBusinessServices/IInsuranceBusinessService.cs b/medical.transfomer.business/InsuranceBusinessServices/IInsuranceBusinessService.cs index 80cb725..369118f 100644 --- a/medical.transfomer.business/InsuranceBusinessServices/IInsuranceBusinessService.cs +++ b/medical.transfomer.business/InsuranceBusinessServices/IInsuranceBusinessService.cs @@ -1,5 +1,4 @@ -using medical.transfomer.dto; namespace medical.insu.transfomer.InsuranceBusinessServices; diff --git a/medical.transfomer.business/MedicalInsuranceService.cs b/medical.transfomer.business/MedicalInsuranceService.cs index b49d8eb..4fddc68 100644 --- a/medical.transfomer.business/MedicalInsuranceService.cs +++ b/medical.transfomer.business/MedicalInsuranceService.cs @@ -4,15 +4,108 @@ using System.ComponentModel.DataAnnotations; using System.Collections.Concurrent; using System.Security.Cryptography; using System.Text.Json; -using medical.insu.transfomer.InsuranceBusinessServices; using System.Net.Http.Json; -using medical.transfomer.dto; +using ReZero.DependencyInjection; +using Newtonsoft.Json.Linq; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; +using medical.transfomer.business; namespace medical.insu.transfomer { - public class MedicalInsuranceService + /// + /// 定义InsuranceRequest类,用于替代缺失的引用 + /// + public class InsuranceRequest + { + public string MsgId { get; set; } = string.Empty; + public string MdtrtareaAdmvs { get; set; } = string.Empty; + public string RecerSysCode { get; set; } = string.Empty; + public string Infver { get; set; } = string.Empty; + public int OpterType { get; set; } + public string Opter { get; set; } = string.Empty; + public string OpterName { get; set; } = string.Empty; + public DateTime InfTime { get; set; } + public string FixmedinsCode { get; set; } = string.Empty; + public string FixmedinsName { get; set; } = string.Empty; + public string Input { get; set; } = string.Empty; + public string InsuplcAdmdvs { get; set; } = string.Empty; + public string Signtype { get; set; } = string.Empty; + public string Infno { get; set; } = string.Empty; + } + + /// + /// 定义ResponseHeader类,用于替代缺失的引用 + /// + public class ResponseHeader + { + public string Version { get; set; } = string.Empty; + public string SenderCode { get; set; } = string.Empty; + public string ReceiverCode { get; set; } = string.Empty; + public string MsgId { get; set; } = string.Empty; + public string InfTime { get; set; } = string.Empty; + public string ResultCode { get; set; } = string.Empty; + public string ResultMsg { get; set; } = string.Empty; + public string SignatureAlgorithm { get; set; } = string.Empty; + public string EncryptionAlgorithm { get; set; } = string.Empty; + } + + /// + /// 定义Response类,用于替代缺失的引用 + /// + public class Response + { + public ResponseHeader Header { get; set; } = new ResponseHeader(); + public string Body { get; set; } = string.Empty; + public string Signature { get; set; } = string.Empty; + } + + /// + /// 业务服务接口 + /// + public interface IInsuranceBusinessService + { + object Execute(InsuranceRequest request); + } + + public class MedicalInsuranceService: IScopeContract { private readonly ConcurrentDictionary _serviceCache = new(); + private readonly TransformerFactory _transformerFactory; + private readonly ILogger _logger; + + public MedicalInsuranceService( + TransformerFactory transformerFactory, + ILogger logger) + { + _transformerFactory = transformerFactory; + _logger = logger; + } + + /// + /// 处理医保交易请求 + /// + /// 操作类型 + /// 交易数据 + /// 处理结果 + public async Task ProcessMedicalInsurance(string action, JObject data) + { + try + { + _logger.LogInformation($"开始处理医保交易请求: {action}"); + + // 使用转换工厂执行医保交易 + var result = await _transformerFactory.ExecuteMethod(action, data); + + _logger.LogInformation($"医保交易请求处理完成: {action}"); + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, $"处理医保交易请求异常: {action}"); + return new { code = -1, msg = $"处理请求异常: {ex.Message}" }; + } + } public void ValidateRequest(InsuranceRequest request) { @@ -53,10 +146,15 @@ namespace medical.insu.transfomer // "2101" => new RegistrationService(), // "2201" => new PreSettlementService(), // _ => throw new BusinessException($"未知的交易编号: {key}") + _ => null }); } - //入口方法 + /// + /// 入口方法 + /// + /// + /// public Response ProcessRequest(InsuranceRequest request) { ValidateRequest(request); @@ -64,11 +162,13 @@ namespace medical.insu.transfomer VerifySignature(request); var service = GetServiceByInfoNo(request.Infno); + if (service == null) + { + throw new InvalidOperationException($"未找到处理服务: {request.Infno}"); + } + var result = service.Execute(request); - - //方法调用路径? - return new Response { Header = GenerateResponseHeader(request), @@ -77,7 +177,11 @@ namespace medical.insu.transfomer }; } - // 新增参数格式验证 + /// + /// 新增参数格式验证 + /// + /// + /// public static void ValidateParameterFormat(InsuranceRequest request) { // 增强msgid格式校验 @@ -104,7 +208,11 @@ namespace medical.insu.transfomer throw new ValidationException("经办人姓名不能超过50个字符"); } - // 增强版响应头生成 + /// + /// 增强版响应头生成 + /// + /// + /// public static ResponseHeader GenerateResponseHeader(InsuranceRequest request) { return new ResponseHeader @@ -122,7 +230,11 @@ namespace medical.insu.transfomer }; } - // 完整签名验证流程 + /// + /// 完整签名验证流程 + /// + /// + /// public static void VerifySignature(InsuranceRequest request) { var rawData = $"{request.MsgId}{request.InfTime}{request.FixmedinsCode}"; @@ -137,7 +249,12 @@ namespace medical.insu.transfomer throw new InvalidOperationException("数字签名验证失败"); } - // 医保系统通信模块 + /// + /// 医保系统通信模块 + /// + /// + /// + /// public async Task CallInsuranceSystem(string endpoint, object payload) { // 由于 _httpClientFactory 不存在,使用新创建的 HttpClient 实例替代 diff --git a/medical.transfomer.business/StartupExtensions.cs b/medical.transfomer.business/StartupExtensions.cs new file mode 100644 index 0000000..034d5e9 --- /dev/null +++ b/medical.transfomer.business/StartupExtensions.cs @@ -0,0 +1,66 @@ +using Microsoft.Extensions.DependencyInjection; +using Polly; +using Polly.Extensions.Http; +using System; +using System.Net.Http; +using Polly.Retry; +using Polly.Timeout; +using Polly.CircuitBreaker; +using Microsoft.Extensions.Http.Resilience; + +namespace medical.transfomer.business +{ + /// + /// 应用程序启动扩展方法 + /// + public static class StartupExtensions + { + /// + /// 注册医保服务所需的服务 + /// + public static IServiceCollection AddMedicalInsuranceServices(this IServiceCollection services, string baseUrl) + { + // 注册HttpClient工厂 + services.AddHttpClient("MedicalInsurance", client => + { + client.BaseAddress = new Uri(baseUrl); + client.DefaultRequestHeaders.Add("Accept", "application/json"); + client.DefaultRequestHeaders.Add("User-Agent", "MedicalInsuranceClient"); + client.Timeout = TimeSpan.FromSeconds(30); + }) + .AddResilienceHandler("MedicalInsuranceResilienceHandler", builder => + { + // 添加重试策略 + builder.AddRetry(new RetryStrategyOptions + { + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => !r.IsSuccessStatusCode), + MaxRetryAttempts = 3, + Delay = TimeSpan.FromSeconds(1), + BackoffType = DelayBackoffType.Exponential + }); + + // 添加断路器策略 + builder.AddCircuitBreaker(new CircuitBreakerStrategyOptions + { + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => !r.IsSuccessStatusCode), + FailureRatio = 0.5, // 50%失败率 + MinimumThroughput = 10, // 最小样本数 + SamplingDuration = TimeSpan.FromSeconds(30), // 采样时间窗口 + BreakDuration = TimeSpan.FromMinutes(1) // 断路时间 + }); + + // 添加超时策略 + builder.AddTimeout(TimeSpan.FromSeconds(30)); + }); + + // 注册转换工厂 + services.AddScoped(); + + return services; + } + } +} \ No newline at end of file diff --git a/medical.transfomer.business/TransformerFactory.cs b/medical.transfomer.business/TransformerFactory.cs new file mode 100644 index 0000000..afa16d7 --- /dev/null +++ b/medical.transfomer.business/TransformerFactory.cs @@ -0,0 +1,455 @@ +using System; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Text.Json; +using System.Collections.Generic; +using medical.transfomer.entity; +using Newtonsoft.Json.Linq; +using Polly; +using Polly.Retry; +using Polly.CircuitBreaker; +using Microsoft.Extensions.Logging; +using SqlSugar; +using ReZero.DependencyInjection; +using System.Reflection; +using System.Linq; +using Polly.Timeout; +using System.Threading; + +namespace medical.transfomer.business +{ + /// + /// 医保转换工厂,负责动态转换和HTTP请求处理 + /// + public class TransformerFactory : IScopeContract + { + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger _logger; + private readonly ISqlSugarClient _db; + private readonly ResiliencePipeline _resiliencePipeline; + + public TransformerFactory(IHttpClientFactory httpClientFactory, ILogger logger, ISqlSugarClient db) + { + _httpClientFactory = httpClientFactory; + _logger = logger; + _db = db; + + // 在Polly 8.x中,我们使用ResiliencePipelineBuilder来创建弹性管道 + var pipelineBuilder = new ResiliencePipelineBuilder(); + + // 添加重试策略 + pipelineBuilder.AddRetry(new RetryStrategyOptions + { + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => !r.IsSuccessStatusCode), + MaxRetryAttempts = 3, + Delay = TimeSpan.FromSeconds(1), + BackoffType = DelayBackoffType.Exponential, + OnRetry = args => + { + _logger.LogWarning($"重试第 {args.AttemptNumber} 次,等待 {args.RetryDelay.TotalSeconds} 秒"); + return ValueTask.CompletedTask; + } + }); + + // 添加断路器策略 + pipelineBuilder.AddCircuitBreaker(new CircuitBreakerStrategyOptions + { + ShouldHandle = new PredicateBuilder() + .Handle() + .HandleResult(r => !r.IsSuccessStatusCode), + FailureRatio = 0.5, // 50%失败率 + MinimumThroughput = 10, // 最小样本数 + SamplingDuration = TimeSpan.FromSeconds(30), // 采样时间窗口 + BreakDuration = TimeSpan.FromMinutes(1), // 断路时间 + OnOpened = args => + { + _logger.LogError($"断路器已断开,将在 {args.BreakDuration.TotalSeconds} 秒后尝试恢复"); + return ValueTask.CompletedTask; + }, + OnClosed = _ => + { + _logger.LogInformation("断路器已关闭,恢复正常操作"); + return ValueTask.CompletedTask; + } + }); + + // 添加超时策略 + pipelineBuilder.AddTimeout(TimeSpan.FromSeconds(30)); + + // 构建弹性管道 + _resiliencePipeline = pipelineBuilder.Build(); + } + + /// + /// 根据方法名执行医保交易 + /// + /// 方法名称 + /// 输入数据 + /// 处理结果 + public async Task ExecuteMethod(string methodName, JObject inputData) + { + try + { + // 1. 获取方法配置 + var methodConfig = await _db.Queryable() + .FirstAsync(m => m.BIND_SYS_CODE == methodName); + + if (methodConfig == null) + { + _logger.LogError($"未找到方法配置: {methodName}"); + return new { code = -1, msg = $"未找到方法配置: {methodName}" }; + } + + // 2. 获取输入对象的映射配置 + var inputAssembly = await _db.Queryable() + .Where(a => a.METHOD_REF == methodConfig.METHOD_ID && a.PARAMETR_TYPE == 1) + .ToListAsync(); + + // 3. 转换输入数据 + var convertedInputData = ConvertToMedicalInsuranceObject(inputData, inputAssembly); + + // 4. 调用医保接口 + var responseData = await CallMedicalInsuranceService(methodConfig.METHOD_VALUE, convertedInputData); + + // 5. 获取输出对象的映射配置 + var outputAssembly = await _db.Queryable() + .Where(a => a.METHOD_REF == methodConfig.METHOD_ID && a.PARAMETR_TYPE == 2) + .ToListAsync(); + + // 6. 转换输出数据 + var convertedOutputData = ConvertToSystemObject(responseData, outputAssembly); + + // 7. 保存交易记录 + if (methodConfig.SAVE_INPUT == 1 || methodConfig.SAVE_OUTPUT == 1) + { + await SaveTransactionLog(methodConfig, inputData, responseData); + } + + return new { code = 0, msg = "处理成功", data = convertedOutputData }; + } + catch (Exception ex) + { + _logger.LogError(ex, $"执行方法 {methodName} 异常"); + return new { code = -1, msg = $"执行异常: {ex.Message}" }; + } + } + + /// + /// 将系统对象转换为医保对象 + /// + private JObject ConvertToMedicalInsuranceObject(JObject systemObject, List assemblies) + { + JObject medicalObject = new JObject(); + + foreach (var assembly in assemblies) + { + // 获取映射字段 + var mappings = _db.Queryable() + .Where(m => m.SYSTEM_TABLE_NAME == assembly.MAPPING_TABLE) + .ToList(); + + if (assembly.OBJECT_TYPE == 1) // 单个对象 + { + JObject targetObject = new JObject(); + + foreach (var mapping in mappings) + { + if (mapping.SYSTEM_FIELD != null && mapping.INTERFACE_FIELD != null && + !string.IsNullOrEmpty(mapping.SYSTEM_FIELD) && !string.IsNullOrEmpty(mapping.INTERFACE_FIELD)) + { + // 从系统对象中提取值 + var paths = assembly.OBJECT_PATH.Split('.'); + JToken currentToken = systemObject; + + foreach (var path in paths) + { + if (currentToken[path] != null) + { + currentToken = currentToken[path]; + } + else + { + currentToken = null; + break; + } + } + + if (currentToken != null && currentToken[mapping.SYSTEM_FIELD] != null) + { + targetObject[mapping.INTERFACE_FIELD] = currentToken[mapping.SYSTEM_FIELD]; + } + } + } + + // 设置到医保对象中 + var objectPath = assembly.OBJECT_PATH; + if (objectPath.Contains('.')) + { + var paths = objectPath.Split('.'); + JObject current = medicalObject; + + for (int i = 0; i < paths.Length - 1; i++) + { + var path = paths[i]; + if (current[path] == null || !(current[path] is JObject)) + { + current[path] = new JObject(); + } + current = (JObject)current[path]; + } + + current[paths[paths.Length - 1]] = targetObject; + } + else + { + medicalObject[objectPath] = targetObject; + } + } + else if (assembly.OBJECT_TYPE == 2) // 列表对象 + { + JArray targetArray = new JArray(); + + // 从系统对象中提取列表 + var paths = assembly.OBJECT_PATH.Split('.'); + JToken currentToken = systemObject; + + foreach (var path in paths) + { + if (currentToken[path] != null) + { + currentToken = currentToken[path]; + } + else + { + currentToken = null; + break; + } + } + + if (currentToken != null && currentToken is JArray sourceArray) + { + foreach (JObject sourceItem in sourceArray) + { + JObject targetItem = new JObject(); + + foreach (var mapping in mappings) + { + if (mapping.SYSTEM_FIELD != null && mapping.INTERFACE_FIELD != null && + !string.IsNullOrEmpty(mapping.SYSTEM_FIELD) && !string.IsNullOrEmpty(mapping.INTERFACE_FIELD)) + { + if (sourceItem[mapping.SYSTEM_FIELD] != null) + { + targetItem[mapping.INTERFACE_FIELD] = sourceItem[mapping.SYSTEM_FIELD]; + } + } + } + + targetArray.Add(targetItem); + } + } + + // 设置到医保对象中 + var objectPath = assembly.OBJECT_PATH; + if (objectPath.Contains('.')) + { + var paths2 = objectPath.Split('.'); + JObject current = medicalObject; + + for (int i = 0; i < paths2.Length - 1; i++) + { + var path = paths2[i]; + if (current[path] == null || !(current[path] is JObject)) + { + current[path] = new JObject(); + } + current = (JObject)current[path]; + } + + current[paths2[paths2.Length - 1]] = targetArray; + } + else + { + medicalObject[objectPath] = targetArray; + } + } + } + + return medicalObject; + } + + /// + /// 将医保对象转换为系统对象 + /// + private JObject ConvertToSystemObject(JObject medicalObject, List assemblies) + { + JObject systemObject = new JObject(); + + foreach (var assembly in assemblies) + { + // 获取映射字段 + var mappings = _db.Queryable() + .Where(m => m.OBJECT_TABLE_NAME == assembly.MAPPING_TABLE) + .ToList(); + + if (assembly.OBJECT_TYPE == 1) // 单个对象 + { + JObject targetObject = new JObject(); + + // 从医保对象中提取值 + var paths = assembly.OBJECT_PATH.Split('.'); + JToken currentToken = medicalObject; + + foreach (var path in paths) + { + if (currentToken[path] != null) + { + currentToken = currentToken[path]; + } + else + { + currentToken = null; + break; + } + } + + if (currentToken != null && currentToken is JObject sourceObject) + { + foreach (var mapping in mappings) + { + if (mapping.INTERFACE_FIELD != null && mapping.SYSTEM_FIELD != null && + !string.IsNullOrEmpty(mapping.INTERFACE_FIELD) && !string.IsNullOrEmpty(mapping.SYSTEM_FIELD)) + { + if (sourceObject[mapping.INTERFACE_FIELD] != null) + { + targetObject[mapping.SYSTEM_FIELD] = sourceObject[mapping.INTERFACE_FIELD]; + } + } + } + } + + // 设置到系统对象中 + systemObject[assembly.MAPPING_TABLE] = targetObject; + } + else if (assembly.OBJECT_TYPE == 2) // 列表对象 + { + JArray targetArray = new JArray(); + + // 从医保对象中提取列表 + var paths = assembly.OBJECT_PATH.Split('.'); + JToken currentToken = medicalObject; + + foreach (var path in paths) + { + if (currentToken[path] != null) + { + currentToken = currentToken[path]; + } + else + { + currentToken = null; + break; + } + } + + if (currentToken != null && currentToken is JArray sourceArray) + { + foreach (JObject sourceItem in sourceArray) + { + JObject targetItem = new JObject(); + + foreach (var mapping in mappings) + { + if (mapping.INTERFACE_FIELD != null && mapping.SYSTEM_FIELD != null && + !string.IsNullOrEmpty(mapping.INTERFACE_FIELD) && !string.IsNullOrEmpty(mapping.SYSTEM_FIELD)) + { + if (sourceItem[mapping.INTERFACE_FIELD] != null) + { + targetItem[mapping.SYSTEM_FIELD] = sourceItem[mapping.INTERFACE_FIELD]; + } + } + } + + targetArray.Add(targetItem); + } + } + + // 设置到系统对象中 + systemObject[assembly.MAPPING_TABLE] = targetArray; + } + } + + return systemObject; + } + + /// + /// 调用医保服务接口 + /// + private async Task CallMedicalInsuranceService(string endpoint, JObject requestData) + { + var httpClient = _httpClientFactory.CreateClient("MedicalInsurance"); + + var content = new StringContent( + requestData.ToString(), + Encoding.UTF8, + "application/json"); + + try + { + // 使用弹性管道执行HTTP请求 + var response = await _resiliencePipeline.ExecuteAsync(async cancellationToken => + { + return await httpClient.PostAsync(endpoint, content, cancellationToken); + }, CancellationToken.None); + + if (response.IsSuccessStatusCode) + { + var responseString = await response.Content.ReadAsStringAsync(); + return JObject.Parse(responseString); + } + else + { + _logger.LogError($"医保接口调用失败: {response.StatusCode}, {await response.Content.ReadAsStringAsync()}"); + throw new Exception($"医保接口调用失败: {response.StatusCode}"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "调用医保服务异常"); + throw; + } + } + + /// + /// 保存交易日志 + /// + private async Task SaveTransactionLog(STD_METHOD_CONFIG methodConfig, JObject inputData, JObject outputData) + { + try + { + // 创建日志对象 + var transLog = new + { + MethodId = methodConfig.METHOD_ID, + MethodName = methodConfig.METHOD_NAME, + RequestTime = DateTime.Now, + RequestData = methodConfig.SAVE_INPUT == 1 ? inputData.ToString() : null, + ResponseTime = DateTime.Now, + ResponseData = methodConfig.SAVE_OUTPUT == 1 ? outputData.ToString() : null, + Status = 1, // 成功 + ErrorMessage = "" + }; + + // 保存到数据库 + // TODO: 实际实现中需要定义日志表并保存数据 + _logger.LogInformation($"保存交易日志: {JsonSerializer.Serialize(transLog)}"); + } + catch (Exception ex) + { + _logger.LogError(ex, "保存交易日志异常"); + } + } + } +} \ No newline at end of file diff --git a/medical.transfomer.business/TransformerService.cs b/medical.transfomer.business/TransformerService.cs deleted file mode 100644 index 578e69b..0000000 --- a/medical.transfomer.business/TransformerService.cs +++ /dev/null @@ -1,451 +0,0 @@ -// -// ReSharper disable All -// Disable StyleCop analysis for this file -// -// Copyright (c) Manus. All rights reserved. -// - -using medical.transfomer.dto; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; -using System.Text.Json.Nodes; - -namespace medical.insu.transfomer -{ - /// - /// JSON配置转换服务类 - /// - public class TransformerService - { - private readonly TransformationConfig _config; - private readonly IValueConverter _valueConverter; // 用于字典转换等 - - /// - /// 构造函数 - /// - /// 转换配置 - /// 值转换器实例 (可选) - public TransformerService(TransformationConfig configuration, IValueConverter valueConverter = null) - { - _config = configuration ?? throw new ArgumentNullException(nameof(configuration)); - _valueConverter = valueConverter ?? new DefaultValueConverter(); // 提供一个默认实现 - } - - /// - /// 从JSON字符串加载配置 - /// - /// 包含配置的JSON字符串 - /// TransformationConfig 实例 - public static TransformationConfig LoadConfigFromJson(string jsonConfig) - { - if (string.IsNullOrWhiteSpace(jsonConfig)) - { - throw new ArgumentException("JSON configuration string cannot be null or empty.", nameof(jsonConfig)); - } - try - { - return JsonSerializer.Deserialize(jsonConfig, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true // 允许属性名不区分大小写 - }); - } - catch (JsonException ex) - { - // 在实际应用中,这里应该记录更详细的错误日志 - throw new InvalidOperationException("Failed to deserialize JSON configuration.", ex); - } - } - - /// - /// 将源数据对象根据指定方法转换为目标JSON字符串 (系统对象 -> 接口JSON) - /// - /// 方法名称 (来自 method_config) - /// 源数据对象 (通常是一个C#对象或字典) - /// 转换后的JSON字符串 - public string Transform(string methodName, object sourceObject) - { - // 1. 查找方法配置 - var method = _config.MethodConfigs.FirstOrDefault(m => m.METHOD_NAME == methodName); - if (method == null) - { - throw new ArgumentException($"Method '{methodName}' not found in configuration.", nameof(methodName)); - } - - // 2. 筛选出适用于当前方法和出参的组装规则 (parametr_type = 2 代表出参) - var assemblyRules = _config.ObjectAssemblies - .Where(a => a.METHOD_REF == method.METHOD_ID && a.PARAMETR_TYPE == 2 && a.ASSEMBLY_TYPE == "interface") // 假设是系统到接口的转换 - .OrderBy(a => a.OBJECT_PATH) // 排序以确保父节点先创建 - .ToList(); - - if (!assemblyRules.Any()) - { - // 如果没有找到针对出参的interface类型组装规则,可能需要抛出错误或返回空JSON - // 根据实际需求,这里也可以查找 'sys' 类型的规则,取决于转换方向的定义 - Console.WriteLine($"Warning: No 'interface' assembly rules found for method '{methodName}' and parameter type 2 (output)."); - return "{}"; // 或者抛出异常 - } - - var rootNode = new JsonObject(); // 创建JSON根节点 - - // 3. 遍历组装规则,构建JSON - foreach (var rule in assemblyRules) - { - // 3.1 查找对应的映射规则 (通过MappingTable关联到ObjectMapping的ObjectTableName或MappingId) - // 假设 MappingTable 直接对应 ObjectMapping 中的 ObjectTableName - var mapping = _config.ObjectMappings.FirstOrDefault(om => om.OBJECT_TABLE_NAME == rule.MAPPING_TABLE && om.INTERFACE_FIELD != null); - - // 如果MappingTable也可能是MappingId, 则需要调整查找逻辑 - // var mapping = _config.ObjectMappings.FirstOrDefault(om => - // (om.ObjectTableName == rule.MappingTable || om.MappingId == rule.MappingTable) && - // om.InterfaceField != null); - - if (mapping == null) - { - Console.WriteLine($"Warning: No object mapping found for MappingTable '{rule.MAPPING_TABLE}' in assembly rule '{rule.ID}'. Skipping path '{rule.OBJECT_PATH}'."); - continue; - } - - // 3.2 从源对象获取值 - // 这里简化处理,假设sourceObject是字典或可以通过反射获取属性 - // 实际应用中需要更健壮的取值逻辑 - object sourceValue = GetValueFromSourceObject(sourceObject, mapping.SYSTEM_FIELD); - - if (sourceValue == null && rule.OBJECT_PATH.Contains("patientId")) // 示例:特定字段的空值处理 - { - // Console.WriteLine($"Debug: Source value for {mapping.SystemField} is null."); - } - - // 3.3 值转换 (字典翻译、类型转换等) - object convertedValue = _valueConverter.Convert( - sourceValue, - mapping.SYSTEM_FIELD_TYPE?.ToString(), // Convert decimal? to string - mapping.OBJECT_FIELD_TYPE?.ToString(), // Convert decimal? to string - mapping.SYSTEM_DICT_NAME, - mapping.OBJECT_DICT_NAME, - rule.PARAMETR_TYPE - ); - - // 3.4 根据ObjectPath设置值到JSON节点 - SetValueByJsonPath(rootNode, rule.OBJECT_PATH, convertedValue, rule.OBJECT_TYPE); - } - - return rootNode.ToJsonString(new JsonSerializerOptions { WriteIndented = true }); - } - - /// - /// 从源对象中获取指定属性的值 (简化实现) - /// - private object GetValueFromSourceObject(object source, string propertyName) - { - if (source == null || string.IsNullOrEmpty(propertyName)) return null; - - if (source is IDictionary dictSource) - { - return dictSource.TryGetValue(propertyName, out var val) ? val : null; - } - if (source is JsonElement jsonElementSource && jsonElementSource.ValueKind == JsonValueKind.Object) - { - return jsonElementSource.TryGetProperty(propertyName, out var prop) ? GetValueFromJsonElement(prop) : null; - } - - // 尝试通过反射获取属性值 - var propInfo = source.GetType().GetProperty(propertyName); - if (propInfo != null) - { - return propInfo.GetValue(source); - } - // 如果是JsonNode,尝试获取 - if (source is JsonNode jnSource) - { - var parts = propertyName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); - JsonNode currentNode = jnSource; - foreach (var part in parts) - { - if (currentNode is JsonObject jo && jo.ContainsKey(part)) - { - currentNode = jo[part]; - } - else - { - return null; // 路径不存在 - } - } - if (currentNode is JsonValue jv) return jv.GetValue(); - return currentNode; //可能是JsonObject或JsonArray - } - - Console.WriteLine($"Warning: Property '{propertyName}' not found in source object of type '{source.GetType().Name}'."); - return null; - } - - private object GetValueFromJsonElement(JsonElement element) - { - switch (element.ValueKind) - { - case JsonValueKind.String: return element.GetString(); - case JsonValueKind.Number: - if (element.TryGetInt32(out int i)) return i; - if (element.TryGetInt64(out long l)) return l; - if (element.TryGetDouble(out double d)) return d; - return element.GetDecimal(); // Fallback - case JsonValueKind.True: return true; - case JsonValueKind.False: return false; - case JsonValueKind.Null: return null; - case JsonValueKind.Object: return element; // Or parse to dictionary - case JsonValueKind.Array: return element; // Or parse to list - default: return null; - } - } - - /// - /// 根据JSONPath在JsonNode中设置值 (简化实现, 支持基本的路径) - /// - private void SetValueByJsonPath(JsonNode rootNode, string path, object value, int objectType) - { - if (string.IsNullOrWhiteSpace(path) || rootNode == null) return; - - // 移除起始的 "$." - if (path.StartsWith("$.")) - { - path = path.Substring(2); - } - - var segments = path.Split('.'); - JsonNode currentNode = rootNode; - - for (int i = 0; i < segments.Length - 1; i++) - { - var segment = segments[i]; - if (currentNode[segment] == null) - { - // 根据下一个segment是否是数组索引来决定创建JsonObject还是JsonArray - // 此处简化:如果下一个路径是数字,则认为是数组索引,但设计文档中object_type更可靠 - // 暂时简单处理,都创建JsonObject,实际应根据object_assembly的object_type判断 - currentNode[segment] = new JsonObject(); - } - currentNode = currentNode[segment]; - if (currentNode == null) // 防御性编程,如果中间节点创建失败或路径无效 - { - Console.WriteLine($"Error: Could not navigate or create path segment '{segment}' in JSON path '{path}'."); - return; - } - } - - var lastSegment = segments.Last(); - JsonNode valueNode = value == null ? null : JsonValue.Create(value); // JsonValue.Create可以处理多种基本类型 - - if (value is JsonElement je) - { - valueNode = JsonNode.Parse(je.GetRawText()); - } - else if (value is IDictionary dictValue) - { - valueNode = new JsonObject(); - foreach(var kvp in dictValue) - { - ((JsonObject)valueNode).Add(kvp.Key, JsonValue.Create(kvp.Value)); - } - } - else if (value is IEnumerable listValue && !(value is string)) // 确保不是字符串 - { - var jsonArray = new JsonArray(); - foreach (var item in listValue) - { - jsonArray.Add(JsonValue.Create(item)); - } - valueNode = jsonArray; - } - - if (objectType == 2) // 列表类型 - { - if (currentNode[lastSegment] == null || !(currentNode[lastSegment] is JsonArray)) - { - currentNode[lastSegment] = new JsonArray(); - } - ((JsonArray)currentNode[lastSegment]).Add(valueNode); - } - else // 对象类型 - { - ((JsonObject)currentNode)[lastSegment] = valueNode; - } - } - - /// - /// 将接口JSON字符串根据指定方法转换为目标系统对象 (接口JSON -> 系统对象) - /// - /// 期望的目标系统对象类型 - /// 方法名称 (来自 method_config) - /// 源JSON字符串 - /// 转换后的目标系统对象 - public T Transform(string methodName, string sourceJson) where T : new() - { - // 1. 查找方法配置 - var method = _config.MethodConfigs.FirstOrDefault(m => m.METHOD_NAME == methodName); - if (method == null) - { - throw new ArgumentException($"Method '{methodName}' not found in configuration.", nameof(methodName)); - } - - // 2. 解析源JSON - JsonNode sourceNode; - try - { - sourceNode = JsonNode.Parse(sourceJson); - } - catch (JsonException ex) - { - throw new ArgumentException("Invalid source JSON string.", nameof(sourceJson), ex); - } - if (sourceNode == null) throw new ArgumentException("Parsed source JSON is null.", nameof(sourceJson)); - - // 3. 筛选出适用于当前方法和入参的组装规则 (parametr_type = 1 代表入参) - var assemblyRules = _config.ObjectAssemblies - .Where(a => a.METHOD_REF == method.METHOD_ID && a.PARAMETR_TYPE == 1 && a.ASSEMBLY_TYPE == "sys") // 假设是接口到系统的转换 - .OrderBy(a => a.OBJECT_PATH) - .ToList(); - - if (!assemblyRules.Any()) - { - Console.WriteLine($"Warning: No 'sys' assembly rules found for method '{methodName}' and parameter type 1 (input)."); - return new T(); // 或者抛出异常 - } - - T targetObject = new T(); - - // 4. 遍历组装规则, 填充目标对象 - foreach (var rule in assemblyRules) - { - var mapping = _config.ObjectMappings.FirstOrDefault(om => om.OBJECT_TABLE_NAME == rule.MAPPING_TABLE && om.SYSTEM_FIELD != null); - if (mapping == null) - { - Console.WriteLine($"Warning: No object mapping found for MappingTable '{rule.MAPPING_TABLE}' in assembly rule '{rule.ID}'. Skipping path '{rule.OBJECT_PATH}'."); - continue; - } - - // 4.1 从源JSON获取值 - object sourceValue = GetValueByJsonPath(sourceNode, rule.OBJECT_PATH); - - // 4.2 值转换 - object convertedValue = _valueConverter.Convert(sourceValue, mapping.OBJECT_FIELD_TYPE, mapping.SYSTEM_FIELD_TYPE, mapping.OBJECT_DICT_NAME, mapping.SYSTEM_DICT_NAME, rule.PARAMETR_TYPE); - - // 4.3 设置到目标对象属性 (简化实现, 假设属性名与SystemField一致) - // 实际应用中可能需要更复杂的属性设置逻辑,例如处理嵌套对象 - var propInfo = typeof(T).GetProperty(mapping.SYSTEM_FIELD); - if (propInfo != null && propInfo.CanWrite) - { - try - { - // 类型转换,确保赋的值与属性类型匹配 - var typedValue = ConvertToPropertyType(convertedValue, propInfo.PropertyType); - propInfo.SetValue(targetObject, typedValue); - } - catch (Exception ex) - { - Console.WriteLine($"Error setting property '{mapping.SYSTEM_FIELD}': {ex.Message}"); - } - } - else - { - Console.WriteLine($"Warning: Property '{mapping.SYSTEM_FIELD}' not found or not writable on type '{typeof(T).Name}'."); - } - } - return targetObject; - } - - /// - /// 根据JSONPath从JsonNode中获取值 (简化实现) - /// - private object GetValueByJsonPath(JsonNode rootNode, string path) - { - if (string.IsNullOrWhiteSpace(path) || rootNode == null) return null; - if (path.StartsWith("$.")) path = path.Substring(2); - - var segments = path.Split('.'); - JsonNode currentNode = rootNode; - foreach (var segment in segments) - { - if (currentNode is JsonObject jo && jo.ContainsKey(segment)) - { - currentNode = jo[segment]; - } - // TODO: Add support for array indexing if needed, e.g., "items[0].name" - else - { - return null; // 路径不存在 - } - } - if (currentNode is JsonValue jv) return jv.GetValue(); // GetValue 会尝试转换为合适的.NET类型 - return currentNode; //可能是JsonObject或JsonArray,调用者需要进一步处理 - } - - /// - /// 将值转换为目标属性类型 (简化实现) - /// - private object ConvertToPropertyType(object value, Type targetType) - { - if (value == null) return null; - if (targetType.IsAssignableFrom(value.GetType())) return value; - try - { - return Convert.ChangeType(value, targetType); - } - catch - { - // 对于复杂类型或JsonElement,可能需要特殊处理 - if (value is JsonElement je && targetType == typeof(string)) return je.ToString(); - // 添加更多转换逻辑... - return null; - } - } - } - - /// - /// 值转换器接口 (用于字典翻译、类型转换等) - /// - public interface IValueConverter - { - /// - /// 执行值转换 - /// - /// 源值 - /// 源类型字符串 (来自配置) - /// 目标类型字符串 (来自配置) - /// 源字典名称 (来自配置) - /// 目标字典名称 (来自配置) - /// 参数方向 (1入参,2出参) - /// 转换后的值 - object Convert(object sourceValue, string sourceType, string targetType, string sourceDictName, string targetDictName, int paramDirection); - object Convert(object sourceValue, decimal? oBJECT_FIELD_TYPE, decimal? sYSTEM_FIELD_TYPE, string oBJECT_DICT_NAME, string sYSTEM_DICT_NAME, int pARAMETR_TYPE); - } - - /// - /// 默认的值转换器实现 (简单类型转换,无字典转换) - /// - public class DefaultValueConverter : IValueConverter - { - public object Convert(object sourceValue, string sourceType, string targetType, string sourceDictName, string targetDictName, int paramDirection) - { - // 简单示例:如果类型字符串匹配C#类型,尝试转换 - if (sourceValue == null) return null; - - try - { - Type targetTypeResolved = Type.GetType(targetType) ?? typeof(object); - return System.Convert.ChangeType(sourceValue, targetTypeResolved); - } - catch - { - return sourceValue; - } - } - - public object Convert(object sourceValue, decimal? objectFieldType, decimal? systemFieldType, string objectDictName, string systemDictName, int paramDirection) - { - // 示例实现:直接返回源值,实际应用中需要根据字段类型和字典名称进行转换 - return sourceValue; - } - } -} - diff --git a/medical.transfomer.business/medical.transfomer.business.csproj b/medical.transfomer.business/medical.transfomer.business.csproj index 245e27c..2618fee 100644 --- a/medical.transfomer.business/medical.transfomer.business.csproj +++ b/medical.transfomer.business/medical.transfomer.business.csproj @@ -6,9 +6,18 @@ enable + + + + + + + + + + - diff --git a/medical.transfomer.service/Class1.cs b/medical.transfomer.service/Class1.cs deleted file mode 100644 index c35087d..0000000 --- a/medical.transfomer.service/Class1.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace medical.transfomer.service -{ - public class Class1 - { - - } -} diff --git a/medical.transfomer.service/MedicalInsuranceTransactionService.cs b/medical.transfomer.service/MedicalInsuranceTransactionService.cs new file mode 100644 index 0000000..fda1c6d --- /dev/null +++ b/medical.transfomer.service/MedicalInsuranceTransactionService.cs @@ -0,0 +1,238 @@ +using System; +using System.Text; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using ReZero.DependencyInjection; +using medical.transfomer.business; +using medical.transfomer.entity; +using SqlSugar; + +namespace medical.transfomer.service +{ + /// + /// 医保交易服务,提供医保接口的请求处理功能 + /// + public class MedicalInsuranceTransactionService : IScopeContract + { + private readonly TransformerFactory _transformerFactory; + private readonly ILogger _logger; + private readonly ISqlSugarClient _db; + private readonly IHttpClientFactory _httpClientFactory; + + public MedicalInsuranceTransactionService( + TransformerFactory transformerFactory, + ILogger logger, + ISqlSugarClient db, + IHttpClientFactory httpClientFactory) + { + _transformerFactory = transformerFactory; + _logger = logger; + _db = db; + _httpClientFactory = httpClientFactory; + } + + /// + /// 执行医保交易 + /// + /// 方法名称 + /// 输入数据 + /// 处理结果 + public async Task ExecuteTransaction(string methodName, JObject inputData) + { + Stopwatch stopwatch = new Stopwatch(); + stopwatch.Start(); + + try + { + _logger.LogInformation($"开始执行医保交易: {methodName}"); + + // 调用转换工厂执行方法 + var result = await _transformerFactory.ExecuteMethod(methodName, inputData); + + _logger.LogInformation($"医保交易执行完成: {methodName}"); + + // 计算耗时 + stopwatch.Stop(); + long elapsedTime = stopwatch.ElapsedMilliseconds; + + // 保存成功日志 + await SaveTransactionLog( + methodName, + inputData.ToString(), + result == null ? null : JsonSerializer.Serialize(result), + 1, + "", + elapsedTime); + + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, $"执行医保交易异常: {methodName}"); + + // 计算耗时 + stopwatch.Stop(); + long elapsedTime = stopwatch.ElapsedMilliseconds; + + // 保存失败日志 + await SaveTransactionLog( + methodName, + inputData.ToString(), + null, + 0, + ex.Message, + elapsedTime); + + return new { code = -1, msg = $"交易执行异常: {ex.Message}" }; + } + } + + /// + /// 根据方法ID获取方法配置信息 + /// + /// 方法ID + /// 方法配置信息 + public async Task GetMethodConfigById(string methodId) + { + return await _db.Queryable() + .FirstAsync(m => m.METHOD_ID == methodId); + } + + /// + /// 根据方法名称获取方法配置信息 + /// + /// 方法名称 + /// 方法配置信息 + public async Task GetMethodConfigByName(string methodName) + { + return await _db.Queryable() + .FirstAsync(m => m.METHOD_NAME == methodName); + } + + /// + /// 获取方法的映射配置 + /// + /// 方法ID + /// 参数类型(1:入参, 2:出参) + /// 对象装配配置列表 + public async Task> GetMethodAssemblies(string methodId, int parameterType) + { + return await _db.Queryable() + .Where(a => a.METHOD_REF == methodId && a.PARAMETR_TYPE == parameterType) + .ToListAsync(); + } + + /// + /// 获取映射表的字段映射关系 + /// + /// 表名 + /// 是否为系统到接口的映射 + /// 字段映射关系列表 + public async Task> GetFieldMappings(string tableName, bool isSystemToInterface) + { + if (isSystemToInterface) + { + return await _db.Queryable() + .Where(m => m.SYSTEM_TABLE_NAME == tableName) + .ToListAsync(); + } + else + { + return await _db.Queryable() + .Where(m => m.OBJECT_TABLE_NAME == tableName) + .ToListAsync(); + } + } + + /// + /// 保存交易日志 + /// + /// 方法名称 + /// 请求数据 + /// 响应数据 + /// 状态(1:成功, 0:失败) + /// 错误信息 + /// 耗时(毫秒) + /// + public async Task SaveTransactionLog( + string methodName, + string requestData, + string responseData, + int status, + string errorMessage, + long elapsedTime) + { + try + { + // 获取方法配置 + var methodConfig = await GetMethodConfigByName(methodName); + + if (methodConfig == null) + { + _logger.LogWarning($"未找到方法配置,无法保存交易日志: {methodName}"); + return; + } + + // 创建日志实体 + var transLog = new STD_TRANSACTION_LOG + { + METHOD_ID = methodConfig.METHOD_ID, + METHOD_NAME = methodName, + REQUEST_TIME = DateTime.Now, + REQUEST_DATA = methodConfig.SAVE_INPUT == 1 ? requestData : null, + RESPONSE_TIME = DateTime.Now, + RESPONSE_DATA = methodConfig.SAVE_OUTPUT == 1 ? responseData : null, + STATUS = status, + ERROR_MESSAGE = errorMessage, + ELAPSED_TIME = elapsedTime, + CLIENT_IP = "", // TODO: 获取客户端IP + USER_ID = "" // TODO: 获取当前用户ID + }; + + // 保存到数据库 + await _db.Insertable(transLog).ExecuteCommandAsync(); + + _logger.LogInformation($"保存交易日志成功: {methodName}, 状态: {status}"); + } + catch (Exception ex) + { + _logger.LogError(ex, "保存交易日志异常"); + } + } + + /// + /// 直接调用医保接口 + /// + /// 接口地址 + /// 请求数据 + /// 响应结果 + public async Task CallMedicalInsuranceApi(string endpoint, JObject requestData) + { + try + { + var httpClient = _httpClientFactory.CreateClient("MedicalInsurance"); + + var content = new StringContent( + requestData.ToString(), + Encoding.UTF8, + "application/json"); + + var response = await httpClient.PostAsync(endpoint, content); + response.EnsureSuccessStatusCode(); + + var responseString = await response.Content.ReadAsStringAsync(); + return JObject.Parse(responseString); + } + catch (Exception ex) + { + _logger.LogError(ex, $"调用医保接口异常: {endpoint}"); + throw; + } + } + } +} \ No newline at end of file diff --git a/medical.transfomer.service/medical.transfomer.service.csproj b/medical.transfomer.service/medical.transfomer.service.csproj index fa71b7a..4030cdb 100644 --- a/medical.transfomer.service/medical.transfomer.service.csproj +++ b/medical.transfomer.service/medical.transfomer.service.csproj @@ -6,4 +6,17 @@ enable + + + + + + + + + + + + +