sync:core algorithm

This commit is contained in:
xiaolipro 2025-08-01 10:21:04 +08:00
parent fc73d4d85c
commit 1e5ebb398e
10 changed files with 443 additions and 247 deletions

View File

@ -4,7 +4,6 @@ using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Web; using System.Web;
using System.Xml; using System.Xml;
using Fake.Application;
using Fake.DependencyInjection; using Fake.DependencyInjection;
using InterfaceForward.Application.Contracts.ForwardCore; using InterfaceForward.Application.Contracts.ForwardCore;
using InterfaceForward.Application.Helpers; using InterfaceForward.Application.Helpers;

View File

@ -45,7 +45,7 @@ public class InterfaceForwardCommon(
.ToObject<ForwardCoreContextCache>(); .ToObject<ForwardCoreContextCache>();
if (cacheContext == null) if (cacheContext == null)
{ {
var systemInterface = await interfaceForwardQuery.GetInterfaceByCodesAsync(upStreamCode); var systemInterface = await interfaceForwardQuery.GetInterfaceByCodeAsync(upStreamCode);
if (systemInterface == null) throw new BusinessException(message: "系统接口不存在"); if (systemInterface == null) throw new BusinessException(message: "系统接口不存在");
logRepository.AppRequestLog.InterfaceCode = systemInterface.Code; logRepository.AppRequestLog.InterfaceCode = systemInterface.Code;

View File

@ -38,7 +38,8 @@ internal class CustomContractResolver : DefaultContractResolver
if (member.DeclaringType == typeof(InterfaceParameterMapDto)) if (member.DeclaringType == typeof(InterfaceParameterMapDto))
{ {
if (member.Name.IsIn(nameof(InterfaceParameterMapDto.Id), nameof(InterfaceParameterMapDto.PId), if (member.Name.IsIn(nameof(InterfaceParameterMapDto.Id), nameof(InterfaceParameterMapDto.PId),
nameof(InterfaceParameterMapDto.InterfaceId))) return true; nameof(InterfaceParameterMapDto.InterfaceId),
nameof(InterfaceParameterMapDto.FreeMap))) return true;
} }
// 其他判断逻辑... // 其他判断逻辑...

View File

@ -59,7 +59,7 @@ public class InterfaceMapPublishService(
public async Task<InterfaceMapPublishedDifferenceVO> GetPublishCompareAsync([Required] string interfaceCode, public async Task<InterfaceMapPublishedDifferenceVO> GetPublishCompareAsync([Required] string interfaceCode,
[Required] int serviceProviderId, string? version) [Required] int serviceProviderId, string? version)
{ {
var systemInterface = await interfaceForwardQuery.GetInterfaceByCodesAsync(interfaceCode); var systemInterface = await interfaceForwardQuery.GetInterfaceByCodeAsync(interfaceCode);
if (systemInterface == null) throw new BusinessException(message: "系统接口不存在"); if (systemInterface == null) throw new BusinessException(message: "系统接口不存在");
var serviceProvider = await interfaceForwardQuery.GetServiceProviderByIdAsync(serviceProviderId); var serviceProvider = await interfaceForwardQuery.GetServiceProviderByIdAsync(serviceProviderId);
@ -98,7 +98,7 @@ public class InterfaceMapPublishService(
[HttpPost("InterfaceMap/Publish")] [HttpPost("InterfaceMap/Publish")]
public async Task<bool> PublishAsync(InterfaceMapPublishInput input) public async Task<bool> PublishAsync(InterfaceMapPublishInput input)
{ {
var systemInterface = await interfaceForwardQuery.GetInterfaceByCodesAsync(input.InterfaceCode); var systemInterface = await interfaceForwardQuery.GetInterfaceByCodeAsync(input.InterfaceCode);
if (systemInterface == null) throw new BusinessException(message: "系统接口不存在"); if (systemInterface == null) throw new BusinessException(message: "系统接口不存在");
var serviceProvider = await interfaceForwardQuery.GetServiceProviderByIdAsync(input.ServiceProviderId); var serviceProvider = await interfaceForwardQuery.GetServiceProviderByIdAsync(input.ServiceProviderId);
@ -151,7 +151,7 @@ public class InterfaceMapPublishService(
[HttpPost("InterfaceMap/PublishRollback")] [HttpPost("InterfaceMap/PublishRollback")]
public async Task<bool> PublishRollbackAsync(InterfaceMapRollbackInput input) public async Task<bool> PublishRollbackAsync(InterfaceMapRollbackInput input)
{ {
var systemInterface = await interfaceForwardQuery.GetInterfaceByCodesAsync(input.InterfaceCode); var systemInterface = await interfaceForwardQuery.GetInterfaceByCodeAsync(input.InterfaceCode);
if (systemInterface == null) throw new BusinessException(message: "系统接口不存在"); if (systemInterface == null) throw new BusinessException(message: "系统接口不存在");
var serviceProvider = await interfaceForwardQuery.GetServiceProviderByIdAsync(input.ServiceProviderId); var serviceProvider = await interfaceForwardQuery.GetServiceProviderByIdAsync(input.ServiceProviderId);
if (serviceProvider == null) throw new BusinessException(message: "服务商不存在"); if (serviceProvider == null) throw new BusinessException(message: "服务商不存在");

View File

@ -108,7 +108,7 @@ public class ServiceProviderAuthService : ApplicationService
} }
res.FixedEffectTime = authConfigs.FirstOrDefault(x => x.Flag == AuthParameterFlag.FixedEffectTime)?.Value1.ToInt(); 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; return res;
} }
@ -192,7 +192,7 @@ public class ServiceProviderAuthService : ApplicationService
{ {
authConfigsEntities.Add(new ServiceProviderAuthConfigEntity authConfigsEntities.Add(new ServiceProviderAuthConfigEntity
{ {
Flag = AuthParameterFlag.Script, Flag = AuthParameterFlag.PrefixScript,
Value1 = input.Script, Value1 = input.Script,
}); });
} }

View File

@ -77,6 +77,11 @@ public class TargetInterfaceSummary
/// </summary> /// </summary>
public List<ParameterNode> InParamTreeList { get; set; } = []; public List<ParameterNode> InParamTreeList { get; set; } = [];
/// <summary>
/// MapAlias不为空的入参映射便于快速查找
/// </summary>
public List<InterfaceParameterMapDto> InParamMappedList { get; set; }
/// <summary> /// <summary>
/// 固定值列表 /// 固定值列表
/// </summary> /// </summary>

View File

@ -33,6 +33,11 @@ public class InterfaceParameterMapDto
/// 映射参数别名(被重构的) /// 映射参数别名(被重构的)
///</summary> ///</summary>
public string MapAlias { get; set; } public string MapAlias { get; set; }
/// <summary>
/// 数组映射别名,原数据存储数组都是结构只映射具体的值,用于重新构建数组映射,有叶子节点映射则构建映射的数组层级
/// </summary>
public string ArrayMapPath { get; set; }
///<summary> ///<summary>
/// 参数类型 /// 参数类型
@ -43,4 +48,9 @@ public class InterfaceParameterMapDto
/// 必填的 /// 必填的
/// </summary> /// </summary>
public bool IsRequired { get; set; } public bool IsRequired { get; set; }
/// <summary>
/// 自由映射
/// </summary>
public bool FreeMap { get; set; }
} }

View File

@ -9,5 +9,6 @@ public enum AuthParameterFlag
Token = 2, Token = 2,
EffectTime = 3, EffectTime = 3,
FixedEffectTime = 4, FixedEffectTime = 4,
Script = 5 PrefixScript = 5,
PostfixScript = 6,
} }

View File

@ -1,4 +1,6 @@
using Fake; using System.Text;
using System.Text.RegularExpressions;
using Fake;
using Fake.Application; using Fake.Application;
using InterfaceForward.Domain.Shared.Dtos; using InterfaceForward.Domain.Shared.Dtos;
using InterfaceForward.Domain.Shared.Enum; using InterfaceForward.Domain.Shared.Enum;
@ -8,37 +10,22 @@ namespace InterfaceForward.Domain.Shared.Helpers;
public static class JsonHelper public static class JsonHelper
{ {
/// <summary> private static readonly Regex StarRegex = new(@"\[\*\]", RegexOptions.Compiled);
/// json重构
/// </summary> public static JToken Restructure(JToken jtoken, List<ParameterNode> treeList,
/// <param name="jtoken"></param>
/// <param name="treeList"></param>
/// <param name="jsonType">返回的json类型</param>
/// <returns></returns>
public static JToken Restructure(JToken jtoken, List<ParameterNode>? treeList,
ParameterType jsonType = ParameterType.Object) ParameterType jsonType = ParameterType.Object)
{ {
// 没有映射时,是空树 if (treeList == null || treeList.Count == 0) return jtoken;
if (treeList == default || treeList.Count == 0) return jtoken;
var idxList = new List<int>(); var idxList = new List<int>();
var hash = Json2Hash(jtoken); // 将json转化成hash var arrLenHashMap = new Dictionary<string,int>(); //为构造数组长度的kv
var hash = Json2Hash(jtoken,arrLenHashMap);
switch (jsonType) return jsonType switch
{ {
case ParameterType.Array: ParameterType.Array => CreateArray(jtoken, treeList[0], hash, idxList),
idxList.Add(-1); ParameterType.Object => CreateObject(jtoken, treeList, hash, idxList),
return dfs_array(jtoken, treeList[0], hash, idxList); _ => new JValue(hash[treeList[0].MapAlias!])
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);
}
} }
public static void RemoveNullProperties(JToken token) public static void RemoveNullProperties(JToken token)
@ -70,28 +57,19 @@ public static class JsonHelper
} }
} }
private static Dictionary<string, JToken> Json2Hash(JToken jtoken) private static Dictionary<string, JToken> Json2Hash(JToken jtoken,Dictionary<string,int> arrLenHashMap)
{ {
if (jtoken is JObject obj) return jtoken switch
{ {
return Jobject2Hash(obj); JObject obj => Jobject2Hash(obj,arrLenHashMap),
} JArray arr => JArrayToHash(arr,arrLenHashMap),
_ => new Dictionary<string, JToken> { { jtoken.Path, jtoken } }
if (jtoken is JArray arr)
{
return JArrayToHash(arr);
}
// jvalue
return new Dictionary<string, JToken>()
{
{ jtoken.Path, jtoken }
}; };
} }
private static Dictionary<string, JToken> Jobject2Hash(JObject jobject) private static Dictionary<string, JToken> Jobject2Hash(JObject jobject,Dictionary<string,int> arrLenHashMap)
{ {
Dictionary<string, JToken> res = new(); var res = new Dictionary<string, JToken>();
var q = new Queue<JProperty>(jobject.Properties()); var q = new Queue<JProperty>(jobject.Properties());
while (q.Count > 0) while (q.Count > 0)
{ {
@ -105,11 +83,11 @@ public static class JsonHelper
} }
else if (cur.Value is JArray arr) else if (cur.Value is JArray arr)
{ {
Add(res, JArrayToHash(arr)); Add(res, JArrayToHash(arr,arrLenHashMap));
} }
else else
{ {
res.Add(cur.Path, cur.Value); res[cur.Path] = cur.Value;
} }
} }
@ -118,33 +96,46 @@ public static class JsonHelper
public static List<ParameterNode> BuildTreeList(IReadOnlyCollection<InterfaceParameterMapDto> list) public static List<ParameterNode> BuildTreeList(IReadOnlyCollection<InterfaceParameterMapDto> list)
{ {
if (list.All(x => x.MapAlias.IsNullOrWhiteSpace())) return list.All(x => x.MapAlias.IsNullOrWhiteSpace()) ? null : DoBuild(list, 0);
{
}
return DoBuild(list, 0);
} }
private static List<ParameterNode> DoBuild(IReadOnlyCollection<InterfaceParameterMapDto> list, int pid) private static List<ParameterNode> DoBuild(IReadOnlyCollection<InterfaceParameterMapDto> list, int pid)
{ {
var res = new List<ParameterNode>(); var res = new List<ParameterNode>();
foreach (var item in list.Where(x => x.PId == pid))
foreach (var child in list.Where(x => x.PId == pid))
{ {
var node = new ParameterNode(); var node = new ParameterNode
node.Id = child.Id; {
node.PId = child.PId; Id = item.Id,
node.Name = child.Name; PId = item.PId,
node.Type = child.Type; Name = item.Name,
node.Alias = child.Alias; Type = item.Type,
node.IsRequired = child.IsRequired; Alias = item.Alias,
IsRequired = item.IsRequired,
MapAlias = item.MapAlias
};
if (node.Type is ParameterType.Object or ParameterType.Array) 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); res.Add(node);
@ -153,24 +144,78 @@ public static class JsonHelper
return res; return res;
} }
private static Dictionary<string, JToken> JArrayToHash(JArray jarr) /// <summary>
/// 构建并可返回数组映射的路径MapPath只在DoBuild递归方法中适用
/// </summary>
/// <param name="node">数组的node</param>
/// <returns></returns>
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<string, JToken> JArrayToHash(JArray jarr,Dictionary<string,int> arrLenHashMap)
{ {
Dictionary<string, JToken> res = new(); var res = new Dictionary<string, JToken>();
//res.Add(jarr.Path, jarr.Count); arrLenHashMap.Add(jarr.Path,jarr.Count);
for (var i = 0; i < jarr.Count; i++) for (var i = 0; i < jarr.Count; i++)
{ {
var jToken = jarr[i]; var jToken = jarr[i];
if (jToken is JArray arr) switch (jToken)
{ {
Add(res, JArrayToHash(arr)); case JArray arr:
} Add(res, JArrayToHash(arr,arrLenHashMap));
else if (jToken is JObject obj) break;
{ case JObject obj:
Add(res, Jobject2Hash(obj)); Add(res, Jobject2Hash(obj,arrLenHashMap));
} break;
else default:
{ res[jToken.Path] = jToken;
res.Add(jToken.Path, jToken); break;
} }
} }
@ -181,179 +226,70 @@ public static class JsonHelper
{ {
foreach (var item in b) foreach (var item in b)
{ {
a.Add(item.Key, item.Value); a[item.Key] = item.Value;
} }
} }
#region V1版本
private static JObject dfs_object(JToken jtoken, List<ParameterNode> list, Dictionary<string, JToken> hash, private static JObject CreateObject(JToken jtoken, List<ParameterNode> list, Dictionary<string, JToken> hash,
List<int> idxList) List<int> idxList)
{ {
var res = new JObject(); var res = new JObject();
foreach (var item in list) foreach (var item in list)
{ {
string name = item.Name; string name = item.Name;
res.Add(name, item.Type switch
if (item.Type == ParameterType.Object)
{ {
res.Add(name, dfs_object(jtoken, item.Children, hash, idxList)); ParameterType.Object => CreateObject(jtoken, item.Children, hash, idxList),
} ParameterType.Array => CreateArray(jtoken, item, hash,idxList),
else if (item.Type == ParameterType.Array) // ParameterType.Array => CreateArray(jtoken, item, hash, idxList),
{ _ => CreateValue(item, hash, idxList)
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);
}
} }
return res; return res;
} }
private static JArray dfs_array(JToken jobject, ParameterNode arr, Dictionary<string, JToken> hash, private static JToken CreateArray(JToken jobject, ParameterNode arr, Dictionary<string, JToken> hash,
List<int> idxList) List<int> idxList)
{ {
idxList.Add(-1);
var res = new JArray(); var res = new JArray();
var item = arr.Children[0]; 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++) for (int idx = 0; idx < len; idx++)
{ {
idxList[^1]++; idxList[^1]++;
if (item.Type == ParameterType.Object) res.Add(item.Type switch
{ {
res.Add(dfs_object(jobject, item.Children, hash, idxList)); ParameterType.Object => CreateObject(jobject, item.Children, hash, idxList),
} ParameterType.Array => CreateArray(jobject, item, hash, idxList),
else if (item.Type == ParameterType.Array) _ => CreateValue(item, hash, idxList)
{ });
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);
}
} }
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<int> idxList)
{ {
if (string.IsNullOrWhiteSpace(item.MapAlias)) if (string.IsNullOrWhiteSpace(item.MapAlias))
{ {
if (item.Type != ParameterType.Array && item.Type != ParameterType.Object) return 0; return item.Type is ParameterType.Array or ParameterType.Object ? GetSubLength(jobject, item, idxList) : 0;
return get_sub_len(jobject, item, cnt);
} }
// 数组层级不对等情况 string path = BuildPath(item.MapAlias, idxList);
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]}]");
// }
// }
return jobject.SelectTokens(path).Count(); return jobject.SelectTokens(path).Count();
} }
private static int GetSubLength(JToken jobject, ParameterNode node, List<int> idxList)
private static int get_sub_len(JToken jobject, ParameterNode node, int cnt)
{ {
int res = 0; int res = 0;
foreach (var item in node.Children) 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 (len == 0) continue;
if (res != 0 && res != len) throw new BusinessException(message: $"组成元素数量不对等,请检查{node.Alias}的组成"); if (res != 0 && res != len) throw new BusinessException(message: $"组成元素数量不对等,请检查{node.Alias}的组成");
res = Math.Max(len, res); res = Math.Max(len, res);
@ -362,13 +298,255 @@ public static class JsonHelper
return res; return res;
} }
private static string BuildPath(string mapAlias, List<int> idxList)
private static JToken ValidationValue(ParameterNode node, JToken val)
{ {
// if (node.IsRequired && val.ToString() == "") var split = mapAlias.Split(new[] { "[*]" }, StringSplitOptions.None);
// throw new BusinessException(message: $"服务商参数{node.Name}是必填的(系统{node.MapAlias}->服务商{node.Alias}"); var path = new StringBuilder();
if (!node.IsRequired && val.Type is JTokenType.Null) return val;
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<string, JToken> hash,
List<int> 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<ParameterNode> treeList,
ParameterType jsonType = ParameterType.Object)
{
if (treeList == null || treeList.Count == 0) return jtoken;
var idxList = new List<int>();
var arrLenHashMap = new Dictionary<string,int>(); //为构造数组长度的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<ParameterNode> list, Dictionary<string, JToken> 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<string, JToken> 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;
}
/// <summary>
/// 获取数组元素的路径
/// </summary>
/// <param name="item">数组item</param>
/// <param name="parentArrItemPath">上级数组的path</param>
/// <param name="idx">下标</param>
/// <returns></returns>
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<int> 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<string, JToken> 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; var equal = false;
if (node.Type == ParameterType.Boolean) if (node.Type == ParameterType.Boolean)
@ -406,10 +584,9 @@ public static class JsonHelper
if (equal) val = v; if (equal) val = v;
} }
if (!equal) if (!equal)
throw new BusinessException(message: 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; return val;
} }

