temp
This commit is contained in:
parent
8f418b4fd7
commit
512c511707
@ -1,8 +1,9 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<!-- <Nullable>enable</Nullable>-->
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<NoWarn>CS1591</NoWarn>
|
||||||
<!-- <WarningsAsErrors>Nullable</WarningsAsErrors>-->
|
<!-- <WarningsAsErrors>Nullable</WarningsAsErrors>-->
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -29,7 +29,6 @@ public class InterfaceForwardApiModule : FakeModule
|
|||||||
services.AddFakeSwaggerGen()
|
services.AddFakeSwaggerGen()
|
||||||
.AddFakeExceptionFilter()
|
.AddFakeExceptionFilter()
|
||||||
.AddFakeValidationActionFilter()
|
.AddFakeValidationActionFilter()
|
||||||
.AddFakeUnitOfWorkActionFilter()
|
|
||||||
.AddFakeAspNetCoreAuditing();
|
.AddFakeAspNetCoreAuditing();
|
||||||
|
|
||||||
services.AddCors(options =>
|
services.AddCors(options =>
|
||||||
|
|||||||
@ -27,11 +27,19 @@
|
|||||||
"http://localhost:8888"
|
"http://localhost:8888"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ConnectionStrings":{
|
"ConnectionStrings": {
|
||||||
"Default": "server=baget.xiaolipro.cn;database=interface_forward;uid=dev;pwd=itd!@#123;charset=utf8mb4;"
|
"Default": "server=baget.xiaolipro.cn;database=interface_forward;uid=dev;pwd=itd!@#123;charset=utf8mb4;"
|
||||||
},
|
},
|
||||||
"FeiShuNotice": {
|
"FeiShuNotice": {
|
||||||
"Title": "InterfaceForward-Dev",
|
"Title": "InterfaceForward-Dev",
|
||||||
"Webhook": "https://open.feishu.cn/open-apis/bot/v2/hook/255c44ff-5891-4902-9b6a-6d0250e745f7"
|
"Webhook": "https://open.feishu.cn/open-apis/bot/v2/hook/255c44ff-5891-4902-9b6a-6d0250e745f7"
|
||||||
|
},
|
||||||
|
"RabbitMQ": {
|
||||||
|
"Default": {
|
||||||
|
"Host": "baget.xiaolipro.cn",
|
||||||
|
"Port": 5672,
|
||||||
|
"UserName": "dev",
|
||||||
|
"Password": "itd!@#123"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,6 +39,11 @@ public class MapItem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string PostfixScript { get; set; }
|
public string PostfixScript { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自由映射(支持不对等映射)
|
||||||
|
/// </summary>
|
||||||
|
public bool FreeMap { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 入参映射明细
|
/// 入参映射明细
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -9,27 +9,27 @@ public class ForwardCoreContext
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 目标服务商接口
|
/// 目标服务商接口
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public InterfaceDto TargetInterface { get; set; }
|
public InterfaceDto TargetInterface { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 服务商接口固定字段列表
|
/// 服务商接口固定字段列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<FixedFieldWithValueDto> FixedFieldList { get; set; }
|
public List<FixedFieldWithValueDto> FixedFieldList { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 服务商账户字段列表
|
/// 服务商账户字段列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<ServiceProviderAccountFieldWithValueDto> AccountFieldList { get; set; }
|
public List<ServiceProviderAccountFieldWithValueDto> AccountFieldList { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 服务商接口返回配置列表
|
/// 服务商接口返回配置列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<InterfaceReturnConfigDto> ReturnConfigList { get; set; }
|
public List<InterfaceReturnConfigDto> ReturnConfigList { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表单字段列表
|
/// 表单字段列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<InterfaceFormFieldDto> FormFieldList { get; set; }
|
public List<InterfaceFormFieldDto> FormFieldList { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 入参映射列表(树形结构)
|
/// 入参映射列表(树形结构)
|
||||||
@ -44,12 +44,12 @@ public class ForwardCoreContext
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求前置脚本
|
/// 请求前置脚本
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string PrefixScript { get; set; }
|
public string? PrefixScript { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求后置脚本
|
/// 请求后置脚本
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string PostfixScript { get; set; }
|
public string? PostfixScript { get; set; }
|
||||||
|
|
||||||
public bool AddHeader(string key, string value)
|
public bool AddHeader(string key, string value)
|
||||||
{
|
{
|
||||||
@ -87,27 +87,27 @@ public class ForwardCoreContext
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 业务系统传参json
|
/// 业务系统传参json
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public JToken OriginalInterfaceInput { get; set; }
|
public JToken OriginalInterfaceInput { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 业务系统传参映射后的json
|
/// 业务系统传参映射后的json
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public JToken OriginalInterfaceMappedInput { get; set; }
|
public JToken OriginalInterfaceMappedInput { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 目标接口返回的json
|
/// 目标接口返回的json
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public JToken TargetInterfaceOutput { get; set; }
|
public JToken TargetInterfaceOutput { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求服务商接口真实传参字符串
|
/// 请求服务商接口真实传参字符串
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string TargetInterfaceInput { get; set; }
|
public string TargetInterfaceInput { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 服务商日志
|
/// 服务商日志
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public RequestLogDto ServiceProviderRequestLog { get; set; }
|
public RequestLogDto ServiceProviderRequestLog { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 专用feature
|
/// 专用feature
|
||||||
|
|||||||
@ -25,7 +25,7 @@ public class InterfaceRelayForwardReplayEvent
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 原报文
|
/// 原报文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string OriginalMessage { get; set; }
|
public string? OriginalMessage { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 附加数据
|
/// 附加数据
|
||||||
|
|||||||
@ -1,14 +1,15 @@
|
|||||||
using System.Text;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using InterfaceForward.Application.Contracts;
|
|
||||||
using InterfaceForward.Application.Contracts.ForwardCore;
|
using InterfaceForward.Application.Contracts.ForwardCore;
|
||||||
using InterfaceForward.Application.Helpers;
|
using InterfaceForward.Application.Helpers;
|
||||||
using InterfaceForward.Application.OSS;
|
using InterfaceForward.Application.OSS;
|
||||||
using InterfaceForward.Domain.Shared;
|
using InterfaceForward.Domain.Shared;
|
||||||
using InterfaceForward.Domain.Shared.Dtos;
|
|
||||||
using InterfaceForward.Domain.Shared.Enum;
|
using InterfaceForward.Domain.Shared.Enum;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
@ -19,16 +20,11 @@ namespace InterfaceForward.Application.ForwardCore;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class DefaultForwardFlow : IForwardFlow
|
public class DefaultForwardFlow : IForwardFlow
|
||||||
{
|
{
|
||||||
public IHttpClientFactory ClientFactory { get; set; }
|
private static readonly ConcurrentDictionary<string, Delegate> DelegateDic = new();
|
||||||
|
[Inject] public IHttpClientFactory ClientFactory { get; set; } = null!;
|
||||||
|
|
||||||
public virtual Task<JToken> InParamRestructure(ForwardCoreContext context)
|
public virtual Task<JToken> InParamRestructure(ForwardCoreContext context)
|
||||||
{
|
{
|
||||||
if (context.OriginalInterfaceInput is JArray)
|
|
||||||
{
|
|
||||||
// 虚构系统入参数组路径
|
|
||||||
VirtualizePath(context.InParamTreeList);
|
|
||||||
}
|
|
||||||
|
|
||||||
var res = JsonHelper.Restructure(context.OriginalInterfaceInput, context.InParamTreeList,
|
var res = JsonHelper.Restructure(context.OriginalInterfaceInput, context.InParamTreeList,
|
||||||
context.TargetInterface.MappedParamType);
|
context.TargetInterface.MappedParamType);
|
||||||
|
|
||||||
@ -40,18 +36,7 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
// 填充占位符
|
// 填充占位符
|
||||||
ReplacePlaceholder(context);
|
ReplacePlaceholder(context);
|
||||||
|
|
||||||
// 使用Natasha构建动态表达式
|
await ExecuteScriptAsync(context, true);
|
||||||
if (!context.PrefixScript.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
if (context.PrefixScript.Contains("await"))
|
|
||||||
{
|
|
||||||
await NDelegate.RandomDomain().AsyncFunc<ForwardCoreContext, Task>(context.PrefixScript)(context);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NDelegate.RandomDomain().Action<ForwardCoreContext>(context.PrefixScript)(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将body序列化成对应的格式字符串
|
// 将body序列化成对应的格式字符串
|
||||||
context.TargetInterfaceInput = SerializationBody(context);
|
context.TargetInterfaceInput = SerializationBody(context);
|
||||||
@ -64,17 +49,7 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
// 将响应的字符串反序列化成jtoken
|
// 将响应的字符串反序列化成jtoken
|
||||||
context.TargetInterfaceOutput = DeserializationResponse(context, res);
|
context.TargetInterfaceOutput = DeserializationResponse(context, res);
|
||||||
|
|
||||||
if (!context.PostfixScript.IsNullOrEmpty())
|
await ExecuteScriptAsync(context, false);
|
||||||
{
|
|
||||||
if (context.PostfixScript.Contains("await"))
|
|
||||||
{
|
|
||||||
await NDelegate.RandomDomain().AsyncFunc<ForwardCoreContext, Task>(context.PostfixScript)(context);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NDelegate.RandomDomain().Action<ForwardCoreContext>(context.PostfixScript)(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 校验状态码,失败发飞书
|
// 校验状态码,失败发飞书
|
||||||
VerifyStatusCode(context);
|
VerifyStatusCode(context);
|
||||||
@ -84,12 +59,6 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
|
|
||||||
public virtual async Task<JToken> OutParamRestructure(ForwardCoreContext context, JToken response)
|
public virtual async Task<JToken> OutParamRestructure(ForwardCoreContext context, JToken response)
|
||||||
{
|
{
|
||||||
if (context.TargetInterfaceOutput is JArray)
|
|
||||||
{
|
|
||||||
// 虚构系统入参数组路径
|
|
||||||
VirtualizePath(context.OutParamTreeList);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 映射
|
// 映射
|
||||||
var mappedRes = JsonHelper.Restructure(response, context.OutParamTreeList);
|
var mappedRes = JsonHelper.Restructure(response, context.OutParamTreeList);
|
||||||
// 非结构化
|
// 非结构化
|
||||||
@ -119,18 +88,18 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
var tokens = response.SelectTokens($"{context.TargetInterface.Code}.{pdfLabelConfig.Path}").ToList();
|
var tokens = response.SelectTokens($"{context.TargetInterface.Code}.{pdfLabelConfig.Path}").ToList();
|
||||||
if (tokens.Count == 0) tokens = response.SelectTokens($"{pdfLabelConfig.Path}").ToList();
|
if (tokens.Count == 0) tokens = response.SelectTokens($"{pdfLabelConfig.Path}").ToList();
|
||||||
if (tokens.Count == 0) return mappedRes;
|
if (tokens.Count == 0) return mappedRes;
|
||||||
var token = tokens[0];
|
var token = tokens.First();
|
||||||
UploadResultDto oss = null;
|
UploadResultDto oss = null!;
|
||||||
switch (Enum.Parse<LabelType>(pdfLabelConfig.Value))
|
switch (Enum.Parse<LabelType>(pdfLabelConfig.Value))
|
||||||
{
|
{
|
||||||
case LabelType.Url:
|
case LabelType.Url:
|
||||||
{
|
{
|
||||||
string url = token.Value<string>();
|
string url = token.Value<string>()!;
|
||||||
using var r = await ClientFactory.CreateClient().GetAsync(url);
|
using var r = await ClientFactory.CreateClient().GetAsync(url);
|
||||||
await using var stream = await r.Content.ReadAsStreamAsync();
|
await using var stream = await r.Content.ReadAsStreamAsync();
|
||||||
oss = OSSHelper.Upload(stream, Guid.NewGuid().ToString("D") + ".pdf");
|
oss = OSSHelper.Upload(stream, Guid.NewGuid().ToString("D") + ".pdf");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LabelType.Base64:
|
case LabelType.Base64:
|
||||||
byte[] bytes = Convert.FromBase64String(token.ToString());
|
byte[] bytes = Convert.FromBase64String(token.ToString());
|
||||||
if (bytes.Length > 0)
|
if (bytes.Length > 0)
|
||||||
@ -143,8 +112,8 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
|
|
||||||
mappedRes["oss_key"] = oss!.Key;
|
mappedRes["oss_key"] = oss.Key;
|
||||||
mappedRes["oss_url"] = oss!.TempUrl;
|
mappedRes["oss_url"] = oss.TempUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -159,7 +128,7 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
/// <exception cref="BusinessException"></exception>
|
/// <exception cref="BusinessException"></exception>
|
||||||
public virtual void AfterRequest(ForwardCoreContext context)
|
public virtual void AfterRequest(ForwardCoreContext context)
|
||||||
{
|
{
|
||||||
if (context.ServiceProviderRequestLog.Response == RequestLogDto.Init)
|
if (context.ServiceProviderRequestLog.Response == null)
|
||||||
{
|
{
|
||||||
ThrowBusinessException(context, "请注意维护服务商日志!");
|
ThrowBusinessException(context, "请注意维护服务商日志!");
|
||||||
}
|
}
|
||||||
@ -205,9 +174,8 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
ThrowBusinessException(context,
|
ThrowBusinessException(context,
|
||||||
$"期望返回类型:{context.TargetInterface.ResponseContentType},解析过程中发生异常",
|
$"期望返回类型:{context.TargetInterface.ResponseContentType},解析过程中发生异常");
|
||||||
RequestServiceProviderFailedCommon.DeSerializeFailedCode);
|
return default!;
|
||||||
return default;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +209,7 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 失败
|
// 失败
|
||||||
string errorMsg = null;
|
string? errorMsg = null;
|
||||||
var errorConfig = context.ReturnConfigList
|
var errorConfig = context.ReturnConfigList
|
||||||
.FirstOrDefault(x => x.ConfigType == InterfaceReturnConfigType.ErrorMsg);
|
.FirstOrDefault(x => x.ConfigType == InterfaceReturnConfigType.ErrorMsg);
|
||||||
if (errorConfig != null)
|
if (errorConfig != null)
|
||||||
@ -250,8 +218,7 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 业务失败发飞书
|
// 业务失败发飞书
|
||||||
ThrowBusinessException(context, errorMsg ?? "未通过状态码验证。",
|
ThrowBusinessException(context, errorMsg ?? "未通过状态码验证。");
|
||||||
RequestServiceProviderFailedCommon.VerifyStatusFailedCode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -259,10 +226,8 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">上下文</param>
|
/// <param name="context">上下文</param>
|
||||||
/// <param name="msg">异常消息</param>
|
/// <param name="msg">异常消息</param>
|
||||||
/// <param name="code">异常状态码</param>
|
|
||||||
/// <exception cref="BusinessException"></exception>
|
/// <exception cref="BusinessException"></exception>
|
||||||
protected virtual void ThrowBusinessException(ForwardCoreContext context, string msg,
|
protected virtual void ThrowBusinessException(ForwardCoreContext context, string msg)
|
||||||
string code = "400")
|
|
||||||
{
|
{
|
||||||
if (context.ServiceProviderRequestLog.Cost == default)
|
if (context.ServiceProviderRequestLog.Cost == default)
|
||||||
{
|
{
|
||||||
@ -334,8 +299,7 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
msg.Headers.Remove(item.FieldName);
|
msg.Headers.Remove(item.FieldName);
|
||||||
if (!msg.Headers.TryAddWithoutValidation(item.FieldName, item.FieldValue))
|
if (!msg.Headers.TryAddWithoutValidation(item.FieldName, item.FieldValue))
|
||||||
{
|
{
|
||||||
ThrowBusinessException(context, $"请求头无法插入 {item.FieldName}:{item.FieldValue}",
|
ThrowBusinessException(context, $"请求头无法插入 {item.FieldName}:{item.FieldValue}");
|
||||||
RequestServiceProviderFailedCommon.HeaderInsertFailed);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,16 +312,14 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
catch (TaskCanceledException ex)
|
catch (TaskCanceledException ex)
|
||||||
{
|
{
|
||||||
// 捕获超时异常
|
// 捕获超时异常
|
||||||
ThrowBusinessException(context, "请求超时," + ex.Message,
|
ThrowBusinessException(context, "请求超时," + ex.Message);
|
||||||
RequestServiceProviderFailedCommon.RequestFailedCode);
|
|
||||||
}
|
}
|
||||||
catch (HttpRequestException rex)
|
catch (HttpRequestException rex)
|
||||||
{
|
{
|
||||||
ThrowBusinessException(context, "请求失败," + rex.Message,
|
ThrowBusinessException(context, "请求失败," + rex.Message);
|
||||||
RequestServiceProviderFailedCommon.RequestFailedCode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return default;
|
return default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual async Task<string> RequestCoreAsync(ForwardCoreContext context, HttpRequestMessage msg)
|
protected virtual async Task<string> RequestCoreAsync(ForwardCoreContext context, HttpRequestMessage msg)
|
||||||
@ -368,21 +330,18 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
client.Timeout = TimeSpan.FromSeconds((double)context.TargetInterface.Timeout);
|
client.Timeout = TimeSpan.FromSeconds((double)context.TargetInterface.Timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
var qps = context.TargetInterface.Qps <= 0
|
|
||||||
? context.Feature.ServiceProviderDefaultQps // 默认qps上限
|
|
||||||
: context.TargetInterface.Qps;
|
|
||||||
if (context.Feature.IsWait)
|
if (context.Feature.IsWait)
|
||||||
{
|
{
|
||||||
while (!await CanVisitInterfaceAsync(context.TargetInterface.Code, qps))
|
while (!await CanVisitInterfaceAsync(context))
|
||||||
{
|
{
|
||||||
await Task.Delay(context.Feature.SpinPeriod);
|
await Task.Delay(context.Feature.SpinPeriod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!await CanVisitInterfaceAsync(context.TargetInterface.Code, qps))
|
if (!await CanVisitInterfaceAsync(context))
|
||||||
{
|
{
|
||||||
throw new BusinessException($"服务商接口qps已达到上限{qps},稍后请求");
|
throw new BusinessException($"服务商接口qps已达到上限{context.Feature.TargetInterfaceQps},稍后请求");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,7 +404,8 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nameValueCollection = JsonConvert.DeserializeObject<Dictionary<string, string>>(body);
|
nameValueCollection = JsonConvert.DeserializeObject<Dictionary<string, string>>(body) ??
|
||||||
|
new Dictionary<string, string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.Content = new FormUrlEncodedContent(nameValueCollection);
|
msg.Content = new FormUrlEncodedContent(nameValueCollection);
|
||||||
@ -485,7 +445,7 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
case ContentType.Xml: // json转xml
|
case ContentType.Xml: // json转xml
|
||||||
var jsonStr = JsonConvert.SerializeObject(body);
|
var jsonStr = JsonConvert.SerializeObject(body);
|
||||||
var xmlDoc = JsonConvert.DeserializeXmlNode(jsonStr);
|
var xmlDoc = JsonConvert.DeserializeXmlNode(jsonStr);
|
||||||
data = xmlDoc?.OuterXml;
|
data = xmlDoc?.OuterXml ?? string.Empty;
|
||||||
break;
|
break;
|
||||||
case ContentType.FormUrlEncoded:
|
case ContentType.FormUrlEncoded:
|
||||||
data = JsonConvert.SerializeObject(body); //到请求时解析成Dictionary<string, string>
|
data = JsonConvert.SerializeObject(body); //到请求时解析成Dictionary<string, string>
|
||||||
@ -535,7 +495,7 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item.FieldValue = context.GetAccountFieldValueOrNull(fieldName);
|
item.FieldValue = context.GetAccountFieldValueOrNull(fieldName) ?? string.Empty;
|
||||||
break;
|
break;
|
||||||
case GlobalConst.Url:
|
case GlobalConst.Url:
|
||||||
item.FieldValue = context.TargetInterface.RequestAddress;
|
item.FieldValue = context.TargetInterface.RequestAddress;
|
||||||
@ -553,7 +513,7 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
: (timeStamp / 1000).ToString(), // s
|
: (timeStamp / 1000).ToString(), // s
|
||||||
GlobalConst.DateTime => TurnDate(now, item.Description),
|
GlobalConst.DateTime => TurnDate(now, item.Description),
|
||||||
GlobalConst.Url => context.TargetInterface.RequestAddress,
|
GlobalConst.Url => context.TargetInterface.RequestAddress,
|
||||||
GlobalConst.FromAccount => context.GetAccountFieldValueOrNull(item.Name),
|
GlobalConst.FromAccount => context.GetAccountFieldValueOrNull(item.Name) ?? string.Empty,
|
||||||
_ => item.Value // 不处理
|
_ => item.Value // 不处理
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -574,21 +534,21 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string TurnDate(DateTime dateTime, string dateStyle)
|
string TurnDate(DateTime dateTime, string? dateStyle)
|
||||||
{
|
{
|
||||||
if (!dateStyle.IsNullOrWhiteSpace())
|
if (dateStyle.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
try
|
return dateTime.ToString(GlobalConst.DefaultDateStyle);
|
||||||
{
|
|
||||||
return dateTime.ToString(dateStyle);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return dateTime.ToString(GlobalConst.DefaultDateStyle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return dateTime.ToString(GlobalConst.DefaultDateStyle);
|
try
|
||||||
|
{
|
||||||
|
return dateTime.ToString(dateStyle);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return dateTime.ToString(GlobalConst.DefaultDateStyle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -651,24 +611,14 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VirtualizePath(List<ParameterNode> list)
|
async Task<bool> CanVisitInterfaceAsync(ForwardCoreContext context)
|
||||||
{
|
{
|
||||||
if (list == null || list.Count == 0) return;
|
string interfaceCode = context.TargetInterface.Code;
|
||||||
foreach (var item in list)
|
int qps = context.Feature.TargetInterfaceQps;
|
||||||
{
|
var hash = context.Feature.AccountHashCode;
|
||||||
if (!string.IsNullOrEmpty(item.MapAlias))
|
|
||||||
{
|
|
||||||
item.MapAlias = "[*]." + item.MapAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
VirtualizePath(item.Children);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<bool> CanVisitInterfaceAsync(string interfaceCode, int qps)
|
|
||||||
{
|
|
||||||
// REDIS限流
|
// REDIS限流
|
||||||
string redisKey = $"{GlobalConst.ServiceProviderInterfaceQPSKeyPrefix}:{interfaceCode}";
|
string redisKey = $"{GlobalConst.ServiceProviderInterfaceQpsKeyPrefix}:{interfaceCode}:{hash}";
|
||||||
var num = await RedisHelper.Client.EvalAsync("""
|
var num = await RedisHelper.Client.EvalAsync("""
|
||||||
local redisKey=ARGV[1];
|
local redisKey=ARGV[1];
|
||||||
local qps=ARGV[2];
|
local qps=ARGV[2];
|
||||||
@ -685,4 +635,25 @@ public class DefaultForwardFlow : IForwardFlow
|
|||||||
""", null, redisKey, qps);
|
""", null, redisKey, qps);
|
||||||
return (long)num != 0;
|
return (long)num != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async Task ExecuteScriptAsync(ForwardCoreContext context, bool isPrefix)
|
||||||
|
{
|
||||||
|
var script = isPrefix ? context.PrefixScript : context.PostfixScript;
|
||||||
|
if (script.IsNullOrEmpty()) return;
|
||||||
|
|
||||||
|
Debug.Assert(script != null, nameof(script) + " != null");
|
||||||
|
var scriptHash = StringHelper.GetHash(script);
|
||||||
|
if (script.StartsWith("//async") || script.Contains("await"))
|
||||||
|
{
|
||||||
|
var @delegate = DelegateDic.GetOrAdd(scriptHash,
|
||||||
|
static (_, arg) => NDelegate.RandomDomain().AsyncFunc<ForwardCoreContext, Task>(arg), script);
|
||||||
|
await (Task)@delegate.DynamicInvoke(context)!;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var @delegate = DelegateDic.GetOrAdd(scriptHash,
|
||||||
|
static (_, arg) => NDelegate.RandomDomain().Action<ForwardCoreContext>(arg), script);
|
||||||
|
@delegate.DynamicInvoke(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -4,8 +4,6 @@ namespace InterfaceForward.Application.Helpers;
|
|||||||
|
|
||||||
public static class CommonExtensions
|
public static class CommonExtensions
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
public static async Task<byte[]> GetAllBytesAsync(this IFormFile file)
|
public static async Task<byte[]> GetAllBytesAsync(this IFormFile file)
|
||||||
{
|
{
|
||||||
using (var stream = file.OpenReadStream())
|
using (var stream = file.OpenReadStream())
|
||||||
|
|||||||
51
src/InterfaceForward.Application/Helpers/StringHelper.cs
Normal file
51
src/InterfaceForward.Application/Helpers/StringHelper.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace InterfaceForward.Application.Helpers;
|
||||||
|
|
||||||
|
public class StringHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 只能输入英文字符和键盘上常用的符号
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool IsValidEnglishString(string input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(input))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular expression to match only English letters and specified symbols
|
||||||
|
var regex = new Regex(@"^[a-zA-Z\(\)@!#$%^&*_\-+=;:""'/><,.\s]+$");
|
||||||
|
return regex.IsMatch(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>获取字符串的hash值</summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetHash(string str)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(str)) return string.Empty;
|
||||||
|
|
||||||
|
// 将字符串转换为字节
|
||||||
|
byte[] data = Encoding.UTF8.GetBytes(str);
|
||||||
|
|
||||||
|
// 创建SHA256实例
|
||||||
|
using SHA256 sha256 = SHA256.Create();
|
||||||
|
// 计算哈希值
|
||||||
|
byte[] hash = sha256.ComputeHash(data);
|
||||||
|
|
||||||
|
// 将哈希值转换为16进制字符串
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
foreach (byte b in hash)
|
||||||
|
{
|
||||||
|
sb.Append(b.ToString("x2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
using InterfaceForward.Domain.Shared.Enum;
|
||||||
|
|
||||||
|
namespace InterfaceForward.Application.HostServices;
|
||||||
|
|
||||||
|
public class AppSubscribeEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 操作类型
|
||||||
|
/// </summary>
|
||||||
|
public OptionType OptionType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用key
|
||||||
|
/// </summary>
|
||||||
|
public string AppKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 系统接口code
|
||||||
|
/// </summary>
|
||||||
|
public string SystemInterfaceCode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 服务商code
|
||||||
|
/// </summary>
|
||||||
|
public string ServiceProviderCode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 【慎重!】mq预取数量(服务并发度,给0就是默认4)
|
||||||
|
/// </summary>
|
||||||
|
public ushort FetchCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 【慎重!】失败重回队列(一般false,即接口通不做重试,否则业务错误可能死循环)
|
||||||
|
/// </summary>
|
||||||
|
public bool FailedRequeue { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 【慎重!】删除队列(删除专用,一般false,即接口通只解除绑定,不删除队列)
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDeleteQueue { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
using System.Net;
|
||||||
|
using Fake.DependencyInjection;
|
||||||
|
using InterfaceForward.Application.Rabbit;
|
||||||
|
using InterfaceForward.Domain.Shared;
|
||||||
|
using InterfaceForward.Domain.Shared.Enum;
|
||||||
|
using Microsoft.IdentityModel.Logging;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace InterfaceForward.Application.HostServices;
|
||||||
|
|
||||||
|
public class AppSubscribeEventHandler : ITransientDependency
|
||||||
|
{
|
||||||
|
public const string AppSubscribeEventSubscribe = "sjzy.interface.relay.app.subscribe";
|
||||||
|
private readonly RabbitClient _rabbitClient;
|
||||||
|
|
||||||
|
public AppSubscribeEventHandler(RabbitClient rabbitClient)
|
||||||
|
{
|
||||||
|
_rabbitClient = rabbitClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Handle(AppSubscribeEvent @event)
|
||||||
|
{
|
||||||
|
switch (@event.OptionType)
|
||||||
|
{
|
||||||
|
case OptionType.无:
|
||||||
|
break;
|
||||||
|
case OptionType.新增:
|
||||||
|
var res = DoAdd(@event);
|
||||||
|
LogHelper.Info($"{Dns.GetHostName()}\n{@event.ToJson()}\n{res}", true);
|
||||||
|
break;
|
||||||
|
case OptionType.修改:
|
||||||
|
var res1 = DoUpdate(@event);
|
||||||
|
LogHelper.Info($"{Dns.GetHostName()}\n{@event.ToJson()}\n{res1}", true);
|
||||||
|
break;
|
||||||
|
case OptionType.删除:
|
||||||
|
var res2 = DoDelete(@event);
|
||||||
|
LogHelper.Info($"{Dns.GetHostName()}\n{@event.ToJson()}\n{res2}", true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string DoUpdate(AppSubscribeEvent @event)
|
||||||
|
{
|
||||||
|
var queueName =
|
||||||
|
$"{BatchForwardEventHandler.BatchForwardEventSubscribe}.{@event.AppKey}.{@event.SystemInterfaceCode}.{@event.ServiceProviderCode}";
|
||||||
|
_rabbitClient.UnSubscribe(queueName, GlobalConst.InterfaceRelayExchange, queueName, @event.IsDeleteQueue);
|
||||||
|
|
||||||
|
_rabbitClient.Subscribe(new ConsumeOptions
|
||||||
|
{
|
||||||
|
Queue = queueName,
|
||||||
|
FetchCount = @event.FetchCount, // 并发控制
|
||||||
|
FailedRequeue = @event.FailedRequeue,
|
||||||
|
Declaration = o =>
|
||||||
|
{
|
||||||
|
o.ExchangeDeclare(GlobalConst.InterfaceRelayExchange, ExchangeType.Direct, true);
|
||||||
|
o.QueueDeclare(queueName, true);
|
||||||
|
o.QueueBind(queueName, GlobalConst.InterfaceRelayExchange, queueName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $"已成功更新队列:{queueName}的消费者参数,可以通过/AppSubscribe/GetList查询所有订阅";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string DoDelete(AppSubscribeEvent @event)
|
||||||
|
{
|
||||||
|
var queueName =
|
||||||
|
$"{BatchForwardEventHandler.BatchForwardEventSubscribe}.{@event.AppKey}.{@event.SystemInterfaceCode}.{@event.ServiceProviderCode}";
|
||||||
|
_rabbitClient.UnSubscribe(queueName, GlobalConst.InterfaceRelayExchange, queueName, @event.IsDeleteQueue);
|
||||||
|
return $"已成功{(@event.IsDeleteQueue ? "删除" : "解绑")}队列:{queueName},可以通过/AppSubscribe/GetList查询所有订阅";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string DoAdd(AppSubscribeEvent @event)
|
||||||
|
{
|
||||||
|
var queueName =
|
||||||
|
$"{BatchForwardEventHandler.BatchForwardEventSubscribe}.{@event.AppKey}.{@event.SystemInterfaceCode}.{@event.ServiceProviderCode}";
|
||||||
|
_rabbitClient.Subscribe(new ConsumeOptions
|
||||||
|
{
|
||||||
|
Queue = queueName,
|
||||||
|
FetchCount = @event.FetchCount, // 并发控制
|
||||||
|
FailedRequeue = @event.FailedRequeue,
|
||||||
|
Declaration = o =>
|
||||||
|
{
|
||||||
|
o.ExchangeDeclare(GlobalConst.InterfaceRelayExchange, ExchangeType.Direct, true);
|
||||||
|
o.QueueDeclare(queueName, true);
|
||||||
|
o.QueueBind(queueName, GlobalConst.InterfaceRelayExchange, queueName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $"已成功绑定队列:{queueName},可以通过/AppSubscribe/GetList查询所有订阅";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
using Fake.Helpers;
|
||||||
|
using InterfaceForward.Application.Helpers;
|
||||||
|
using InterfaceForward.Application.Rabbit;
|
||||||
|
using InterfaceForward.Domain.Shared;
|
||||||
|
using InterfaceForward.Repositories;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.IdentityModel.Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace InterfaceForward.Application.HostServices;
|
||||||
|
|
||||||
|
public class AppSubscribeHostService : IHostedService
|
||||||
|
{
|
||||||
|
private readonly AppSubscribeEventHandler _appSubscribeEventHandler;
|
||||||
|
private readonly RabbitClient _rabbitClient;
|
||||||
|
|
||||||
|
public AppSubscribeHostService(IBasicRepository<AppSubscribeConfigEntity> appSubscribeConfigRepository,
|
||||||
|
RabbitClient rabbitClient, AppSubscribeEventHandler appSubscribeEventHandler)
|
||||||
|
{
|
||||||
|
_appSubscribeConfigRepository = appSubscribeConfigRepository;
|
||||||
|
_rabbitClient = rabbitClient;
|
||||||
|
_appSubscribeEventHandler = appSubscribeEventHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
// todo:redis注册连接恢复订阅
|
||||||
|
RedisHelper.Client.Subscribe(AppSubscribeEventHandler.AppSubscribeEventSubscribe, AppSubScribe);
|
||||||
|
|
||||||
|
// 根据应用订阅表,绑定消费者
|
||||||
|
var subscribes = await _appSubscribeConfigRepository.GetListAsync(x => true);
|
||||||
|
foreach (var subscribe in subscribes)
|
||||||
|
{
|
||||||
|
var queueName = $"{BatchForwardEventHandler.BatchForwardEventSubscribe}.{subscribe.AppKey}.{subscribe
|
||||||
|
.SystemInterfaceCode}.{subscribe.ServiceProviderCode}";
|
||||||
|
_rabbitClient.Subscribe(new ConsumeOptions
|
||||||
|
{
|
||||||
|
Queue = queueName,
|
||||||
|
FetchCount = subscribe.FetchCount, // 并发控制
|
||||||
|
FailedRequeue = subscribe.FailedRequeue,
|
||||||
|
Declaration = o =>
|
||||||
|
{
|
||||||
|
o.ExchangeDeclare(GlobalConst.InterfaceRelayExchange, ExchangeType.Direct, true);
|
||||||
|
o.QueueDeclare(queueName, true);
|
||||||
|
o.QueueBind(queueName, GlobalConst.InterfaceRelayExchange, queueName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
RedisHelper.Client.UnSubscribe(AppSubscribeEventHandler.AppSubscribeEventSubscribe);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AppSubScribe(string chan, object msg)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
LogHelper.Info($"接受到来自redis的订阅:{chan}\n{msg.ToJson()}", true);
|
||||||
|
#endif
|
||||||
|
if (chan == AppSubscribeEventHandler.AppSubscribeEventSubscribe && msg is string str)
|
||||||
|
{
|
||||||
|
var @event = JsonConvert.DeserializeObject<AppSubscribeEvent>(str);
|
||||||
|
AsyncHelper.RunSync(() => _appSubscribeEventHandler.Handle(@event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,6 +18,7 @@
|
|||||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.13.0" />
|
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.13.0" />
|
||||||
<PackageReference Include="DotNetCore.Natasha.CSharp" Version="5.2.2.1"/>
|
<PackageReference Include="DotNetCore.Natasha.CSharp" Version="5.2.2.1"/>
|
||||||
<PackageReference Include="Fake.AspNetCore" Version="8.0.0-preview8.5" />
|
<PackageReference Include="Fake.AspNetCore" Version="8.0.0-preview8.5" />
|
||||||
|
<PackageReference Include="Fake.EventBus.RabbitMQ" Version="1.0.0" />
|
||||||
<PackageReference Include="Fake.ObjectMapping.AutoMapper" Version="8.0.0-preview8.5" />
|
<PackageReference Include="Fake.ObjectMapping.AutoMapper" Version="8.0.0-preview8.5" />
|
||||||
<PackageReference Include="FreeRedis" Version="1.2.15" />
|
<PackageReference Include="FreeRedis" Version="1.2.15" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
using Fake.AspNetCore;
|
using Fake.AspNetCore;
|
||||||
|
using Fake.EventBus.RabbitMQ;
|
||||||
using Fake.Modularity;
|
using Fake.Modularity;
|
||||||
using Fake.ObjectMapping.AutoMapper;
|
using Fake.ObjectMapping.AutoMapper;
|
||||||
using InterfaceForward.Application.Contracts;
|
using InterfaceForward.Application.Contracts;
|
||||||
@ -10,6 +11,7 @@ namespace InterfaceForward.Application;
|
|||||||
|
|
||||||
[DependsOn(typeof(FakeAspNetCoreModule))]
|
[DependsOn(typeof(FakeAspNetCoreModule))]
|
||||||
[DependsOn(typeof(FakeObjectMappingAutoMapperModule))]
|
[DependsOn(typeof(FakeObjectMappingAutoMapperModule))]
|
||||||
|
[DependsOn(typeof(FakeEventBusRabbitMqModule))]
|
||||||
[DependsOn(typeof(InterfaceForwardRepositoriesModule))]
|
[DependsOn(typeof(InterfaceForwardRepositoriesModule))]
|
||||||
public class InterfaceForwardApplicationModule:FakeModule
|
public class InterfaceForwardApplicationModule:FakeModule
|
||||||
{
|
{
|
||||||
|
|||||||
20
src/InterfaceForward.Application/Rabbit/BatchForwardEvent.cs
Normal file
20
src/InterfaceForward.Application/Rabbit/BatchForwardEvent.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
namespace InterfaceForward.Application.Rabbit;
|
||||||
|
|
||||||
|
public class BatchForwardEvent
|
||||||
|
{
|
||||||
|
public Guid RequestId { get; set; }
|
||||||
|
|
||||||
|
public int AppId { get; set; }
|
||||||
|
|
||||||
|
public string UpStreamCode { get; set; }
|
||||||
|
|
||||||
|
public string ServiceProviderCode { get; set; }
|
||||||
|
|
||||||
|
public string Attach { get; set; }
|
||||||
|
|
||||||
|
public string ResSubscribeName { get; set; }
|
||||||
|
|
||||||
|
public bool IsReturnMessage { get; set; }
|
||||||
|
|
||||||
|
public string Data { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Fake.DependencyInjection;
|
||||||
|
using InterfaceForward.Application.Contracts.ForwardCore;
|
||||||
|
using InterfaceForward.Application.Services;
|
||||||
|
using InterfaceForward.Domain.Shared;
|
||||||
|
using InterfaceForward.Domain.Shared.FeiShu;
|
||||||
|
using InterfaceForward.Repositories.Log.Services;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using SJZY.InterfaceRelay.Application.Contracts;
|
||||||
|
|
||||||
|
namespace InterfaceForward.Application.Rabbit;
|
||||||
|
|
||||||
|
public class BatchForwardEventHandler(
|
||||||
|
InterfaceForwardCommon forwardCommon,
|
||||||
|
RabbitClient rabbitClient,
|
||||||
|
ILogRepository logRepository,
|
||||||
|
ILogger<BatchForwardEventHandler> logger,
|
||||||
|
IFeiShuNoticer feiShuNoticer)
|
||||||
|
: ITransientDependency, IRabbitHandler
|
||||||
|
{
|
||||||
|
public const string BatchForwardEventSubscribe = "sjzy.interface.relay.batch";
|
||||||
|
private readonly RabbitClient _rabbitClient = rabbitClient;
|
||||||
|
|
||||||
|
public bool Enable(ConsumeOptions options)
|
||||||
|
{
|
||||||
|
return options.Queue.StartsWith(BatchForwardEventSubscribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(IServiceProvider sp, BasicDeliverEventArgs args, ConsumeOptions options)
|
||||||
|
{
|
||||||
|
var msg = Encoding.UTF8.GetString(args.Body.ToArray());
|
||||||
|
logger.LogDebug($"rabbit on queue({options.Queue}) received msg");
|
||||||
|
|
||||||
|
var @event = System.Text.Json.JsonSerializer.Deserialize<BatchForwardEvent>(msg);
|
||||||
|
|
||||||
|
// important:恢复应用现场 全局贯彻
|
||||||
|
logRepository.AppId = @event.AppId;
|
||||||
|
logRepository.AppRequestLog.RequestId = @event.RequestId;
|
||||||
|
|
||||||
|
var contextList = await forwardCommon.VerifyBusinessAndBuildForwardContexts(@event.UpStreamCode,
|
||||||
|
@event.ServiceProviderCode);
|
||||||
|
|
||||||
|
// 深拷贝解决多线程上下文数据干扰问题
|
||||||
|
foreach (var item in contextList)
|
||||||
|
{
|
||||||
|
item.ServiceProviderRequestLog.RequestId = @event.RequestId;
|
||||||
|
item.Feature.IsWait = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await HandleReplayAsync(contextList, @event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleReplayAsync(List<ForwardCoreContext> contextList, BatchForwardEvent @event)
|
||||||
|
{
|
||||||
|
var req = JToken.Parse(@event.Data);
|
||||||
|
|
||||||
|
var context = contextList.First();
|
||||||
|
|
||||||
|
var replayEvent = new InterfaceRelayForwardReplayEvent
|
||||||
|
{
|
||||||
|
RequestId = context.ServiceProviderRequestLog.RequestId,
|
||||||
|
RequestParams = JsonConvert.SerializeObject(req),
|
||||||
|
Attach = @event.Attach,
|
||||||
|
Message = "请求成功"
|
||||||
|
};
|
||||||
|
try
|
||||||
|
{
|
||||||
|
context.OriginalInterfaceInput = contextList.Count == 1
|
||||||
|
? req
|
||||||
|
: new JObject
|
||||||
|
{
|
||||||
|
[@event.UpStreamCode!] = req
|
||||||
|
};
|
||||||
|
|
||||||
|
var res = contextList.Count == 1
|
||||||
|
? await forwardCommon.InternalForwardAsync(context)
|
||||||
|
: await forwardCommon.InternalForward2Async(contextList);
|
||||||
|
|
||||||
|
replayEvent.OriginalMessage = @event.IsReturnMessage ? logRepository.ServiceProviderRequestLog.Response : null;
|
||||||
|
replayEvent.Data = JsonConvert.SerializeObject(res); // 解决cap序列化object问题
|
||||||
|
replayEvent.IsSuccess = true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
replayEvent.Message = ex.Message;
|
||||||
|
await feiShuNoticer.NoticeAsync(ex.ToString());
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
var data = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(replayEvent);
|
||||||
|
_rabbitClient.Publish(GlobalConst.InterfaceRelayExchange, @event.ResSubscribeName, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/InterfaceForward.Application/Rabbit/RabbitClient.cs
Normal file
32
src/InterfaceForward.Application/Rabbit/RabbitClient.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using Fake.RabbitMQ;
|
||||||
|
|
||||||
|
namespace InterfaceForward.Application.Rabbit;
|
||||||
|
|
||||||
|
public class RabbitClient(IRabbitMqConnector connector)
|
||||||
|
{
|
||||||
|
public void Subscribe(ConsumeOptions consumeOptions)
|
||||||
|
{
|
||||||
|
using var channel = connector.CreateChannel();
|
||||||
|
var consumer = new EventingBasicConsumer(channel);
|
||||||
|
consumer.Received += (model, ea) =>
|
||||||
|
{
|
||||||
|
var body = ea.Body.ToArray();
|
||||||
|
var message = Encoding.UTF8.GetString(body);
|
||||||
|
Console.WriteLine(" [x] Received {0}", message);
|
||||||
|
};
|
||||||
|
channel.BasicConsume(queue: consumeOptions.Queue,
|
||||||
|
autoAck: true,
|
||||||
|
consumer: consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConsumeOptions
|
||||||
|
{
|
||||||
|
public string Queue { get; set; }
|
||||||
|
|
||||||
|
public ushort FetchCount { get; set; }
|
||||||
|
|
||||||
|
public bool FailedRequeue { get; set; }
|
||||||
|
|
||||||
|
public Action<IDeclaration>
|
||||||
|
}
|
||||||
@ -1,4 +1,7 @@
|
|||||||
using System.Runtime.ExceptionServices;
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
using Fake.DependencyInjection;
|
using Fake.DependencyInjection;
|
||||||
using InterfaceForward.Application.Contracts;
|
using InterfaceForward.Application.Contracts;
|
||||||
using InterfaceForward.Application.Contracts.ForwardCore;
|
using InterfaceForward.Application.Contracts.ForwardCore;
|
||||||
@ -16,93 +19,85 @@ using SJZY.InterfaceRelay.Repository.Log.ValueObjects;
|
|||||||
|
|
||||||
namespace InterfaceForward.Application.Services;
|
namespace InterfaceForward.Application.Services;
|
||||||
|
|
||||||
public class InterfaceForwardCommon : ITransientDependency
|
public class InterfaceForwardCommon(
|
||||||
|
InterfaceForwardQuery interfaceForwardQuery,
|
||||||
|
ILogRepository logRepository,
|
||||||
|
IServiceProvider serviceProvider,
|
||||||
|
IOptionsSnapshot<InterfaceRelayOptions> options)
|
||||||
|
: ITransientDependency
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上下文缓存数据:映射,账户
|
/// 缓存数据:映射,账户 默认过期时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly int ContextCacheTtl = int.MaxValue;
|
public static readonly int DefaultCacheTtl = int.MaxValue;
|
||||||
|
|
||||||
private readonly Dictionary<string, IForwardFlow> _flows = new();
|
private readonly Dictionary<string, IForwardFlow> _flows = new();
|
||||||
|
|
||||||
private readonly InterfaceForwardQuery _interfaceForwardQuery;
|
private readonly InterfaceRelayOptions _options = options.Value;
|
||||||
private readonly InterfaceRelayOptions _interfaceRelayOptions;
|
|
||||||
private readonly ILogRepository _logRepository;
|
|
||||||
// private readonly IEntrySegmentContextAccessor _segContext;
|
|
||||||
private readonly IServiceProvider _serviceProvider;
|
|
||||||
|
|
||||||
public InterfaceForwardCommon(InterfaceForwardQuery interfaceForwardQuery
|
|
||||||
, ILogRepository logRepository
|
|
||||||
// , IEntrySegmentContextAccessor segContext
|
|
||||||
, IServiceProvider serviceProvider
|
|
||||||
, IOptions<InterfaceRelayOptions> options)
|
|
||||||
{
|
|
||||||
_interfaceForwardQuery = interfaceForwardQuery;
|
|
||||||
_logRepository = logRepository;
|
|
||||||
// _segContext = segContext;
|
|
||||||
_serviceProvider = serviceProvider;
|
|
||||||
_interfaceRelayOptions = options.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<ForwardCoreContext>> VerifyBusinessAndBuildForwardContexts(string upStreamCode,
|
public async Task<List<ForwardCoreContext>> VerifyBusinessAndBuildForwardContexts(string upStreamCode,
|
||||||
string serviceProviderCode)
|
string serviceProviderCode)
|
||||||
{
|
{
|
||||||
|
// todo:字符串优化
|
||||||
// tips:上下文的缓存不维护,在发布时统一替代。
|
// tips:上下文的缓存不维护,在发布时统一替代。
|
||||||
var contextCacheKey = GetContextCacheKey(upStreamCode, serviceProviderCode);
|
var contextCacheKey = GetContextCacheKey(upStreamCode, serviceProviderCode);
|
||||||
// _segContext.Context.Span.AddLog(LogEvent.Message("【转发核心上下文】开始构建"));
|
|
||||||
var cacheContext = (await RedisHelper.Client.GetAsync<string>(contextCacheKey))
|
var cacheContext = (await RedisHelper.Client.GetAsync<string>(contextCacheKey))
|
||||||
.ToObject<ForwardCoreContextCache>();
|
.ToObject<ForwardCoreContextCache>();
|
||||||
if (cacheContext == null)
|
if (cacheContext == null)
|
||||||
{
|
{
|
||||||
// _segContext.Context.Span.AddLog(LogEvent.Message("【转发核心上下文】没能命中缓存,重新构建"));
|
var systemInterface = await interfaceForwardQuery.GetInterfaceByCodeAsync(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;
|
||||||
_logRepository.AppRequestLog.InterfaceName = systemInterface.Name;
|
logRepository.AppRequestLog.InterfaceName = systemInterface.Name;
|
||||||
|
|
||||||
var serviceProvider = await _interfaceForwardQuery.GetServiceProviderByCodeAsync(serviceProviderCode);
|
var serviceProviderDto = await interfaceForwardQuery.GetServiceProviderByCodeAsync(serviceProviderCode);
|
||||||
if (serviceProvider == null) throw new BusinessException(message: "服务商不存在");
|
if (serviceProviderDto == null) throw new BusinessException(message: "服务商不存在");
|
||||||
cacheContext = await _interfaceForwardQuery.BuildForwardCoreContext(systemInterface, serviceProvider);
|
cacheContext = await interfaceForwardQuery.BuildForwardCoreContext(systemInterface, serviceProviderDto);
|
||||||
await RedisHelper.Client.SetAsync(contextCacheKey, cacheContext.ToJson(), ContextCacheTtl);
|
await RedisHelper.Client.SetAsync(contextCacheKey, cacheContext.ToJson(), DefaultCacheTtl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// _segContext.Context.Span.AddLog(LogEvent.Message("【转发核心上下文】构建完成"));
|
|
||||||
// 记录系统接口日志
|
// 记录系统接口日志
|
||||||
_logRepository.AppRequestLog.InterfaceCode = cacheContext.OriginalInterface.Code;
|
logRepository.AppRequestLog.InterfaceCode = cacheContext.OriginalInterface.Code;
|
||||||
_logRepository.AppRequestLog.InterfaceName = cacheContext.OriginalInterface.Name;
|
logRepository.AppRequestLog.InterfaceName = cacheContext.OriginalInterface.Name;
|
||||||
|
|
||||||
// tips:服务商主体账户的缓存实时维护
|
// tips:服务商主体账户的缓存实时维护
|
||||||
// _segContext.Context.Span.AddLog(LogEvent.Message("【服务商应用账户】开始构建"));
|
var accountCacheKey = GetAccountCacheKey(serviceProviderCode, logRepository.AppId);
|
||||||
var accountCacheKey = GetAccountCacheKey(serviceProviderCode, _logRepository.AppId);
|
|
||||||
var accountFieldCache = (await RedisHelper.Client.GetAsync<string>(accountCacheKey))
|
var accountFieldCache = (await RedisHelper.Client.GetAsync<string>(accountCacheKey))
|
||||||
.ToObject<List<ServiceProviderAccountFieldWithValueDto>>();
|
.ToObject<List<ServiceProviderAccountFieldWithValueDto>>();
|
||||||
if (accountFieldCache == null)
|
if (accountFieldCache == null)
|
||||||
{
|
{
|
||||||
// _segContext.Context.Span.AddLog(LogEvent.Message("【服务商账户】没能命中缓存,重新构建"));
|
var serviceProviderDto = cacheContext.TargetServiceProvider;
|
||||||
var serviceProvider = cacheContext.TargetServiceProvider;
|
var accountId = await interfaceForwardQuery.GetAccountIdAsync(serviceProviderDto.Id, logRepository.AppId);
|
||||||
var accountId = await _interfaceForwardQuery.GetAccountIdAsync(serviceProvider.Id, _logRepository.AppId);
|
|
||||||
// 服务商授权
|
// 服务商授权
|
||||||
if (!serviceProvider.IsDisableAuth && accountId == default)
|
if (!serviceProviderDto.IsDisableAuth && accountId == default)
|
||||||
{
|
{
|
||||||
throw new BusinessException(message: $"服务商:{serviceProvider.Name} 下不存在此账户,请联系管理员或前往接口通创建");
|
throw new BusinessException(message: $"服务商:{serviceProviderDto.Name} 下不存在此账户,请联系管理员或前往接口通创建");
|
||||||
}
|
}
|
||||||
|
|
||||||
accountFieldCache = await _interfaceForwardQuery.GetAccountFieldListAsync(accountId);
|
accountFieldCache = await interfaceForwardQuery.GetAccountFieldListAsync(accountId);
|
||||||
|
accountFieldCache.Add(new ServiceProviderAccountFieldWithValueDto
|
||||||
|
{
|
||||||
|
FieldName = GlobalConst.AccountHashCode,
|
||||||
|
// 根据字段值计算hash,请求时作为qps key依据,即相同的账户会共享一个qps
|
||||||
|
FieldValue = GetHash(accountFieldCache.Select(x => x.FieldValue).JoinAsString("_"))
|
||||||
|
});
|
||||||
var ttl = await HandleAuthInterfaceAsync(cacheContext, accountFieldCache);
|
var ttl = await HandleAuthInterfaceAsync(cacheContext, accountFieldCache);
|
||||||
|
|
||||||
await RedisHelper.Client.SetAsync(accountCacheKey, accountFieldCache.ToJson(), ttl);
|
await RedisHelper.Client.SetAsync(accountCacheKey, accountFieldCache.ToJson(), ttl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// _segContext.Context.Span.AddLog(LogEvent.Message("【服务商账户】构建完成"));
|
|
||||||
|
|
||||||
// 组装转发上下文
|
// 组装转发上下文
|
||||||
var res = new List<ForwardCoreContext>();
|
var res = new List<ForwardCoreContext>();
|
||||||
|
|
||||||
foreach (var item in cacheContext.TargetInterfaces)
|
foreach (var item in cacheContext.TargetInterfaces)
|
||||||
{
|
{
|
||||||
item.TargetInterface.Timeout ??= cacheContext.TargetServiceProvider.Timeout;
|
item.TargetInterface.Timeout ??= cacheContext.TargetServiceProvider.Timeout;
|
||||||
|
item.TargetInterface.FlowCode = item.TargetInterface.FlowCode.IsNullOrWhiteSpace()
|
||||||
|
? cacheContext.TargetServiceProvider.FlowCode.IsNullOrWhiteSpace()
|
||||||
|
? nameof(DefaultForwardFlow)
|
||||||
|
: cacheContext.TargetServiceProvider.FlowCode
|
||||||
|
: item.TargetInterface.FlowCode;
|
||||||
var context = new ForwardCoreContext
|
var context = new ForwardCoreContext
|
||||||
{
|
{
|
||||||
TargetInterface = item.TargetInterface,
|
TargetInterface = item.TargetInterface,
|
||||||
@ -117,42 +112,37 @@ public class InterfaceForwardCommon : ITransientDependency
|
|||||||
var token = context.FixedFieldList.FirstOrDefault(x => x.FieldValue == GlobalConst.Token);
|
var token = context.FixedFieldList.FirstOrDefault(x => x.FieldValue == GlobalConst.Token);
|
||||||
if (token != default)
|
if (token != default)
|
||||||
{
|
{
|
||||||
token.FieldValue = context.GetAccountFieldValueOrNull(GlobalConst.Token);
|
token.FieldValue = context.GetAccountFieldValueOrNull(GlobalConst.Token)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.ServiceProviderRequestLog = new RequestLogDto(_logRepository.AppRequestLog.RequestId);
|
context.Feature.AccountHashCode = context.GetAccountFieldValueOrNull(GlobalConst.AccountHashCode)!;
|
||||||
|
|
||||||
|
context.ServiceProviderRequestLog = new RequestLogDto(logRepository.AppRequestLog.RequestId);
|
||||||
context.ServiceProviderRequestLog.Name = cacheContext.TargetServiceProvider.Name;
|
context.ServiceProviderRequestLog.Name = cacheContext.TargetServiceProvider.Name;
|
||||||
context.ServiceProviderRequestLog.Address = context.TargetInterface.RequestAddress;
|
context.ServiceProviderRequestLog.Address = context.TargetInterface.RequestAddress;
|
||||||
context.ServiceProviderRequestLog.InterfaceCode = context.TargetInterface.Code;
|
context.ServiceProviderRequestLog.InterfaceCode = context.TargetInterface.Code;
|
||||||
context.ServiceProviderRequestLog.InterfaceName = context.TargetInterface.Name;
|
context.ServiceProviderRequestLog.InterfaceName = context.TargetInterface.Name;
|
||||||
|
|
||||||
var flow = GetForwardFlow(context.TargetInterface.FlowCode, cacheContext.TargetServiceProvider.FlowCode);
|
Debug.Assert(context.TargetInterface.FlowCode != null, "context.TargetInterface.FlowCode != null");
|
||||||
|
var flow = GetForwardFlow(context.TargetInterface.FlowCode);
|
||||||
if (flow == default)
|
if (flow == default)
|
||||||
throw new BusinessException(message: "找不到接口转发流程" + context.TargetInterface.FlowCode);
|
throw new BusinessException(message: "找不到接口转发流程" + context.TargetInterface.FlowCode);
|
||||||
_flows.Add(context.TargetInterface.Code, flow);
|
_flows.Add(context.TargetInterface.Code, flow);
|
||||||
|
|
||||||
|
|
||||||
res.Add(context);
|
res.Add(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
// tips:把出参映射树放到最后一个上下文中,是为了在流程结束时执行出参映射
|
// tips:把出参映射树放到最后一个上下文中,是为了在流程结束时执行出参映射
|
||||||
res.Last().OutParamTreeList = cacheContext.OutParamTreeList;
|
res.Last().OutParamTreeList = cacheContext.OutParamTreeList;
|
||||||
|
|
||||||
// _segContext.Context.Span.AddLog(LogEvent.Message("【转发上下文】全部组装完成"));
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先用服务商接口指定流程,其次使用服务商的,再其次是默认的
|
// 优先用服务商接口指定流程,其次使用服务商的,再其次是默认的
|
||||||
public IForwardFlow GetForwardFlow(string interfaceFlowCode, string serviceProviderFlowCode)
|
public IForwardFlow GetForwardFlow(string flowCode)
|
||||||
{
|
{
|
||||||
var flowCode = interfaceFlowCode.IsNullOrWhiteSpace()
|
return serviceProvider.GetServices<IForwardFlow>()
|
||||||
? serviceProviderFlowCode.IsNullOrWhiteSpace()
|
.FirstOrDefault(x => x.GetType().Name == flowCode)?? throw new BusinessException("");
|
||||||
? nameof(DefaultForwardFlow)
|
|
||||||
: serviceProviderFlowCode
|
|
||||||
: interfaceFlowCode;
|
|
||||||
return _serviceProvider.GetServices<IForwardFlow>()
|
|
||||||
.FirstOrDefault(x => x.GetType().Name == flowCode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -170,7 +160,7 @@ public class InterfaceForwardCommon : ITransientDependency
|
|||||||
|
|
||||||
if (interfaceDto == default)
|
if (interfaceDto == default)
|
||||||
{
|
{
|
||||||
return ContextCacheTtl;
|
return DefaultCacheTtl;
|
||||||
}
|
}
|
||||||
|
|
||||||
var configs = cacheContext.ServiceProviderAuthConfigs;
|
var configs = cacheContext.ServiceProviderAuthConfigs;
|
||||||
@ -179,10 +169,16 @@ public class InterfaceForwardCommon : ITransientDependency
|
|||||||
var body = new JObject();
|
var body = new JObject();
|
||||||
foreach (var item in configs.Where(x => x.Flag == AuthParameterFlag.InParameter))
|
foreach (var item in configs.Where(x => x.Flag == AuthParameterFlag.InParameter))
|
||||||
{
|
{
|
||||||
|
Debug.Assert(item.ParameterAlias != null, "item.ParameterAlias != null");
|
||||||
body[item.ParameterAlias] = item.Value1;
|
body[item.ParameterAlias] = item.Value1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建授权上下文
|
// 构建授权上下文
|
||||||
|
interfaceDto.TargetInterface.FlowCode = interfaceDto.TargetInterface.FlowCode.IsNullOrWhiteSpace()
|
||||||
|
? cacheContext.TargetServiceProvider.FlowCode.IsNullOrWhiteSpace()
|
||||||
|
? nameof(DefaultForwardFlow)
|
||||||
|
: cacheContext.TargetServiceProvider.FlowCode
|
||||||
|
: interfaceDto.TargetInterface.FlowCode;
|
||||||
var authContext = new ForwardCoreContext
|
var authContext = new ForwardCoreContext
|
||||||
{
|
{
|
||||||
TargetInterface = interfaceDto.TargetInterface,
|
TargetInterface = interfaceDto.TargetInterface,
|
||||||
@ -191,12 +187,12 @@ public class InterfaceForwardCommon : ITransientDependency
|
|||||||
ReturnConfigList = interfaceDto.ReturnConfigList,
|
ReturnConfigList = interfaceDto.ReturnConfigList,
|
||||||
InParamTreeList = null,
|
InParamTreeList = null,
|
||||||
OutParamTreeList = null,
|
OutParamTreeList = null,
|
||||||
PrefixScript = null,
|
PrefixScript = interfaceDto.PrefixScript,
|
||||||
PostfixScript = null,
|
PostfixScript = interfaceDto.PostfixScript,
|
||||||
OriginalInterfaceInput = body,
|
OriginalInterfaceInput = body,
|
||||||
FormFieldList = interfaceDto.FormFieldList // normal case is null
|
FormFieldList = interfaceDto.FormFieldList // normal case is null
|
||||||
};
|
};
|
||||||
authContext.ServiceProviderRequestLog = new RequestLogDto(_logRepository.AppRequestLog.RequestId)
|
authContext.ServiceProviderRequestLog = new RequestLogDto(logRepository.AppRequestLog.RequestId)
|
||||||
{
|
{
|
||||||
Name = cacheContext.TargetServiceProvider.Name,
|
Name = cacheContext.TargetServiceProvider.Name,
|
||||||
Address = authContext.TargetInterface.RequestAddress,
|
Address = authContext.TargetInterface.RequestAddress,
|
||||||
@ -204,22 +200,16 @@ public class InterfaceForwardCommon : ITransientDependency
|
|||||||
InterfaceName = authContext.TargetInterface.Name
|
InterfaceName = authContext.TargetInterface.Name
|
||||||
};
|
};
|
||||||
|
|
||||||
// 走动态脚本
|
|
||||||
var scriptConfig = configs.FirstOrDefault(x => x.Flag == AuthParameterFlag.Script);
|
|
||||||
if (scriptConfig != default && !scriptConfig.Value1.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
// 使用Natasha构建动态表达式
|
|
||||||
return NDelegate.RandomDomain().Func<ForwardCoreContext, int>(scriptConfig.Value1)(authContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 授权也走主流程
|
// 授权也走主流程
|
||||||
if (!cacheContext.TargetServiceProvider.AuthInterfaceCode.IsNullOrWhiteSpace())
|
if (!cacheContext.TargetServiceProvider.AuthInterfaceCode.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
var flow = GetForwardFlow(interfaceDto.TargetInterface.FlowCode,
|
Debug.Assert(cacheContext.ServiceProviderAuthInterface != null, "cacheContext.ServiceProviderAuthInterface != null");
|
||||||
cacheContext.TargetServiceProvider.FlowCode);
|
var flow = GetForwardFlow(cacheContext.ServiceProviderAuthInterface.TargetInterface.FlowCode!);
|
||||||
if (flow == default)
|
if (flow == default)
|
||||||
throw new BusinessException(message: "找不到接口转发流程" + interfaceDto.TargetInterface.FlowCode);
|
throw new BusinessException(message: "找不到接口转发流程" +
|
||||||
_flows.Add(interfaceDto.TargetInterface.Code, flow);
|
cacheContext.ServiceProviderAuthInterface.TargetInterface
|
||||||
|
.FlowCode);
|
||||||
|
_flows.Add(cacheContext.ServiceProviderAuthInterface.TargetInterface.Code, flow);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 请求授权接口
|
// 请求授权接口
|
||||||
@ -229,12 +219,14 @@ public class InterfaceForwardCommon : ITransientDependency
|
|||||||
var tokenConfig = configs.FirstOrDefault(x => x.Flag == AuthParameterFlag.Token);
|
var tokenConfig = configs.FirstOrDefault(x => x.Flag == AuthParameterFlag.Token);
|
||||||
if (tokenConfig != default)
|
if (tokenConfig != default)
|
||||||
{
|
{
|
||||||
|
Debug.Assert(tokenConfig.ParameterAlias != null, "tokenConfig.ParameterAlias != null");
|
||||||
accountFieldCache.Add(new ServiceProviderAccountFieldWithValueDto
|
accountFieldCache.Add(new ServiceProviderAccountFieldWithValueDto
|
||||||
{
|
{
|
||||||
FieldName = GlobalConst.Token,
|
FieldName = GlobalConst.Token,
|
||||||
FieldValue = tokenConfig.Value1.IsNullOrWhiteSpace()
|
FieldValue = tokenConfig.Value1.IsNullOrWhiteSpace()
|
||||||
? response[tokenConfig.ParameterAlias]?.ToString()
|
? response.SelectToken(tokenConfig.ParameterAlias)?.ToString() ??
|
||||||
: tokenConfig.Value1.Trim() + " " + response[tokenConfig.ParameterAlias]
|
throw new BusinessException($"授权接口返回报文中未找到{tokenConfig.ParameterAlias}")
|
||||||
|
: tokenConfig.Value1!.Trim() + " " + response.SelectToken(tokenConfig.ParameterAlias)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,8 +239,10 @@ public class InterfaceForwardCommon : ITransientDependency
|
|||||||
return ttl;
|
return ttl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (int.TryParse(response[effectTimeConfig.ParameterAlias]?.ToString(), out var expiredValue))
|
Debug.Assert(effectTimeConfig.ParameterAlias != null, "effectTimeConfig.ParameterAlias != null");
|
||||||
|
if (int.TryParse(response.SelectToken(effectTimeConfig.ParameterAlias)?.ToString(), out var expiredValue))
|
||||||
{
|
{
|
||||||
|
Debug.Assert(effectTimeConfig.Value1 != null, "effectTimeConfig.Value1 != null");
|
||||||
ttl = Enum.Parse<EffectTimeUnit>(effectTimeConfig.Value1) switch
|
ttl = Enum.Parse<EffectTimeUnit>(effectTimeConfig.Value1) switch
|
||||||
{
|
{
|
||||||
EffectTimeUnit.Second => expiredValue,
|
EffectTimeUnit.Second => expiredValue,
|
||||||
@ -357,20 +351,23 @@ public class InterfaceForwardCommon : ITransientDependency
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
context.Feature.TargetInterfaceQps = context.TargetInterface.Qps <= 0
|
||||||
|
? _options.ServiceProviderDefaultQps // 默认qps上限
|
||||||
|
: context.TargetInterface.Qps;
|
||||||
|
context.Feature.SpinPeriod = _options.SpinPeriod; // 异步自旋周期
|
||||||
|
|
||||||
var flow = _flows[context.TargetInterface.Code];
|
var flow = _flows[context.TargetInterface.Code];
|
||||||
// _segContext.Context.Span.AddLog(LogEvent.Message("开始入参重构"));
|
|
||||||
context.OriginalInterfaceMappedInput = await flow.InParamRestructure(context);
|
context.OriginalInterfaceMappedInput = await flow.InParamRestructure(context);
|
||||||
|
|
||||||
// _segContext.Context.Span.AddLog(LogEvent.Message("* 开始请求"));
|
|
||||||
context.Feature.ServiceProviderDefaultQps = _interfaceRelayOptions.ServiceProviderDefaultQps;
|
|
||||||
context.Feature.SpinPeriod = _interfaceRelayOptions.SpinPeriod;
|
|
||||||
var response = await flow.ForwardAsync(context);
|
var response = await flow.ForwardAsync(context);
|
||||||
// _segContext.Context.Span.AddLog(LogEvent.Message("* 请求结束"));
|
|
||||||
|
|
||||||
if (isMulti) return response; // 一对多不在这映射
|
if (isMulti) return response; // 一对多不在这映射
|
||||||
var res = await flow.OutParamRestructure(context, response);
|
var res = await flow.OutParamRestructure(context, response);
|
||||||
|
if (context.Feature.IsWait)
|
||||||
// _segContext.Context.Span.AddLog(LogEvent.Message("完成反参重构"));
|
{
|
||||||
|
context.ServiceProviderRequestLog.Response2 = res.ToJson();
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -384,10 +381,15 @@ public class InterfaceForwardCommon : ITransientDependency
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
if (context.ServiceProviderRequestLog.RequestTime == default)
|
||||||
|
{
|
||||||
|
context.ServiceProviderRequestLog.RequestTime = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
// 关联当前服务商日志
|
// 关联当前服务商日志
|
||||||
_logRepository.ServiceProviderRequestLog = context.ServiceProviderRequestLog;
|
logRepository.ServiceProviderRequestLog = context.ServiceProviderRequestLog;
|
||||||
// 写服务商日志
|
// 写服务商日志
|
||||||
await _logRepository.AddLogAsync(context.ServiceProviderRequestLog);
|
await logRepository.AddLogAsync(context.ServiceProviderRequestLog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,4 +418,27 @@ public class InterfaceForwardCommon : ITransientDependency
|
|||||||
|
|
||||||
return objectArray;
|
return objectArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>获取字符串的hash值</summary>
|
||||||
|
/// <param name="str"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetHash(string str)
|
||||||
|
{
|
||||||
|
// 将字符串转换为字节
|
||||||
|
byte[] data = Encoding.UTF8.GetBytes(str);
|
||||||
|
|
||||||
|
// 创建SHA256实例
|
||||||
|
using SHA256 sha256 = SHA256.Create();
|
||||||
|
// 计算哈希值
|
||||||
|
byte[] hash = sha256.ComputeHash(data);
|
||||||
|
|
||||||
|
// 将哈希值转换为16进制字符串
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
foreach (byte b in hash)
|
||||||
|
{
|
||||||
|
sb.Append(b.ToString("x2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -5,8 +5,11 @@ using Newtonsoft.Json.Linq;
|
|||||||
using InterfaceForward.Application.Contracts;
|
using InterfaceForward.Application.Contracts;
|
||||||
using InterfaceForward.Application.Contracts.ForwardCore;
|
using InterfaceForward.Application.Contracts.ForwardCore;
|
||||||
using InterfaceForward.Application.Filters;
|
using InterfaceForward.Application.Filters;
|
||||||
|
using InterfaceForward.Application.Rabbit;
|
||||||
|
using InterfaceForward.Domain.Shared;
|
||||||
using InterfaceForward.Repositories.Log.Services;
|
using InterfaceForward.Repositories.Log.Services;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
namespace InterfaceForward.Application.Services;
|
namespace InterfaceForward.Application.Services;
|
||||||
|
|
||||||
@ -21,29 +24,100 @@ public class InterfaceForwardService : BaseService, IInterfaceForwardService
|
|||||||
{
|
{
|
||||||
private readonly InterfaceForwardCommon _forwardCommon;
|
private readonly InterfaceForwardCommon _forwardCommon;
|
||||||
private readonly ILogRepository _logRepository;
|
private readonly ILogRepository _logRepository;
|
||||||
private readonly InterfaceRelayOptions _options;
|
private readonly RabbitClient _rabbitClient;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public InterfaceForwardService(
|
public InterfaceForwardService(ILogRepository logRepository,
|
||||||
ILogRepository logRepository
|
InterfaceForwardCommon forwardCommon, RabbitClient rabbitClient)
|
||||||
, InterfaceForwardCommon forwardCommon
|
|
||||||
, IOptionsSnapshot<InterfaceRelayOptions> options
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
_logRepository = logRepository;
|
_logRepository = logRepository;
|
||||||
_forwardCommon = forwardCommon;
|
_forwardCommon = forwardCommon;
|
||||||
_options = options.Value;
|
_rabbitClient = rabbitClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[HttpPost("BatchForwardThenPushToQueue")]
|
||||||
|
public async Task<InterfaceRelayUnifyResultDto> BatchForwardThenPushToQueueAsync(
|
||||||
|
[FromQuery] [Required] string upStreamCode,
|
||||||
|
[FromQuery] [Required] string serviceProviderCode,
|
||||||
|
[FromBody] [Required] List<object> data,
|
||||||
|
[FromHeader] [Required] string resSubscribeName,
|
||||||
|
[FromHeader] string attach = null,
|
||||||
|
[FromHeader] bool isReturnMessage = false)
|
||||||
|
{
|
||||||
|
var contextList = await _forwardCommon.VerifyBusinessAndBuildForwardContexts(upStreamCode,
|
||||||
|
serviceProviderCode);
|
||||||
|
|
||||||
|
var @event = new BatchForwardEvent
|
||||||
|
{
|
||||||
|
RequestId = _logRepository.AppRequestLog.RequestId,
|
||||||
|
AppId = _logRepository.AppId,
|
||||||
|
UpStreamCode = upStreamCode,
|
||||||
|
ServiceProviderCode = serviceProviderCode,
|
||||||
|
Attach = attach,
|
||||||
|
IsReturnMessage = isReturnMessage,
|
||||||
|
ResSubscribeName = resSubscribeName
|
||||||
|
};
|
||||||
|
|
||||||
|
var queueName = $"{BatchForwardEventHandler.BatchForwardEventSubscribe}.{SJZYABPCore.HttpContext.Request
|
||||||
|
.Headers["appKey"]}.{upStreamCode}.{serviceProviderCode}";
|
||||||
|
|
||||||
|
// tips:订阅需要持久化
|
||||||
|
_rabbitClient.Subscribe(new ConsumeOptions
|
||||||
|
{
|
||||||
|
Queue = queueName,
|
||||||
|
FetchCount = AppSubscribeService.DefaultFetchCount, // 并发控制
|
||||||
|
FailedRequeue = AppSubscribeService.DefaultFailedRequeue,
|
||||||
|
Declaration = o =>
|
||||||
|
{
|
||||||
|
o.ExchangeDeclare(GlobalConst.InterfaceRelayExchange, ExchangeType.Direct, true);
|
||||||
|
o.QueueDeclare(queueName, true);
|
||||||
|
o.QueueBind(queueName, GlobalConst.InterfaceRelayExchange, queueName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 手工切割任务数量
|
||||||
|
var chunk = data.Count;
|
||||||
|
var minLimit = contextList.Min(x => x.TargetInterface.Limit);
|
||||||
|
var limit = minLimit > 0 ? minLimit : 1;
|
||||||
|
if (limit > 1)
|
||||||
|
{
|
||||||
|
chunk /= limit;
|
||||||
|
if (data.Count % limit != 0) chunk++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int x = 0; x < chunk; x++)
|
||||||
|
{
|
||||||
|
var arr = new JArray();
|
||||||
|
for (int i = 0; i < limit && x * limit + i < data.Count; i++)
|
||||||
|
{
|
||||||
|
arr.Add(data[x * limit + i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var subEvent = @event.DeepCopy();
|
||||||
|
|
||||||
|
subEvent.Data = arr.ToJson(); // always arr
|
||||||
|
var eventBytes = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(subEvent);
|
||||||
|
_rabbitClient.Publish(GlobalConst.InterfaceRelayExchange, queueName, eventBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InterfaceRelayUnifyResultDto(true, _logRepository.AppRequestLog.RequestId)
|
||||||
|
{
|
||||||
|
RequestId = _logRepository.AppRequestLog.RequestId,
|
||||||
|
OriginalMessage = null,
|
||||||
|
Attach = attach
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[CustomerResult]
|
||||||
[HttpPost("ForwardWithSubscribe")]
|
[HttpPost("ForwardWithSubscribe")]
|
||||||
public async Task<InterfaceRelayUnifyResultDto> ForwardWithSubscribeAsync(
|
public async Task<InterfaceRelayUnifyResultDto> ForwardWithSubscribeAsync(
|
||||||
[FromQuery] [Required] string upStreamCode,
|
[FromQuery] [Required] string upStreamCode,
|
||||||
[FromQuery] [Required] string serviceProviderCode,
|
[FromQuery] [Required] string serviceProviderCode,
|
||||||
[FromQuery] int mainBodyId,
|
|
||||||
[FromBody] [Required] object data,
|
[FromBody] [Required] object data,
|
||||||
[FromHeader] string? attach = null,
|
[FromHeader] string attach = null,
|
||||||
[FromHeader] bool isReturnMessage = false,
|
[FromHeader] bool isReturnMessage = false,
|
||||||
[FromHeader] string? failedSubscribeName = null)
|
[FromHeader] string failedSubscribeName = null)
|
||||||
{
|
{
|
||||||
if (JToken.FromObject(data) is not JObject jobject)
|
if (JToken.FromObject(data) is not JObject jobject)
|
||||||
{
|
{
|
||||||
@ -64,7 +138,7 @@ public class InterfaceForwardService : BaseService, IInterfaceForwardService
|
|||||||
? await _forwardCommon.InternalForwardAsync(context)
|
? await _forwardCommon.InternalForwardAsync(context)
|
||||||
: await _forwardCommon.InternalForward2Async(contextList);
|
: await _forwardCommon.InternalForward2Async(contextList);
|
||||||
|
|
||||||
return new InterfaceRelayUnifyResultDto
|
return new InterfaceRelayUnifyResultDto(true, res)
|
||||||
{
|
{
|
||||||
RequestId = context.ServiceProviderRequestLog.RequestId,
|
RequestId = context.ServiceProviderRequestLog.RequestId,
|
||||||
OriginalMessage = isReturnMessage ? context.ServiceProviderRequestLog.Response : null,
|
OriginalMessage = isReturnMessage ? context.ServiceProviderRequestLog.Response : null,
|
||||||
@ -76,12 +150,11 @@ public class InterfaceForwardService : BaseService, IInterfaceForwardService
|
|||||||
public async Task<InterfaceRelayUnifyResultDto> ReForwardAsync(
|
public async Task<InterfaceRelayUnifyResultDto> ReForwardAsync(
|
||||||
[FromQuery] [Required] string upStreamCode,
|
[FromQuery] [Required] string upStreamCode,
|
||||||
[FromQuery] [Required] string serviceProviderCode,
|
[FromQuery] [Required] string serviceProviderCode,
|
||||||
[FromQuery] int mainBodyId,
|
|
||||||
[FromHeader] [Required] Guid requestId,
|
[FromHeader] [Required] Guid requestId,
|
||||||
[FromBody] [Required] object data,
|
[FromBody] [Required] object data,
|
||||||
[FromHeader] string? attach = null,
|
[FromHeader] string attach = null,
|
||||||
[FromHeader] bool isReturnMessage = false,
|
[FromHeader] bool isReturnMessage = false,
|
||||||
[FromHeader] string? failedSubscribeName = null)
|
[FromHeader] string failedSubscribeName = null)
|
||||||
{
|
{
|
||||||
if (JToken.FromObject(data) is not JObject jobject)
|
if (JToken.FromObject(data) is not JObject jobject)
|
||||||
{
|
{
|
||||||
@ -100,7 +173,7 @@ public class InterfaceForwardService : BaseService, IInterfaceForwardService
|
|||||||
? jobject
|
? jobject
|
||||||
: new JObject
|
: new JObject
|
||||||
{
|
{
|
||||||
[upStreamCode] = jobject
|
[upStreamCode!] = jobject
|
||||||
};
|
};
|
||||||
|
|
||||||
// 从第一个失败的开始重试
|
// 从第一个失败的开始重试
|
||||||
@ -109,7 +182,7 @@ public class InterfaceForwardService : BaseService, IInterfaceForwardService
|
|||||||
? await _forwardCommon.InternalForwardAsync(context)
|
? await _forwardCommon.InternalForwardAsync(context)
|
||||||
: await _forwardCommon.InternalForward3Async(contextList, requestLogs);
|
: await _forwardCommon.InternalForward3Async(contextList, requestLogs);
|
||||||
|
|
||||||
return new InterfaceRelayUnifyResultDto
|
return new InterfaceRelayUnifyResultDto(true, res)
|
||||||
{
|
{
|
||||||
RequestId = context.ServiceProviderRequestLog.RequestId,
|
RequestId = context.ServiceProviderRequestLog.RequestId,
|
||||||
OriginalMessage = isReturnMessage ? _logRepository.ServiceProviderRequestLog.Response : null,
|
OriginalMessage = isReturnMessage ? _logRepository.ServiceProviderRequestLog.Response : null,
|
||||||
|
|||||||
@ -2,17 +2,16 @@
|
|||||||
using DiffPlex;
|
using DiffPlex;
|
||||||
using DiffPlex.DiffBuilder;
|
using DiffPlex.DiffBuilder;
|
||||||
using DiffPlex.DiffBuilder.Model;
|
using DiffPlex.DiffBuilder.Model;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Converters;
|
|
||||||
using InterfaceForward.Application.Contracts.Dtos.Interface;
|
using InterfaceForward.Application.Contracts.Dtos.Interface;
|
||||||
using InterfaceForward.Application.Helpers;
|
using InterfaceForward.Application.Helpers;
|
||||||
using InterfaceForward.Domain.Shared.Dtos;
|
using InterfaceForward.Domain.Shared.Dtos;
|
||||||
using InterfaceForward.Domain.Shared.Helpers;
|
|
||||||
using InterfaceForward.Repositories;
|
using InterfaceForward.Repositories;
|
||||||
using InterfaceForward.Repositories.Interface.Entitys;
|
using InterfaceForward.Repositories.Interface.Entitys;
|
||||||
using InterfaceForward.Repositories.Interface.Services;
|
using InterfaceForward.Repositories.Interface.Services;
|
||||||
using InterfaceForward.Repositories.Interface.ValueObjects;
|
using InterfaceForward.Repositories.Interface.ValueObjects;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
|
||||||
namespace InterfaceForward.Application.Services.InterfaceMap;
|
namespace InterfaceForward.Application.Services.InterfaceMap;
|
||||||
|
|
||||||
@ -20,27 +19,19 @@ namespace InterfaceForward.Application.Services.InterfaceMap;
|
|||||||
/// 接口映射发布修改
|
/// 接口映射发布修改
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiExplorerSettings(GroupName = "接口映射服务")]
|
[ApiExplorerSettings(GroupName = "接口映射服务")]
|
||||||
public class InterfaceMapPublishService : BaseService
|
public class InterfaceMapPublishService(
|
||||||
|
IInterfaceMapPublishRepository interfaceMapPublishRepository,
|
||||||
|
InterfaceForwardQuery interfaceForwardQuery,
|
||||||
|
IInterfaceMapRepository interfaceMapRepository)
|
||||||
|
: ApplicationService
|
||||||
{
|
{
|
||||||
private readonly static JsonSerializerSettings Settings = new JsonSerializerSettings
|
private static readonly JsonSerializerSettings Settings = new()
|
||||||
{
|
{
|
||||||
Formatting = Formatting.Indented, // 设置格式化输出
|
Formatting = Formatting.Indented, // 设置格式化输出
|
||||||
Converters = { new StringEnumConverter() },
|
Converters = { new StringEnumConverter() },
|
||||||
ContractResolver = new CustomContractResolver() // 设置自定义的ContractResolver
|
ContractResolver = new CustomContractResolver() // 设置自定义的ContractResolver
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly InterfaceForwardQuery _interfaceForwardQuery;
|
|
||||||
private readonly IInterfaceMapPublishRepository _interfaceMapPublishRepository;
|
|
||||||
private readonly IInterfaceMapRepository _interfaceMapRepository;
|
|
||||||
|
|
||||||
public InterfaceMapPublishService(IInterfaceMapPublishRepository interfaceMapPublishRepository,
|
|
||||||
InterfaceForwardQuery interfaceForwardQuery, IInterfaceMapRepository interfaceMapRepository)
|
|
||||||
{
|
|
||||||
_interfaceMapPublishRepository = interfaceMapPublishRepository;
|
|
||||||
_interfaceForwardQuery = interfaceForwardQuery;
|
|
||||||
_interfaceMapRepository = interfaceMapRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取接口映射 发布记录列表
|
/// 获取接口映射 发布记录列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -53,7 +44,7 @@ public class InterfaceMapPublishService : BaseService
|
|||||||
public async Task<List<InterfaceMapPublishedOutPutVO>> GetPublishedListAsync([Required] int interfaceId,
|
public async Task<List<InterfaceMapPublishedOutPutVO>> GetPublishedListAsync([Required] int interfaceId,
|
||||||
[Required] int serviceProviderId, int pageIndex = 1, int pageSize = 50)
|
[Required] int serviceProviderId, int pageIndex = 1, int pageSize = 50)
|
||||||
{
|
{
|
||||||
return await _interfaceMapPublishRepository.GetPublishedListAsync(interfaceId, serviceProviderId, pageIndex,
|
return await interfaceMapPublishRepository.GetPublishedListAsync(interfaceId, serviceProviderId, pageIndex,
|
||||||
pageSize);
|
pageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,18 +59,18 @@ public class InterfaceMapPublishService : BaseService
|
|||||||
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.GetInterfaceByCodeAsync(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);
|
||||||
if (serviceProvider == null) throw new BusinessException(message: "服务商不存在");
|
if (serviceProvider == null) throw new BusinessException(message: "服务商不存在");
|
||||||
|
|
||||||
// 没有指定比对的版本,就是比对数据库最新数据
|
// 没有指定比对的版本,就是比对数据库最新数据
|
||||||
string dbContent = version.IsNullOrWhiteSpace()
|
var dbContent = version.IsNullOrWhiteSpace()
|
||||||
? JsonConvert.SerializeObject(
|
? JsonConvert.SerializeObject(
|
||||||
await _interfaceForwardQuery.BuildForwardCoreContext(systemInterface, serviceProvider),
|
await interfaceForwardQuery.BuildForwardCoreContext(systemInterface, serviceProvider),
|
||||||
Settings)
|
Settings)
|
||||||
: await _interfaceMapPublishRepository.GetFirstAsync(a =>
|
: await interfaceMapPublishRepository.GetFirstAsync(a =>
|
||||||
a.InterfaceId == systemInterface.Id && a.ServiceProviderId == serviceProviderId &&
|
a.InterfaceId == systemInterface.Id && a.ServiceProviderId == serviceProviderId &&
|
||||||
a.PublishedVersion == version, a => a.PublishedContent);
|
a.PublishedVersion == version, a => a.PublishedContent);
|
||||||
|
|
||||||
@ -88,7 +79,7 @@ public class InterfaceMapPublishService : BaseService
|
|||||||
string redisContent = JsonConvert.SerializeObject(cache, Settings);
|
string redisContent = JsonConvert.SerializeObject(cache, Settings);
|
||||||
|
|
||||||
|
|
||||||
var differenceContent = CompareTexts(redisContent, dbContent ?? "");
|
var differenceContent = CompareTexts(redisContent, dbContent);
|
||||||
|
|
||||||
return new InterfaceMapPublishedDifferenceVO
|
return new InterfaceMapPublishedDifferenceVO
|
||||||
{
|
{
|
||||||
@ -107,21 +98,21 @@ public class InterfaceMapPublishService : BaseService
|
|||||||
[HttpPost("InterfaceMap/Publish")]
|
[HttpPost("InterfaceMap/Publish")]
|
||||||
public async Task<bool> PublishAsync(InterfaceMapPublishInput input)
|
public async Task<bool> PublishAsync(InterfaceMapPublishInput input)
|
||||||
{
|
{
|
||||||
var systemInterface = await _interfaceForwardQuery.GetInterfaceByCodeAsync(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: "服务商不存在");
|
||||||
|
|
||||||
// 确认比对期间数据是否被篡改
|
// 确认比对期间数据是否被篡改
|
||||||
var differ = new Differ();
|
var differ = new Differ();
|
||||||
var inlineBuilder = new InlineDiffBuilder(differ);
|
var inlineBuilder = new InlineDiffBuilder(differ);
|
||||||
var context = await _interfaceForwardQuery.BuildForwardCoreContext(systemInterface, serviceProvider);
|
var context = await interfaceForwardQuery.BuildForwardCoreContext(systemInterface, serviceProvider);
|
||||||
var content = JsonConvert.SerializeObject(context, Settings);
|
var content = JsonConvert.SerializeObject(context, Settings);
|
||||||
var diffResult = inlineBuilder.BuildDiffModel(content, input.DBContent);
|
var diffResult = inlineBuilder.BuildDiffModel(content, input.DBContent);
|
||||||
if (diffResult.HasDifferences) throw new BusinessException(message: "数据库已经发生变更,请重新获取最新数据进行比对");
|
if (diffResult.HasDifferences) throw new BusinessException(message: "数据库已经发生变更,请重新获取最新数据进行比对");
|
||||||
|
|
||||||
var mapsEntities = await _interfaceMapRepository.GetListAsync(x =>
|
var mapsEntities = await interfaceMapRepository.GetListAsync(x =>
|
||||||
x.UpStreamId == systemInterface.Id && x.ServiceProviderId == serviceProvider.Id);
|
x.UpStreamId == systemInterface.Id && x.ServiceProviderId == serviceProvider.Id);
|
||||||
var publishEntity = new InterfaceMapPublishedEntity
|
var publishEntity = new InterfaceMapPublishedEntity
|
||||||
{
|
{
|
||||||
@ -137,15 +128,15 @@ public class InterfaceMapPublishService : BaseService
|
|||||||
|
|
||||||
|
|
||||||
using var ts = TransacationHelper.GetReadCommitted();
|
using var ts = TransacationHelper.GetReadCommitted();
|
||||||
await _interfaceMapPublishRepository.InsertAsync(publishEntity);
|
await interfaceMapPublishRepository.InsertAsync(publishEntity);
|
||||||
|
|
||||||
// 更新版本号
|
// 更新版本号
|
||||||
mapsEntities.ForEach(x => x.PublishedVersion = publishEntity.PublishedVersion);
|
mapsEntities.ForEach(x => x.PublishedVersion = publishEntity.PublishedVersion);
|
||||||
await _interfaceMapRepository.UpdateRangeAsync(mapsEntities, x => x.PublishedVersion);
|
await interfaceMapRepository.UpdateRangeAsync(mapsEntities, x => x.PublishedVersion);
|
||||||
|
|
||||||
// update cache
|
// update cache
|
||||||
var redisKey = InterfaceForwardCommon.GetContextCacheKey(systemInterface.Code, serviceProvider.Code);
|
var redisKey = InterfaceForwardCommon.GetContextCacheKey(systemInterface.Code, serviceProvider.Code);
|
||||||
await RedisHelper.Client.SetAsync(redisKey, content, InterfaceForwardCommon.ContextCacheTtl);
|
await RedisHelper.Client.SetAsync(redisKey, content, InterfaceForwardCommon.DefaultCacheTtl);
|
||||||
ts.Complete();
|
ts.Complete();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -160,19 +151,19 @@ public class InterfaceMapPublishService : BaseService
|
|||||||
[HttpPost("InterfaceMap/PublishRollback")]
|
[HttpPost("InterfaceMap/PublishRollback")]
|
||||||
public async Task<bool> PublishRollbackAsync(InterfaceMapRollbackInput input)
|
public async Task<bool> PublishRollbackAsync(InterfaceMapRollbackInput input)
|
||||||
{
|
{
|
||||||
var systemInterface = await _interfaceForwardQuery.GetInterfaceByCodeAsync(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: "服务商不存在");
|
||||||
|
|
||||||
var mapsEntities = await _interfaceMapRepository.GetListAsync(x =>
|
var mapsEntities = await interfaceMapRepository.GetListAsync(x =>
|
||||||
x.UpStreamId == systemInterface.Id && x.ServiceProviderId == input.ServiceProviderId);
|
x.UpStreamId == systemInterface.Id && x.ServiceProviderId == input.ServiceProviderId);
|
||||||
if (mapsEntities == null || mapsEntities.Count == 0)
|
if (mapsEntities == null || mapsEntities.Count == 0)
|
||||||
{
|
{
|
||||||
throw new BusinessException(message: "映射不存在");
|
throw new BusinessException(message: "映射不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetVersionContent = await _interfaceMapPublishRepository.GetFirstAsync(a =>
|
var targetVersionContent = await interfaceMapPublishRepository.GetFirstAsync(a =>
|
||||||
a.InterfaceId == systemInterface.Id && a.ServiceProviderId == input.ServiceProviderId &&
|
a.InterfaceId == systemInterface.Id && a.ServiceProviderId == input.ServiceProviderId &&
|
||||||
a.PublishedVersion == input.Version, a => a.PublishedContent);
|
a.PublishedVersion == input.Version, a => a.PublishedContent);
|
||||||
// tips:空是合法的,虽然在业务上不存在
|
// tips:空是合法的,虽然在业务上不存在
|
||||||
@ -198,14 +189,14 @@ public class InterfaceMapPublishService : BaseService
|
|||||||
|
|
||||||
using var ts = TransacationHelper.GetReadCommitted();
|
using var ts = TransacationHelper.GetReadCommitted();
|
||||||
|
|
||||||
await _interfaceMapPublishRepository.InsertAsync(publishEntity);
|
await interfaceMapPublishRepository.InsertAsync(publishEntity);
|
||||||
|
|
||||||
// 更新版本号
|
// 更新版本号
|
||||||
mapsEntities.ForEach(x => x.PublishedVersion = publishEntity.PublishedVersion);
|
mapsEntities.ForEach(x => x.PublishedVersion = publishEntity.PublishedVersion);
|
||||||
await _interfaceMapRepository.UpdateRangeAsync(mapsEntities, x => x.PublishedVersion);
|
await interfaceMapRepository.UpdateRangeAsync(mapsEntities, x => x.PublishedVersion);
|
||||||
|
|
||||||
await RedisHelper.Client.SetAsync(redisKey, publishEntity.PublishedContent,
|
await RedisHelper.Client.SetAsync(redisKey, publishEntity.PublishedContent,
|
||||||
InterfaceForwardCommon.ContextCacheTtl);
|
InterfaceForwardCommon.DefaultCacheTtl);
|
||||||
|
|
||||||
ts.Complete();
|
ts.Complete();
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,245 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using InterfaceForward.Application.Contracts.Dtos.Interface;
|
||||||
|
using InterfaceForward.Application.Helpers;
|
||||||
|
using InterfaceForward.Domain.Shared.Enum;
|
||||||
|
using InterfaceForward.Repositories.Interface.Entitys;
|
||||||
|
using InterfaceForward.Repositories.Interface.ValueObjects;
|
||||||
|
using InterfaceForward.Repositories.ServiceProvider.Entitys;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace InterfaceForward.Application.Services.InterfaceMap;
|
||||||
|
|
||||||
|
public partial class InterfaceMapService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取映射详情(编辑页)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="interfaceId">系统接口id</param>
|
||||||
|
/// <param name="serviceProviderId">服务商id</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("InterfaceMap/EditMapDetails")]
|
||||||
|
public async Task<EditMapDetailsOutput> EditMapDetailsAsync([Required] int interfaceId,
|
||||||
|
[Required] int serviceProviderId)
|
||||||
|
{
|
||||||
|
var maps = await _interfaceMapRepository.GetListAsync(x =>
|
||||||
|
x.UpStreamId == interfaceId && x.ServiceProviderId == serviceProviderId,
|
||||||
|
x => new { x.Id, x.DownStreamId, x.PrefixScript, x.PostfixScript, x.FreeMap });
|
||||||
|
if (maps == null || maps.Count == 0)
|
||||||
|
{
|
||||||
|
throw new BusinessException(message: "映射不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceProviderInterfaceIds = maps.Select(x => x.DownStreamId).ToList();
|
||||||
|
var serviceProviderInterfaces =
|
||||||
|
await _interfaceRepository.GetListAsync(x => serviceProviderInterfaceIds.Contains(x.Id),
|
||||||
|
x => new { x.Id, x.Name });
|
||||||
|
var res = new EditMapDetailsOutput();
|
||||||
|
|
||||||
|
var mapIds = maps.Select(x => x.Id).ToList();
|
||||||
|
// 服务商入参 多个
|
||||||
|
var inParaMaps = await _interfaceMapRepository.GetParameterMapListAsync(mapIds, true);
|
||||||
|
foreach (var map in maps)
|
||||||
|
{
|
||||||
|
var serviceProviderInterface = serviceProviderInterfaces.First(x => x.Id == map.DownStreamId);
|
||||||
|
res.InParaMapList.Add(new MapSummary
|
||||||
|
{
|
||||||
|
InterfaceMapId = map.Id,
|
||||||
|
InterfaceId = serviceProviderInterface.Id,
|
||||||
|
InterfaceName = serviceProviderInterface.Name,
|
||||||
|
PrefixScript = map.PrefixScript,
|
||||||
|
PostfixScript = map.PostfixScript,
|
||||||
|
FreeMap = map.FreeMap,
|
||||||
|
MapDetails = inParaMaps.Where(x => x.InterfaceId == serviceProviderInterface.Id).ToList()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 系统出参 1个
|
||||||
|
res.MapDetails = await _interfaceMapRepository.GetParameterMapListAsync(mapIds, false);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取映射参数级联列表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="interfaceId">系统接口id</param>
|
||||||
|
/// <param name="serviceProviderId">服务商id</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("InterfaceMap/GetMapParameterCascadedList")]
|
||||||
|
public async Task<List<MapParameterCascadedOutput>>
|
||||||
|
GetServiceProviderInterfaceCascadedListAsync([Required] int interfaceId,
|
||||||
|
[Required] int serviceProviderId)
|
||||||
|
{
|
||||||
|
var serviceProviderInterfaceIds = await _interfaceMapRepository.GetListAsync(x =>
|
||||||
|
x.UpStreamId == interfaceId && x.ServiceProviderId == serviceProviderId,
|
||||||
|
x => x.DownStreamId);
|
||||||
|
if (serviceProviderInterfaceIds == null || serviceProviderInterfaceIds.Count == 0)
|
||||||
|
{
|
||||||
|
throw new BusinessException(message: "映射不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
var interfaceIds = new List<int>(serviceProviderInterfaceIds) { interfaceId };
|
||||||
|
var interfaces = await _interfaceRepository.GetListAsync(x => interfaceIds.Contains(x.Id),
|
||||||
|
x => new
|
||||||
|
{
|
||||||
|
x.Id,
|
||||||
|
x.Code,
|
||||||
|
x.Name
|
||||||
|
});
|
||||||
|
if (interfaces == null || interfaces.Count != interfaceIds.Count)
|
||||||
|
{
|
||||||
|
throw new BusinessException(message: "映射接口已丢失,请联系管理员");
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = new List<MapParameterCascadedOutput>();
|
||||||
|
|
||||||
|
// 一个系统接口入参
|
||||||
|
var inParams = await _parameterQueries.GetListAsync(
|
||||||
|
x => x.InterfaceId == interfaceId && x.IsInPara && x.Type != ParameterType.Array &&
|
||||||
|
x.Type != ParameterType.Object);
|
||||||
|
var systemInterface = interfaces.First(x => x.Id == interfaceId);
|
||||||
|
res.Add(new MapParameterCascadedOutput
|
||||||
|
{
|
||||||
|
Name = "系统接口",
|
||||||
|
InterfaceId = systemInterface.Id,
|
||||||
|
InterfaceCode = systemInterface.Code,
|
||||||
|
Parameters = ObjectMapper.Map<List<ParameterEntity>, List<MapParameterCascadedItem>>(inParams
|
||||||
|
.OrderBy(x => x.Sort)
|
||||||
|
.ToList())
|
||||||
|
});
|
||||||
|
|
||||||
|
// 所有服务商接口的出参
|
||||||
|
var outParams =
|
||||||
|
await _parameterQueries.GetListAsync(
|
||||||
|
x => serviceProviderInterfaceIds.Contains(x.InterfaceId) && !x.IsInPara);
|
||||||
|
foreach (var id in serviceProviderInterfaceIds)
|
||||||
|
{
|
||||||
|
var @interface = interfaces.First(x => x.Id == id);
|
||||||
|
var @params = outParams.Where(x => x.InterfaceId == id)
|
||||||
|
.OrderBy(x => x.Sort)
|
||||||
|
.ToList();
|
||||||
|
res.Add(new MapParameterCascadedOutput
|
||||||
|
{
|
||||||
|
Name = @interface.Name,
|
||||||
|
InterfaceId = @interface.Id,
|
||||||
|
InterfaceCode = @interface.Code,
|
||||||
|
Parameters = ObjectMapper.Map<List<ParameterEntity>, List<MapParameterCascadedItem>>(@params)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存参数映射
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost("InterfaceMap/SaveParameterMaps")]
|
||||||
|
public async Task<bool> SaveParameterMapsAsync(SaveParameterMapsInput input)
|
||||||
|
{
|
||||||
|
var mapIds = input.InParaMapList.Select(x => x.InterfaceMapId).ToList();
|
||||||
|
if (mapIds.Count == 0 || mapIds.Count != await _interfaceMapRepository.CountAsync(x => mapIds.Contains(x.Id)))
|
||||||
|
{
|
||||||
|
throw new BusinessException(message: "非法保存");
|
||||||
|
}
|
||||||
|
|
||||||
|
var entitiesForUpdate = new List<InterfaceMapDetailEntity>();
|
||||||
|
var entitiesForInsert = new List<InterfaceMapDetailEntity>();
|
||||||
|
var fixedValues = new List<FixedParameterEntity>();
|
||||||
|
VerifyAndFillEntities(input, entitiesForInsert, entitiesForUpdate, fixedValues);
|
||||||
|
|
||||||
|
using var ts = TransacationHelper.GetReadCommitted();
|
||||||
|
|
||||||
|
// 更新脚本
|
||||||
|
await _interfaceMapRepository.UpdateRangeAsync(input.InParaMapList.Select(x => new InterfaceMapEntity
|
||||||
|
{
|
||||||
|
Id = x.InterfaceMapId,
|
||||||
|
PrefixScript = x.PrefixScript,
|
||||||
|
PostfixScript = x.PostfixScript,
|
||||||
|
FreeMap = x.FreeMap
|
||||||
|
}).ToList(), x => new
|
||||||
|
{
|
||||||
|
x.PrefixScript, x.PostfixScript, x.FreeMap, x.UpdateTime, x.UpdateUserId
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新参数映射
|
||||||
|
await _interfaceMapDetailRepository.UpdateRangeAsync(entitiesForUpdate, x => new
|
||||||
|
{
|
||||||
|
x.MappedParaId, x.UpdateTime, x.UpdateUserId
|
||||||
|
});
|
||||||
|
await _interfaceMapDetailRepository.InsertRangeAsync(entitiesForInsert);
|
||||||
|
|
||||||
|
// 维护参数固定值
|
||||||
|
var delList = fixedValues.Where(x => x.Id != 0 && x.FieldValue.IsNullOrEmpty()).ToList();
|
||||||
|
await _fixedParameterRepository.SoftDeleteAsync(x => delList.Select(t => t.Id).Contains(x.Id));
|
||||||
|
fixedValues.RemoveAll(delList);
|
||||||
|
|
||||||
|
var updList = fixedValues.Where(x => x.Id != 0).ToList();
|
||||||
|
await _fixedParameterRepository.UpdateRangeAsync(updList,
|
||||||
|
x => new { x.FieldName, x.FieldValue, x.UpdateTime, x.UpdateUserId });
|
||||||
|
fixedValues.RemoveAll(updList);
|
||||||
|
|
||||||
|
var insertList = fixedValues.Where(x => !x.FieldValue.IsNullOrEmpty()).ToList();
|
||||||
|
await _fixedParameterRepository.InsertRangeAsync(insertList);
|
||||||
|
|
||||||
|
ts.Complete();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 校验必填项,并填充实体
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <param name="entitiesForInsert"></param>
|
||||||
|
/// <param name="entitiesForUpdate"></param>
|
||||||
|
/// <param name="fixedParameterEntities"></param>
|
||||||
|
/// <exception cref="BusinessException"></exception>
|
||||||
|
private static void VerifyAndFillEntities(SaveParameterMapsInput input,
|
||||||
|
List<InterfaceMapDetailEntity> entitiesForInsert, List<InterfaceMapDetailEntity> entitiesForUpdate,
|
||||||
|
List<FixedParameterEntity> fixedParameterEntities)
|
||||||
|
{
|
||||||
|
var details = input.InParaMapList.SelectMany(x => x.MapDetails).Concat(input.MapDetails).ToList();
|
||||||
|
|
||||||
|
foreach (var x in details)
|
||||||
|
{
|
||||||
|
if (x.IsRequired)
|
||||||
|
{
|
||||||
|
if (x.MappedParaId == null && x.FixedValue.IsNullOrEmpty())
|
||||||
|
throw new BusinessException(message: $"{x.Alias}必填不能空");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.InterfaceMapDetailId == null)
|
||||||
|
{
|
||||||
|
entitiesForInsert.Add(new InterfaceMapDetailEntity
|
||||||
|
{
|
||||||
|
InterfaceMapId = x.InterfaceMapId,
|
||||||
|
ParaId = x.ParaId,
|
||||||
|
MappedParaId = x.MappedParaId,
|
||||||
|
IsInPara = x.IsInPara
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entitiesForUpdate.Add(new InterfaceMapDetailEntity
|
||||||
|
{
|
||||||
|
Id = (int)x.InterfaceMapDetailId,
|
||||||
|
MappedParaId = x.MappedParaId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in input.InParaMapList)
|
||||||
|
{
|
||||||
|
fixedParameterEntities.AddRange(item.MapDetails.Select(x => new FixedParameterEntity
|
||||||
|
{
|
||||||
|
Id = x.FixedId,
|
||||||
|
InterfaceId = item.InterfaceId,
|
||||||
|
FieldName = x.Alias,
|
||||||
|
FieldValue = x.FixedValue,
|
||||||
|
FieldPositions = FieldPosition.Body.ToString(),
|
||||||
|
Description = x.Description
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,7 +2,6 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using InterfaceForward.Application.Contracts.Dtos.Interface;
|
using InterfaceForward.Application.Contracts.Dtos.Interface;
|
||||||
using InterfaceForward.Application.Helpers;
|
using InterfaceForward.Application.Helpers;
|
||||||
using InterfaceForward.Domain.Shared.Enum;
|
|
||||||
using InterfaceForward.Repositories;
|
using InterfaceForward.Repositories;
|
||||||
using InterfaceForward.Repositories.Interface.Entitys;
|
using InterfaceForward.Repositories.Interface.Entitys;
|
||||||
using InterfaceForward.Repositories.Interface.Services;
|
using InterfaceForward.Repositories.Interface.Services;
|
||||||
@ -16,13 +15,14 @@ namespace InterfaceForward.Application.Services.InterfaceMap;
|
|||||||
/// 接口映射服务
|
/// 接口映射服务
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiExplorerSettings(GroupName = "接口映射服务")]
|
[ApiExplorerSettings(GroupName = "接口映射服务")]
|
||||||
public class InterfaceMapService : BaseService
|
public partial class InterfaceMapService : ApplicationService
|
||||||
{
|
{
|
||||||
|
private readonly IBasicRepository<FixedParameterEntity> _fixedParameterRepository;
|
||||||
|
private readonly IBasicRepository<InterfaceMapDetailEntity> _interfaceMapDetailRepository;
|
||||||
private readonly IInterfaceMapRepository _interfaceMapRepository;
|
private readonly IInterfaceMapRepository _interfaceMapRepository;
|
||||||
private readonly IBasicRepository<InterfaceEntity> _interfaceRepository;
|
private readonly IBasicRepository<InterfaceEntity> _interfaceRepository;
|
||||||
private readonly IBasicRepository<ParameterEntity> _parameterQueries;
|
private readonly IBasicRepository<ParameterEntity> _parameterQueries;
|
||||||
private readonly IBasicRepository<InterfaceMapDetailEntity> _interfaceMapDetailRepository;
|
private readonly IBasicRepository<ServiceProviderEntity> _serviceProviderQueries;
|
||||||
private readonly IBasicRepository<FixedParameterEntity> _fixedParameterRepository;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public InterfaceMapService(
|
public InterfaceMapService(
|
||||||
@ -30,17 +30,17 @@ public class InterfaceMapService : BaseService
|
|||||||
, IBasicRepository<InterfaceEntity> interfaceRepository
|
, IBasicRepository<InterfaceEntity> interfaceRepository
|
||||||
, IBasicRepository<ParameterEntity> parameterQueries
|
, IBasicRepository<ParameterEntity> parameterQueries
|
||||||
, IBasicRepository<InterfaceMapDetailEntity> interfaceMapDetailRepository
|
, IBasicRepository<InterfaceMapDetailEntity> interfaceMapDetailRepository
|
||||||
, IBasicRepository<FixedParameterEntity> fixedParameterRepository)
|
, IBasicRepository<FixedParameterEntity> fixedParameterRepository
|
||||||
|
, IBasicRepository<ServiceProviderEntity> serviceProviderQueries)
|
||||||
{
|
{
|
||||||
_interfaceMapRepository = interfaceMapRepository;
|
_interfaceMapRepository = interfaceMapRepository;
|
||||||
_interfaceRepository = interfaceRepository;
|
_interfaceRepository = interfaceRepository;
|
||||||
_parameterQueries = parameterQueries;
|
_parameterQueries = parameterQueries;
|
||||||
_interfaceMapDetailRepository = interfaceMapDetailRepository;
|
_interfaceMapDetailRepository = interfaceMapDetailRepository;
|
||||||
_fixedParameterRepository = fixedParameterRepository;
|
_fixedParameterRepository = fixedParameterRepository;
|
||||||
|
_serviceProviderQueries = serviceProviderQueries;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region 接口映射
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建接口映射
|
/// 创建接口映射
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -84,7 +84,7 @@ public class InterfaceMapService : BaseService
|
|||||||
PrefixScript = null,
|
PrefixScript = null,
|
||||||
PostfixScript = null
|
PostfixScript = null
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
var maps2 = input.DownStreamIds.Select(id => maps.Single(x => x.DownStreamId == id)).ToList();
|
var maps2 = input.DownStreamIds.Select(id => maps.Single(x => x.DownStreamId == id)).ToList();
|
||||||
|
|
||||||
return await _interfaceMapRepository.InsertRangeAsync(maps2);
|
return await _interfaceMapRepository.InsertRangeAsync(maps2);
|
||||||
@ -93,12 +93,12 @@ public class InterfaceMapService : BaseService
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取接口映射列表
|
/// 获取接口映射列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="interfaceId">系统接口id</param>
|
/// <param name="inputVo"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet("InterfaceMap/GetMapListById")]
|
[HttpGet("InterfaceMap/GetMapList")]
|
||||||
public async Task<List<InterfaceMapOutputValueObject>> GetMapListByIdAsync([Required] int interfaceId)
|
public async Task<PagedListDto<InterfaceMapOutputValueObject>> GetMapListByIdAsync(GetMapListInputVO inputVo)
|
||||||
{
|
{
|
||||||
return await _interfaceMapRepository.GetMapListByIdAsync(interfaceId);
|
return await _interfaceMapRepository.GetMapListAsync(inputVo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -111,6 +111,19 @@ public class InterfaceMapService : BaseService
|
|||||||
[HttpPost("InterfaceMap/Delete")]
|
[HttpPost("InterfaceMap/Delete")]
|
||||||
public async Task<bool> DeleteAsync([Required] int interfaceId, [Required] int serviceProviderId)
|
public async Task<bool> DeleteAsync([Required] int interfaceId, [Required] int serviceProviderId)
|
||||||
{
|
{
|
||||||
|
var systemInterfaceCode = await _interfaceRepository.GetFirstAsync(x => x.Id == interfaceId, x => x.Code);
|
||||||
|
if (systemInterfaceCode.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
throw new BusinessException(message: "系统接口不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceProviderCode =
|
||||||
|
await _serviceProviderQueries.GetFirstAsync(x => x.Id == serviceProviderId, x => x.Code);
|
||||||
|
if (serviceProviderCode.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
throw new BusinessException(message: "服务商不存在!");
|
||||||
|
}
|
||||||
|
|
||||||
var mapIds = await _interfaceMapRepository.GetListAsync(x =>
|
var mapIds = await _interfaceMapRepository.GetListAsync(x =>
|
||||||
x.UpStreamId == interfaceId && x.ServiceProviderId == serviceProviderId, x => x.Id);
|
x.UpStreamId == interfaceId && x.ServiceProviderId == serviceProviderId, x => x.Id);
|
||||||
if (mapIds == null || mapIds.Count == 0) throw new BusinessException(message: "映射不存在");
|
if (mapIds == null || mapIds.Count == 0) throw new BusinessException(message: "映射不存在");
|
||||||
@ -124,240 +137,9 @@ public class InterfaceMapService : BaseService
|
|||||||
|
|
||||||
ts.Complete();
|
ts.Complete();
|
||||||
|
|
||||||
|
var contextCacheKey = InterfaceForwardCommon.GetContextCacheKey(systemInterfaceCode, serviceProviderCode);
|
||||||
|
await RedisHelper.Client.DelAsync(contextCacheKey);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 映射详情
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取映射详情(编辑页)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="interfaceId">系统接口id</param>
|
|
||||||
/// <param name="serviceProviderId">服务商id</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet("InterfaceMap/EditMapDetails")]
|
|
||||||
public async Task<EditMapDetailsOutput> EditMapDetailsAsync([Required] int interfaceId,
|
|
||||||
[Required] int serviceProviderId)
|
|
||||||
{
|
|
||||||
var maps = await _interfaceMapRepository.GetListAsync(x =>
|
|
||||||
x.UpStreamId == interfaceId && x.ServiceProviderId == serviceProviderId,
|
|
||||||
x => new { x.Id, x.DownStreamId, x.PrefixScript, x.PostfixScript });
|
|
||||||
if (maps == null || maps.Count == 0)
|
|
||||||
{
|
|
||||||
throw new BusinessException(message: "映射不存在");
|
|
||||||
}
|
|
||||||
|
|
||||||
var serviceProviderInterfaceIds = maps.Select(x => x.DownStreamId).ToList();
|
|
||||||
var serviceProviderInterfaces =
|
|
||||||
await _interfaceRepository.GetListAsync(x => serviceProviderInterfaceIds.Contains(x.Id),
|
|
||||||
x => new { x.Id, x.Name });
|
|
||||||
var res = new EditMapDetailsOutput();
|
|
||||||
|
|
||||||
var mapIds = maps.Select(x => x.Id).ToList();
|
|
||||||
// 服务商入参 多个
|
|
||||||
var inParaMaps = await _interfaceMapRepository.GetParameterMapListAsync(mapIds, true);
|
|
||||||
foreach (var map in maps)
|
|
||||||
{
|
|
||||||
var serviceProviderInterface = serviceProviderInterfaces.First(x => x.Id == map.DownStreamId);
|
|
||||||
res.InParaMapList.Add(new MapSummary
|
|
||||||
{
|
|
||||||
InterfaceMapId = map.Id,
|
|
||||||
InterfaceId = serviceProviderInterface.Id,
|
|
||||||
InterfaceName = serviceProviderInterface.Name,
|
|
||||||
PrefixScript = map.PrefixScript,
|
|
||||||
PostfixScript = map.PostfixScript,
|
|
||||||
MapDetails = inParaMaps.Where(x => x.InterfaceId == serviceProviderInterface.Id).ToList()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 系统出参 1个
|
|
||||||
res.MapDetails = await _interfaceMapRepository.GetParameterMapListAsync(mapIds, false);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取映射参数级联列表
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="interfaceId">系统接口id</param>
|
|
||||||
/// <param name="serviceProviderId">服务商id</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet("InterfaceMap/GetMapParameterCascadedList")]
|
|
||||||
public async Task<List<MapParameterCascadedOutput>>
|
|
||||||
GetServiceProviderInterfaceCascadedListAsync([Required] int interfaceId,
|
|
||||||
[Required] int serviceProviderId)
|
|
||||||
{
|
|
||||||
var serviceProviderInterfaceIds = await _interfaceMapRepository.GetListAsync(x =>
|
|
||||||
x.UpStreamId == interfaceId && x.ServiceProviderId == serviceProviderId,
|
|
||||||
x => x.DownStreamId);
|
|
||||||
if (serviceProviderInterfaceIds == null || serviceProviderInterfaceIds.Count == 0)
|
|
||||||
{
|
|
||||||
throw new BusinessException(message: "映射不存在");
|
|
||||||
}
|
|
||||||
|
|
||||||
var interfaceIds = new List<int>(serviceProviderInterfaceIds) { interfaceId };
|
|
||||||
var interfaces = await _interfaceRepository.GetListAsync(x => interfaceIds.Contains(x.Id),
|
|
||||||
x => new
|
|
||||||
{
|
|
||||||
x.Id,
|
|
||||||
x.Code,
|
|
||||||
x.Name
|
|
||||||
});
|
|
||||||
if (interfaces == null || interfaces.Count != interfaceIds.Count)
|
|
||||||
{
|
|
||||||
throw new BusinessException(message: "映射接口已丢失,请联系管理员");
|
|
||||||
}
|
|
||||||
|
|
||||||
var res = new List<MapParameterCascadedOutput>();
|
|
||||||
|
|
||||||
// 一个系统接口入参
|
|
||||||
var inParams = await _parameterQueries.GetListAsync(
|
|
||||||
x => x.InterfaceId == interfaceId && x.IsInPara && x.Type != ParameterType.Array &&
|
|
||||||
x.Type != ParameterType.Object);
|
|
||||||
var systemInterface = interfaces.First(x => x.Id == interfaceId);
|
|
||||||
res.Add(new MapParameterCascadedOutput
|
|
||||||
{
|
|
||||||
Name = "系统接口",
|
|
||||||
InterfaceId = systemInterface.Id,
|
|
||||||
InterfaceCode = systemInterface.Code,
|
|
||||||
Parameters = ObjectMapper.Map<List<ParameterEntity>, List<MapParameterCascadedItem>>(inParams
|
|
||||||
.OrderBy(x => x.Sort)
|
|
||||||
.ToList())
|
|
||||||
});
|
|
||||||
|
|
||||||
// 所有服务商接口的出参
|
|
||||||
var outParams =
|
|
||||||
await _parameterQueries.GetListAsync(
|
|
||||||
x => serviceProviderInterfaceIds.Contains(x.InterfaceId) && !x.IsInPara);
|
|
||||||
foreach (var id in serviceProviderInterfaceIds)
|
|
||||||
{
|
|
||||||
var @interface = interfaces.First(x => x.Id == id);
|
|
||||||
var @params = outParams.Where(x => x.InterfaceId == id)
|
|
||||||
.OrderBy(x => x.Sort)
|
|
||||||
.ToList();
|
|
||||||
res.Add(new MapParameterCascadedOutput
|
|
||||||
{
|
|
||||||
Name = @interface.Name,
|
|
||||||
InterfaceId = @interface.Id,
|
|
||||||
InterfaceCode = @interface.Code,
|
|
||||||
Parameters = ObjectMapper.Map<List<ParameterEntity>, List<MapParameterCascadedItem>>(@params)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 保存参数映射
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="input"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost("InterfaceMap/SaveParameterMaps")]
|
|
||||||
public async Task<bool> SaveParameterMapsAsync(SaveParameterMapsInput input)
|
|
||||||
{
|
|
||||||
var mapIds = input.InParaMapList.Select(x => x.InterfaceMapId).ToList();
|
|
||||||
if (mapIds.Count == 0 || mapIds.Count != await _interfaceMapRepository.CountAsync(x => mapIds.Contains(x.Id)))
|
|
||||||
{
|
|
||||||
throw new BusinessException(message: "非法保存");
|
|
||||||
}
|
|
||||||
|
|
||||||
var entitiesForUpdate = new List<InterfaceMapDetailEntity>();
|
|
||||||
var entitiesForInsert = new List<InterfaceMapDetailEntity>();
|
|
||||||
var fixedValues = new List<FixedParameterEntity>();
|
|
||||||
VerifyAndFillEntities(input, entitiesForInsert, entitiesForUpdate, fixedValues);
|
|
||||||
|
|
||||||
using var ts = TransacationHelper.GetReadCommitted();
|
|
||||||
|
|
||||||
// 更新脚本
|
|
||||||
await _interfaceMapRepository.UpdateRangeAsync(input.InParaMapList.Select(x => new InterfaceMapEntity
|
|
||||||
{
|
|
||||||
Id = x.InterfaceMapId,
|
|
||||||
PrefixScript = x.PrefixScript,
|
|
||||||
PostfixScript = x.PostfixScript
|
|
||||||
}).ToList(), x => new
|
|
||||||
{
|
|
||||||
x.PrefixScript,
|
|
||||||
x.PostfixScript
|
|
||||||
});
|
|
||||||
|
|
||||||
// 更新参数映射
|
|
||||||
await _interfaceMapDetailRepository.UpdateRangeAsync(entitiesForUpdate, x => x.MappedParaId);
|
|
||||||
await _interfaceMapDetailRepository.InsertRangeAsync(entitiesForInsert);
|
|
||||||
|
|
||||||
// 维护参数固定值
|
|
||||||
var delList = fixedValues.Where(x => x.Id != 0 && x.FieldValue.IsNullOrEmpty()).ToList();
|
|
||||||
await _fixedParameterRepository.SoftDeleteAsync(x => delList.Select(t => t.Id).Contains(x.Id));
|
|
||||||
fixedValues.RemoveAll(delList);
|
|
||||||
|
|
||||||
var updList = fixedValues.Where(x => x.Id != 0).ToList();
|
|
||||||
await _fixedParameterRepository.UpdateRangeAsync(updList,
|
|
||||||
x => new { x.FieldName, x.FieldValue, x.UpdateTime, x.UpdateUserId });
|
|
||||||
fixedValues.RemoveAll(updList);
|
|
||||||
|
|
||||||
var insertList = fixedValues.Where(x => !x.FieldValue.IsNullOrEmpty()).ToList();
|
|
||||||
await _fixedParameterRepository.InsertRangeAsync(insertList);
|
|
||||||
|
|
||||||
ts.Complete();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 校验必填项,并填充实体
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="input"></param>
|
|
||||||
/// <param name="entitiesForInsert"></param>
|
|
||||||
/// <param name="entitiesForUpdate"></param>
|
|
||||||
/// <param name="fixedParameterEntities"></param>
|
|
||||||
/// <exception cref="BusinessException"></exception>
|
|
||||||
private static void VerifyAndFillEntities(SaveParameterMapsInput input,
|
|
||||||
List<InterfaceMapDetailEntity> entitiesForInsert, List<InterfaceMapDetailEntity> entitiesForUpdate,
|
|
||||||
List<FixedParameterEntity> fixedParameterEntities)
|
|
||||||
{
|
|
||||||
var details = input.InParaMapList.SelectMany(x => x.MapDetails).Concat(input.MapDetails).ToList();
|
|
||||||
|
|
||||||
foreach (var x in details)
|
|
||||||
{
|
|
||||||
if (x.IsRequired)
|
|
||||||
{
|
|
||||||
if (x.MappedParaId == null && x.FixedValue.IsNullOrEmpty())
|
|
||||||
throw new BusinessException(message: $"{x.Alias}必填不能空");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x.InterfaceMapDetailId == null)
|
|
||||||
{
|
|
||||||
entitiesForInsert.Add(new InterfaceMapDetailEntity
|
|
||||||
{
|
|
||||||
InterfaceMapId = x.InterfaceMapId,
|
|
||||||
ParaId = x.ParaId,
|
|
||||||
MappedParaId = x.MappedParaId,
|
|
||||||
IsInPara = x.IsInPara
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
entitiesForUpdate.Add(new InterfaceMapDetailEntity
|
|
||||||
{
|
|
||||||
Id = (int)x.InterfaceMapDetailId,
|
|
||||||
MappedParaId = x.MappedParaId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var item in input.InParaMapList)
|
|
||||||
{
|
|
||||||
fixedParameterEntities.AddRange(item.MapDetails.Select(x => new FixedParameterEntity
|
|
||||||
{
|
|
||||||
Id = x.FixedId,
|
|
||||||
InterfaceId = item.InterfaceId,
|
|
||||||
FieldName = x.Alias,
|
|
||||||
FieldValue = x.FixedValue,
|
|
||||||
FieldPositions = FieldPosition.Body.ToString(),
|
|
||||||
Description = x.Description
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
@ -3,9 +3,9 @@
|
|||||||
public class ContextFeature
|
public class ContextFeature
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 服务商接口默认QPS
|
/// 目标接口QPS
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int ServiceProviderDefaultQps { get; set; }
|
public int TargetInterfaceQps { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否等待
|
/// 是否等待
|
||||||
@ -16,4 +16,9 @@ public class ContextFeature
|
|||||||
/// 自旋周期ms
|
/// 自旋周期ms
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int SpinPeriod { get; set; }
|
public int SpinPeriod { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 账户hash
|
||||||
|
/// </summary>
|
||||||
|
public string AccountHashCode { get; set; } = null!;
|
||||||
}
|
}
|
||||||
@ -8,16 +8,16 @@ public class FixedFieldWithValueDto
|
|||||||
/// 字段所属
|
/// 字段所属
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public FieldOwner FieldOwner { get; set; }
|
public FieldOwner FieldOwner { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 字段名称
|
/// 字段名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string FieldName { get; set; }
|
public string FieldName { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 字段值
|
/// 字段值
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string FieldValue { get; set; }
|
public string FieldValue { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 字段位置
|
/// 字段位置
|
||||||
@ -27,7 +27,7 @@ public class FixedFieldWithValueDto
|
|||||||
///<summary>
|
///<summary>
|
||||||
/// 字段描述
|
/// 字段描述
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Description { get; set; }
|
public string? Description { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum FieldOwner
|
public enum FieldOwner
|
||||||
|
|||||||
@ -26,7 +26,7 @@ public class ForwardCoreContextCache
|
|||||||
public List<ParameterNode> OutParamTreeList { get; set; }
|
public List<ParameterNode> OutParamTreeList { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 目标服务商授权接口
|
/// 目标服务商授权接口(serviceProvider.AuthInterfaceCode存在时,一定不为null)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TargetInterfaceSummary? ServiceProviderAuthInterface { get; set; }
|
public TargetInterfaceSummary? ServiceProviderAuthInterface { get; set; }
|
||||||
|
|
||||||
@ -46,13 +46,13 @@ public class ServiceProviderAuthConfigDto
|
|||||||
///<summary>
|
///<summary>
|
||||||
/// 参数路径
|
/// 参数路径
|
||||||
///</summary>
|
///</summary>
|
||||||
public string ParameterAlias { get; set; }
|
public string? ParameterAlias { get; set; }
|
||||||
|
|
||||||
public string Value1 { get; set; }
|
public string? Value1 { get; set; }
|
||||||
|
|
||||||
public string Value2 { get; set; }
|
public string? Value2 { get; set; }
|
||||||
|
|
||||||
public string Value3 { get; set; }
|
public string? Value3 { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TargetInterfaceSummary
|
public class TargetInterfaceSummary
|
||||||
@ -60,35 +60,35 @@ public class TargetInterfaceSummary
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 目标服务商接口
|
/// 目标服务商接口
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public InterfaceDto TargetInterface { get; set; }
|
public InterfaceDto TargetInterface { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求前置脚本
|
/// 请求前置脚本
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string PrefixScript { get; set; }
|
public string? PrefixScript { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求后置脚本
|
/// 请求后置脚本
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string PostfixScript { get; set; }
|
public string? PostfixScript { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 入参映射列表(树形结构)
|
/// 入参映射列表(树形结构)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<ParameterNode> InParamTreeList { get; set; }
|
public List<ParameterNode> InParamTreeList { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 固定值列表
|
/// 固定值列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<FixedFieldWithValueDto> FixedFieldList { get; set; }
|
public List<FixedFieldWithValueDto> FixedFieldList { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 返回配置列表
|
/// 返回配置列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<InterfaceReturnConfigDto> ReturnConfigList { get; set; }
|
public List<InterfaceReturnConfigDto> ReturnConfigList { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表单字段列表
|
/// 表单字段列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<InterfaceFormFieldDto> FormFieldList { get; set; }
|
public List<InterfaceFormFieldDto> FormFieldList { get; set; } = [];
|
||||||
}
|
}
|
||||||
@ -12,7 +12,7 @@ public class InterfaceDto
|
|||||||
///<summary>
|
///<summary>
|
||||||
/// 接口名
|
/// 接口名
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 服务商id
|
/// 服务商id
|
||||||
@ -22,12 +22,12 @@ public class InterfaceDto
|
|||||||
///<summary>
|
///<summary>
|
||||||
/// 接口代码
|
/// 接口代码
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Code { get; set; }
|
public string Code { get; set; } = null!;
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 请求地址
|
/// 请求地址
|
||||||
///</summary>
|
///</summary>
|
||||||
public string RequestAddress { get; set; }
|
public string RequestAddress { get; set; } = null!;
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 请求协议
|
/// 请求协议
|
||||||
@ -37,7 +37,7 @@ public class InterfaceDto
|
|||||||
///<summary>
|
///<summary>
|
||||||
/// 请求方式
|
/// 请求方式
|
||||||
///</summary>
|
///</summary>
|
||||||
public string RequestMethod { get; set; }
|
public string RequestMethod { get; set; } = null!;
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 内容类型
|
/// 内容类型
|
||||||
@ -67,7 +67,7 @@ public class InterfaceDto
|
|||||||
///<summary>
|
///<summary>
|
||||||
/// 流程代码
|
/// 流程代码
|
||||||
///</summary>
|
///</summary>
|
||||||
public string FlowCode { get; set; }
|
public string? FlowCode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否禁用授权
|
/// 是否禁用授权
|
||||||
|
|||||||
@ -5,15 +5,15 @@ public class InterfaceFormFieldDto
|
|||||||
///<summary>
|
///<summary>
|
||||||
/// 参数名
|
/// 参数名
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; } = null!;
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 参数值
|
/// 参数值
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Value { get; set; }
|
public string Value { get; set; } = null!;
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 参数描述
|
/// 参数描述
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Description { get; set; }
|
public string? Description { get; set; }
|
||||||
}
|
}
|
||||||
@ -20,11 +20,11 @@ namespace InterfaceForward.Domain.Shared.Dtos
|
|||||||
///<summary>
|
///<summary>
|
||||||
/// 路径
|
/// 路径
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Path { get; set; }
|
public string Path { get; set; } = null!;
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 值
|
/// 值
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Value { get; set; }
|
public string Value { get; set; } = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
public class RequestLogDto(Guid requestId)
|
public class RequestLogDto(Guid requestId)
|
||||||
{
|
{
|
||||||
public const string Init = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 请求Id
|
/// 请求Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -22,7 +20,7 @@ public class RequestLogDto(Guid requestId)
|
|||||||
///<summary>
|
///<summary>
|
||||||
/// 应用/服务商名称
|
/// 应用/服务商名称
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 发起请求IP
|
/// 发起请求IP
|
||||||
@ -32,27 +30,27 @@ public class RequestLogDto(Guid requestId)
|
|||||||
///<summary>
|
///<summary>
|
||||||
/// 接口代码
|
/// 接口代码
|
||||||
///</summary>
|
///</summary>
|
||||||
public string InterfaceCode { get; set; }
|
public string? InterfaceCode { get; set; }
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 请求接口名称
|
/// 请求接口名称
|
||||||
///</summary>
|
///</summary>
|
||||||
public string InterfaceName { get; set; }
|
public string? InterfaceName { get; set; }
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 请求地址
|
/// 请求地址
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Address { get; set; }
|
public string? Address { get; set; }
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 请求头
|
/// 请求头
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Headers { get; set; }
|
public string? Headers { get; set; }
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 请求体
|
/// 请求体
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Content { get; set; }
|
public string? Content { get; set; }
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 响应耗时ms
|
/// 响应耗时ms
|
||||||
@ -62,7 +60,12 @@ public class RequestLogDto(Guid requestId)
|
|||||||
///<summary>
|
///<summary>
|
||||||
/// 响应结果
|
/// 响应结果
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Response { get; set; } = Init;
|
public string? Response { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// 响应结果映射后
|
||||||
|
///</summary>
|
||||||
|
public string? Response2 { get; set; }
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 是否成功调用
|
/// 是否成功调用
|
||||||
@ -72,5 +75,5 @@ public class RequestLogDto(Guid requestId)
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 异常
|
/// 异常
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Exception { get; set; }
|
public string? Exception { get; set; }
|
||||||
}
|
}
|
||||||
@ -11,6 +11,8 @@ public static class RequestServiceProviderFailedCommon
|
|||||||
public static string VerifyStatusFailedCode = "4003";
|
public static string VerifyStatusFailedCode = "4003";
|
||||||
|
|
||||||
public static string HeaderInsertFailed = "4004";
|
public static string HeaderInsertFailed = "4004";
|
||||||
|
|
||||||
|
public static string QpsOutLimit = "4005";
|
||||||
|
|
||||||
public static bool IsRequestServiceProviderFailed(string code)
|
public static bool IsRequestServiceProviderFailed(string code)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -10,12 +10,12 @@ public class ServiceProviderDto
|
|||||||
///<summary>
|
///<summary>
|
||||||
/// 服务商名称
|
/// 服务商名称
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; } = null!;
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 服务商代码
|
/// 服务商代码
|
||||||
///</summary>
|
///</summary>
|
||||||
public string Code { get; set; }
|
public string Code { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否禁用授权
|
/// 是否禁用授权
|
||||||
@ -25,12 +25,12 @@ public class ServiceProviderDto
|
|||||||
///<summary>
|
///<summary>
|
||||||
/// 流程代码
|
/// 流程代码
|
||||||
///</summary>
|
///</summary>
|
||||||
public string FlowCode { get; set; }
|
public string? FlowCode { get; set; }
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 授权接口Code
|
/// 授权接口Code
|
||||||
///</summary>
|
///</summary>
|
||||||
public string AuthInterfaceCode { get; set; }
|
public string? AuthInterfaceCode { get; set; }
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 请求超时(s)
|
/// 请求超时(s)
|
||||||
|
|||||||
@ -8,6 +8,7 @@ public static class GlobalConst
|
|||||||
public const string DateTime = "$DateTime";
|
public const string DateTime = "$DateTime";
|
||||||
public const string Url = "$Url";
|
public const string Url = "$Url";
|
||||||
public const string Token = "$token";
|
public const string Token = "$token";
|
||||||
|
public const string AccountHashCode = "$hash";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 不执行动态代码
|
/// 不执行动态代码
|
||||||
@ -27,5 +28,7 @@ public static class GlobalConst
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 服务商接口qps redis key前缀
|
/// 服务商接口qps redis key前缀
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string ServiceProviderInterfaceQPSKeyPrefix = "InterfaceQPS";
|
public const string ServiceProviderInterfaceQpsKeyPrefix = "InterfaceQPS";
|
||||||
|
|
||||||
|
public const string InterfaceRelayExchange = "sjzy.interface.relay.direct";
|
||||||
}
|
}
|
||||||
@ -23,7 +23,7 @@
|
|||||||
/// 系统接口Code
|
/// 系统接口Code
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnName = "UpStreamCode")]
|
[SugarColumn(ColumnName = "UpStreamCode")]
|
||||||
public string UpStreamCode { get; set; }
|
public string UpStreamCode { get; set; } = null!;
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 服务商ID
|
/// 服务商ID
|
||||||
@ -41,24 +41,30 @@
|
|||||||
/// 服务商接口Code
|
/// 服务商接口Code
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnName = "DownStreamCode")]
|
[SugarColumn(ColumnName = "DownStreamCode")]
|
||||||
public string DownStreamCode { get; set; }
|
public string DownStreamCode { get; set; } = null!;
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 当前线上版本
|
/// 当前线上版本
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnName = "PublishedVersion")]
|
[SugarColumn(ColumnName = "PublishedVersion")]
|
||||||
public string PublishedVersion { get; set; }
|
public string? PublishedVersion { get; set; }
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 前置脚本
|
/// 前置脚本
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnName = "PrefixScript")]
|
[SugarColumn(ColumnName = "PrefixScript")]
|
||||||
public string PrefixScript { get; set; }
|
public string? PrefixScript { get; set; }
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 后置脚本
|
/// 后置脚本
|
||||||
///</summary>
|
///</summary>
|
||||||
[SugarColumn(ColumnName = "PostfixScript")]
|
[SugarColumn(ColumnName = "PostfixScript")]
|
||||||
public string PostfixScript { get; set; }
|
public string? PostfixScript { get; set; }
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// 支持不对等映射(自由映射)
|
||||||
|
///</summary>
|
||||||
|
[SugarColumn(ColumnName = "FreeMap")]
|
||||||
|
public bool FreeMap { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11,9 +11,9 @@ namespace InterfaceForward.Repositories.Interface.Services
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取接口映射列表
|
/// 获取接口映射列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="upstreamId">系统接口id</param>
|
/// <param name="inputVo"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<List<InterfaceMapOutputValueObject>> GetMapListByIdAsync(int upstreamId);
|
Task<PagedListDto<InterfaceMapOutputValueObject>> GetMapListAsync(GetMapListInputVO inputVo);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取参数映射列表
|
/// 获取参数映射列表
|
||||||
|
|||||||
@ -13,9 +13,9 @@ public class InterfaceMapRepository : BasicRepository<InterfaceMapEntity>, IScop
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取映射列表
|
/// 获取映射列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="upstreamId">上游接口id</param>
|
/// <param name="inputVo"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<List<InterfaceMapOutputValueObject>> GetMapListByIdAsync(int upstreamId)
|
public async Task<PagedListDto<InterfaceMapOutputValueObject>> GetMapListAsync(GetMapListInputVO inputVo)
|
||||||
{
|
{
|
||||||
// 上下游一对多关系
|
// 上下游一对多关系
|
||||||
var query = Context.Queryable<InterfaceMapEntity>()
|
var query = Context.Queryable<InterfaceMapEntity>()
|
||||||
@ -23,8 +23,10 @@ public class InterfaceMapRepository : BasicRepository<InterfaceMapEntity>, IScop
|
|||||||
.LeftJoin<InterfaceEntity>((a, b, c) => a.DownStreamId == c.Id) // 服务商接口 N
|
.LeftJoin<InterfaceEntity>((a, b, c) => a.DownStreamId == c.Id) // 服务商接口 N
|
||||||
.LeftJoin<ServiceProviderEntity>((a, b, c, d) => c.ServiceProviderId == d.Id)
|
.LeftJoin<ServiceProviderEntity>((a, b, c, d) => c.ServiceProviderId == d.Id)
|
||||||
.Filter(null, true) // 禁用全局过滤器
|
.Filter(null, true) // 禁用全局过滤器
|
||||||
.Where(a => a.UpStreamId == upstreamId)
|
.Where(a => a.UpStreamId == inputVo.InterfaceId)
|
||||||
.Where((a, b, c, d) => !a.IsDeleted && !b.IsDeleted && !c.IsDeleted && !d.IsDeleted)
|
.Where((a, b, c, d) => !a.IsDeleted && !b.IsDeleted && !c.IsDeleted && !d.IsDeleted)
|
||||||
|
.WhereIF(!inputVo.NameOrCode.IsNullOrWhiteSpace(),
|
||||||
|
(a, b, c, d) => d.Code.Contains(inputVo.NameOrCode) || d.Name.Contains(inputVo.NameOrCode))
|
||||||
.GroupBy(a => new { a.UpStreamId, a.ServiceProviderId })
|
.GroupBy(a => new { a.UpStreamId, a.ServiceProviderId })
|
||||||
.OrderBy(a => a.UpdateTime, OrderByType.Desc);
|
.OrderBy(a => a.UpdateTime, OrderByType.Desc);
|
||||||
|
|
||||||
@ -40,24 +42,25 @@ public class InterfaceMapRepository : BasicRepository<InterfaceMapEntity>, IScop
|
|||||||
InterfaceCode = SqlFunc.MappingColumn<string>("GROUP_CONCAT(c.Code ORDER BY a.id SEPARATOR ',')"),
|
InterfaceCode = SqlFunc.MappingColumn<string>("GROUP_CONCAT(c.Code ORDER BY a.id SEPARATOR ',')"),
|
||||||
UpdateTime = a.UpdateTime,
|
UpdateTime = a.UpdateTime,
|
||||||
PublishedVersion = a.PublishedVersion
|
PublishedVersion = a.PublishedVersion
|
||||||
}).ToListAsync();
|
}).ToPagedListAsync(inputVo.PageIndex, inputVo.PageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<List<InterfaceParameterMapOutputValueObject>> GetParameterMapListAsync(List<int> mapIds, bool isInParaMap)
|
public async Task<List<InterfaceParameterMapOutputValueObject>> GetParameterMapListAsync(List<int> mapIds,
|
||||||
|
bool isInParaMap)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
select `a`.`Id` AS `InterfaceMapDetailId` , `t`.`Id` AS `InterfaceMapId` , `b`.`Id` AS `Id`, b.InterfaceId, `b`.`Alias` AS `Alias` , `b`.`CnName` AS `CnName` , `e`.`Name` AS `Type` , `b`.`IsInPara` AS `IsInPara`, `b`.`IsRequired` AS `IsRequired` , `b`.`Description` AS `Description` , `d`.`Id` AS `FixedId` , `d`.`FieldValue` AS `FixedValue` , `a`.`MappedParaId` AS `MapId` , `c`.`Alias` AS `MapAlias`, `c`.`CnName` AS `MapCnName` , `c`.`Description` AS `MapDescription`
|
select `a`.`Id` AS `InterfaceMapDetailId` , `t`.`Id` AS `InterfaceMapId` , `b`.`Id` AS `Id`, b.InterfaceId, `b`.`Alias` AS `Alias` , `b`.`CnName` AS `CnName` , `e`.`Name` AS `Type` , `b`.`IsInPara` AS `IsInPara`, `b`.`IsRequired` AS `IsRequired` , `b`.`Description` AS `Description` , `d`.`Id` AS `FixedId` , `d`.`FieldValue` AS `FixedValue` , `a`.`MappedParaId` AS `MapId` , `c`.`Alias` AS `MapAlias`, `c`.`CnName` AS `MapCnName` , `c`.`Description` AS `MapDescription`
|
||||||
from t_interface_map t
|
from t_interface_map t
|
||||||
LEFT JOIN `t_parameter` `b` ON ( `t`.`UpStreamId` = `b`.`InterfaceId` ) and b.IsInPara = false AND ( `b`.`Type` <> 12 ) AND ( `b`.`Type` <> 10 ) and `b`.`IsDeleted` = 0
|
LEFT JOIN `t_parameter` `b` ON ( `t`.`UpStreamId` = `b`.`InterfaceId` ) and b.IsInPara = false AND ( `b`.`Type` <> 12 ) AND ( `b`.`Type` <> 10 ) 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
|
||||||
Left JOIN `t_parameter` `c` ON ( `a`.`MappedParaId` = `c`.`Id` ) AND ( `c`.`IsDeleted` = 0 )
|
Left JOIN `t_parameter` `c` ON ( `a`.`MappedParaId` = `c`.`Id` ) AND ( `c`.`IsDeleted` = 0 )
|
||||||
Left JOIN `t_parameter_fixed` `d` ON d.InterfaceId = b.InterfaceId and `b`.`Alias` = `d`.`FieldName` AND `d`.`FieldPositions` like concat('%', CAST(N'Body' AS CHAR),'%') AND d.IsDeleted = 0
|
Left JOIN `t_parameter_fixed` `d` ON d.InterfaceId = b.InterfaceId and `b`.`Alias` = `d`.`FieldName` AND `d`.`FieldPositions` like concat('%', CAST(N'Body' AS CHAR),'%') AND d.IsDeleted = 0
|
||||||
Left JOIN `t_dictionary` `e` ON (( `b`.`Type` = `e`.`Id` ) AND ( `e`.`Type` = N'ParameterType' ))
|
Left JOIN `t_dictionary` `e` ON (( `b`.`Type` = `e`.`Id` ) AND ( `e`.`Type` = N'ParameterType' ))
|
||||||
WHERE (( `t`.`UpStreamId` = 30 ) AND ( `t`.`ServiceProviderId` = 137 )) and t.IsDeleted = 0 and t.DownStreamId = 509
|
WHERE (( `t`.`UpStreamId` = 30 ) AND ( `t`.`ServiceProviderId` = 137 )) and t.IsDeleted = 0 and t.DownStreamId = 509
|
||||||
ORDER BY `b`.`Sort` ASC
|
ORDER BY `b`.`Sort` ASC
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var ids = mapIds.JoinAsString(",");
|
var ids = mapIds.JoinAsString(",");
|
||||||
// 因为入参映射有多个 出参映射只有一个(挂在第一个映射)
|
// 因为入参映射有多个 出参映射只有一个(挂在第一个映射)
|
||||||
return await Context.SqlQueryable<InterfaceParameterMapOutputValueObject>(@$"
|
return await Context.SqlQueryable<InterfaceParameterMapOutputValueObject>(@$"
|
||||||
|
|||||||
@ -5,12 +5,12 @@ public class EditMapDetailsOutput
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 入参映射
|
/// 入参映射
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<MapSummary> InParaMapList { get; set; } = new();
|
public List<MapSummary> InParaMapList { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 出参映射明细
|
/// 出参映射明细
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<InterfaceParameterMapOutputValueObject> MapDetails { get; set; } = new();
|
public List<InterfaceParameterMapOutputValueObject> MapDetails { get; set; } = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MapSummary
|
public class MapSummary
|
||||||
@ -24,21 +24,26 @@ public class MapSummary
|
|||||||
/// 接口id
|
/// 接口id
|
||||||
///</summary>
|
///</summary>
|
||||||
public int InterfaceId { get; set; }
|
public int InterfaceId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 接口Name
|
/// 接口Name
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string InterfaceName { get; set; }
|
public string InterfaceName { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 前置脚本
|
/// 前置脚本
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string PrefixScript { get; set; }
|
public string? PrefixScript { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 后置脚本
|
/// 后置脚本
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string PostfixScript { get; set; }
|
public string? PostfixScript { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自由映射(不对等映射)
|
||||||
|
/// </summary>
|
||||||
|
public bool FreeMap { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 映射明细
|
/// 映射明细
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using InterfaceForward.Domain.Shared.Dtos;
|
||||||
|
|
||||||
|
namespace InterfaceForward.Repositories.Interface.ValueObjects;
|
||||||
|
|
||||||
|
public class GetMapListInputVO : PagedQueryInput
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public int InterfaceId { get; set; }
|
||||||
|
|
||||||
|
public string NameOrCode { get; set; }
|
||||||
|
}
|
||||||
@ -5,15 +5,15 @@ public class InterfaceMapPublishedDifferenceVO
|
|||||||
///<summary>
|
///<summary>
|
||||||
/// 线上的缓存
|
/// 线上的缓存
|
||||||
///</summary>
|
///</summary>
|
||||||
public string PublishedContent { get; set; }
|
public string? PublishedContent { get; set; }
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
/// 数据库的内容
|
/// 数据库的内容
|
||||||
///</summary>
|
///</summary>
|
||||||
public string DbContent { get; set; }
|
public string? DbContent { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 与线上版本的差异内容
|
/// 与线上版本的差异内容
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string DifferenceContent { get; set; }
|
public string? DifferenceContent { get; set; }
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ ORDER BY `b`.`Sort` ASC").ToListAsync();
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="interfaceCode"></param>
|
/// <param name="interfaceCode"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<InterfaceDto> GetInterfaceByCodeAsync(string interfaceCode)
|
public async Task<InterfaceDto?> GetInterfaceByCodeAsync(string interfaceCode)
|
||||||
{
|
{
|
||||||
var res = await GetInterfaceByCodesAsync(interfaceCode);
|
var res = await GetInterfaceByCodesAsync(interfaceCode);
|
||||||
return res.FirstOrDefault();
|
return res.FirstOrDefault();
|
||||||
@ -193,22 +193,21 @@ ORDER BY `b`.`Sort` ASC").ToListAsync();
|
|||||||
) // 构建系统返参数树形结构
|
) // 构建系统返参数树形结构
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!serviceProvider.AuthInterfaceCode.IsNullOrEmpty())
|
if (serviceProvider.AuthInterfaceCode != null)
|
||||||
{
|
{
|
||||||
var authInterface = await GetInterfaceByCodeAsync(serviceProvider.AuthInterfaceCode);
|
var authInterface = await GetInterfaceByCodeAsync(serviceProvider.AuthInterfaceCode);
|
||||||
if (authInterface != null)
|
if (authInterface == null)
|
||||||
{
|
{
|
||||||
context.ServiceProviderAuthInterface = new TargetInterfaceSummary
|
throw new BusinessException($"授权接口{serviceProvider.AuthInterfaceCode}不存在");
|
||||||
{
|
|
||||||
TargetInterface = authInterface,
|
|
||||||
PrefixScript = null,
|
|
||||||
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);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user