From 1e5ebb398eebd0ae1f0266261c0fa3513b160bab Mon Sep 17 00:00:00 2001 From: xiaolipro <2357729423@qq.com> Date: Fri, 1 Aug 2025 10:21:04 +0800 Subject: [PATCH] =?UTF-8?q?sync=EF=BC=9Acore=20algorithm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ForwardCore/DefaultForwardFlow.cs | 1 - .../Services/InterfaceForwardCommon.cs | 2 +- .../InterfaceMap/CustomContractResolver.cs | 3 +- .../InterfaceMapPublishService.cs | 6 +- .../ServiceProviderAuthService.cs | 4 +- .../Dtos/ForwardCoreContextCache.cs | 5 + .../Dtos/InterfaceParameterMapDto.cs | 10 + .../Enum/AuthParameterFlag.cs | 3 +- .../Helpers/JsonHelper.cs | 609 +++++++++++------- .../InterfaceForwardQuery.cs | 47 +- 10 files changed, 443 insertions(+), 247 deletions(-) diff --git a/src/InterfaceForward.Application/ForwardCore/DefaultForwardFlow.cs b/src/InterfaceForward.Application/ForwardCore/DefaultForwardFlow.cs index f392804..600198d 100644 --- a/src/InterfaceForward.Application/ForwardCore/DefaultForwardFlow.cs +++ b/src/InterfaceForward.Application/ForwardCore/DefaultForwardFlow.cs @@ -4,7 +4,6 @@ using System.Text; using System.Text.RegularExpressions; using System.Web; using System.Xml; -using Fake.Application; using Fake.DependencyInjection; using InterfaceForward.Application.Contracts.ForwardCore; using InterfaceForward.Application.Helpers; diff --git a/src/InterfaceForward.Application/Services/InterfaceForwardCommon.cs b/src/InterfaceForward.Application/Services/InterfaceForwardCommon.cs index 99d023a..001e440 100644 --- a/src/InterfaceForward.Application/Services/InterfaceForwardCommon.cs +++ b/src/InterfaceForward.Application/Services/InterfaceForwardCommon.cs @@ -45,7 +45,7 @@ public class InterfaceForwardCommon( .ToObject(); if (cacheContext == null) { - var systemInterface = await interfaceForwardQuery.GetInterfaceByCodesAsync(upStreamCode); + var systemInterface = await interfaceForwardQuery.GetInterfaceByCodeAsync(upStreamCode); if (systemInterface == null) throw new BusinessException(message: "系统接口不存在"); logRepository.AppRequestLog.InterfaceCode = systemInterface.Code; diff --git a/src/InterfaceForward.Application/Services/InterfaceMap/CustomContractResolver.cs b/src/InterfaceForward.Application/Services/InterfaceMap/CustomContractResolver.cs index 8d754dc..306a071 100644 --- a/src/InterfaceForward.Application/Services/InterfaceMap/CustomContractResolver.cs +++ b/src/InterfaceForward.Application/Services/InterfaceMap/CustomContractResolver.cs @@ -38,7 +38,8 @@ internal class CustomContractResolver : DefaultContractResolver if (member.DeclaringType == typeof(InterfaceParameterMapDto)) { if (member.Name.IsIn(nameof(InterfaceParameterMapDto.Id), nameof(InterfaceParameterMapDto.PId), - nameof(InterfaceParameterMapDto.InterfaceId))) return true; + nameof(InterfaceParameterMapDto.InterfaceId), + nameof(InterfaceParameterMapDto.FreeMap))) return true; } // 其他判断逻辑... diff --git a/src/InterfaceForward.Application/Services/InterfaceMap/InterfaceMapPublishService.cs b/src/InterfaceForward.Application/Services/InterfaceMap/InterfaceMapPublishService.cs index f52dd85..94402ac 100644 --- a/src/InterfaceForward.Application/Services/InterfaceMap/InterfaceMapPublishService.cs +++ b/src/InterfaceForward.Application/Services/InterfaceMap/InterfaceMapPublishService.cs @@ -59,7 +59,7 @@ public class InterfaceMapPublishService( public async Task GetPublishCompareAsync([Required] string interfaceCode, [Required] int serviceProviderId, string? version) { - var systemInterface = await interfaceForwardQuery.GetInterfaceByCodesAsync(interfaceCode); + var systemInterface = await interfaceForwardQuery.GetInterfaceByCodeAsync(interfaceCode); if (systemInterface == null) throw new BusinessException(message: "系统接口不存在"); var serviceProvider = await interfaceForwardQuery.GetServiceProviderByIdAsync(serviceProviderId); @@ -98,7 +98,7 @@ public class InterfaceMapPublishService( [HttpPost("InterfaceMap/Publish")] public async Task PublishAsync(InterfaceMapPublishInput input) { - var systemInterface = await interfaceForwardQuery.GetInterfaceByCodesAsync(input.InterfaceCode); + var systemInterface = await interfaceForwardQuery.GetInterfaceByCodeAsync(input.InterfaceCode); if (systemInterface == null) throw new BusinessException(message: "系统接口不存在"); var serviceProvider = await interfaceForwardQuery.GetServiceProviderByIdAsync(input.ServiceProviderId); @@ -151,7 +151,7 @@ public class InterfaceMapPublishService( [HttpPost("InterfaceMap/PublishRollback")] public async Task PublishRollbackAsync(InterfaceMapRollbackInput input) { - var systemInterface = await interfaceForwardQuery.GetInterfaceByCodesAsync(input.InterfaceCode); + var systemInterface = await interfaceForwardQuery.GetInterfaceByCodeAsync(input.InterfaceCode); if (systemInterface == null) throw new BusinessException(message: "系统接口不存在"); var serviceProvider = await interfaceForwardQuery.GetServiceProviderByIdAsync(input.ServiceProviderId); if (serviceProvider == null) throw new BusinessException(message: "服务商不存在"); diff --git a/src/InterfaceForward.Application/Services/ServiceProvider/ServiceProviderAuthService.cs b/src/InterfaceForward.Application/Services/ServiceProvider/ServiceProviderAuthService.cs index a4529bb..455471b 100644 --- a/src/InterfaceForward.Application/Services/ServiceProvider/ServiceProviderAuthService.cs +++ b/src/InterfaceForward.Application/Services/ServiceProvider/ServiceProviderAuthService.cs @@ -108,7 +108,7 @@ public class ServiceProviderAuthService : ApplicationService } res.FixedEffectTime = authConfigs.FirstOrDefault(x => x.Flag == AuthParameterFlag.FixedEffectTime)?.Value1.ToInt(); - res.Script = authConfigs.FirstOrDefault(x => x.Flag == AuthParameterFlag.Script)?.Value1; + res.Script = authConfigs.FirstOrDefault(x => x.Flag == AuthParameterFlag.PrefixScript)?.Value1; return res; } @@ -192,7 +192,7 @@ public class ServiceProviderAuthService : ApplicationService { authConfigsEntities.Add(new ServiceProviderAuthConfigEntity { - Flag = AuthParameterFlag.Script, + Flag = AuthParameterFlag.PrefixScript, Value1 = input.Script, }); } diff --git a/src/InterfaceForward.Domain.Shared/Dtos/ForwardCoreContextCache.cs b/src/InterfaceForward.Domain.Shared/Dtos/ForwardCoreContextCache.cs index 13bb303..362b234 100644 --- a/src/InterfaceForward.Domain.Shared/Dtos/ForwardCoreContextCache.cs +++ b/src/InterfaceForward.Domain.Shared/Dtos/ForwardCoreContextCache.cs @@ -77,6 +77,11 @@ public class TargetInterfaceSummary /// public List InParamTreeList { get; set; } = []; + /// + /// MapAlias不为空的入参映射,便于快速查找 + /// + public List InParamMappedList { get; set; } + /// /// 固定值列表 /// diff --git a/src/InterfaceForward.Domain.Shared/Dtos/InterfaceParameterMapDto.cs b/src/InterfaceForward.Domain.Shared/Dtos/InterfaceParameterMapDto.cs index 2d9fe10..29361ef 100644 --- a/src/InterfaceForward.Domain.Shared/Dtos/InterfaceParameterMapDto.cs +++ b/src/InterfaceForward.Domain.Shared/Dtos/InterfaceParameterMapDto.cs @@ -33,6 +33,11 @@ public class InterfaceParameterMapDto /// 映射参数别名(被重构的) /// public string MapAlias { get; set; } + + /// + /// 数组映射别名,原数据存储数组都是结构只映射具体的值,用于重新构建数组映射,有叶子节点映射则构建映射的数组层级 + /// + public string ArrayMapPath { get; set; } /// /// 参数类型 @@ -43,4 +48,9 @@ public class InterfaceParameterMapDto /// 必填的 /// public bool IsRequired { get; set; } + + /// + /// 自由映射 + /// + public bool FreeMap { get; set; } } \ No newline at end of file diff --git a/src/InterfaceForward.Domain.Shared/Enum/AuthParameterFlag.cs b/src/InterfaceForward.Domain.Shared/Enum/AuthParameterFlag.cs index 3b27b78..430f612 100644 --- a/src/InterfaceForward.Domain.Shared/Enum/AuthParameterFlag.cs +++ b/src/InterfaceForward.Domain.Shared/Enum/AuthParameterFlag.cs @@ -9,5 +9,6 @@ public enum AuthParameterFlag Token = 2, EffectTime = 3, FixedEffectTime = 4, - Script = 5 + PrefixScript = 5, + PostfixScript = 6, } \ No newline at end of file diff --git a/src/InterfaceForward.Domain.Shared/Helpers/JsonHelper.cs b/src/InterfaceForward.Domain.Shared/Helpers/JsonHelper.cs index 6f79903..50503c7 100644 --- a/src/InterfaceForward.Domain.Shared/Helpers/JsonHelper.cs +++ b/src/InterfaceForward.Domain.Shared/Helpers/JsonHelper.cs @@ -1,4 +1,6 @@ -using Fake; +using System.Text; +using System.Text.RegularExpressions; +using Fake; using Fake.Application; using InterfaceForward.Domain.Shared.Dtos; using InterfaceForward.Domain.Shared.Enum; @@ -8,37 +10,22 @@ namespace InterfaceForward.Domain.Shared.Helpers; public static class JsonHelper { - /// - /// json重构 - /// - /// - /// - /// 返回的json类型 - /// - public static JToken Restructure(JToken jtoken, List? treeList, + private static readonly Regex StarRegex = new(@"\[\*\]", RegexOptions.Compiled); + + public static JToken Restructure(JToken jtoken, List treeList, ParameterType jsonType = ParameterType.Object) { - // 没有映射时,是空树 - if (treeList == default || treeList.Count == 0) return jtoken; + if (treeList == null || treeList.Count == 0) return jtoken; var idxList = new List(); - var hash = Json2Hash(jtoken); // 将json转化成hash - - switch (jsonType) + var arrLenHashMap = new Dictionary(); //为构造数组长度的kv + var hash = Json2Hash(jtoken,arrLenHashMap); + return jsonType switch { - case ParameterType.Array: - idxList.Add(-1); - return dfs_array(jtoken, treeList[0], hash, idxList); - case ParameterType.String: - case ParameterType.Float: - case ParameterType.Boolean: - case ParameterType.Integer: - var value = hash[treeList[0].MapAlias!]; - return new JValue(value); - case ParameterType.Object: - default: - return dfs_object(jtoken, treeList, hash, idxList); - } + ParameterType.Array => CreateArray(jtoken, treeList[0], hash, idxList), + ParameterType.Object => CreateObject(jtoken, treeList, hash, idxList), + _ => new JValue(hash[treeList[0].MapAlias!]) + }; } public static void RemoveNullProperties(JToken token) @@ -70,28 +57,19 @@ public static class JsonHelper } } - private static Dictionary Json2Hash(JToken jtoken) + private static Dictionary Json2Hash(JToken jtoken,Dictionary arrLenHashMap) { - if (jtoken is JObject obj) + return jtoken switch { - return Jobject2Hash(obj); - } - - if (jtoken is JArray arr) - { - return JArrayToHash(arr); - } - - // jvalue - return new Dictionary() - { - { jtoken.Path, jtoken } + JObject obj => Jobject2Hash(obj,arrLenHashMap), + JArray arr => JArrayToHash(arr,arrLenHashMap), + _ => new Dictionary { { jtoken.Path, jtoken } } }; } - private static Dictionary Jobject2Hash(JObject jobject) + private static Dictionary Jobject2Hash(JObject jobject,Dictionary arrLenHashMap) { - Dictionary res = new(); + var res = new Dictionary(); var q = new Queue(jobject.Properties()); while (q.Count > 0) { @@ -105,11 +83,11 @@ public static class JsonHelper } else if (cur.Value is JArray arr) { - Add(res, JArrayToHash(arr)); + Add(res, JArrayToHash(arr,arrLenHashMap)); } else { - res.Add(cur.Path, cur.Value); + res[cur.Path] = cur.Value; } } @@ -118,33 +96,46 @@ public static class JsonHelper public static List BuildTreeList(IReadOnlyCollection list) { - if (list.All(x => x.MapAlias.IsNullOrWhiteSpace())) - { - } - - return DoBuild(list, 0); + return list.All(x => x.MapAlias.IsNullOrWhiteSpace()) ? null : DoBuild(list, 0); } private static List DoBuild(IReadOnlyCollection list, int pid) { var res = new List(); - - foreach (var child in list.Where(x => x.PId == pid)) + foreach (var item in list.Where(x => x.PId == pid)) { - var node = new ParameterNode(); - node.Id = child.Id; - node.PId = child.PId; - node.Name = child.Name; - node.Type = child.Type; - node.Alias = child.Alias; - node.IsRequired = child.IsRequired; + var node = new ParameterNode + { + Id = item.Id, + PId = item.PId, + Name = item.Name, + Type = item.Type, + Alias = item.Alias, + IsRequired = item.IsRequired, + MapAlias = item.MapAlias + }; + if (node.Type is ParameterType.Object or ParameterType.Array) { - node.Children = DoBuild(list, child.Id); + node.Children = DoBuild(list, item.Id); + GetMapPath(node); } - else + else if (item.FreeMap && !item.MapAlias.IsNullOrEmpty()) { - node.MapAlias = child.MapAlias; + var left = item.Alias.Split("[*]"); + var right = item.MapAlias.Split("[*]"); + int count = Math.Abs(left.Length - right.Length); + if (count != 0) + { + if (left.Length > right.Length) + { + node.Alias = StarRegex.Replace(item.Alias, m => count-- > 0 ? "[0]" : m.Value); + } + else + { + node.MapAlias = StarRegex.Replace(item.MapAlias, m => count-- > 0 ? "[0]" : m.Value); + } + } } res.Add(node); @@ -153,24 +144,78 @@ public static class JsonHelper return res; } - private static Dictionary JArrayToHash(JArray jarr) + /// + /// 构建并可返回数组映射的路径MapPath,只在DoBuild递归方法中适用 + /// + /// 数组的node + /// + private static string GetMapPath(ParameterNode node){ + string path=null; + if(node.Children.Count>0){ + //如果是数组,则以第一个子节点MapAlias的上一级构造为数组的MapPath;如果数组是一个基础类型则直接取MapAlias + //逐级构造,但是只有是数组时才取用,其他类型只为数组提供依据 + if((node.Type is ParameterType.Array ) && (node.Children[0].Type is ParameterType.Object || node.Children[0].Type is ParameterType.Array) ){ + if (!node.Children[0].ArrayMapPath.IsNullOrWhiteSpace()){ + int len=node.Children[0].ArrayMapPath.LastIndexOf('.')>0?node.Children[0].ArrayMapPath.LastIndexOf('.'):node.Children[0].ArrayMapPath.Length; + path=string.IsNullOrWhiteSpace(node.Children[0].ArrayMapPath)?null: + node.Children[0].ArrayMapPath.Substring(0,len); + // else{ + // path=node.Children[0].ArrayMapPath; + // } + } + } + else{ + if(node.Type is ParameterType.Array){ //子对象为基础数据类型 + path=node.Children[0].MapAlias; + } + + if(node.Type is ParameterType.Object){ + if(!(node.Children[0].Type is ParameterType.Object || node.Children[0].Type is ParameterType.Array) && !string.IsNullOrWhiteSpace(node.Children[0].MapAlias)){ + if (!node.Children[0].MapAlias.IsNullOrWhiteSpace() && node.Name!="obj"){ + int len=node.Children[0].MapAlias.LastIndexOf('.')>0?node.Children[0].MapAlias.LastIndexOf('.'):node.Children[0].MapAlias.Length; + path=string.IsNullOrWhiteSpace(node.Children[0].MapAlias)?null: + node.Children[0].MapAlias.Substring(0,len); + } + else{ + path=node.Children[0].MapAlias; + } + } + else{ + if (!node.Children[0].ArrayMapPath.IsNullOrWhiteSpace() && node.Name!="obj"){ + int len=node.Children[0].ArrayMapPath.LastIndexOf('.')>0?node.Children[0].ArrayMapPath.LastIndexOf('.'):node.Children[0].ArrayMapPath.Length; + path=string.IsNullOrWhiteSpace(node.Children[0].ArrayMapPath)?null: + node.Children[0].ArrayMapPath.Substring(0,len); + } + else{ + path=node.Children[0].ArrayMapPath; + } + } + } + } + } + + node.ArrayMapPath=path; + return path; + } + + private static Dictionary JArrayToHash(JArray jarr,Dictionary arrLenHashMap) { - Dictionary res = new(); - //res.Add(jarr.Path, jarr.Count); + var res = new Dictionary(); + arrLenHashMap.Add(jarr.Path,jarr.Count); for (var i = 0; i < jarr.Count; i++) { var jToken = jarr[i]; - if (jToken is JArray arr) + switch (jToken) { - Add(res, JArrayToHash(arr)); - } - else if (jToken is JObject obj) - { - Add(res, Jobject2Hash(obj)); - } - else - { - res.Add(jToken.Path, jToken); + case JArray arr: + Add(res, JArrayToHash(arr,arrLenHashMap)); + break; + case JObject obj: + Add(res, Jobject2Hash(obj,arrLenHashMap)); + break; + default: + res[jToken.Path] = jToken; + break; } } @@ -181,179 +226,70 @@ public static class JsonHelper { foreach (var item in b) { - a.Add(item.Key, item.Value); + a[item.Key] = item.Value; } } - - private static JObject dfs_object(JToken jtoken, List list, Dictionary hash, +#region V1版本 + private static JObject CreateObject(JToken jtoken, List list, Dictionary hash, List idxList) { var res = new JObject(); foreach (var item in list) { string name = item.Name; - - if (item.Type == ParameterType.Object) + res.Add(name, item.Type switch { - res.Add(name, dfs_object(jtoken, item.Children, hash, idxList)); - } - else if (item.Type == ParameterType.Array) - { - idxList.Add(-1); - var jarray = dfs_array(jtoken, item, hash, idxList); - - bool isEmptyArray = jarray.All(el => el.Type == JTokenType.Null); - res.Add(item.Name, isEmptyArray ? null : jarray); - - idxList.RemoveAt(idxList.Count - 1); - } - else - { - if (string.IsNullOrEmpty(item.MapAlias)) - { - // if (item.IsRequired) - // throw new BusinessException(message: $"必填参数{item.Name}({item.Alias})必须建立映射"); - res.Add(name, null); - continue; - } - - //if (item.Alias.Split("[*]").Length != item.MapAlias.Split("[*]").Length) continue; - JToken val = JValue.CreateNull(); - - string path = ""; - - if (item.Alias.IndexOf('[') == -1 && item.MapAlias.IndexOf('[') != -1) - path = item.MapAlias.Replace("[*]", "[0]"); - else - { - for (int i = 0, j = 0; i < item.MapAlias.Length; i++) - { - if (item.MapAlias[i] == '[') - { - i += 2; - if (j >= idxList.Count) - throw new BusinessException( - message: $"{item.Name}({item.MapAlias} -> {item.Alias})是非法映射"); - path += $"[{idxList[j]}]"; - j++; - } - else path += item.MapAlias[i]; - } - } - - if (hash.ContainsKey(path!)) - { - val = hash[path]; - } - - val = ValidationValue(item, val); - res.Add(name, val); - } + ParameterType.Object => CreateObject(jtoken, item.Children, hash, idxList), + ParameterType.Array => CreateArray(jtoken, item, hash,idxList), + // ParameterType.Array => CreateArray(jtoken, item, hash, idxList), + _ => CreateValue(item, hash, idxList) + }); } return res; } - private static JArray dfs_array(JToken jobject, ParameterNode arr, Dictionary hash, + private static JToken CreateArray(JToken jobject, ParameterNode arr, Dictionary hash, List idxList) { + idxList.Add(-1); var res = new JArray(); - var item = arr.Children[0]; - int len = get_len(jobject, item, idxList.Count); + int len = GetLength(jobject, item, idxList); for (int idx = 0; idx < len; idx++) { idxList[^1]++; - if (item.Type == ParameterType.Object) + res.Add(item.Type switch { - res.Add(dfs_object(jobject, item.Children, hash, idxList)); - } - else if (item.Type == ParameterType.Array) - { - idxList.Add(-1); - var jarray = dfs_array(jobject, item, hash, idxList); - res.Add(jarray); - } - else - { - if (string.IsNullOrEmpty(item.MapAlias)) - { - // if (item.IsRequired) - // throw new BusinessException(message: $"必填参数{item.Name}({item.Alias})必须建立映射"); - continue; - } - - //if (item.Alias.Split("[*]").Length != item.MapAlias.Split("[*]").Length) continue; - //var key = item.MapAlias.Replace("[*]", $"[{idx}]"); - - JToken val; - - string path = ""; - var strs = item.MapAlias.Split("[*]"); - if (strs.Length < 2) path = item.MapAlias; - var i = 0; - for (; i < strs.Length - 1; i++) - { - path += $"{strs[i]}[{idxList[i]}]"; - } - - if (i != 0) path += strs[i]; // suffix - - if (hash.ContainsKey(path!)) - { - val = hash[path]; - } - else break; - - val = ValidationValue(item, val); - res.Add(val); - } + ParameterType.Object => CreateObject(jobject, item.Children, hash, idxList), + ParameterType.Array => CreateArray(jobject, item, hash, idxList), + _ => CreateValue(item, hash, idxList) + }); } - return res; + idxList.RemoveAt(idxList.Count - 1); + return res.Count == 0 ? JValue.CreateNull() : res; } - private static int get_len(JToken jobject, ParameterNode item, int cnt) + private static int GetLength(JToken jobject, ParameterNode item, List idxList) { if (string.IsNullOrWhiteSpace(item.MapAlias)) { - if (item.Type != ParameterType.Array && item.Type != ParameterType.Object) return 0; - return get_sub_len(jobject, item, cnt); + return item.Type is ParameterType.Array or ParameterType.Object ? GetSubLength(jobject, item, idxList) : 0; } - // 数组层级不对等情况 - if (item.Alias.Split("[*]").Length != item.MapAlias.Split("[*]").Length) return 1; - - int index = -1; - for (int i = 0; i < cnt; i++) - { - index = item.MapAlias.IndexOf("[*]", index + 1, StringComparison.Ordinal); - if (index == -1) - { - break; - } - } - - var path = item.MapAlias.Substring(0, index + 3); - // for (int i = 0; i < idxList.Count; i++) - // { - // var idx = path.IndexOf("[*]", StringComparison.Ordinal); - // if (idx >= 0) - // { - // path = path.Remove(idx,3).Insert(idx,$"[{idxList[i]}]"); - // } - // } + string path = BuildPath(item.MapAlias, idxList); return jobject.SelectTokens(path).Count(); } - - private static int get_sub_len(JToken jobject, ParameterNode node, int cnt) + private static int GetSubLength(JToken jobject, ParameterNode node, List idxList) { int res = 0; foreach (var item in node.Children) { - var len = get_len(jobject, item, cnt); + if (item.Type == ParameterType.Array) continue; + var len = GetLength(jobject, item, idxList); if (len == 0) continue; if (res != 0 && res != len) throw new BusinessException(message: $"组成元素数量不对等,请检查{node.Alias}的组成"); res = Math.Max(len, res); @@ -362,13 +298,255 @@ public static class JsonHelper return res; } - - private static JToken ValidationValue(ParameterNode node, JToken val) + private static string BuildPath(string mapAlias, List idxList) { - // if (node.IsRequired && val.ToString() == "") - // throw new BusinessException(message: $"服务商参数{node.Name}是必填的(系统{node.MapAlias}->服务商{node.Alias})"); - if (!node.IsRequired && val.Type is JTokenType.Null) return val; + var split = mapAlias.Split(new[] { "[*]" }, StringSplitOptions.None); + var path = new StringBuilder(); + for (int i = 0; i < split.Length; i++) + { + path.Append(split[i]); + if (i + 1 < split.Length) // array path + { + var idx = idxList[idxList.Count - split.Length + i + 1]; + path.Append(idx == -1 ? "[*]" : $"[{idx}]"); + } + } + + return path.ToString(); + } + + private static JToken CreateValue(ParameterNode item, Dictionary hash, + List idxList) + { + if (string.IsNullOrEmpty(item.MapAlias)) + { + if (item.IsRequired) + throw new BusinessException(message: $"必填参数{item.Name}({item.Alias})必须建立映射或传入固定值"); + return JValue.CreateNull(); + } + + if (item.Alias.Split("[*]").Length != item.MapAlias.Split("[*]").Length) + { + throw new BusinessException(message: $"{item.Alias}<-{item.MapAlias}是不对等映射"); + } + + string path = BuildPath(item.MapAlias, idxList); + JToken val = hash.TryGetValue(path, out var value) ? value : JValue.CreateNull(); + return ValidateValue(item, val); + } +#endregion + +#region V2版本 + public static JToken RestructureV2(JToken jtoken, List treeList, + ParameterType jsonType = ParameterType.Object) + { + if (treeList == null || treeList.Count == 0) return jtoken; + + var idxList = new List(); + var arrLenHashMap = new Dictionary(); //为构造数组长度的kv + var hash = Json2Hash(jtoken,arrLenHashMap); + return jsonType switch + { + ParameterType.Array => CreateArrayV2(jtoken, treeList[0], hash,string.Empty), + ParameterType.Object => CreateObjectV2(jtoken, treeList, hash, string.Empty), + _ => new JValue(hash[treeList[0].MapAlias!]) + }; + } + + private static JObject CreateObjectV2(JToken jtoken, List list, Dictionary hash,string parentArrItemPath) + { + var res = new JObject(); + foreach (var item in list) + { + string name = item.Name; + res.Add(name, item.Type switch + { + ParameterType.Object => CreateObjectV2(jtoken, item.Children, hash, parentArrItemPath), + ParameterType.Array => CreateArrayV2(jtoken, item, hash,parentArrItemPath), + // ParameterType.Array => CreateArray(jtoken, item, hash, idxList), + _ => CreateValueV2(item, hash,-1,parentArrItemPath) + }); + } + + return res; + } + + private static JToken CreateArrayV2(JToken jobject, ParameterNode arr, Dictionary hash,string parentArrItemPath) + { + var res = new JArray(); + var item = arr.Children[0]; //拿数组元素的结构 + var len = GetArrLen(jobject, arr, parentArrItemPath); //拿数组长度 + + for (int idx = 0; idx < len; idx++) + { + var arrItemPath=GetArrItemPath(arr,parentArrItemPath,idx); + res.Add(item.Type switch + { + ParameterType.Object => CreateObjectV2(jobject, item.Children, hash, arrItemPath), + ParameterType.Array => CreateArrayV2(jobject, item, hash,arrItemPath), + _ => CreateValueV2(item, hash, -1, arrItemPath) + }); + } + + return res.Count == 0 ? JValue.CreateNull() : res; + } + + /// + /// 获取数组元素的路径 + /// + /// 数组item + /// 上级数组的path + /// 下标 + /// + private static string GetArrItemPath(ParameterNode item,string parentArrItemPath,int idx) + { + var path = new StringBuilder(); + if (item.Type is ParameterType.Array && !string.IsNullOrWhiteSpace(item.ArrayMapPath)) + { + var itemPathArr=item.ArrayMapPath.Trim().Split(new[] { "[*]" }, StringSplitOptions.RemoveEmptyEntries); + + if(item.Alias.Split(new []{"[*]" },StringSplitOptions.RemoveEmptyEntries).Length!=item.ArrayMapPath.Split(new []{"[*]" },StringSplitOptions.RemoveEmptyEntries).Length){ + if(item.ArrayMapPath.Split(new []{"[*]" },StringSplitOptions.RemoveEmptyEntries).Length>1){ + //如果存在层级,则需判断层级是否在父层级中,在则增加父级路径,不在无法判断映射层级关系(抛异常或者只取所有父级0元素的最后一级数组) + string pattern = @"\[\*]|\[.*?\]"; + string parentPath = Regex.Replace(parentArrItemPath, pattern, string.Empty); + string itemPath = Regex.Replace(item.ArrayMapPath, pattern, string.Empty); + if(itemPath.Contains(parentPath)&& (itemPath.Split('.').Length-parentPath.Split('.').Length)==1){ + path.Append(parentArrItemPath); + } + else{ + for (int i = 0; i < itemPathArr.Length-1; i++) + { + path.Append(itemPathArr[i]); + path.Append($"[0]"); + } + + } + // if(!string.IsNullOrWhiteSpace(parentArrItemPath)){ + // path.Append(parentArrItemPath); + // } + } + path.Append(itemPathArr[itemPathArr.Length-1]); + if(idx>-1){ + path.Append($"[{idx}]"); + } + } + else{ + if(!string.IsNullOrWhiteSpace(parentArrItemPath)){ + path.Append(parentArrItemPath); + } + path.Append(itemPathArr[itemPathArr.Length-1]); + if(idx>-1){ + path.Append($"[{idx}]"); + } + } + } + + return path.ToString(); + } + private static int GetArrLen(JToken jobject,ParameterNode item,string parentArrItemPath) + { + var len = 0; + + if (item.Type is ParameterType.Array && !string.IsNullOrWhiteSpace(item.ArrayMapPath)) + { + if(string.IsNullOrWhiteSpace(parentArrItemPath)){ + // len= jobject.SelectTokens(item.ArrayMapPath.TrimEnd("[*]".ToArray())).Count(); + len= jobject.SelectTokens(item.ArrayMapPath).Count(); + } + else{ + //服用GetArrItemPath方法来得到数组的路径 + var arrPath= GetArrItemPath(item,parentArrItemPath,-1)+"[*]"; + len= jobject.SelectTokens(arrPath).Count(); + } + + } + + return len; + } + + + + // private static string BuildPath(string mapAlias, List idxList) + // { + // var split = mapAlias.Split(new[] { "[*]" }, StringSplitOptions.None); + // var path = new StringBuilder(); + + // for (int i = 0; i < split.Length; i++) + // { + // path.Append(split[i]); + // if (i + 1 < split.Length) // array path + // { + // path.Append("[0]"); + // } + // } + + // return path.ToString(); + // } + + private static string BuildPathV2(string mapAlias, int arrIndex,string parentArrItemPath) + { + var split = mapAlias.Split(new[] { "[*]" }, StringSplitOptions.None); + var path = new StringBuilder(); + + if(!string.IsNullOrWhiteSpace(parentArrItemPath)){ + path.Append(parentArrItemPath); + + path.Append(split[split.Length-1]); + if(arrIndex>-1){ + path.Append($"[{arrIndex}]"); + } + } + else{ + for (int i = 0; i < split.Length; i++) + { + path.Append(split[i]); + if (i + 1 < split.Length) // array path + { + path.Append($"[{arrIndex}]"); + } + } + } + // for (int i = 0; i < split.Length; i++) + // { + // path.Append(split[i]); + // if (i + 1 < split.Length) // array path + // { + // path.Append($"[{arrIndex}]"); + // } + // } + + return path.ToString(); + } + + private static JToken CreateValueV2(ParameterNode item, Dictionary hash, + int arrIndex, string parentArrItemPath) + { + if (string.IsNullOrEmpty(item.MapAlias)) + { + if (item.IsRequired) + throw new BusinessException(message: $"必填参数{item.Name}({item.Alias})必须建立映射或传入固定值"); + return JValue.CreateNull(); + } + + if (item.Alias.Split("[*]").Length != item.MapAlias.Split("[*]").Length) + { + throw new BusinessException(message: $"{item.Alias}<-{item.MapAlias}是不对等映射"); + } + + string path = BuildPathV2(item.MapAlias, arrIndex,parentArrItemPath); + JToken val = hash.TryGetValue(path, out var value) ? value : JValue.CreateNull(); + return ValidateValue(item, val); + } + +#endregion + private static JToken ValidateValue(ParameterNode node, JToken val) + { + if (node.IsRequired && val.Type is JTokenType.Null) + throw new BusinessException(message: $"服务商参数{node.Name}是必填的(系统{node.MapAlias}->服务商{node.Alias})"); + + if (val.Type is JTokenType.Null) return val; var equal = false; if (node.Type == ParameterType.Boolean) @@ -406,10 +584,9 @@ public static class JsonHelper if (equal) val = v; } - if (!equal) throw new BusinessException(message: - $"参数{node.Name}({node.Alias})期望的类型是{node.Type},当实际分配的是{val.Type}类型的{val}({val.Path})"); + $"服务商参数{node.Name}({node.Alias})期望的类型是{node.Type},但实际分配的是{val.Type}类型的{val}({val.Path})"); return val; } diff --git a/src/InterfaceForward.Repositories/InterfaceForwardQuery.cs b/src/InterfaceForward.Repositories/InterfaceForwardQuery.cs index 9899552..2c457b9 100644 --- a/src/InterfaceForward.Repositories/InterfaceForwardQuery.cs +++ b/src/InterfaceForward.Repositories/InterfaceForwardQuery.cs @@ -127,7 +127,8 @@ ORDER BY `b`.`Sort` ASC var isMultiMap = mapIds.Count > 1; // 因为入参映射有多个 出参映射只有一个(挂在第一个映射) return await Context.SqlQueryable(@$" -select `b`.`Id` AS `Id` , `b`.`PId` AS `PId` , b.InterfaceId, `b`.`Name` AS `Name` ,b.Sort, `b`.`Type` AS `Type` , `b`.`Alias` AS `Alias` , {(isMultiMap ? "CONCAT(d.Code, '.', c.Alias)" : "c.Alias")} AS `MapAlias` , `b`.`IsRequired` AS `IsRequired` +select `b`.`Id` AS `Id` , `b`.`PId` AS `PId` , b.InterfaceId, `b`.`Name` AS `Name` ,b.Sort, `b`.`Type` AS `Type` , `b`.`Alias` AS `Alias` , +{(isMultiMap ? "CONCAT(d.Code, '.', c.Alias)" : "c.Alias")} AS `MapAlias` , `b`.`IsRequired` AS `IsRequired` , t.FreeMap from t_interface_map t LEFT JOIN `t_parameter` `b` ON {(isInParaMap ? "t.DownStreamId" : "t.UpStreamId")} = `b`.`InterfaceId` and b.IsInPara = {isInParaMap} AND ( `b`.`IsDeleted` = 0 ) Left JOIN `t_interface_map_detail` `a` ON ( `t`.`Id` = `a`.`InterfaceMapId` ) and b.id = a.ParaId and a.IsDeleted = 0 @@ -142,9 +143,9 @@ ORDER BY `b`.`Sort` ASC").ToListAsync(); /// /// /// - public async Task GetInterfaceByCodesAsync(string interfaceCode) + public async Task GetInterfaceByCodeAsync(string interfaceCode) { - var res = await GetInterfaceByCodesAsync(new[] { interfaceCode }); + var res = await GetInterfaceByCodesAsync(interfaceCode); return res.FirstOrDefault(); } @@ -191,21 +192,24 @@ ORDER BY `b`.`Sort` ASC").ToListAsync(); ) // 构建系统返参数树形结构 }; - if (serviceProvider.AuthInterfaceCode != null) + if (!serviceProvider.AuthInterfaceCode.IsNullOrEmpty()) { - var authInterface = await GetInterfaceByCodesAsync(serviceProvider.AuthInterfaceCode); - if (authInterface == null) + var authInterface = await GetInterfaceByCodeAsync(serviceProvider.AuthInterfaceCode); + if (authInterface != null) { - throw new BusinessException($"授权接口{serviceProvider.AuthInterfaceCode}不存在"); + var authPrefixScript = context.ServiceProviderAuthConfigs + .FirstOrDefault(x => x.Flag == AuthParameterFlag.PrefixScript)?.Value1 ?? String.Empty; + context.ServiceProviderAuthInterface = new TargetInterfaceSummary + { + TargetInterface = authInterface, + PrefixScript = authPrefixScript, + PostfixScript = null, + InParamTreeList = null, + FixedFieldList = await GetAllFixedFieldListAsync(serviceProvider.Id, authInterface.Id), + ReturnConfigList = await GetInterfaceReturnConfigListAsync(authInterface.Id), + FormFieldList = await GetFormFieldListAsync(authInterface.Id), + }; } - - context.ServiceProviderAuthInterface = new TargetInterfaceSummary - { - TargetInterface = authInterface, - FixedFieldList = await GetAllFixedFieldListAsync(serviceProvider.Id, authInterface.Id), - ReturnConfigList = await GetInterfaceReturnConfigListAsync(authInterface.Id), - FormFieldList = await GetFormFieldListAsync(authInterface.Id), - }; } var inParaMaps = await GetParameterMapListAsync(mapIds, true); @@ -214,20 +218,19 @@ ORDER BY `b`.`Sort` ASC").ToListAsync(); var targetInterface = targetInterfaceList.First(x => map.DownStreamCode == x.Code); var items = inParaMaps.Where(x => x.InterfaceId == targetInterface.Id).ToList(); var fixedFieldList = await GetAllFixedFieldListAsync(serviceProvider.Id, targetInterface.Id); - if (targetInterface.MappedParamType == ParameterType.Array) + + foreach (var item in items) { - var rootLen = items.First(x => x.PId == 0).Alias.Length; - foreach (var item in fixedFieldList.Where(x => x.FieldPosition == FieldPosition.Body)) - { - // 去除固定参数[*]前面的路径 - item.FieldName = item.FieldName[rootLen..]; - } + var fixedField = fixedFieldList.FirstOrDefault(x => + x.FieldPosition == FieldPosition.Body && x.FieldName == item.Alias); + if (fixedField != default) item.IsRequired = false; // 有固定值就不需要校验必填了 } context.TargetInterfaces.Add(new TargetInterfaceSummary { TargetInterface = targetInterface, InParamTreeList = JsonHelper.BuildTreeList(items), // 构建目标接口入参树形结构 + InParamMappedList=items?.Where(x=>x.MapAlias!=null).ToList(), FixedFieldList = fixedFieldList, ReturnConfigList = await GetInterfaceReturnConfigListAsync(targetInterface.Id), PrefixScript = map.PrefixScript,