You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
451 lines
20 KiB
451 lines
20 KiB
// <auto-generated/> |
|
// ReSharper disable All |
|
// Disable StyleCop analysis for this file |
|
// <copyright file="TransformerService.cs" company="Manus"> |
|
// Copyright (c) Manus. All rights reserved. |
|
// </copyright> |
|
|
|
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 |
|
{ |
|
/// <summary> |
|
/// JSON配置转换服务类 |
|
/// </summary> |
|
public class TransformerService |
|
{ |
|
private readonly TransformationConfig _config; |
|
private readonly IValueConverter _valueConverter; // 用于字典转换等 |
|
|
|
/// <summary> |
|
/// 构造函数 |
|
/// </summary> |
|
/// <param name="configuration">转换配置</param> |
|
/// <param name="valueConverter">值转换器实例 (可选)</param> |
|
public TransformerService(TransformationConfig configuration, IValueConverter valueConverter = null) |
|
{ |
|
_config = configuration ?? throw new ArgumentNullException(nameof(configuration)); |
|
_valueConverter = valueConverter ?? new DefaultValueConverter(); // 提供一个默认实现 |
|
} |
|
|
|
/// <summary> |
|
/// 从JSON字符串加载配置 |
|
/// </summary> |
|
/// <param name="jsonConfig">包含配置的JSON字符串</param> |
|
/// <returns>TransformationConfig 实例</returns> |
|
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<TransformationConfig>(jsonConfig, new JsonSerializerOptions |
|
{ |
|
PropertyNameCaseInsensitive = true // 允许属性名不区分大小写 |
|
}); |
|
} |
|
catch (JsonException ex) |
|
{ |
|
// 在实际应用中,这里应该记录更详细的错误日志 |
|
throw new InvalidOperationException("Failed to deserialize JSON configuration.", ex); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 将源数据对象根据指定方法转换为目标JSON字符串 (系统对象 -> 接口JSON) |
|
/// </summary> |
|
/// <param name="methodName">方法名称 (来自 method_config)</param> |
|
/// <param name="sourceObject">源数据对象 (通常是一个C#对象或字典)</param> |
|
/// <returns>转换后的JSON字符串</returns> |
|
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 }); |
|
} |
|
|
|
/// <summary> |
|
/// 从源对象中获取指定属性的值 (简化实现) |
|
/// </summary> |
|
private object GetValueFromSourceObject(object source, string propertyName) |
|
{ |
|
if (source == null || string.IsNullOrEmpty(propertyName)) return null; |
|
|
|
if (source is IDictionary<string, object> 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<object>(); |
|
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; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 根据JSONPath在JsonNode中设置值 (简化实现, 支持基本的路径) |
|
/// </summary> |
|
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<string, object> dictValue) |
|
{ |
|
valueNode = new JsonObject(); |
|
foreach(var kvp in dictValue) |
|
{ |
|
((JsonObject)valueNode).Add(kvp.Key, JsonValue.Create(kvp.Value)); |
|
} |
|
} |
|
else if (value is IEnumerable<object> 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; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 将接口JSON字符串根据指定方法转换为目标系统对象 (接口JSON -> 系统对象) |
|
/// </summary> |
|
/// <typeparam name="T">期望的目标系统对象类型</typeparam> |
|
/// <param name="methodName">方法名称 (来自 method_config)</param> |
|
/// <param name="sourceJson">源JSON字符串</param> |
|
/// <returns>转换后的目标系统对象</returns> |
|
public T Transform<T>(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; |
|
} |
|
|
|
/// <summary> |
|
/// 根据JSONPath从JsonNode中获取值 (简化实现) |
|
/// </summary> |
|
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<object>(); // GetValue<object> 会尝试转换为合适的.NET类型 |
|
return currentNode; //可能是JsonObject或JsonArray,调用者需要进一步处理 |
|
} |
|
|
|
/// <summary> |
|
/// 将值转换为目标属性类型 (简化实现) |
|
/// </summary> |
|
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; |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 值转换器接口 (用于字典翻译、类型转换等) |
|
/// </summary> |
|
public interface IValueConverter |
|
{ |
|
/// <summary> |
|
/// 执行值转换 |
|
/// </summary> |
|
/// <param name="sourceValue">源值</param> |
|
/// <param name="sourceType">源类型字符串 (来自配置)</param> |
|
/// <param name="targetType">目标类型字符串 (来自配置)</param> |
|
/// <param name="sourceDictName">源字典名称 (来自配置)</param> |
|
/// <param name="targetDictName">目标字典名称 (来自配置)</param> |
|
/// <param name="paramDirection">参数方向 (1入参,2出参)</param> |
|
/// <returns>转换后的值</returns> |
|
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); |
|
} |
|
|
|
/// <summary> |
|
/// 默认的值转换器实现 (简单类型转换,无字典转换) |
|
/// </summary> |
|
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; |
|
} |
|
} |
|
} |
|
|
|
|