View File

@ -127,7 +127,8 @@ ORDER BY `b`.`Sort` ASC
var isMultiMap = mapIds.Count > 1; var isMultiMap = mapIds.Count > 1;
// 因为入参映射有多个 出参映射只有一个(挂在第一个映射) // 因为入参映射有多个 出参映射只有一个(挂在第一个映射)
return await Context.SqlQueryable<InterfaceParameterMapDto>(@$" return await Context.SqlQueryable<InterfaceParameterMapDto>(@$"
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 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_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 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();
/// </summary> /// </summary>
/// <param name="interfaceCode"></param> /// <param name="interfaceCode"></param>
/// <returns></returns> /// <returns></returns>
public async Task<InterfaceDto?> GetInterfaceByCodesAsync(string interfaceCode) public async Task<InterfaceDto?> GetInterfaceByCodeAsync(string interfaceCode)
{ {
var res = await GetInterfaceByCodesAsync(new[] { interfaceCode }); var res = await GetInterfaceByCodesAsync(interfaceCode);
return res.FirstOrDefault(); 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); var authInterface = await GetInterfaceByCodeAsync(serviceProvider.AuthInterfaceCode);
if (authInterface == null) 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); 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 targetInterface = targetInterfaceList.First(x => map.DownStreamCode == x.Code);
var items = inParaMaps.Where(x => x.InterfaceId == targetInterface.Id).ToList(); var items = inParaMaps.Where(x => x.InterfaceId == targetInterface.Id).ToList();
var fixedFieldList = await GetAllFixedFieldListAsync(serviceProvider.Id, targetInterface.Id); 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; var fixedField = fixedFieldList.FirstOrDefault(x =>
foreach (var item in fixedFieldList.Where(x => x.FieldPosition == FieldPosition.Body)) x.FieldPosition == FieldPosition.Body && x.FieldName == item.Alias);
{ if (fixedField != default) item.IsRequired = false; // 有固定值就不需要校验必填了
// 去除固定参数[*]前面的路径
item.FieldName = item.FieldName[rootLen..];
}
} }
context.TargetInterfaces.Add(new TargetInterfaceSummary context.TargetInterfaces.Add(new TargetInterfaceSummary
{ {
TargetInterface = targetInterface, TargetInterface = targetInterface,
InParamTreeList = JsonHelper.BuildTreeList(items), // 构建目标接口入参树形结构 InParamTreeList = JsonHelper.BuildTreeList(items), // 构建目标接口入参树形结构
InParamMappedList=items?.Where(x=>x.MapAlias!=null).ToList(),
FixedFieldList = fixedFieldList, FixedFieldList = fixedFieldList,
ReturnConfigList = await GetInterfaceReturnConfigListAsync(targetInterface.Id), ReturnConfigList = await GetInterfaceReturnConfigListAsync(targetInterface.Id),
PrefixScript = map.PrefixScript, PrefixScript = map.PrefixScript,